dir_link.go 4.5 KB

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