112 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			112 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								package fuse
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"fmt"
							 | 
						||
| 
								 | 
							
									"log"
							 | 
						||
| 
								 | 
							
									"os"
							 | 
						||
| 
								 | 
							
									"os/exec"
							 | 
						||
| 
								 | 
							
									"strings"
							 | 
						||
| 
								 | 
							
									"sync"
							 | 
						||
| 
								 | 
							
									"syscall"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func handleMountFusefsStderr(errCh chan<- error) func(line string) (ignore bool) {
							 | 
						||
| 
								 | 
							
									return func(line string) (ignore bool) {
							 | 
						||
| 
								 | 
							
										const (
							 | 
						||
| 
								 | 
							
											noMountpointPrefix = `mount_fusefs: `
							 | 
						||
| 
								 | 
							
											noMountpointSuffix = `: No such file or directory`
							 | 
						||
| 
								 | 
							
										)
							 | 
						||
| 
								 | 
							
										if strings.HasPrefix(line, noMountpointPrefix) && strings.HasSuffix(line, noMountpointSuffix) {
							 | 
						||
| 
								 | 
							
											// re-extract it from the error message in case some layer
							 | 
						||
| 
								 | 
							
											// changed the path
							 | 
						||
| 
								 | 
							
											mountpoint := line[len(noMountpointPrefix) : len(line)-len(noMountpointSuffix)]
							 | 
						||
| 
								 | 
							
											err := &MountpointDoesNotExistError{
							 | 
						||
| 
								 | 
							
												Path: mountpoint,
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											select {
							 | 
						||
| 
								 | 
							
											case errCh <- err:
							 | 
						||
| 
								 | 
							
												return true
							 | 
						||
| 
								 | 
							
											default:
							 | 
						||
| 
								 | 
							
												// not the first error; fall back to logging it
							 | 
						||
| 
								 | 
							
												return false
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return false
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// isBoringMountFusefsError returns whether the Wait error is
							 | 
						||
| 
								 | 
							
								// uninteresting; exit status 1 is.
							 | 
						||
| 
								 | 
							
								func isBoringMountFusefsError(err error) bool {
							 | 
						||
| 
								 | 
							
									if err, ok := err.(*exec.ExitError); ok && err.Exited() {
							 | 
						||
| 
								 | 
							
										if status, ok := err.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 1 {
							 | 
						||
| 
								 | 
							
											return true
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return false
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (*os.File, error) {
							 | 
						||
| 
								 | 
							
									for k, v := range conf.options {
							 | 
						||
| 
								 | 
							
										if strings.Contains(k, ",") || strings.Contains(v, ",") {
							 | 
						||
| 
								 | 
							
											// Silly limitation but the mount helper does not
							 | 
						||
| 
								 | 
							
											// understand any escaping. See TestMountOptionCommaError.
							 | 
						||
| 
								 | 
							
											return nil, fmt.Errorf("mount options cannot contain commas on FreeBSD: %q=%q", k, v)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									f, err := os.OpenFile("/dev/fuse", os.O_RDWR, 0000)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										*errp = err
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									cmd := exec.Command(
							 | 
						||
| 
								 | 
							
										"/sbin/mount_fusefs",
							 | 
						||
| 
								 | 
							
										"--safe",
							 | 
						||
| 
								 | 
							
										"-o", conf.getOptions(),
							 | 
						||
| 
								 | 
							
										"3",
							 | 
						||
| 
								 | 
							
										dir,
							 | 
						||
| 
								 | 
							
									)
							 | 
						||
| 
								 | 
							
									cmd.ExtraFiles = []*os.File{f}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									stdout, err := cmd.StdoutPipe()
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("setting up mount_fusefs stderr: %v", err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									stderr, err := cmd.StderrPipe()
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("setting up mount_fusefs stderr: %v", err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if err := cmd.Start(); err != nil {
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("mount_fusefs: %v", err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									helperErrCh := make(chan error, 1)
							 | 
						||
| 
								 | 
							
									var wg sync.WaitGroup
							 | 
						||
| 
								 | 
							
									wg.Add(2)
							 | 
						||
| 
								 | 
							
									go lineLogger(&wg, "mount helper output", neverIgnoreLine, stdout)
							 | 
						||
| 
								 | 
							
									go lineLogger(&wg, "mount helper error", handleMountFusefsStderr(helperErrCh), stderr)
							 | 
						||
| 
								 | 
							
									wg.Wait()
							 | 
						||
| 
								 | 
							
									if err := cmd.Wait(); err != nil {
							 | 
						||
| 
								 | 
							
										// see if we have a better error to report
							 | 
						||
| 
								 | 
							
										select {
							 | 
						||
| 
								 | 
							
										case helperErr := <-helperErrCh:
							 | 
						||
| 
								 | 
							
											// log the Wait error if it's not what we expected
							 | 
						||
| 
								 | 
							
											if !isBoringMountFusefsError(err) {
							 | 
						||
| 
								 | 
							
												log.Printf("mount helper failed: %v", err)
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											// and now return what we grabbed from stderr as the real
							 | 
						||
| 
								 | 
							
											// error
							 | 
						||
| 
								 | 
							
											return nil, helperErr
							 | 
						||
| 
								 | 
							
										default:
							 | 
						||
| 
								 | 
							
											// nope, fall back to generic message
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("mount_fusefs: %v", err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									close(ready)
							 | 
						||
| 
								 | 
							
									return f, nil
							 | 
						||
| 
								 | 
							
								}
							 |