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.

383 lines
9.1 KiB

7 years ago
7 years ago
7 years ago
7 years ago
6 years ago
6 years ago
  1. package filesys
  2. import (
  3. "context"
  4. "os"
  5. "path"
  6. "time"
  7. "github.com/chrislusf/seaweedfs/weed/filer2"
  8. "github.com/chrislusf/seaweedfs/weed/glog"
  9. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  10. "github.com/seaweedfs/fuse"
  11. "github.com/seaweedfs/fuse/fs"
  12. )
  13. type Dir struct {
  14. Path string
  15. wfs *WFS
  16. attributes *filer_pb.FuseAttributes
  17. }
  18. var _ = fs.Node(&Dir{})
  19. var _ = fs.NodeCreater(&Dir{})
  20. var _ = fs.NodeMkdirer(&Dir{})
  21. var _ = fs.NodeRequestLookuper(&Dir{})
  22. var _ = fs.HandleReadDirAller(&Dir{})
  23. var _ = fs.NodeRemover(&Dir{})
  24. var _ = fs.NodeRenamer(&Dir{})
  25. var _ = fs.NodeSetattrer(&Dir{})
  26. func (dir *Dir) Attr(ctx context.Context, attr *fuse.Attr) error {
  27. // https://github.com/bazil/fuse/issues/196
  28. attr.Valid = time.Second
  29. if dir.Path == dir.wfs.option.FilerMountRootPath {
  30. attr.Uid = dir.wfs.option.MountUid
  31. attr.Gid = dir.wfs.option.MountGid
  32. attr.Mode = dir.wfs.option.MountMode
  33. return nil
  34. }
  35. item := dir.wfs.listDirectoryEntriesCache.Get(dir.Path)
  36. if item != nil && !item.Expired() {
  37. entry := item.Value().(*filer_pb.Entry)
  38. attr.Mtime = time.Unix(entry.Attributes.Mtime, 0)
  39. attr.Ctime = time.Unix(entry.Attributes.Crtime, 0)
  40. attr.Mode = os.FileMode(entry.Attributes.FileMode)
  41. attr.Gid = entry.Attributes.Gid
  42. attr.Uid = entry.Attributes.Uid
  43. return nil
  44. }
  45. entry, err := filer2.GetEntry(ctx, dir.wfs, dir.Path)
  46. if err != nil {
  47. glog.V(2).Infof("read dir %s attr: %v, error: %v", dir.Path, dir.attributes, err)
  48. return err
  49. }
  50. dir.attributes = entry.Attributes
  51. glog.V(2).Infof("dir %s: %v perm: %v", dir.Path, dir.attributes, os.FileMode(dir.attributes.FileMode))
  52. attr.Mode = os.FileMode(dir.attributes.FileMode) | os.ModeDir
  53. attr.Mtime = time.Unix(dir.attributes.Mtime, 0)
  54. attr.Ctime = time.Unix(dir.attributes.Crtime, 0)
  55. attr.Gid = dir.attributes.Gid
  56. attr.Uid = dir.attributes.Uid
  57. return nil
  58. }
  59. func (dir *Dir) newFile(name string, entry *filer_pb.Entry) *File {
  60. return &File{
  61. Name: name,
  62. dir: dir,
  63. wfs: dir.wfs,
  64. entry: entry,
  65. entryViewCache: nil,
  66. }
  67. }
  68. func (dir *Dir) Create(ctx context.Context, req *fuse.CreateRequest,
  69. resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
  70. request := &filer_pb.CreateEntryRequest{
  71. Directory: dir.Path,
  72. Entry: &filer_pb.Entry{
  73. Name: req.Name,
  74. IsDirectory: req.Mode&os.ModeDir > 0,
  75. Attributes: &filer_pb.FuseAttributes{
  76. Mtime: time.Now().Unix(),
  77. Crtime: time.Now().Unix(),
  78. FileMode: uint32(req.Mode),
  79. Uid: req.Uid,
  80. Gid: req.Gid,
  81. Collection: dir.wfs.option.Collection,
  82. Replication: dir.wfs.option.Replication,
  83. TtlSec: dir.wfs.option.TtlSec,
  84. },
  85. },
  86. }
  87. glog.V(1).Infof("create: %v", request)
  88. if request.Entry.IsDirectory {
  89. if err := dir.wfs.WithFilerClient(ctx, func(client filer_pb.SeaweedFilerClient) error {
  90. if _, err := client.CreateEntry(ctx, request); err != nil {
  91. glog.V(0).Infof("create %s/%s: %v", dir.Path, req.Name, err)
  92. return fuse.EIO
  93. }
  94. return nil
  95. }); err != nil {
  96. return nil, nil, err
  97. }
  98. }
  99. file := dir.newFile(req.Name, request.Entry)
  100. if !request.Entry.IsDirectory {
  101. file.isOpen = true
  102. }
  103. fh := dir.wfs.AcquireHandle(file, req.Uid, req.Gid)
  104. fh.dirtyMetadata = true
  105. return file, fh, nil
  106. }
  107. func (dir *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
  108. err := dir.wfs.WithFilerClient(ctx, func(client filer_pb.SeaweedFilerClient) error {
  109. request := &filer_pb.CreateEntryRequest{
  110. Directory: dir.Path,
  111. Entry: &filer_pb.Entry{
  112. Name: req.Name,
  113. IsDirectory: true,
  114. Attributes: &filer_pb.FuseAttributes{
  115. Mtime: time.Now().Unix(),
  116. Crtime: time.Now().Unix(),
  117. FileMode: uint32(req.Mode),
  118. Uid: req.Uid,
  119. Gid: req.Gid,
  120. },
  121. },
  122. }
  123. glog.V(1).Infof("mkdir: %v", request)
  124. if _, err := client.CreateEntry(ctx, request); err != nil {
  125. glog.V(0).Infof("mkdir %s/%s: %v", dir.Path, req.Name, err)
  126. return fuse.EIO
  127. }
  128. return nil
  129. })
  130. if err == nil {
  131. node := &Dir{Path: path.Join(dir.Path, req.Name), wfs: dir.wfs}
  132. return node, nil
  133. }
  134. return nil, err
  135. }
  136. func (dir *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (node fs.Node, err error) {
  137. var entry *filer_pb.Entry
  138. fullFilePath := path.Join(dir.Path, req.Name)
  139. item := dir.wfs.listDirectoryEntriesCache.Get(fullFilePath)
  140. if item != nil && !item.Expired() {
  141. entry = item.Value().(*filer_pb.Entry)
  142. }
  143. if entry == nil {
  144. entry, err = filer2.GetEntry(ctx, dir.wfs, fullFilePath)
  145. if err != nil {
  146. return nil, err
  147. }
  148. }
  149. if entry != nil {
  150. if entry.IsDirectory {
  151. node = &Dir{Path: path.Join(dir.Path, req.Name), wfs: dir.wfs, attributes: entry.Attributes}
  152. } else {
  153. node = dir.newFile(req.Name, entry)
  154. }
  155. resp.EntryValid = time.Duration(0)
  156. resp.Attr.Mtime = time.Unix(entry.Attributes.Mtime, 0)
  157. resp.Attr.Ctime = time.Unix(entry.Attributes.Crtime, 0)
  158. resp.Attr.Mode = os.FileMode(entry.Attributes.FileMode)
  159. resp.Attr.Gid = entry.Attributes.Gid
  160. resp.Attr.Uid = entry.Attributes.Uid
  161. return node, nil
  162. }
  163. return nil, fuse.ENOENT
  164. }
  165. func (dir *Dir) ReadDirAll(ctx context.Context) (ret []fuse.Dirent, err error) {
  166. err = dir.wfs.WithFilerClient(ctx, func(client filer_pb.SeaweedFilerClient) error {
  167. paginationLimit := 1024
  168. remaining := dir.wfs.option.DirListingLimit
  169. lastEntryName := ""
  170. for remaining >= 0 {
  171. request := &filer_pb.ListEntriesRequest{
  172. Directory: dir.Path,
  173. StartFromFileName: lastEntryName,
  174. Limit: uint32(paginationLimit),
  175. }
  176. glog.V(4).Infof("read directory: %v", request)
  177. resp, err := client.ListEntries(ctx, request)
  178. if err != nil {
  179. glog.V(0).Infof("list %s: %v", dir.Path, err)
  180. return fuse.EIO
  181. }
  182. cacheTtl := estimatedCacheTtl(len(resp.Entries))
  183. for _, entry := range resp.Entries {
  184. if entry.IsDirectory {
  185. dirent := fuse.Dirent{Name: entry.Name, Type: fuse.DT_Dir}
  186. ret = append(ret, dirent)
  187. } else {
  188. dirent := fuse.Dirent{Name: entry.Name, Type: fuse.DT_File}
  189. ret = append(ret, dirent)
  190. }
  191. dir.wfs.listDirectoryEntriesCache.Set(path.Join(dir.Path, entry.Name), entry, cacheTtl)
  192. lastEntryName = entry.Name
  193. }
  194. remaining -= len(resp.Entries)
  195. if len(resp.Entries) < paginationLimit {
  196. break
  197. }
  198. }
  199. return nil
  200. })
  201. return ret, err
  202. }
  203. func (dir *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
  204. if !req.Dir {
  205. return dir.removeOneFile(ctx, req)
  206. }
  207. return dir.removeFolder(ctx, req)
  208. }
  209. func (dir *Dir) removeOneFile(ctx context.Context, req *fuse.RemoveRequest) error {
  210. entry, err := filer2.GetEntry(ctx, dir.wfs, path.Join(dir.Path, req.Name))
  211. if err != nil {
  212. return err
  213. }
  214. dir.wfs.deleteFileChunks(ctx, entry.Chunks)
  215. return dir.wfs.WithFilerClient(ctx, func(client filer_pb.SeaweedFilerClient) error {
  216. request := &filer_pb.DeleteEntryRequest{
  217. Directory: dir.Path,
  218. Name: req.Name,
  219. IsDeleteData: false,
  220. }
  221. glog.V(3).Infof("remove file: %v", request)
  222. _, err := client.DeleteEntry(ctx, request)
  223. if err != nil {
  224. glog.V(3).Infof("remove file %s/%s: %v", dir.Path, req.Name, err)
  225. return fuse.ENOENT
  226. }
  227. dir.wfs.listDirectoryEntriesCache.Delete(path.Join(dir.Path, req.Name))
  228. return nil
  229. })
  230. }
  231. func (dir *Dir) removeFolder(ctx context.Context, req *fuse.RemoveRequest) error {
  232. return dir.wfs.WithFilerClient(ctx, func(client filer_pb.SeaweedFilerClient) error {
  233. request := &filer_pb.DeleteEntryRequest{
  234. Directory: dir.Path,
  235. Name: req.Name,
  236. IsDeleteData: true,
  237. }
  238. glog.V(3).Infof("remove directory entry: %v", request)
  239. _, err := client.DeleteEntry(ctx, request)
  240. if err != nil {
  241. glog.V(3).Infof("remove %s/%s: %v", dir.Path, req.Name, err)
  242. return fuse.ENOENT
  243. }
  244. dir.wfs.listDirectoryEntriesCache.Delete(path.Join(dir.Path, req.Name))
  245. return nil
  246. })
  247. }
  248. func (dir *Dir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {
  249. if dir.attributes == nil {
  250. return nil
  251. }
  252. glog.V(3).Infof("%v dir setattr %+v, fh=%d", dir.Path, req, req.Handle)
  253. if req.Valid.Mode() {
  254. dir.attributes.FileMode = uint32(req.Mode)
  255. }
  256. if req.Valid.Uid() {
  257. dir.attributes.Uid = req.Uid
  258. }
  259. if req.Valid.Gid() {
  260. dir.attributes.Gid = req.Gid
  261. }
  262. if req.Valid.Mtime() {
  263. dir.attributes.Mtime = req.Mtime.Unix()
  264. }
  265. parentDir, name := filer2.FullPath(dir.Path).DirAndName()
  266. return dir.wfs.WithFilerClient(ctx, func(client filer_pb.SeaweedFilerClient) error {
  267. request := &filer_pb.UpdateEntryRequest{
  268. Directory: parentDir,
  269. Entry: &filer_pb.Entry{
  270. Name: name,
  271. Attributes: dir.attributes,
  272. },
  273. }
  274. glog.V(1).Infof("set attr directory entry: %v", request)
  275. _, err := client.UpdateEntry(ctx, request)
  276. if err != nil {
  277. glog.V(0).Infof("UpdateEntry %s: %v", dir.Path, err)
  278. return fuse.EIO
  279. }
  280. dir.wfs.listDirectoryEntriesCache.Delete(dir.Path)
  281. return nil
  282. })
  283. }
  284. func estimatedCacheTtl(numEntries int) time.Duration {
  285. if numEntries < 100 {
  286. // 30 ms per entry
  287. return 3 * time.Second
  288. }
  289. if numEntries < 1000 {
  290. // 10 ms per entry
  291. return 10 * time.Second
  292. }
  293. if numEntries < 10000 {
  294. // 10 ms per entry
  295. return 100 * time.Second
  296. }
  297. // 2 ms per entry
  298. return time.Duration(numEntries*2) * time.Millisecond
  299. }