mount_std.go 8.9 KB

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