You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							271 lines
						
					
					
						
							7.1 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							271 lines
						
					
					
						
							7.1 KiB
						
					
					
				| //go:build linux || darwin | |
| // +build linux darwin | |
|  | |
| package command | |
| 
 | |
| import ( | |
| 	"fmt" | |
| 	"math" | |
| 	"os" | |
| 	"os/signal" | |
| 	"strconv" | |
| 	"strings" | |
| 	"syscall" | |
| 	"time" | |
| ) | |
| 
 | |
| type parameter struct { | |
| 	name  string | |
| 	value string | |
| } | |
| 
 | |
| func runFuse(cmd *Command, args []string) bool { | |
| 	rawArgs := strings.Join(args, " ") | |
| 	rawArgsLen := len(rawArgs) | |
| 	option := strings.Builder{} | |
| 	options := []parameter{} | |
| 	masterProcess := true | |
| 	fusermountPath := "" | |
| 
 | |
| 	// first parameter | |
| 	i := 0 | |
| 	for i = 0; i < rawArgsLen && rawArgs[i] != ' '; i++ { | |
| 		option.WriteByte(rawArgs[i]) | |
| 	} | |
| 	options = append(options, parameter{"arg0", option.String()}) | |
| 	option.Reset() | |
| 
 | |
| 	for i++; i < rawArgsLen; i++ { | |
| 
 | |
| 		// space separator check for filled option | |
| 		if rawArgs[i] == ' ' { | |
| 			if option.Len() > 0 { | |
| 				options = append(options, parameter{option.String(), "true"}) | |
| 				option.Reset() | |
| 			} | |
| 
 | |
| 			// dash separator read option until next space | |
| 		} else if rawArgs[i] == '-' { | |
| 			for i++; i < rawArgsLen && rawArgs[i] != ' '; i++ { | |
| 				option.WriteByte(rawArgs[i]) | |
| 			} | |
| 			// ignore "-o" | |
| 			if option.String() != "o" { | |
| 				options = append(options, parameter{option.String(), "true"}) | |
| 			} | |
| 			option.Reset() | |
| 
 | |
| 			// equal separator start option with pending value | |
| 		} else if rawArgs[i] == '=' { | |
| 			name := option.String() | |
| 			option.Reset() | |
| 
 | |
| 			for i++; i < rawArgsLen && rawArgs[i] != ',' && rawArgs[i] != ' '; i++ { | |
| 				// double quote separator read option until next double quote | |
| 				if rawArgs[i] == '"' { | |
| 					for i++; i < rawArgsLen && rawArgs[i] != '"'; i++ { | |
| 						option.WriteByte(rawArgs[i]) | |
| 					} | |
| 
 | |
| 					// single quote separator read option until next single quote | |
| 				} else if rawArgs[i] == '\'' { | |
| 					for i++; i < rawArgsLen && rawArgs[i] != '\''; i++ { | |
| 						option.WriteByte(rawArgs[i]) | |
| 					} | |
| 
 | |
| 					// add chars before comma | |
| 				} else if rawArgs[i] != ' ' { | |
| 					option.WriteByte(rawArgs[i]) | |
| 				} | |
| 			} | |
| 
 | |
| 			options = append(options, parameter{name, option.String()}) | |
| 			option.Reset() | |
| 
 | |
| 			// comma separator just read current option | |
| 		} else if rawArgs[i] == ',' { | |
| 			options = append(options, parameter{option.String(), "true"}) | |
| 			option.Reset() | |
| 
 | |
| 			// what is not a separator fill option buffer | |
| 		} else { | |
| 			option.WriteByte(rawArgs[i]) | |
| 		} | |
| 	} | |
| 
 | |
| 	// get residual option data | |
| 	if option.Len() > 0 { | |
| 		// add value to pending option | |
| 		options = append(options, parameter{option.String(), "true"}) | |
| 		option.Reset() | |
| 	} | |
| 
 | |
| 	// scan each parameter | |
| 	for i := 0; i < len(options); i++ { | |
| 		parameter := options[i] | |
| 
 | |
| 		switch parameter.name { | |
| 		case "child": | |
| 			masterProcess = false | |
| 			if parsed, err := strconv.ParseInt(parameter.value, 10, 64); err == nil { | |
| 				if parsed > math.MaxInt || parsed <= 0 { | |
| 					panic(fmt.Errorf("parent PID %d is invalid", parsed)) | |
| 				} | |
| 				mountOptions.fuseCommandPid = int(parsed) | |
| 			} else { | |
| 				panic(fmt.Errorf("parent PID %s is invalid: %w", parameter.value, err)) | |
| 			} | |
| 		case "arg0": | |
| 			mountOptions.dir = ¶meter.value | |
| 		case "filer": | |
| 			mountOptions.filer = ¶meter.value | |
| 		case "filer.path": | |
| 			mountOptions.filerMountRootPath = ¶meter.value | |
| 		case "dirAutoCreate": | |
| 			if parsed, err := strconv.ParseBool(parameter.value); err == nil { | |
| 				mountOptions.dirAutoCreate = &parsed | |
| 			} else { | |
| 				panic(fmt.Errorf("dirAutoCreate: %s", err)) | |
| 			} | |
| 		case "collection": | |
| 			mountOptions.collection = ¶meter.value | |
| 		case "replication": | |
| 			mountOptions.replication = ¶meter.value | |
| 		case "disk": | |
| 			mountOptions.diskType = ¶meter.value | |
| 		case "ttl": | |
| 			if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err == nil { | |
| 				intValue := int(parsed) | |
| 				mountOptions.ttlSec = &intValue | |
| 			} else { | |
| 				panic(fmt.Errorf("ttl: %s", err)) | |
| 			} | |
| 		case "chunkSizeLimitMB": | |
| 			if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err == nil { | |
| 				intValue := int(parsed) | |
| 				mountOptions.chunkSizeLimitMB = &intValue | |
| 			} else { | |
| 				panic(fmt.Errorf("chunkSizeLimitMB: %s", err)) | |
| 			} | |
| 		case "concurrentWriters": | |
| 			i++ | |
| 			if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err == nil { | |
| 				intValue := int(parsed) | |
| 				mountOptions.concurrentWriters = &intValue | |
| 			} else { | |
| 				panic(fmt.Errorf("concurrentWriters: %s", err)) | |
| 			} | |
| 		case "cacheDir": | |
| 			mountOptions.cacheDirForRead = ¶meter.value | |
| 		case "cacheCapacityMB": | |
| 			if parsed, err := strconv.ParseInt(parameter.value, 0, 64); err == nil { | |
| 				mountOptions.cacheSizeMBForRead = &parsed | |
| 			} else { | |
| 				panic(fmt.Errorf("cacheCapacityMB: %s", err)) | |
| 			} | |
| 		case "cacheDirWrite": | |
| 			mountOptions.cacheDirForWrite = ¶meter.value | |
| 		case "dataCenter": | |
| 			mountOptions.dataCenter = ¶meter.value | |
| 		case "allowOthers": | |
| 			if parsed, err := strconv.ParseBool(parameter.value); err == nil { | |
| 				mountOptions.allowOthers = &parsed | |
| 			} else { | |
| 				panic(fmt.Errorf("allowOthers: %s", err)) | |
| 			} | |
| 		case "umask": | |
| 			mountOptions.umaskString = ¶meter.value | |
| 		case "nonempty": | |
| 			if parsed, err := strconv.ParseBool(parameter.value); err == nil { | |
| 				mountOptions.nonempty = &parsed | |
| 			} else { | |
| 				panic(fmt.Errorf("nonempty: %s", err)) | |
| 			} | |
| 		case "volumeServerAccess": | |
| 			mountOptions.volumeServerAccess = ¶meter.value | |
| 		case "map.uid": | |
| 			mountOptions.uidMap = ¶meter.value | |
| 		case "map.gid": | |
| 			mountOptions.gidMap = ¶meter.value | |
| 		case "readOnly": | |
| 			if parsed, err := strconv.ParseBool(parameter.value); err == nil { | |
| 				mountOptions.readOnly = &parsed | |
| 			} else { | |
| 				panic(fmt.Errorf("readOnly: %s", err)) | |
| 			} | |
| 		case "disableXAttr": | |
| 			if parsed, err := strconv.ParseBool(parameter.value); err == nil { | |
| 
 | |
| 				mountOptions.disableXAttr = &parsed | |
| 			} else { | |
| 				panic(fmt.Errorf("disableXAttr: %s", err)) | |
| 			} | |
| 		case "cpuprofile": | |
| 			mountCpuProfile = ¶meter.value | |
| 		case "memprofile": | |
| 			mountMemProfile = ¶meter.value | |
| 		case "readRetryTime": | |
| 			if parsed, err := time.ParseDuration(parameter.value); err == nil { | |
| 				mountReadRetryTime = &parsed | |
| 			} else { | |
| 				panic(fmt.Errorf("readRetryTime: %s", err)) | |
| 			} | |
| 		case "fusermount.path": | |
| 			fusermountPath = parameter.value | |
| 		default: | |
| 			t := parameter.name | |
| 			if parameter.value != "true" { | |
| 				t = fmt.Sprintf("%s=%s", parameter.name, parameter.value) | |
| 			} | |
| 			mountOptions.extraOptions = append(mountOptions.extraOptions, t) | |
| 		} | |
| 	} | |
| 
 | |
| 	// the master start the child, release it then finish himself | |
| 	if masterProcess { | |
| 		arg0, err := os.Executable() | |
| 		if err != nil { | |
| 			panic(err) | |
| 		} | |
| 
 | |
| 		// pass our PID to the child process | |
| 		pid := os.Getpid() | |
| 		argv := append(os.Args, "-o", "child="+strconv.Itoa(pid)) | |
| 
 | |
| 		c := make(chan os.Signal, 1) | |
| 		signal.Notify(c, syscall.SIGTERM) | |
| 
 | |
| 		attr := os.ProcAttr{} | |
| 		attr.Env = os.Environ() | |
| 
 | |
| 		child, err := os.StartProcess(arg0, argv, &attr) | |
| 
 | |
| 		if err != nil { | |
| 			panic(fmt.Errorf("master process can not start child process: %s", err)) | |
| 		} | |
| 
 | |
| 		err = child.Release() | |
| 
 | |
| 		if err != nil { | |
| 			panic(fmt.Errorf("master process can not release child process: %s", err)) | |
| 		} | |
| 
 | |
| 		select { | |
| 		case <-c: | |
| 			return true | |
| 		} | |
| 	} | |
| 
 | |
| 	if fusermountPath != "" { | |
| 		if err := os.Setenv("PATH", fusermountPath); err != nil { | |
| 			panic(fmt.Errorf("setenv: %s", err)) | |
| 		} | |
| 	} else if os.Getenv("PATH") == "" { | |
| 		if err := os.Setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin"); err != nil { | |
| 			panic(fmt.Errorf("setenv: %s", err)) | |
| 		} | |
| 	} | |
| 
 | |
| 	// just call "weed mount" command | |
| 	return runMount(cmdMount, []string{}) | |
| }
 |