From 2dcb8cb93b951bd8457cdb670edf2146b068a836 Mon Sep 17 00:00:00 2001 From: chrislu Date: Tue, 11 Jan 2022 23:44:48 -0800 Subject: [PATCH] POSIX: ensure file and directory inodes are different this is just an in memory representation. POSIX wants different inode numbers for the same named file or directory. --- weed/filesys/dir.go | 15 +++++----- weed/filesys/dir_rename.go | 30 +++++++++++-------- weed/filesys/file.go | 2 +- weed/filesys/meta_cache/meta_cache.go | 8 ++--- .../meta_cache/meta_cache_subscribe.go | 6 ++-- weed/filesys/wfs.go | 6 ++-- weed/util/fullpath.go | 10 +++++-- 7 files changed, 43 insertions(+), 34 deletions(-) diff --git a/weed/filesys/dir.go b/weed/filesys/dir.go index 386b40c74..7471e53fa 100644 --- a/weed/filesys/dir.go +++ b/weed/filesys/dir.go @@ -103,7 +103,7 @@ func (dir *Dir) Fsync(ctx context.Context, req *fuse.FsyncRequest) error { func (dir *Dir) newFile(name string) fs.Node { fileFullPath := util.NewFullPath(dir.FullPath(), name) - fileId := fileFullPath.AsInode() + fileId := fileFullPath.AsInode(false) dir.wfs.handlesLock.Lock() existingHandle, found := dir.wfs.handles[fileId] dir.wfs.handlesLock.Unlock() @@ -122,7 +122,7 @@ func (dir *Dir) newFile(name string) fs.Node { func (dir *Dir) newDirectory(fullpath util.FullPath) fs.Node { - return &Dir{name: fullpath.Name(), wfs: dir.wfs, parent: dir, id: fullpath.AsInode()} + return &Dir{name: fullpath.Name(), wfs: dir.wfs, parent: dir, id: fullpath.AsInode(true)} } @@ -316,7 +316,6 @@ func (dir *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse. } // resp.EntryValid = time.Second - resp.Attr.Inode = fullFilePath.AsInode() resp.Attr.Valid = time.Second resp.Attr.Size = localEntry.FileSize resp.Attr.Mtime = localEntry.Attr.Mtime @@ -342,10 +341,10 @@ func (dir *Dir) ReadDirAll(ctx context.Context) (ret []fuse.Dirent, err error) { processEachEntryFn := func(entry *filer.Entry, isLast bool) { if entry.IsDirectory() { - dirent := fuse.Dirent{Name: entry.Name(), Type: fuse.DT_Dir, Inode: dirPath.Child(entry.Name()).AsInode()} + dirent := fuse.Dirent{Name: entry.Name(), Type: fuse.DT_Dir, Inode: dirPath.Child(entry.Name()).AsInode(true)} ret = append(ret, dirent) } else { - dirent := fuse.Dirent{Name: entry.Name(), Type: findFileType(uint16(entry.Attr.Mode)), Inode: dirPath.Child(entry.Name()).AsInode()} + dirent := fuse.Dirent{Name: entry.Name(), Type: findFileType(uint16(entry.Attr.Mode)), Inode: dirPath.Child(entry.Name()).AsInode(false)} ret = append(ret, dirent) } } @@ -365,7 +364,7 @@ func (dir *Dir) ReadDirAll(ctx context.Context) (ret []fuse.Dirent, err error) { // create proper . and .. directories ret = append(ret, fuse.Dirent{ - Inode: dirPath.AsInode(), + Inode: dirPath.AsInode(true), Name: ".", Type: fuse.DT_Dir, }) @@ -375,7 +374,7 @@ func (dir *Dir) ReadDirAll(ctx context.Context) (ret []fuse.Dirent, err error) { if string(dirPath) == dir.wfs.option.FilerMountRootPath { inode = dir.wfs.option.MountParentInode } else { - inode = util.FullPath(dir.parent.FullPath()).AsInode() + inode = util.FullPath(dir.parent.FullPath()).AsInode(true) } ret = append(ret, fuse.Dirent{ @@ -444,7 +443,7 @@ func (dir *Dir) removeOneFile(req *fuse.RemoveRequest) error { // remove current file handle if any dir.wfs.handlesLock.Lock() defer dir.wfs.handlesLock.Unlock() - inodeId := filePath.AsInode() + inodeId := filePath.AsInode(false) if fh, ok := dir.wfs.handles[inodeId]; ok { delete(dir.wfs.handles, inodeId) fh.isDeleted = true diff --git a/weed/filesys/dir_rename.go b/weed/filesys/dir_rename.go index 01a8df175..6628b0799 100644 --- a/weed/filesys/dir_rename.go +++ b/weed/filesys/dir_rename.go @@ -84,11 +84,13 @@ func (dir *Dir) handleRenameResponse(ctx context.Context, resp *filer_pb.StreamR oldParent, newParent := util.FullPath(resp.Directory), util.FullPath(resp.EventNotification.NewParentPath) oldName, newName := resp.EventNotification.OldEntry.Name, resp.EventNotification.NewEntry.Name + isDirectory := newEntry.IsDirectory() + oldPath := oldParent.Child(oldName) newPath := newParent.Child(newName) - oldFsNode := NodeWithId(oldPath.AsInode()) - newFsNode := NodeWithId(newPath.AsInode()) - newDirNode, found := dir.wfs.Server.FindInternalNode(NodeWithId(newParent.AsInode())) + oldFsNode := NodeWithId(oldPath.AsInode(isDirectory)) + newFsNode := NodeWithId(newPath.AsInode(isDirectory)) + newDirNode, found := dir.wfs.Server.FindInternalNode(NodeWithId(newParent.AsInode(true))) var newDir *Dir if found { newDir = newDirNode.(*Dir) @@ -113,17 +115,19 @@ func (dir *Dir) handleRenameResponse(ctx context.Context, resp *filer_pb.StreamR }) // change file handle - inodeId := oldPath.AsInode() - dir.wfs.handlesLock.Lock() - if existingHandle, found := dir.wfs.handles[inodeId]; found && existingHandle != nil { - glog.V(4).Infof("opened file handle %s => %s", oldPath, newPath) - delete(dir.wfs.handles, inodeId) - existingHandle.handle = newPath.AsInode() - existingHandle.f.entry.Name = newName - existingHandle.f.id = newPath.AsInode() - dir.wfs.handles[newPath.AsInode()] = existingHandle + if !isDirectory { + inodeId := oldPath.AsInode(isDirectory) + dir.wfs.handlesLock.Lock() + if existingHandle, found := dir.wfs.handles[inodeId]; found && existingHandle != nil { + glog.V(4).Infof("opened file handle %s => %s", oldPath, newPath) + delete(dir.wfs.handles, inodeId) + existingHandle.handle = newPath.AsInode(isDirectory) + existingHandle.f.entry.Name = newName + existingHandle.f.id = newPath.AsInode(isDirectory) + dir.wfs.handles[newPath.AsInode(isDirectory)] = existingHandle + } + dir.wfs.handlesLock.Unlock() } - dir.wfs.handlesLock.Unlock() } else if resp.EventNotification.OldEntry != nil { // without new entry, only old entry name exists. This is the second step to delete old entry diff --git a/weed/filesys/file.go b/weed/filesys/file.go index 244ed38ea..d9b81469b 100644 --- a/weed/filesys/file.go +++ b/weed/filesys/file.go @@ -259,7 +259,7 @@ func (file *File) Fsync(ctx context.Context, req *fuse.FsyncRequest) error { func (file *File) Forget() { t := util.NewFullPath(file.dir.FullPath(), file.Name) glog.V(4).Infof("Forget file %s", t) - file.wfs.ReleaseHandle(t, fuse.HandleID(t.AsInode())) + file.wfs.ReleaseHandle(t, fuse.HandleID(t.AsInode(false))) } func (file *File) maybeLoadEntry(ctx context.Context) (entry *filer_pb.Entry, err error) { diff --git a/weed/filesys/meta_cache/meta_cache.go b/weed/filesys/meta_cache/meta_cache.go index 6d5eeca03..adb3f144f 100644 --- a/weed/filesys/meta_cache/meta_cache.go +++ b/weed/filesys/meta_cache/meta_cache.go @@ -18,16 +18,16 @@ type MetaCache struct { // sync.RWMutex visitedBoundary *bounded_tree.BoundedTree uidGidMapper *UidGidMapper - invalidateFunc func(util.FullPath) + invalidateFunc func(fullpath util.FullPath, isDirectory bool) } -func NewMetaCache(dbFolder string, baseDir util.FullPath, uidGidMapper *UidGidMapper, invalidateFunc func(util.FullPath)) *MetaCache { +func NewMetaCache(dbFolder string, baseDir util.FullPath, uidGidMapper *UidGidMapper, invalidateFunc func(util.FullPath, bool)) *MetaCache { return &MetaCache{ localStore: openMetaStore(dbFolder), visitedBoundary: bounded_tree.NewBoundedTree(baseDir), uidGidMapper: uidGidMapper, - invalidateFunc: func(fullpath util.FullPath) { - invalidateFunc(fullpath) + invalidateFunc: func(fullpath util.FullPath, isDirectory bool) { + invalidateFunc(fullpath, isDirectory) }, } } diff --git a/weed/filesys/meta_cache/meta_cache_subscribe.go b/weed/filesys/meta_cache/meta_cache_subscribe.go index 64a8b76b9..f33461dd4 100644 --- a/weed/filesys/meta_cache/meta_cache_subscribe.go +++ b/weed/filesys/meta_cache/meta_cache_subscribe.go @@ -40,16 +40,16 @@ func SubscribeMetaEvents(mc *MetaCache, selfSignature int32, client filer_pb.Fil if err == nil { if message.OldEntry != nil && message.NewEntry != nil { oldKey := util.NewFullPath(resp.Directory, message.OldEntry.Name) - mc.invalidateFunc(oldKey) + mc.invalidateFunc(oldKey, message.OldEntry.IsDirectory) if message.OldEntry.Name != message.NewEntry.Name { newKey := util.NewFullPath(dir, message.NewEntry.Name) - mc.invalidateFunc(newKey) + mc.invalidateFunc(newKey, message.NewEntry.IsDirectory) } } else if message.OldEntry == nil && message.NewEntry != nil { // no need to invaalidate } else if message.OldEntry != nil && message.NewEntry == nil { oldKey := util.NewFullPath(resp.Directory, message.OldEntry.Name) - mc.invalidateFunc(oldKey) + mc.invalidateFunc(oldKey, message.OldEntry.IsDirectory) } } diff --git a/weed/filesys/wfs.go b/weed/filesys/wfs.go index c9a7ac0a1..63b83f7ef 100644 --- a/weed/filesys/wfs.go +++ b/weed/filesys/wfs.go @@ -109,15 +109,15 @@ func NewSeaweedFileSystem(option *Option) *WFS { wfs.chunkCache = chunk_cache.NewTieredChunkCache(256, option.getUniqueCacheDir(), option.CacheSizeMB, 1024*1024) } - wfs.metaCache = meta_cache.NewMetaCache(path.Join(option.getUniqueCacheDir(), "meta"), util.FullPath(option.FilerMountRootPath), option.UidGidMapper, func(filePath util.FullPath) { + wfs.metaCache = meta_cache.NewMetaCache(path.Join(option.getUniqueCacheDir(), "meta"), util.FullPath(option.FilerMountRootPath), option.UidGidMapper, func(filePath util.FullPath, isDirectory bool) { - fsNode := NodeWithId(filePath.AsInode()) + fsNode := NodeWithId(filePath.AsInode(isDirectory)) if err := wfs.Server.InvalidateNodeData(fsNode); err != nil { glog.V(4).Infof("InvalidateNodeData %s : %v", filePath, err) } dir, name := filePath.DirAndName() - parent := NodeWithId(util.FullPath(dir).AsInode()) + parent := NodeWithId(util.FullPath(dir).AsInode(true)) if dir == option.FilerMountRootPath { parent = NodeWithId(1) } diff --git a/weed/util/fullpath.go b/weed/util/fullpath.go index 85028b052..404187e8b 100644 --- a/weed/util/fullpath.go +++ b/weed/util/fullpath.go @@ -41,8 +41,14 @@ func (fp FullPath) Child(name string) FullPath { return FullPath(dir + "/" + noPrefix) } -func (fp FullPath) AsInode() uint64 { - return uint64(HashStringToLong(string(fp))) +func (fp FullPath) AsInode(isDirectory bool) uint64 { + inode := uint64(HashStringToLong(string(fp))) + if isDirectory { + inode = inode - inode%2 // even + } else { + inode = inode - inode%2 + 1 // odd + } + return inode } // split, but skipping the root