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.

199 lines
5.3 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. package mount
  2. import (
  3. "context"
  4. "github.com/chrislusf/seaweedfs/weed/filer"
  5. "github.com/chrislusf/seaweedfs/weed/glog"
  6. "github.com/chrislusf/seaweedfs/weed/mount/meta_cache"
  7. "github.com/chrislusf/seaweedfs/weed/util"
  8. "github.com/hanwen/go-fuse/v2/fuse"
  9. "math"
  10. "os"
  11. "sync"
  12. )
  13. type DirectoryHandleId uint64
  14. type DirectoryHandle struct {
  15. isFinished bool
  16. counter uint32
  17. lastEntryName string
  18. }
  19. type DirectoryHandleToInode struct {
  20. // shares the file handle id sequencer with FileHandleToInode{nextFh}
  21. sync.Mutex
  22. dir2inode map[DirectoryHandleId]*DirectoryHandle
  23. }
  24. func NewDirectoryHandleToInode() *DirectoryHandleToInode {
  25. return &DirectoryHandleToInode{
  26. dir2inode: make(map[DirectoryHandleId]*DirectoryHandle),
  27. }
  28. }
  29. func (wfs *WFS) AcquireDirectoryHandle() (DirectoryHandleId, *DirectoryHandle) {
  30. wfs.fhmap.Lock()
  31. fh := wfs.fhmap.nextFh
  32. wfs.fhmap.nextFh++
  33. wfs.fhmap.Unlock()
  34. wfs.dhmap.Lock()
  35. defer wfs.dhmap.Unlock()
  36. dh := &DirectoryHandle{
  37. isFinished: false,
  38. lastEntryName: "",
  39. }
  40. wfs.dhmap.dir2inode[DirectoryHandleId(fh)] = dh
  41. return DirectoryHandleId(fh), dh
  42. }
  43. func (wfs *WFS) GetDirectoryHandle(dhid DirectoryHandleId) *DirectoryHandle {
  44. wfs.dhmap.Lock()
  45. defer wfs.dhmap.Unlock()
  46. if dh, found := wfs.dhmap.dir2inode[dhid]; found {
  47. return dh
  48. }
  49. dh := &DirectoryHandle{
  50. isFinished: false,
  51. lastEntryName: "",
  52. }
  53. wfs.dhmap.dir2inode[dhid] = dh
  54. return dh
  55. }
  56. func (wfs *WFS) ReleaseDirectoryHandle(dhid DirectoryHandleId) {
  57. wfs.dhmap.Lock()
  58. defer wfs.dhmap.Unlock()
  59. delete(wfs.dhmap.dir2inode, dhid)
  60. }
  61. // Directory handling
  62. /** Open directory
  63. *
  64. * Unless the 'default_permissions' mount option is given,
  65. * this method should check if opendir is permitted for this
  66. * directory. Optionally opendir may also return an arbitrary
  67. * filehandle in the fuse_file_info structure, which will be
  68. * passed to readdir, releasedir and fsyncdir.
  69. */
  70. func (wfs *WFS) OpenDir(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) (code fuse.Status) {
  71. if !wfs.inodeToPath.HasInode(input.NodeId) {
  72. return fuse.ENOENT
  73. }
  74. dhid, _ := wfs.AcquireDirectoryHandle()
  75. out.Fh = uint64(dhid)
  76. return fuse.OK
  77. }
  78. /** Release directory
  79. *
  80. * If the directory has been removed after the call to opendir, the
  81. * path parameter will be NULL.
  82. */
  83. func (wfs *WFS) ReleaseDir(input *fuse.ReleaseIn) {
  84. wfs.ReleaseDirectoryHandle(DirectoryHandleId(input.Fh))
  85. }
  86. /** Synchronize directory contents
  87. *
  88. * If the directory has been removed after the call to opendir, the
  89. * path parameter will be NULL.
  90. *
  91. * If the datasync parameter is non-zero, then only the user data
  92. * should be flushed, not the meta data
  93. */
  94. func (wfs *WFS) FsyncDir(cancel <-chan struct{}, input *fuse.FsyncIn) (code fuse.Status) {
  95. return fuse.OK
  96. }
  97. /** Read directory
  98. *
  99. * The filesystem may choose between two modes of operation:
  100. *
  101. * 1) The readdir implementation ignores the offset parameter, and
  102. * passes zero to the filler function's offset. The filler
  103. * function will not return '1' (unless an error happens), so the
  104. * whole directory is read in a single readdir operation.
  105. *
  106. * 2) The readdir implementation keeps track of the offsets of the
  107. * directory entries. It uses the offset parameter and always
  108. * passes non-zero offset to the filler function. When the buffer
  109. * is full (or an error happens) the filler function will return
  110. * '1'.
  111. */
  112. func (wfs *WFS) ReadDir(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) (code fuse.Status) {
  113. return wfs.doReadDirectory(input, out, false)
  114. }
  115. func (wfs *WFS) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) (code fuse.Status) {
  116. return wfs.doReadDirectory(input, out, true)
  117. }
  118. func (wfs *WFS) doReadDirectory(input *fuse.ReadIn, out *fuse.DirEntryList, isPlusMode bool) fuse.Status {
  119. dh := wfs.GetDirectoryHandle(DirectoryHandleId(input.Fh))
  120. if dh.isFinished {
  121. return fuse.OK
  122. }
  123. dirPath := wfs.inodeToPath.GetPath(input.NodeId)
  124. var dirEntry fuse.DirEntry
  125. if input.Offset == 0 && !isPlusMode {
  126. dh.counter++
  127. dirEntry.Ino = input.NodeId
  128. dirEntry.Name = "."
  129. dirEntry.Mode = toSystemMode(os.ModeDir)
  130. out.AddDirEntry(dirEntry)
  131. dh.counter++
  132. parentDir, _ := dirPath.DirAndName()
  133. parentInode := wfs.inodeToPath.GetInode(util.FullPath(parentDir))
  134. dirEntry.Ino = parentInode
  135. dirEntry.Name = ".."
  136. dirEntry.Mode = toSystemMode(os.ModeDir)
  137. out.AddDirEntry(dirEntry)
  138. }
  139. processEachEntryFn := func(entry *filer.Entry, isLast bool) bool {
  140. dh.counter++
  141. dirEntry.Name = entry.Name()
  142. inode := wfs.inodeToPath.GetInode(dirPath.Child(dirEntry.Name))
  143. dirEntry.Ino = inode
  144. dirEntry.Mode = toSystemMode(entry.Mode)
  145. if !isPlusMode {
  146. if !out.AddDirEntry(dirEntry) {
  147. return false
  148. }
  149. } else {
  150. entryOut := out.AddDirLookupEntry(dirEntry)
  151. if entryOut == nil {
  152. return false
  153. }
  154. wfs.outputFilerEntry(entryOut, inode, entry)
  155. }
  156. dh.lastEntryName = entry.Name()
  157. return true
  158. }
  159. if err := meta_cache.EnsureVisited(wfs.metaCache, wfs, dirPath); err != nil {
  160. glog.Errorf("dir ReadDirAll %s: %v", dirPath, err)
  161. return fuse.EIO
  162. }
  163. listErr := wfs.metaCache.ListDirectoryEntries(context.Background(), dirPath, dh.lastEntryName, false, int64(math.MaxInt32), func(entry *filer.Entry) bool {
  164. return processEachEntryFn(entry, false)
  165. })
  166. if listErr != nil {
  167. glog.Errorf("list meta cache: %v", listErr)
  168. return fuse.EIO
  169. }
  170. if dh.counter < input.Length {
  171. dh.isFinished = true
  172. }
  173. return fuse.OK
  174. }