aboutsummaryrefslogtreecommitdiff
path: root/weed/mount
diff options
context:
space:
mode:
Diffstat (limited to 'weed/mount')
-rw-r--r--weed/mount/inode_to_path.go98
-rw-r--r--weed/mount/inode_to_path_test.go93
-rw-r--r--weed/mount/meta_cache/meta_cache_subscribe.go4
-rw-r--r--weed/mount/weedfs.go16
-rw-r--r--weed/mount/weedfs_attr.go27
-rw-r--r--weed/mount/weedfs_filehandle.go2
-rw-r--r--weed/mount/weedfs_link.go4
-rw-r--r--weed/mount/weedfs_rename.go4
-rw-r--r--weed/mount/weedfs_xattr.go14
-rw-r--r--weed/mount/wfs_save.go2
10 files changed, 233 insertions, 31 deletions
diff --git a/weed/mount/inode_to_path.go b/weed/mount/inode_to_path.go
index 29635efca..c287f4791 100644
--- a/weed/mount/inode_to_path.go
+++ b/weed/mount/inode_to_path.go
@@ -5,6 +5,7 @@ import (
"github.com/chrislusf/seaweedfs/weed/util"
"github.com/hanwen/go-fuse/v2/fuse"
"sync"
+ "time"
)
type InodeToPath struct {
@@ -14,22 +15,59 @@ type InodeToPath struct {
path2inode map[util.FullPath]uint64
}
type InodeEntry struct {
- util.FullPath
+ paths []util.FullPath
nlookup uint64
isDirectory bool
isChildrenCached bool
}
+func (ie *InodeEntry) removeOnePath(p util.FullPath) bool {
+ if len(ie.paths) == 0 {
+ return false
+ }
+ idx := -1
+ for i, x := range ie.paths {
+ if x == p {
+ idx = i
+ break
+ }
+ }
+ if idx < 0 {
+ return false
+ }
+ for x := idx; x < len(ie.paths)-1; x++ {
+ ie.paths[x] = ie.paths[x+1]
+ }
+ ie.paths = ie.paths[0 : len(ie.paths)-1]
+ ie.nlookup--
+ return true
+}
+
func NewInodeToPath(root util.FullPath) *InodeToPath {
t := &InodeToPath{
inode2path: make(map[uint64]*InodeEntry),
path2inode: make(map[util.FullPath]uint64),
}
- t.inode2path[1] = &InodeEntry{root, 1, true, false}
+ t.inode2path[1] = &InodeEntry{[]util.FullPath{root}, 1, true, false}
t.path2inode[root] = 1
return t
}
+// EnsurePath make sure the full path is tracked, used by symlink.
+func (i *InodeToPath) EnsurePath(path util.FullPath, isDirectory bool) bool {
+ for {
+ dir, _ := path.DirAndName()
+ if dir == "/" {
+ return true
+ }
+ if i.EnsurePath(util.FullPath(dir), true) {
+ i.Lookup(path, time.Now().Unix(), isDirectory, false, 0, false)
+ return true
+ }
+ }
+ return false
+}
+
func (i *InodeToPath) Lookup(path util.FullPath, unixTime int64, isDirectory bool, isHardlink bool, possibleInode uint64, isLookup bool) uint64 {
i.Lock()
defer i.Unlock()
@@ -54,9 +92,9 @@ func (i *InodeToPath) Lookup(path util.FullPath, unixTime int64, isDirectory boo
}
} else {
if !isLookup {
- i.inode2path[inode] = &InodeEntry{path, 0, isDirectory, false}
+ i.inode2path[inode] = &InodeEntry{[]util.FullPath{path}, 0, isDirectory, false}
} else {
- i.inode2path[inode] = &InodeEntry{path, 1, isDirectory, false}
+ i.inode2path[inode] = &InodeEntry{[]util.FullPath{path}, 1, isDirectory, false}
}
}
@@ -94,10 +132,10 @@ func (i *InodeToPath) GetPath(inode uint64) (util.FullPath, fuse.Status) {
i.RLock()
defer i.RUnlock()
path, found := i.inode2path[inode]
- if !found {
+ if !found || len(path.paths) == 0 {
return "", fuse.ENOENT
}
- return path.FullPath, fuse.OK
+ return path.paths[0], fuse.OK
}
func (i *InodeToPath) HasPath(path util.FullPath) bool {
@@ -142,12 +180,44 @@ func (i *InodeToPath) HasInode(inode uint64) bool {
return found
}
+func (i *InodeToPath) AddPath(inode uint64, path util.FullPath) {
+ i.Lock()
+ defer i.Unlock()
+ i.path2inode[path] = inode
+
+ ie, found := i.inode2path[inode]
+ if found {
+ ie.paths = append(ie.paths, path)
+ ie.nlookup++
+ } else {
+ i.inode2path[inode] = &InodeEntry{
+ paths: []util.FullPath{path},
+ nlookup: 1,
+ isDirectory: false,
+ isChildrenCached: false,
+ }
+ }
+}
+
func (i *InodeToPath) RemovePath(path util.FullPath) {
i.Lock()
defer i.Unlock()
inode, found := i.path2inode[path]
if found {
delete(i.path2inode, path)
+ i.removePathFromInode2Path(inode, path)
+ }
+}
+
+func (i *InodeToPath) removePathFromInode2Path(inode uint64, path util.FullPath) {
+ ie, found := i.inode2path[inode]
+ if !found {
+ return
+ }
+ if !ie.removeOnePath(path) {
+ return
+ }
+ if len(ie.paths) == 0 {
delete(i.inode2path, inode)
}
}
@@ -158,7 +228,7 @@ func (i *InodeToPath) MovePath(sourcePath, targetPath util.FullPath) (sourceInod
sourceInode, sourceFound := i.path2inode[sourcePath]
targetInode, targetFound := i.path2inode[targetPath]
if targetFound {
- delete(i.inode2path, targetInode)
+ i.removePathFromInode2Path(targetInode, targetPath)
delete(i.path2inode, targetPath)
}
if sourceFound {
@@ -170,7 +240,11 @@ func (i *InodeToPath) MovePath(sourcePath, targetPath util.FullPath) (sourceInod
return
}
if entry, entryFound := i.inode2path[sourceInode]; entryFound {
- entry.FullPath = targetPath
+ for i, p := range entry.paths {
+ if p == sourcePath {
+ entry.paths[i] = targetPath
+ }
+ }
entry.isChildrenCached = false
if !targetFound {
entry.nlookup++
@@ -187,7 +261,9 @@ func (i *InodeToPath) Forget(inode, nlookup uint64, onForgetDir func(dir util.Fu
if found {
path.nlookup -= nlookup
if path.nlookup <= 0 {
- delete(i.path2inode, path.FullPath)
+ for _, p := range path.paths {
+ delete(i.path2inode, p)
+ }
delete(i.inode2path, inode)
}
}
@@ -195,7 +271,9 @@ func (i *InodeToPath) Forget(inode, nlookup uint64, onForgetDir func(dir util.Fu
if found {
if path.isDirectory && path.nlookup <= 0 && onForgetDir != nil {
path.isChildrenCached = false
- onForgetDir(path.FullPath)
+ for _, p := range path.paths {
+ onForgetDir(p)
+ }
}
}
}
diff --git a/weed/mount/inode_to_path_test.go b/weed/mount/inode_to_path_test.go
new file mode 100644
index 000000000..0e7af21ee
--- /dev/null
+++ b/weed/mount/inode_to_path_test.go
@@ -0,0 +1,93 @@
+package mount
+
+import (
+ "github.com/chrislusf/seaweedfs/weed/util"
+ "testing"
+)
+
+func TestInodeEntry_removeOnePath(t *testing.T) {
+ tests := []struct {
+ name string
+ entry InodeEntry
+ p util.FullPath
+ want bool
+ count int
+ }{
+ {
+ name: "actual case",
+ entry: InodeEntry{
+ paths: []util.FullPath{"/pjd/nx", "/pjd/n0"},
+ },
+ p: "/pjd/nx",
+ want: true,
+ count: 1,
+ },
+ {
+ name: "empty",
+ entry: InodeEntry{},
+ p: "x",
+ want: false,
+ count: 0,
+ },
+ {
+ name: "single",
+ entry: InodeEntry{
+ paths: []util.FullPath{"/x"},
+ },
+ p: "/x",
+ want: true,
+ count: 0,
+ },
+ {
+ name: "first",
+ entry: InodeEntry{
+ paths: []util.FullPath{"/x", "/y", "/z"},
+ },
+ p: "/x",
+ want: true,
+ count: 2,
+ },
+ {
+ name: "middle",
+ entry: InodeEntry{
+ paths: []util.FullPath{"/x", "/y", "/z"},
+ },
+ p: "/y",
+ want: true,
+ count: 2,
+ },
+ {
+ name: "last",
+ entry: InodeEntry{
+ paths: []util.FullPath{"/x", "/y", "/z"},
+ },
+ p: "/z",
+ want: true,
+ count: 2,
+ },
+ {
+ name: "not found",
+ entry: InodeEntry{
+ paths: []util.FullPath{"/x", "/y", "/z"},
+ },
+ p: "/t",
+ want: false,
+ count: 3,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := tt.entry.removeOnePath(tt.p); got != tt.want {
+ t.Errorf("removeOnePath() = %v, want %v", got, tt.want)
+ }
+ if tt.count != len(tt.entry.paths) {
+ t.Errorf("removeOnePath path count = %v, want %v", len(tt.entry.paths), tt.count)
+ }
+ for i, p := range tt.entry.paths {
+ if p == tt.p {
+ t.Errorf("removeOnePath found path still exists at %v, %v", i, p)
+ }
+ }
+ })
+ }
+}
diff --git a/weed/mount/meta_cache/meta_cache_subscribe.go b/weed/mount/meta_cache/meta_cache_subscribe.go
index c8ccdd375..7086cd0a3 100644
--- a/weed/mount/meta_cache/meta_cache_subscribe.go
+++ b/weed/mount/meta_cache/meta_cache_subscribe.go
@@ -57,8 +57,10 @@ func SubscribeMetaEvents(mc *MetaCache, selfSignature int32, client filer_pb.Fil
}
+ var clientEpoch int32
util.RetryForever("followMetaUpdates", func() error {
- return pb.WithFilerClientFollowMetadata(client, "mount", selfSignature, dir, &lastTsNs, 0, selfSignature, processEventFn, pb.FatalOnError)
+ clientEpoch++
+ return pb.WithFilerClientFollowMetadata(client, "mount", selfSignature, clientEpoch, dir, &lastTsNs, 0, selfSignature, processEventFn, pb.FatalOnError)
}, func(err error) bool {
glog.Errorf("follow metadata updates: %v", err)
return true
diff --git a/weed/mount/weedfs.go b/weed/mount/weedfs.go
index 584174202..f360594f9 100644
--- a/weed/mount/weedfs.go
+++ b/weed/mount/weedfs.go
@@ -125,7 +125,7 @@ func (wfs *WFS) Init(server *fuse.Server) {
wfs.fuseServer = server
}
-func (wfs *WFS) maybeReadEntry(inode uint64) (path util.FullPath, fh *FileHandle, entry *filer_pb.Entry, status fuse.Status) {
+func (wfs *WFS) maybeReadEntry(inode uint64, followSymLink bool) (path util.FullPath, fh *FileHandle, entry *filer_pb.Entry, targetInode uint64, status fuse.Status) {
path, status = wfs.inodeToPath.GetPath(inode)
if status != fuse.OK {
return
@@ -136,9 +136,19 @@ func (wfs *WFS) maybeReadEntry(inode uint64) (path util.FullPath, fh *FileHandle
if entry != nil && fh.entry.Attributes == nil {
entry.Attributes = &filer_pb.FuseAttributes{}
}
- return path, fh, entry, fuse.OK
+ } else {
+ entry, status = wfs.maybeLoadEntry(path)
+ }
+ targetInode = inode
+ if status == fuse.OK && followSymLink && entry.FileMode()&os.ModeSymlink != 0 {
+ if entry != nil && entry.Attributes != nil && entry.Attributes.Inode != 0 {
+ targetInode = entry.Attributes.Inode
+ }
+ target := util.FullPath(filepath.Join(string(path), "../"+entry.Attributes.SymlinkTarget))
+ targetParent, _ := target.DirAndName()
+ wfs.inodeToPath.EnsurePath(util.FullPath(targetParent), true)
+ entry, status = wfs.maybeLoadEntry(target)
}
- entry, status = wfs.maybeLoadEntry(path)
return
}
diff --git a/weed/mount/weedfs_attr.go b/weed/mount/weedfs_attr.go
index be504f5e2..dc7a3bc85 100644
--- a/weed/mount/weedfs_attr.go
+++ b/weed/mount/weedfs_attr.go
@@ -16,15 +16,16 @@ func (wfs *WFS) GetAttr(cancel <-chan struct{}, input *fuse.GetAttrIn, out *fuse
return fuse.OK
}
- _, _, entry, status := wfs.maybeReadEntry(input.NodeId)
+ inode := input.NodeId
+ _, _, entry, inode, status := wfs.maybeReadEntry(inode, false)
if status == fuse.OK {
out.AttrValid = 1
- wfs.setAttrByPbEntry(&out.Attr, input.NodeId, entry)
+ wfs.setAttrByPbEntry(&out.Attr, inode, entry)
return status
} else {
- if fh, found := wfs.fhmap.FindFileHandle(input.NodeId); found {
+ if fh, found := wfs.fhmap.FindFileHandle(inode); found {
out.AttrValid = 1
- wfs.setAttrByPbEntry(&out.Attr, input.NodeId, fh.entry)
+ wfs.setAttrByPbEntry(&out.Attr, inode, fh.entry)
out.Nlink = 0
return fuse.OK
}
@@ -39,7 +40,7 @@ func (wfs *WFS) SetAttr(cancel <-chan struct{}, input *fuse.SetAttrIn, out *fuse
return fuse.Status(syscall.ENOSPC)
}
- path, fh, entry, status := wfs.maybeReadEntry(input.NodeId)
+ path, fh, entry, inode, status := wfs.maybeReadEntry(input.NodeId, false)
if status != fuse.OK {
return status
}
@@ -48,7 +49,7 @@ func (wfs *WFS) SetAttr(cancel <-chan struct{}, input *fuse.SetAttrIn, out *fuse
defer fh.entryLock.Unlock()
}
- if size, ok := input.GetSize(); ok {
+ if size, ok := input.GetSize(); ok && entry != nil {
glog.V(4).Infof("%v setattr set size=%v chunks=%d", path, size, len(entry.Chunks))
if size < filer.FileSize(entry) {
// fmt.Printf("truncate %v \n", fullPath)
@@ -111,7 +112,7 @@ func (wfs *WFS) SetAttr(cancel <-chan struct{}, input *fuse.SetAttrIn, out *fuse
}
out.AttrValid = 1
- wfs.setAttrByPbEntry(&out.Attr, input.NodeId, entry)
+ wfs.setAttrByPbEntry(&out.Attr, inode, entry)
if fh != nil {
fh.dirtyMetadata = true
@@ -138,9 +139,18 @@ func (wfs *WFS) setRootAttr(out *fuse.AttrOut) {
func (wfs *WFS) setAttrByPbEntry(out *fuse.Attr, inode uint64, entry *filer_pb.Entry) {
out.Ino = inode
+ if entry.Attributes != nil && entry.Attributes.Inode != 0 {
+ out.Ino = entry.Attributes.Inode
+ }
out.Size = filer.FileSize(entry)
+ if entry.FileMode()&os.ModeSymlink != 0 {
+ out.Size = uint64(len(entry.Attributes.SymlinkTarget))
+ }
out.Blocks = (out.Size + blockSize - 1) / blockSize
setBlksize(out, blockSize)
+ if entry == nil {
+ return
+ }
out.Mtime = uint64(entry.Attributes.Mtime)
out.Ctime = uint64(entry.Attributes.Mtime)
out.Atime = uint64(entry.Attributes.Mtime)
@@ -158,6 +168,9 @@ func (wfs *WFS) setAttrByPbEntry(out *fuse.Attr, inode uint64, entry *filer_pb.E
func (wfs *WFS) setAttrByFilerEntry(out *fuse.Attr, inode uint64, entry *filer.Entry) {
out.Ino = inode
out.Size = entry.FileSize
+ if entry.Mode&os.ModeSymlink != 0 {
+ out.Size = uint64(len(entry.SymlinkTarget))
+ }
out.Blocks = (out.Size + blockSize - 1) / blockSize
setBlksize(out, blockSize)
out.Atime = uint64(entry.Attr.Mtime.Unix())
diff --git a/weed/mount/weedfs_filehandle.go b/weed/mount/weedfs_filehandle.go
index d769e51c5..ec174dd11 100644
--- a/weed/mount/weedfs_filehandle.go
+++ b/weed/mount/weedfs_filehandle.go
@@ -7,7 +7,7 @@ import (
func (wfs *WFS) AcquireHandle(inode uint64, uid, gid uint32) (fileHandle *FileHandle, status fuse.Status) {
var entry *filer_pb.Entry
- _, _, entry, status = wfs.maybeReadEntry(inode)
+ _, _, entry, inode, status = wfs.maybeReadEntry(inode, true)
if status == fuse.OK {
// need to AcquireFileHandle again to ensure correct handle counter
fileHandle = wfs.fhmap.AcquireFileHandle(wfs, inode, entry)
diff --git a/weed/mount/weedfs_link.go b/weed/mount/weedfs_link.go
index 2ab412fd5..56c89dabe 100644
--- a/weed/mount/weedfs_link.go
+++ b/weed/mount/weedfs_link.go
@@ -102,9 +102,9 @@ func (wfs *WFS) Link(cancel <-chan struct{}, in *fuse.LinkIn, name string, out *
return fuse.EIO
}
- inode := wfs.inodeToPath.Lookup(newEntryPath, oldEntry.Attributes.Crtime, oldEntry.IsDirectory, true, oldEntry.Attributes.Inode, true)
+ wfs.inodeToPath.AddPath(oldEntry.Attributes.Inode, newEntryPath)
- wfs.outputPbEntry(out, inode, request.Entry)
+ wfs.outputPbEntry(out, oldEntry.Attributes.Inode, request.Entry)
return fuse.OK
}
diff --git a/weed/mount/weedfs_rename.go b/weed/mount/weedfs_rename.go
index 538cfead7..eae86270f 100644
--- a/weed/mount/weedfs_rename.go
+++ b/weed/mount/weedfs_rename.go
@@ -239,11 +239,11 @@ func (wfs *WFS) handleRenameResponse(ctx context.Context, resp *filer_pb.StreamR
fh.entry.Name = newName
}
// invalidate attr and data
- wfs.fuseServer.InodeNotify(sourceInode, 0, -1)
+ // wfs.fuseServer.InodeNotify(sourceInode, 0, -1)
}
if targetInode != 0 {
// invalidate attr and data
- wfs.fuseServer.InodeNotify(targetInode, 0, -1)
+ // wfs.fuseServer.InodeNotify(targetInode, 0, -1)
}
} else if resp.EventNotification.OldEntry != nil {
diff --git a/weed/mount/weedfs_xattr.go b/weed/mount/weedfs_xattr.go
index 64cc0f6f0..74ccd9336 100644
--- a/weed/mount/weedfs_xattr.go
+++ b/weed/mount/weedfs_xattr.go
@@ -36,7 +36,7 @@ func (wfs *WFS) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr str
return 0, fuse.EINVAL
}
- _, _, entry, status := wfs.maybeReadEntry(header.NodeId)
+ _, _, entry, _, status := wfs.maybeReadEntry(header.NodeId, true)
if status != fuse.OK {
return 0, status
}
@@ -102,10 +102,13 @@ func (wfs *WFS) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, attr st
}
}
- path, fh, entry, status := wfs.maybeReadEntry(input.NodeId)
+ path, fh, entry, _, status := wfs.maybeReadEntry(input.NodeId, true)
if status != fuse.OK {
return status
}
+ if entry == nil {
+ return fuse.ENOENT
+ }
if fh != nil {
fh.entryLock.Lock()
defer fh.entryLock.Unlock()
@@ -140,7 +143,7 @@ func (wfs *WFS) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, dest []
return 0, fuse.Status(syscall.ENOTSUP)
}
- _, _, entry, status := wfs.maybeReadEntry(header.NodeId)
+ _, _, entry, _, status := wfs.maybeReadEntry(header.NodeId, true)
if status != fuse.OK {
return 0, status
}
@@ -177,10 +180,13 @@ func (wfs *WFS) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr
if len(attr) == 0 {
return fuse.EINVAL
}
- path, fh, entry, status := wfs.maybeReadEntry(header.NodeId)
+ path, fh, entry, _, status := wfs.maybeReadEntry(header.NodeId, true)
if status != fuse.OK {
return status
}
+ if entry == nil {
+ return fuse.OK
+ }
if fh != nil {
fh.entryLock.Lock()
defer fh.entryLock.Unlock()
diff --git a/weed/mount/wfs_save.go b/weed/mount/wfs_save.go
index 0cac30453..964f1713d 100644
--- a/weed/mount/wfs_save.go
+++ b/weed/mount/wfs_save.go
@@ -60,7 +60,7 @@ func (wfs *WFS) mapPbIdFromLocalToFiler(entry *filer_pb.Entry) {
}
func checkName(name string) fuse.Status {
- if len(name) >= 256 {
+ if len(name) >= 4096 {
return fuse.Status(syscall.ENAMETOOLONG)
}
return fuse.OK