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.

444 lines
10 KiB

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