fuse.go 7.3 KB

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