fuse_std.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. //go:build linux || darwin
  2. // +build linux darwin
  3. package command
  4. import (
  5. "fmt"
  6. "math"
  7. "os"
  8. "os/signal"
  9. "strconv"
  10. "strings"
  11. "syscall"
  12. "time"
  13. )
  14. type parameter struct {
  15. name string
  16. value string
  17. }
  18. func runFuse(cmd *Command, args []string) bool {
  19. rawArgs := strings.Join(args, " ")
  20. rawArgsLen := len(rawArgs)
  21. option := strings.Builder{}
  22. options := []parameter{}
  23. masterProcess := true
  24. fusermountPath := ""
  25. // first parameter
  26. i := 0
  27. for i = 0; i < rawArgsLen && rawArgs[i] != ' '; i++ {
  28. option.WriteByte(rawArgs[i])
  29. }
  30. options = append(options, parameter{"arg0", option.String()})
  31. option.Reset()
  32. for i++; i < rawArgsLen; i++ {
  33. // space separator check for filled option
  34. if rawArgs[i] == ' ' {
  35. if option.Len() > 0 {
  36. options = append(options, parameter{option.String(), "true"})
  37. option.Reset()
  38. }
  39. // dash separator read option until next space
  40. } else if rawArgs[i] == '-' {
  41. for i++; i < rawArgsLen && rawArgs[i] != ' '; i++ {
  42. option.WriteByte(rawArgs[i])
  43. }
  44. // ignore "-o"
  45. if option.String() != "o" {
  46. options = append(options, parameter{option.String(), "true"})
  47. }
  48. option.Reset()
  49. // equal separator start option with pending value
  50. } else if rawArgs[i] == '=' {
  51. name := option.String()
  52. option.Reset()
  53. for i++; i < rawArgsLen && rawArgs[i] != ',' && rawArgs[i] != ' '; i++ {
  54. // double quote separator read option until next double quote
  55. if rawArgs[i] == '"' {
  56. for i++; i < rawArgsLen && rawArgs[i] != '"'; i++ {
  57. option.WriteByte(rawArgs[i])
  58. }
  59. // single quote separator read option until next single quote
  60. } else if rawArgs[i] == '\'' {
  61. for i++; i < rawArgsLen && rawArgs[i] != '\''; i++ {
  62. option.WriteByte(rawArgs[i])
  63. }
  64. // add chars before comma
  65. } else if rawArgs[i] != ' ' {
  66. option.WriteByte(rawArgs[i])
  67. }
  68. }
  69. options = append(options, parameter{name, option.String()})
  70. option.Reset()
  71. // comma separator just read current option
  72. } else if rawArgs[i] == ',' {
  73. options = append(options, parameter{option.String(), "true"})
  74. option.Reset()
  75. // what is not a separator fill option buffer
  76. } else {
  77. option.WriteByte(rawArgs[i])
  78. }
  79. }
  80. // get residual option data
  81. if option.Len() > 0 {
  82. // add value to pending option
  83. options = append(options, parameter{option.String(), "true"})
  84. option.Reset()
  85. }
  86. // scan each parameter
  87. for i := 0; i < len(options); i++ {
  88. parameter := options[i]
  89. switch parameter.name {
  90. case "child":
  91. masterProcess = false
  92. if parsed, err := strconv.ParseInt(parameter.value, 10, 64); err == nil {
  93. if parsed > math.MaxInt || parsed <= 0 {
  94. panic(fmt.Errorf("parent PID %d is invalid", parsed))
  95. }
  96. mountOptions.fuseCommandPid = int(parsed)
  97. } else {
  98. panic(fmt.Errorf("parent PID %s is invalid: %w", parameter.value, err))
  99. }
  100. case "arg0":
  101. mountOptions.dir = &parameter.value
  102. case "filer":
  103. mountOptions.filer = &parameter.value
  104. case "filer.path":
  105. mountOptions.filerMountRootPath = &parameter.value
  106. case "dirAutoCreate":
  107. if parsed, err := strconv.ParseBool(parameter.value); err == nil {
  108. mountOptions.dirAutoCreate = &parsed
  109. } else {
  110. panic(fmt.Errorf("dirAutoCreate: %s", err))
  111. }
  112. case "collection":
  113. mountOptions.collection = &parameter.value
  114. case "replication":
  115. mountOptions.replication = &parameter.value
  116. case "disk":
  117. mountOptions.diskType = &parameter.value
  118. case "ttl":
  119. if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err == nil {
  120. intValue := int(parsed)
  121. mountOptions.ttlSec = &intValue
  122. } else {
  123. panic(fmt.Errorf("ttl: %s", err))
  124. }
  125. case "chunkSizeLimitMB":
  126. if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err == nil {
  127. intValue := int(parsed)
  128. mountOptions.chunkSizeLimitMB = &intValue
  129. } else {
  130. panic(fmt.Errorf("chunkSizeLimitMB: %s", err))
  131. }
  132. case "concurrentWriters":
  133. i++
  134. if parsed, err := strconv.ParseInt(parameter.value, 0, 32); err == nil {
  135. intValue := int(parsed)
  136. mountOptions.concurrentWriters = &intValue
  137. } else {
  138. panic(fmt.Errorf("concurrentWriters: %s", err))
  139. }
  140. case "cacheDir":
  141. mountOptions.cacheDirForRead = &parameter.value
  142. case "cacheCapacityMB":
  143. if parsed, err := strconv.ParseInt(parameter.value, 0, 64); err == nil {
  144. mountOptions.cacheSizeMBForRead = &parsed
  145. } else {
  146. panic(fmt.Errorf("cacheCapacityMB: %s", err))
  147. }
  148. case "cacheDirWrite":
  149. mountOptions.cacheDirForWrite = &parameter.value
  150. case "dataCenter":
  151. mountOptions.dataCenter = &parameter.value
  152. case "allowOthers":
  153. if parsed, err := strconv.ParseBool(parameter.value); err == nil {
  154. mountOptions.allowOthers = &parsed
  155. } else {
  156. panic(fmt.Errorf("allowOthers: %s", err))
  157. }
  158. case "umask":
  159. mountOptions.umaskString = &parameter.value
  160. case "nonempty":
  161. if parsed, err := strconv.ParseBool(parameter.value); err == nil {
  162. mountOptions.nonempty = &parsed
  163. } else {
  164. panic(fmt.Errorf("nonempty: %s", err))
  165. }
  166. case "volumeServerAccess":
  167. mountOptions.volumeServerAccess = &parameter.value
  168. case "map.uid":
  169. mountOptions.uidMap = &parameter.value
  170. case "map.gid":
  171. mountOptions.gidMap = &parameter.value
  172. case "readOnly":
  173. if parsed, err := strconv.ParseBool(parameter.value); err == nil {
  174. mountOptions.readOnly = &parsed
  175. } else {
  176. panic(fmt.Errorf("readOnly: %s", err))
  177. }
  178. case "disableXAttr":
  179. if parsed, err := strconv.ParseBool(parameter.value); err == nil {
  180. mountOptions.disableXAttr = &parsed
  181. } else {
  182. panic(fmt.Errorf("disableXAttr: %s", err))
  183. }
  184. case "cpuprofile":
  185. mountCpuProfile = &parameter.value
  186. case "memprofile":
  187. mountMemProfile = &parameter.value
  188. case "readRetryTime":
  189. if parsed, err := time.ParseDuration(parameter.value); err == nil {
  190. mountReadRetryTime = &parsed
  191. } else {
  192. panic(fmt.Errorf("readRetryTime: %s", err))
  193. }
  194. case "fusermount.path":
  195. fusermountPath = parameter.value
  196. default:
  197. t := parameter.name
  198. if parameter.value != "true" {
  199. t = fmt.Sprintf("%s=%s", parameter.name, parameter.value)
  200. }
  201. mountOptions.extraOptions = append(mountOptions.extraOptions, t)
  202. }
  203. }
  204. // the master start the child, release it then finish himself
  205. if masterProcess {
  206. arg0, err := os.Executable()
  207. if err != nil {
  208. panic(err)
  209. }
  210. // pass our PID to the child process
  211. pid := os.Getpid()
  212. argv := append(os.Args, "-o", "child="+strconv.Itoa(pid))
  213. c := make(chan os.Signal, 1)
  214. signal.Notify(c, syscall.SIGTERM)
  215. attr := os.ProcAttr{}
  216. attr.Env = os.Environ()
  217. child, err := os.StartProcess(arg0, argv, &attr)
  218. if err != nil {
  219. panic(fmt.Errorf("master process can not start child process: %s", err))
  220. }
  221. err = child.Release()
  222. if err != nil {
  223. panic(fmt.Errorf("master process can not release child process: %s", err))
  224. }
  225. select {
  226. case <-c:
  227. return true
  228. }
  229. }
  230. if fusermountPath != "" {
  231. if err := os.Setenv("PATH", fusermountPath); err != nil {
  232. panic(fmt.Errorf("setenv: %s", err))
  233. }
  234. } else if os.Getenv("PATH") == "" {
  235. if err := os.Setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin"); err != nil {
  236. panic(fmt.Errorf("setenv: %s", err))
  237. }
  238. }
  239. // just call "weed mount" command
  240. return runMount(cmdMount, []string{})
  241. }