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.
		
		
		
		
		
			
		
			
				
					
					
						
							292 lines
						
					
					
						
							6.6 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							292 lines
						
					
					
						
							6.6 KiB
						
					
					
				| package mount | |
| 
 | |
| import ( | |
| 	"github.com/hanwen/go-fuse/v2/fuse" | |
| 	"github.com/seaweedfs/seaweedfs/weed/glog" | |
| 	"github.com/seaweedfs/seaweedfs/weed/util" | |
| 	"sync" | |
| 	"time" | |
| ) | |
| 
 | |
| type InodeToPath struct { | |
| 	sync.RWMutex | |
| 	nextInodeId     uint64 | |
| 	cacheMetaTtlSec time.Duration | |
| 	inode2path      map[uint64]*InodeEntry | |
| 	path2inode      map[util.FullPath]uint64 | |
| } | |
| type InodeEntry struct { | |
| 	paths             []util.FullPath | |
| 	nlookup           uint64 | |
| 	isDirectory       bool | |
| 	isChildrenCached  bool | |
| 	cachedExpiresTime time.Time | |
| } | |
| 
 | |
| 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, ttlSec int) *InodeToPath { | |
| 	t := &InodeToPath{ | |
| 		inode2path:      make(map[uint64]*InodeEntry), | |
| 		path2inode:      make(map[util.FullPath]uint64), | |
| 		cacheMetaTtlSec: time.Second * time.Duration(ttlSec), | |
| 	} | |
| 	t.inode2path[1] = &InodeEntry{[]util.FullPath{root}, 1, true, false, time.Time{}} | |
| 	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() | |
| 	inode, found := i.path2inode[path] | |
| 	if !found { | |
| 		if possibleInode == 0 { | |
| 			inode = path.AsInode(unixTime) | |
| 		} else { | |
| 			inode = possibleInode | |
| 		} | |
| 		if !isHardlink { | |
| 			for _, found := i.inode2path[inode]; found; inode++ { | |
| 				_, found = i.inode2path[inode+1] | |
| 			} | |
| 		} | |
| 	} | |
| 	i.path2inode[path] = inode | |
| 
 | |
| 	if _, found := i.inode2path[inode]; found { | |
| 		if isLookup { | |
| 			i.inode2path[inode].nlookup++ | |
| 		} | |
| 	} else { | |
| 		if !isLookup { | |
| 			i.inode2path[inode] = &InodeEntry{[]util.FullPath{path}, 0, isDirectory, false, time.Time{}} | |
| 		} else { | |
| 			i.inode2path[inode] = &InodeEntry{[]util.FullPath{path}, 1, isDirectory, false, time.Time{}} | |
| 		} | |
| 	} | |
| 
 | |
| 	return inode | |
| } | |
| 
 | |
| func (i *InodeToPath) AllocateInode(path util.FullPath, unixTime int64) uint64 { | |
| 	if path == "/" { | |
| 		return 1 | |
| 	} | |
| 	i.Lock() | |
| 	defer i.Unlock() | |
| 	inode := path.AsInode(unixTime) | |
| 	for _, found := i.inode2path[inode]; found; inode++ { | |
| 		_, found = i.inode2path[inode] | |
| 	} | |
| 	return inode | |
| } | |
| 
 | |
| func (i *InodeToPath) GetInode(path util.FullPath) (uint64, bool) { | |
| 	if path == "/" { | |
| 		return 1, true | |
| 	} | |
| 	i.Lock() | |
| 	defer i.Unlock() | |
| 	inode, found := i.path2inode[path] | |
| 	if !found { | |
| 		// glog.Fatalf("GetInode unknown inode for %s", path) | |
| 		// this could be the parent for mount point | |
| 	} | |
| 	return inode, found | |
| } | |
| 
 | |
| func (i *InodeToPath) GetPath(inode uint64) (util.FullPath, fuse.Status) { | |
| 	i.RLock() | |
| 	defer i.RUnlock() | |
| 	path, found := i.inode2path[inode] | |
| 	if !found || len(path.paths) == 0 { | |
| 		return "", fuse.ENOENT | |
| 	} | |
| 	return path.paths[0], fuse.OK | |
| } | |
| 
 | |
