mount_freebsd.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. package fuse
  2. import (
  3. "fmt"
  4. "log"
  5. "os"
  6. "os/exec"
  7. "strings"
  8. "sync"
  9. "syscall"
  10. )
  11. func handleMountFusefsStderr(errCh chan<- error) func(line string) (ignore bool) {
  12. return func(line string) (ignore bool) {
  13. const (
  14. noMountpointPrefix = `mount_fusefs: `
  15. noMountpointSuffix = `: No such file or directory`
  16. )
  17. if strings.HasPrefix(line, noMountpointPrefix) && strings.HasSuffix(line, noMountpointSuffix) {
  18. // re-extract it from the error message in case some layer
  19. // changed the path
  20. mountpoint := line[len(noMountpointPrefix) : len(line)-len(noMountpointSuffix)]
  21. err := &MountpointDoesNotExistError{
  22. Path: mountpoint,
  23. }
  24. select {
  25. case errCh <- err:
  26. return true
  27. default:
  28. // not the first error; fall back to logging it
  29. return false
  30. }
  31. }
  32. return false
  33. }
  34. }
  35. // isBoringMountFusefsError returns whether the Wait error is
  36. // uninteresting; exit status 1 is.
  37. func isBoringMountFusefsError(err error) bool {
  38. if err, ok := err.(*exec.ExitError); ok && err.Exited() {
  39. if status, ok := err.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 1 {
  40. return true
  41. }
  42. }
  43. return false
  44. }
  45. func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
  46. for k, v := range conf.options {
  47. if strings.Contains(k, ",") || strings.Contains(v, ",") {
  48. // Silly limitation but the mount helper does not
  49. // understand any escaping. See TestMountOptionCommaError.
  50. return nil, fmt.Errorf("mount options cannot contain commas on FreeBSD: %q=%q", k, v)
  51. }
  52. }
  53. f, err := os.OpenFile("/dev/fuse", os.O_RDWR, 0000)
  54. if err != nil {
  55. *errp = err
  56. return nil, err
  57. }
  58. cmd := exec.Command(
  59. "/sbin/mount_fusefs",
  60. "--safe",
  61. "-o", conf.getOptions(),
  62. "3",
  63. dir,
  64. )
  65. cmd.ExtraFiles = []*os.File{f}
  66. stdout, err := cmd.StdoutPipe()
  67. if err != nil {
  68. return nil, fmt.Errorf("setting up mount_fusefs stderr: %v", err)
  69. }
  70. stderr, err := cmd.StderrPipe()
  71. if err != nil {
  72. return nil, fmt.Errorf("setting up mount_fusefs stderr: %v", err)
  73. }
  74. if err := cmd.Start(); err != nil {
  75. return nil, fmt.Errorf("mount_fusefs: %v", err)
  76. }
  77. helperErrCh := make(chan error, 1)
  78. var wg sync.WaitGroup
  79. wg.Add(2)
  80. go lineLogger(&wg, "mount helper output", neverIgnoreLine, stdout)
  81. go lineLogger(&wg, "mount helper error", handleMountFusefsStderr(helperErrCh), stderr)
  82. wg.Wait()
  83. if err := cmd.Wait(); err != nil {
  84. // see if we have a better error to report
  85. select {
  86. case helperErr := <-helperErrCh:
  87. // log the Wait error if it's not what we expected
  88. if !isBoringMountFusefsError(err) {
  89. log.Printf("mount helper failed: %v", err)
  90. }
  91. // and now return what we grabbed from stderr as the real
  92. // error
  93. return nil, helperErr
  94. default:
  95. // nope, fall back to generic message
  96. }
  97. return nil, fmt.Errorf("mount_fusefs: %v", err)
  98. }
  99. close(ready)
  100. return f, nil
  101. }