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.

149 lines
4.3 KiB

  1. package filesys
  2. import (
  3. "context"
  4. "github.com/chrislusf/seaweedfs/weed/util"
  5. "os"
  6. "syscall"
  7. "time"
  8. "github.com/chrislusf/seaweedfs/weed/filer"
  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. var _ = fs.NodeLinker(&Dir{})
  15. var _ = fs.NodeSymlinker(&Dir{})
  16. var _ = fs.NodeReadlinker(&File{})
  17. func (dir *Dir) Link(ctx context.Context, req *fuse.LinkRequest, old fs.Node) (fs.Node, error) {
  18. oldFile, ok := old.(*File)
  19. if !ok {
  20. glog.Errorf("old node is not a file: %+v", old)
  21. }
  22. glog.V(4).Infof("Link: %v/%v -> %v/%v", oldFile.dir.FullPath(), oldFile.Name, dir.FullPath(), req.NewName)
  23. if err := oldFile.maybeLoadEntry(ctx); err != nil {
  24. return nil, err
  25. }
  26. // update old file to hardlink mode
  27. if oldFile.entry.HardLinkId == nil {
  28. oldFile.entry.HardLinkId = util.RandomBytes(16)
  29. oldFile.entry.HardLinkCounter = 1
  30. }
  31. oldFile.entry.HardLinkCounter++
  32. updateOldEntryRequest := &filer_pb.UpdateEntryRequest{
  33. Directory: oldFile.dir.FullPath(),
  34. Entry: oldFile.entry,
  35. Signatures: []int32{dir.wfs.signature},
  36. }
  37. // CreateLink 1.2 : update new file to hardlink mode
  38. request := &filer_pb.CreateEntryRequest{
  39. Directory: dir.FullPath(),
  40. Entry: &filer_pb.Entry{
  41. Name: req.NewName,
  42. IsDirectory: false,
  43. Attributes: oldFile.entry.Attributes,
  44. Chunks: oldFile.entry.Chunks,
  45. Extended: oldFile.entry.Extended,
  46. HardLinkId: oldFile.entry.HardLinkId,
  47. HardLinkCounter: oldFile.entry.HardLinkCounter,
  48. },
  49. Signatures: []int32{dir.wfs.signature},
  50. }
  51. // apply changes to the filer, and also apply to local metaCache
  52. err := dir.wfs.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
  53. dir.wfs.mapPbIdFromLocalToFiler(request.Entry)
  54. defer dir.wfs.mapPbIdFromFilerToLocal(request.Entry)
  55. if err := filer_pb.UpdateEntry(client, updateOldEntryRequest); err != nil {
  56. glog.V(0).Infof("Link %v/%v -> %s/%s: %v", oldFile.dir.FullPath(), oldFile.Name, dir.FullPath(), req.NewName, err)
  57. return fuse.EIO
  58. }
  59. dir.wfs.metaCache.UpdateEntry(context.Background(), filer.FromPbEntry(updateOldEntryRequest.Directory, updateOldEntryRequest.Entry))
  60. if err := filer_pb.CreateEntry(client, request); err != nil {
  61. glog.V(0).Infof("Link %v/%v -> %s/%s: %v", oldFile.dir.FullPath(), oldFile.Name, dir.FullPath(), req.NewName, err)
  62. return fuse.EIO
  63. }
  64. dir.wfs.metaCache.InsertEntry(context.Background(), filer.FromPbEntry(request.Directory, request.Entry))
  65. return nil
  66. })
  67. // create new file node
  68. newNode := dir.newFile(req.NewName, request.Entry)
  69. newFile := newNode.(*File)
  70. if err := newFile.maybeLoadEntry(ctx); err != nil {
  71. return nil, err
  72. }
  73. return newFile, err
  74. }
  75. func (dir *Dir) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (fs.Node, error) {
  76. glog.V(4).Infof("Symlink: %v/%v to %v", dir.FullPath(), req.NewName, req.Target)
  77. request := &filer_pb.CreateEntryRequest{
  78. Directory: dir.FullPath(),
  79. Entry: &filer_pb.Entry{
  80. Name: req.NewName,
  81. IsDirectory: false,
  82. Attributes: &filer_pb.FuseAttributes{
  83. Mtime: time.Now().Unix(),
  84. Crtime: time.Now().Unix(),
  85. FileMode: uint32((os.FileMode(0777) | os.ModeSymlink) &^ dir.wfs.option.Umask),
  86. Uid: req.Uid,
  87. Gid: req.Gid,
  88. SymlinkTarget: req.Target,
  89. },
  90. },
  91. Signatures: []int32{dir.wfs.signature},
  92. }
  93. err := dir.wfs.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
  94. dir.wfs.mapPbIdFromLocalToFiler(request.Entry)
  95. defer dir.wfs.mapPbIdFromFilerToLocal(request.Entry)
  96. if err := filer_pb.CreateEntry(client, request); err != nil {
  97. glog.V(0).Infof("symlink %s/%s: %v", dir.FullPath(), req.NewName, err)
  98. return fuse.EIO
  99. }
  100. dir.wfs.metaCache.InsertEntry(context.Background(), filer.FromPbEntry(request.Directory, request.Entry))
  101. return nil
  102. })
  103. symlink := dir.newFile(req.NewName, request.Entry)
  104. return symlink, err
  105. }
  106. func (file *File) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) {
  107. if err := file.maybeLoadEntry(ctx); err != nil {
  108. return "", err
  109. }
  110. if os.FileMode(file.entry.Attributes.FileMode)&os.ModeSymlink == 0 {
  111. return "", fuse.Errno(syscall.EINVAL)
  112. }
  113. glog.V(4).Infof("Readlink: %v/%v => %v", file.dir.FullPath(), file.Name, file.entry.Attributes.SymlinkTarget)
  114. return file.entry.Attributes.SymlinkTarget, nil
  115. }