| func (i *InodeToPath) HasPath(path util.FullPath) bool { | |
| 	i.RLock() | |
| 	defer i.RUnlock() | |
| 	_, found := i.path2inode[path] | |
| 	return found | |
| } | |
| 
 | |
| func (i *InodeToPath) MarkChildrenCached(fullpath util.FullPath) { | |
| 	i.Lock() | |
| 	defer i.Unlock() | |
| 	inode, found := i.path2inode[fullpath] | |
| 	if !found { | |
| 		// https://github.com/seaweedfs/seaweedfs/issues/4968 | |
| 		// glog.Fatalf("MarkChildrenCached not found inode %v", fullpath) | |
| 		glog.Warningf("MarkChildrenCached not found inode %v", fullpath) | |
| 		return | |
| 	} | |
| 	path, found := i.inode2path[inode] | |
| 	path.isChildrenCached = true | |
| 	if i.cacheMetaTtlSec > 0 { | |
| 		path.cachedExpiresTime = time.Now().Add(i.cacheMetaTtlSec) | |
| 	} | |
| } | |
| 
 | |
| func (i *InodeToPath) IsChildrenCached(fullpath util.FullPath) bool { | |
| 	i.RLock() | |
| 	defer i.RUnlock() | |
| 	inode, found := i.path2inode[fullpath] | |
| 	if !found { | |
| 		return false | |
| 	} | |
| 	path, found := i.inode2path[inode] | |
| 	if !found { | |
| 		return false | |
| 	} | |
| 	if path.isChildrenCached { | |
| 		return path.cachedExpiresTime.IsZero() || time.Now().Before(path.cachedExpiresTime) | |
| 	} | |
| 	return false | |
| } | |
| 
 | |
| func (i *InodeToPath) HasInode(inode uint64) bool { | |
| 	if inode == 1 { | |
| 		return true | |
| 	} | |
| 	i.RLock() | |
| 	defer i.RUnlock() | |
| 	_, found := i.inode2path[inode] | |
| 	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) | |
| 	} | |
| } | |
| 
 | |
| func (i *InodeToPath) MovePath(sourcePath, targetPath util.FullPath) (sourceInode, targetInode uint64) { | |
| 	i.Lock() | |
| 	defer i.Unlock() | |
| 	sourceInode, sourceFound := i.path2inode[sourcePath] | |
| 	targetInode, targetFound := i.path2inode[targetPath] | |
| 	if targetFound { | |
| 		i.removePathFromInode2Path(targetInode, targetPath) | |
| 		delete(i.path2inode, targetPath) | |
| 	} | |
| 	if sourceFound { | |
| 		delete(i.path2inode, sourcePath) | |
| 		i.path2inode[targetPath] = sourceInode | |
| 	} else { | |
| 		// it is possible some source folder items has not been visited before | |
| 		// so no need to worry about their source inodes | |
| 		return | |
| 	} | |
| 	if entry, entryFound := i.inode2path[sourceInode]; entryFound { | |
| 		for i, p := range entry.paths { | |
| 			if p == sourcePath { | |
| 				entry.paths[i] = targetPath | |
| 			} | |
| 		} | |
| 		entry.isChildrenCached = false | |
| 		if !targetFound { | |
| 			entry.nlookup++ | |
| 		} | |
| 	} else { | |
| 		glog.Errorf("MovePath %s to %s: sourceInode %d not found", sourcePath, targetPath, sourceInode) | |
| 	} | |
| 	return | |
| } | |
| 
 | |
| func (i *InodeToPath) Forget(inode, nlookup uint64, onForgetDir func(dir util.FullPath)) { | |
| 	i.Lock() | |
| 	path, found := i.inode2path[inode] | |
| 	if found { | |
| 		path.nlookup -= nlookup | |
| 		if path.nlookup <= 0 { | |
| 			for _, p := range path.paths { | |
| 				delete(i.path2inode, p) | |
| 			} | |
| 			delete(i.inode2path, inode) | |
| 		} | |
| 	} | |
| 	i.Unlock() | |
| 	if found { | |
| 		if path.isDirectory && path.nlookup <= 0 && onForgetDir != nil { | |
| 			path.isChildrenCached = false | |
| 			for _, p := range path.paths { | |
| 				onForgetDir(p) | |
| 			} | |
| 		} | |
| 	} | |
| }
 |