mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Preserve directory inode number after copy up in overlayfs.
Earlier we were using the topmost inode and device number to compute the directory inode number. But the topmost layer changes after a copy up which causes a new inode number to be generated after a copy up. This confuses some applications like GNU's FTS library. See fts_safe_changedir() for example which errors out with ENOENT if it observes that the dirctory inode or device number has changed. Now we use the bottommost layer's inode and device numbers. Linux and VFS1 also preserve the directory's inode number after copy up. PiperOrigin-RevId: 456147019
This commit is contained in:
@@ -295,6 +295,13 @@ func (fs *filesystem) lookupLocked(ctx context.Context, parent *dentry, name str
|
||||
return false
|
||||
}
|
||||
|
||||
// Directories use the lowest layer inode and device numbers to generate a
|
||||
// filesystem local inode number. This way the inode number does not change
|
||||
// after copy ups.
|
||||
child.devMajor = atomicbitops.FromUint32(stat.DevMajor)
|
||||
child.devMinor = atomicbitops.FromUint32(stat.DevMinor)
|
||||
child.ino = atomicbitops.FromUint64(stat.Ino)
|
||||
|
||||
// Directories are merged with directories from lower layers if they
|
||||
// are not explicitly opaque.
|
||||
opaqueVal, err := vfsObj.GetXattrAt(ctx, fs.creds, &vfs.PathOperation{
|
||||
@@ -316,9 +323,10 @@ func (fs *filesystem) lookupLocked(ctx context.Context, parent *dentry, name str
|
||||
return nil, topLookupLayer, linuxerr.ENOENT
|
||||
}
|
||||
|
||||
// Device and inode numbers were copied from the topmost layer above;
|
||||
// override them if necessary. We can use RacyLoad() because child is still
|
||||
// being initialized.
|
||||
// Device and inode numbers were copied from the topmost layer above for
|
||||
// non-directories. They were copied from the bottommost layer for
|
||||
// directories. Override them if necessary. We can use RacyLoad() because
|
||||
// child is still being initialized.
|
||||
if child.isDir() {
|
||||
child.ino.Store(fs.newDirIno(child.devMajor.RacyLoad(), child.devMinor.RacyLoad(), child.ino.RacyLoad()))
|
||||
child.devMajor = atomicbitops.FromUint32(linux.UNNAMED_MAJOR)
|
||||
|
||||
@@ -117,7 +117,7 @@ type filesystem struct {
|
||||
renameMu renameRWMutex `state:"nosave"`
|
||||
|
||||
// dirInoCache caches overlay-private directory inode numbers by mapped
|
||||
// topmost device numbers and inode number. dirInoCache is protected by
|
||||
// bottommost device numbers and inode number. dirInoCache is protected by
|
||||
// dirInoCacheMu.
|
||||
dirInoCacheMu dirInoCacheMutex `state:"nosave"`
|
||||
dirInoCache map[layerDevNoAndIno]uint64
|
||||
@@ -310,6 +310,8 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
|
||||
if rootStat.Mode&linux.S_IFMT == linux.S_IFDIR {
|
||||
root.devMajor = atomicbitops.FromUint32(linux.UNNAMED_MAJOR)
|
||||
root.devMinor = atomicbitops.FromUint32(fs.dirDevMinor)
|
||||
// For root dir, it is okay to use top most level's stat to compute inode
|
||||
// number because we don't allow copy ups on root dentries.
|
||||
root.ino.Store(fs.newDirIno(rootStat.DevMajor, rootStat.DevMinor, rootStat.Ino))
|
||||
} else if !root.upperVD.Ok() {
|
||||
root.devMajor = atomicbitops.FromUint32(linux.UNNAMED_MAJOR)
|
||||
|
||||
Reference in New Issue
Block a user