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.

445 lines
11 KiB

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