fuse.go 7.0 KB

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