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.

171 lines
4.6 KiB

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