mount2_std.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. //go:build linux || darwin
  2. // +build linux darwin
  3. package command
  4. import (
  5. "context"
  6. "fmt"
  7. "github.com/chrislusf/seaweedfs/weed/glog"
  8. "github.com/chrislusf/seaweedfs/weed/mount"
  9. "github.com/chrislusf/seaweedfs/weed/mount/meta_cache"
  10. "github.com/chrislusf/seaweedfs/weed/mount/unmount"
  11. "github.com/chrislusf/seaweedfs/weed/pb"
  12. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  13. "github.com/chrislusf/seaweedfs/weed/security"
  14. "github.com/chrislusf/seaweedfs/weed/storage/types"
  15. "github.com/hanwen/go-fuse/v2/fuse"
  16. "net/http"
  17. "os"
  18. "os/user"
  19. "runtime"
  20. "strconv"
  21. "strings"
  22. "time"
  23. "github.com/chrislusf/seaweedfs/weed/util"
  24. "github.com/chrislusf/seaweedfs/weed/util/grace"
  25. )
  26. func runMount2(cmd *Command, args []string) bool {
  27. if *mount2Options.debug {
  28. go http.ListenAndServe(fmt.Sprintf(":%d", *mount2Options.debugPort), nil)
  29. }
  30. grace.SetupProfiling(*mountCpuProfile, *mountMemProfile)
  31. if *mountReadRetryTime < time.Second {
  32. *mountReadRetryTime = time.Second
  33. }
  34. util.RetryWaitTime = *mountReadRetryTime
  35. umask, umaskErr := strconv.ParseUint(*mount2Options.umaskString, 8, 64)
  36. if umaskErr != nil {
  37. fmt.Printf("can not parse umask %s", *mount2Options.umaskString)
  38. return false
  39. }
  40. if len(args) > 0 {
  41. return false
  42. }
  43. return RunMount2(&mount2Options, os.FileMode(umask))
  44. }
  45. func RunMount2(option *Mount2Options, umask os.FileMode) bool {
  46. // basic checks
  47. chunkSizeLimitMB := *mount2Options.chunkSizeLimitMB
  48. if chunkSizeLimitMB <= 0 {
  49. fmt.Printf("Please specify a reasonable buffer size.")
  50. return false
  51. }
  52. // try to connect to filer
  53. filerAddresses := pb.ServerAddresses(*option.filer).ToAddresses()
  54. util.LoadConfiguration("security", false)
  55. grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client")
  56. var cipher bool
  57. var err error
  58. for i := 0; i < 10; i++ {
  59. err = pb.WithOneOfGrpcFilerClients(false, filerAddresses, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
  60. resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
  61. if err != nil {
  62. return fmt.Errorf("get filer grpc address %v configuration: %v", filerAddresses, err)
  63. }
  64. cipher = resp.Cipher
  65. return nil
  66. })
  67. if err != nil {
  68. glog.V(0).Infof("failed to talk to filer %v: %v", filerAddresses, err)
  69. glog.V(0).Infof("wait for %d seconds ...", i+1)
  70. time.Sleep(time.Duration(i+1) * time.Second)
  71. }
  72. }
  73. if err != nil {
  74. glog.Errorf("failed to talk to filer %v: %v", filerAddresses, err)
  75. return true
  76. }
  77. filerMountRootPath := *option.filerMountRootPath
  78. // clean up mount point
  79. dir := util.ResolvePath(*option.dir)
  80. if dir == "" {
  81. fmt.Printf("Please specify the mount directory via \"-dir\"")
  82. return false
  83. }
  84. unmount.Unmount(dir)
  85. // detect mount folder mode
  86. if *option.dirAutoCreate {
  87. os.MkdirAll(dir, os.FileMode(0777)&^umask)
  88. }
  89. fileInfo, err := os.Stat(dir)
  90. // collect uid, gid
  91. uid, gid := uint32(0), uint32(0)
  92. mountMode := os.ModeDir | 0777
  93. if err == nil {
  94. mountMode = os.ModeDir | os.FileMode(0777)&^umask
  95. uid, gid = util.GetFileUidGid(fileInfo)
  96. fmt.Printf("mount point owner uid=%d gid=%d mode=%s\n", uid, gid, mountMode)
  97. } else {
  98. fmt.Printf("can not stat %s\n", dir)
  99. return false
  100. }
  101. // detect uid, gid
  102. if uid == 0 {
  103. if u, err := user.Current(); err == nil {
  104. if parsedId, pe := strconv.ParseUint(u.Uid, 10, 32); pe == nil {
  105. uid = uint32(parsedId)
  106. }
  107. if parsedId, pe := strconv.ParseUint(u.Gid, 10, 32); pe == nil {
  108. gid = uint32(parsedId)
  109. }
  110. fmt.Printf("current uid=%d gid=%d\n", uid, gid)
  111. }
  112. }
  113. // mapping uid, gid
  114. uidGidMapper, err := meta_cache.NewUidGidMapper(*option.uidMap, *option.gidMap)
  115. if err != nil {
  116. fmt.Printf("failed to parse %s %s: %v\n", *option.uidMap, *option.gidMap, err)
  117. return false
  118. }
  119. // Ensure target mount point availability
  120. if isValid := checkMountPointAvailable(dir); !isValid {
  121. glog.Fatalf("Expected mount to still be active, target mount point: %s, please check!", dir)
  122. return true
  123. }
  124. // mount fuse
  125. fuseMountOptions := &fuse.MountOptions{
  126. AllowOther: *option.allowOthers,
  127. Options: nil,
  128. MaxBackground: 128,
  129. MaxWrite: 1024 * 1024 * 2,
  130. MaxReadAhead: 1024 * 1024 * 2,
  131. IgnoreSecurityLabels: false,
  132. RememberInodes: false,
  133. FsName: *option.filer + ":" + filerMountRootPath,
  134. Name: "seaweedfs",
  135. SingleThreaded: false,
  136. DisableXAttrs: false,
  137. Debug: *option.debug,
  138. EnableLocks: false,
  139. ExplicitDataCacheControl: false,
  140. DirectMount: true,
  141. DirectMountFlags: 0,
  142. //SyncRead: false, // set to false to enable the FUSE_CAP_ASYNC_READ capability
  143. //EnableAcl: true,
  144. }
  145. if *option.nonempty {
  146. fuseMountOptions.Options = append(fuseMountOptions.Options, "nonempty")
  147. }
  148. if *option.readOnly {
  149. if runtime.GOOS == "darwin" {
  150. fuseMountOptions.Options = append(fuseMountOptions.Options, "rdonly")
  151. } else {
  152. fuseMountOptions.Options = append(fuseMountOptions.Options, "ro")
  153. }
  154. }
  155. if runtime.GOOS == "darwin" {
  156. // https://github-wiki-see.page/m/macfuse/macfuse/wiki/Mount-Options
  157. ioSizeMB := 1
  158. for ioSizeMB*2 <= *option.chunkSizeLimitMB && ioSizeMB*2 <= 32 {
  159. ioSizeMB *= 2
  160. }
  161. fuseMountOptions.Options = append(fuseMountOptions.Options, "daemon_timeout=600")
  162. fuseMountOptions.Options = append(fuseMountOptions.Options, "noapplexattr")
  163. // fuseMountOptions.Options = append(fuseMountOptions.Options, "novncache") // need to test effectiveness
  164. fuseMountOptions.Options = append(fuseMountOptions.Options, "slow_statfs")
  165. fuseMountOptions.Options = append(fuseMountOptions.Options, "volname="+*option.filer)
  166. fuseMountOptions.Options = append(fuseMountOptions.Options, fmt.Sprintf("iosize=%d", ioSizeMB*1024*1024))
  167. }
  168. // find mount point
  169. mountRoot := filerMountRootPath
  170. if mountRoot != "/" && strings.HasSuffix(mountRoot, "/") {
  171. mountRoot = mountRoot[0 : len(mountRoot)-1]
  172. }
  173. seaweedFileSystem := mount.NewSeaweedFileSystem(&mount.Option{
  174. MountDirectory: dir,
  175. FilerAddresses: filerAddresses,
  176. GrpcDialOption: grpcDialOption,
  177. FilerMountRootPath: mountRoot,
  178. Collection: *option.collection,
  179. Replication: *option.replication,
  180. TtlSec: int32(*option.ttlSec),
  181. DiskType: types.ToDiskType(*option.diskType),
  182. ChunkSizeLimit: int64(chunkSizeLimitMB) * 1024 * 1024,
  183. ConcurrentWriters: *option.concurrentWriters,
  184. CacheDir: *option.cacheDir,
  185. CacheSizeMB: *option.cacheSizeMB,
  186. DataCenter: *option.dataCenter,
  187. MountUid: uid,
  188. MountGid: gid,
  189. MountMode: mountMode,
  190. MountCtime: fileInfo.ModTime(),
  191. MountMtime: time.Now(),
  192. Umask: umask,
  193. VolumeServerAccess: *mount2Options.volumeServerAccess,
  194. Cipher: cipher,
  195. UidGidMapper: uidGidMapper,
  196. })
  197. server, err := fuse.NewServer(seaweedFileSystem, dir, fuseMountOptions)
  198. if err != nil {
  199. glog.Fatalf("Mount fail: %v", err)
  200. }
  201. grace.OnInterrupt(func() {
  202. unmount.Unmount(dir)
  203. })
  204. seaweedFileSystem.StartBackgroundTasks()
  205. fmt.Printf("This is SeaweedFS version %s %s %s\n", util.Version(), runtime.GOOS, runtime.GOARCH)
  206. server.Serve()
  207. return true
  208. }