You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

233 lines
7.3 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
1 year ago
3 years ago
3 years ago
1 year ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
1 year ago
3 years ago
1 year ago
3 years ago
1 year ago
1 year ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
1 year ago
1 year ago
3 years ago
1 year ago
3 years ago
  1. package mount
  2. import (
  3. "context"
  4. "math/rand"
  5. "os"
  6. "path"
  7. "path/filepath"
  8. "sync/atomic"
  9. "time"
  10. "github.com/hanwen/go-fuse/v2/fuse"
  11. "google.golang.org/grpc"
  12. "github.com/seaweedfs/seaweedfs/weed/filer"
  13. "github.com/seaweedfs/seaweedfs/weed/mount/meta_cache"
  14. "github.com/seaweedfs/seaweedfs/weed/pb"
  15. "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
  16. "github.com/seaweedfs/seaweedfs/weed/pb/mount_pb"
  17. "github.com/seaweedfs/seaweedfs/weed/storage/types"
  18. "github.com/seaweedfs/seaweedfs/weed/util"
  19. "github.com/seaweedfs/seaweedfs/weed/util/chunk_cache"
  20. "github.com/seaweedfs/seaweedfs/weed/util/grace"
  21. "github.com/seaweedfs/seaweedfs/weed/wdclient"
  22. "github.com/hanwen/go-fuse/v2/fs"
  23. )
  24. type Option struct {
  25. filerIndex int32 // align memory for atomic read/write
  26. FilerAddresses []pb.ServerAddress
  27. MountDirectory string
  28. GrpcDialOption grpc.DialOption
  29. FilerMountRootPath string
  30. Collection string
  31. Replication string
  32. TtlSec int32
  33. DiskType types.DiskType
  34. ChunkSizeLimit int64
  35. ConcurrentWriters int
  36. CacheDirForRead string
  37. CacheSizeMBForRead int64
  38. CacheDirForWrite string
  39. DataCenter string
  40. Umask os.FileMode
  41. Quota int64
  42. DisableXAttr bool
  43. MountUid uint32
  44. MountGid uint32
  45. MountMode os.FileMode
  46. MountCtime time.Time
  47. MountMtime time.Time
  48. MountParentInode uint64
  49. VolumeServerAccess string // how to access volume servers
  50. Cipher bool // whether encrypt data on volume server
  51. UidGidMapper *meta_cache.UidGidMapper
  52. uniqueCacheDirForRead string
  53. uniqueCacheDirForWrite string
  54. }
  55. type WFS struct {
  56. // https://dl.acm.org/doi/fullHtml/10.1145/3310148
  57. // follow https://github.com/hanwen/go-fuse/blob/master/fuse/api.go
  58. fuse.RawFileSystem
  59. mount_pb.UnimplementedSeaweedMountServer
  60. fs.Inode
  61. option *Option
  62. metaCache *meta_cache.MetaCache
  63. stats statsCache
  64. chunkCache *chunk_cache.TieredChunkCache
  65. signature int32
  66. concurrentWriters *util.LimitedConcurrentExecutor
  67. inodeToPath *InodeToPath
  68. fhmap *FileHandleToInode
  69. dhmap *DirectoryHandleToInode
  70. fuseServer *fuse.Server
  71. IsOverQuota bool
  72. fhLockTable *util.LockTable[FileHandleId]
  73. }
  74. func NewSeaweedFileSystem(option *Option) *WFS {
  75. wfs := &WFS{
  76. RawFileSystem: fuse.NewDefaultRawFileSystem(),
  77. option: option,
  78. signature: util.RandomInt32(),
  79. inodeToPath: NewInodeToPath(util.FullPath(option.FilerMountRootPath)),
  80. fhmap: NewFileHandleToInode(),
  81. dhmap: NewDirectoryHandleToInode(),
  82. fhLockTable: util.NewLockTable[FileHandleId](),
  83. }
  84. wfs.option.filerIndex = int32(rand.Intn(len(option.FilerAddresses)))
  85. wfs.option.setupUniqueCacheDirectory()
  86. if option.CacheSizeMBForRead > 0 {
  87. wfs.chunkCache = chunk_cache.NewTieredChunkCache(256, option.getUniqueCacheDirForRead(), option.CacheSizeMBForRead, 1024*1024)
  88. }
  89. wfs.metaCache = meta_cache.NewMetaCache(path.Join(option.getUniqueCacheDirForRead(), "meta"), option.UidGidMapper,
  90. util.FullPath(option.FilerMountRootPath),
  91. func(path util.FullPath) {
  92. wfs.inodeToPath.MarkChildrenCached(path)
  93. }, func(path util.FullPath) bool {
  94. return wfs.inodeToPath.IsChildrenCached(path)
  95. }, func(filePath util.FullPath, entry *filer_pb.Entry) {
  96. // Find inode if it is not a deleted path
  97. if inode, inode_found := wfs.inodeToPath.GetInode(filePath); inode_found {
  98. // Find open file handle
  99. if fh, fh_found := wfs.fhmap.FindFileHandle(inode); fh_found {
  100. fhActiveLock := fh.wfs.fhLockTable.AcquireLock("invalidateFunc", fh.fh, util.ExclusiveLock)
  101. defer fh.wfs.fhLockTable.ReleaseLock(fh.fh, fhActiveLock)
  102. fh.entryLock.Lock()
  103. defer fh.entryLock.Unlock()
  104. // Recreate dirty pages
  105. fh.dirtyPages.Destroy()
  106. fh.dirtyPages = newPageWriter(fh, wfs.option.ChunkSizeLimit)
  107. // Update handle entry
  108. newentry, status := wfs.maybeLoadEntry(filePath)
  109. if status == fuse.OK {
  110. if fh.GetEntry() != newentry {
  111. fh.SetEntry(newentry)
  112. }
  113. }
  114. }
  115. }
  116. })
  117. grace.OnInterrupt(func() {
  118. wfs.metaCache.Shutdown()
  119. os.RemoveAll(option.getUniqueCacheDirForWrite())
  120. os.RemoveAll(option.getUniqueCacheDirForRead())
  121. })
  122. if wfs.option.ConcurrentWriters > 0 {
  123. wfs.concurrentWriters = util.NewLimitedConcurrentExecutor(wfs.option.ConcurrentWriters)
  124. }
  125. return wfs
  126. }
  127. func (wfs *WFS) StartBackgroundTasks() {
  128. startTime := time.Now()
  129. go meta_cache.SubscribeMetaEvents(wfs.metaCache, wfs.signature, wfs, wfs.option.FilerMountRootPath, startTime.UnixNano())
  130. go wfs.loopCheckQuota()
  131. }
  132. func (wfs *WFS) String() string {
  133. return "seaweedfs"
  134. }
  135. func (wfs *WFS) Init(server *fuse.Server) {
  136. wfs.fuseServer = server
  137. }
  138. func (wfs *WFS) maybeReadEntry(inode uint64) (path util.FullPath, fh *FileHandle, entry *filer_pb.Entry, status fuse.Status) {
  139. path, status = wfs.inodeToPath.GetPath(inode)
  140. if status != fuse.OK {
  141. return
  142. }
  143. var found bool
  144. if fh, found = wfs.fhmap.FindFileHandle(inode); found {
  145. entry = fh.UpdateEntry(func(entry *filer_pb.Entry) {
  146. if entry != nil && fh.entry.Attributes == nil {
  147. entry.Attributes = &filer_pb.FuseAttributes{}
  148. }
  149. })
  150. } else {
  151. entry, status = wfs.maybeLoadEntry(path)
  152. }
  153. return
  154. }
  155. func (wfs *WFS) maybeLoadEntry(fullpath util.FullPath) (*filer_pb.Entry, fuse.Status) {
  156. // glog.V(3).Infof("read entry cache miss %s", fullpath)
  157. dir, name := fullpath.DirAndName()
  158. // return a valid entry for the mount root
  159. if string(fullpath) == wfs.option.FilerMountRootPath {
  160. return &filer_pb.Entry{
  161. Name: name,
  162. IsDirectory: true,
  163. Attributes: &filer_pb.FuseAttributes{
  164. Mtime: wfs.option.MountMtime.Unix(),
  165. FileMode: uint32(wfs.option.MountMode),
  166. Uid: wfs.option.MountUid,
  167. Gid: wfs.option.MountGid,
  168. Crtime: wfs.option.MountCtime.Unix(),
  169. },
  170. }, fuse.OK
  171. }
  172. // read from async meta cache
  173. meta_cache.EnsureVisited(wfs.metaCache, wfs, util.FullPath(dir))
  174. cachedEntry, cacheErr := wfs.metaCache.FindEntry(context.Background(), fullpath)
  175. if cacheErr == filer_pb.ErrNotFound {
  176. return nil, fuse.ENOENT
  177. }
  178. return cachedEntry.ToProtoEntry(), fuse.OK
  179. }
  180. func (wfs *WFS) LookupFn() wdclient.LookupFileIdFunctionType {
  181. if wfs.option.VolumeServerAccess == "filerProxy" {
  182. return func(fileId string) (targetUrls []string, err error) {
  183. return []string{"http://" + wfs.getCurrentFiler().ToHttpAddress() + "/?proxyChunkId=" + fileId}, nil
  184. }
  185. }
  186. return filer.LookupFn(wfs)
  187. }
  188. func (wfs *WFS) getCurrentFiler() pb.ServerAddress {
  189. i := atomic.LoadInt32(&wfs.option.filerIndex)
  190. return wfs.option.FilerAddresses[i]
  191. }
  192. func (option *Option) setupUniqueCacheDirectory() {
  193. cacheUniqueId := util.Md5String([]byte(option.MountDirectory + string(option.FilerAddresses[0]) + option.FilerMountRootPath + util.Version()))[0:8]
  194. option.uniqueCacheDirForRead = path.Join(option.CacheDirForRead, cacheUniqueId)
  195. os.MkdirAll(option.uniqueCacheDirForRead, os.FileMode(0777)&^option.Umask)
  196. option.uniqueCacheDirForWrite = filepath.Join(path.Join(option.CacheDirForWrite, cacheUniqueId), "swap")
  197. os.MkdirAll(option.uniqueCacheDirForWrite, os.FileMode(0777)&^option.Umask)
  198. }
  199. func (option *Option) getUniqueCacheDirForWrite() string {
  200. return option.uniqueCacheDirForWrite
  201. }
  202. func (option *Option) getUniqueCacheDirForRead() string {
  203. return option.uniqueCacheDirForRead
  204. }