[vfs2] Fix tmpfs mounting.

Earlier we were using NLink to decide if /tmp is empty or not. However, NLink
at best tells us about the number of subdirectories (via the ".." entries).
NLink = n + 2 for n subdirectories. But it does not tell us if the directory is
empty. There still might be non-directory files. We could also not rely on
NLink because host overlayfs always returned 1.

VFS1 uses Readdir to decide if the directory is empty. Used a similar approach.
We now use IterDirents to decide if the "/tmp" directory is empty.

Fixes #3369

PiperOrigin-RevId: 325554234
This commit is contained in:
Ayush Ranjan
2020-08-07 20:08:47 -07:00
committed by gVisor bot
parent 977618c8e1
commit 3be26a271c
2 changed files with 25 additions and 12 deletions
+6 -4
View File
@@ -51,7 +51,7 @@ func (d *dentry) collectWhiteoutsForRmdirLocked(ctx context.Context) (map[string
// Reuse slice allocated for maybeWhiteouts from a previous layer to
// reduce allocations.
maybeWhiteouts = maybeWhiteouts[:0]
if err := layerFD.IterDirents(ctx, vfs.IterDirentsCallbackFunc(func(dirent vfs.Dirent) error {
err = layerFD.IterDirents(ctx, vfs.IterDirentsCallbackFunc(func(dirent vfs.Dirent) error {
if dirent.Name == "." || dirent.Name == ".." {
return nil
}
@@ -68,7 +68,8 @@ func (d *dentry) collectWhiteoutsForRmdirLocked(ctx context.Context) (map[string
}
// Non-whiteout file in the directory prevents rmdir.
return syserror.ENOTEMPTY
})); err != nil {
}))
if err != nil {
readdirErr = err
return false
}
@@ -182,7 +183,7 @@ func (d *dentry) getDirents(ctx context.Context) ([]vfs.Dirent, error) {
// Reuse slice allocated for maybeWhiteouts from a previous layer to
// reduce allocations.
maybeWhiteouts = maybeWhiteouts[:0]
if err := layerFD.IterDirents(ctx, vfs.IterDirentsCallbackFunc(func(dirent vfs.Dirent) error {
err = layerFD.IterDirents(ctx, vfs.IterDirentsCallbackFunc(func(dirent vfs.Dirent) error {
if dirent.Name == "." || dirent.Name == ".." {
return nil
}
@@ -201,7 +202,8 @@ func (d *dentry) getDirents(ctx context.Context) ([]vfs.Dirent, error) {
dirent.NextOff = int64(len(dirents) + 1)
dirents = append(dirents, dirent)
return nil
})); err != nil {
}))
if err != nil {
readdirErr = err
return false
}
+19 -8
View File
@@ -400,21 +400,28 @@ func (c *containerMounter) mountTmpVFS2(ctx context.Context, conf *Config, creds
Path: fspath.Parse("/tmp"),
}
// TODO(gvisor.dev/issue/2782): Use O_PATH when available.
statx, err := c.k.VFS().StatAt(ctx, creds, &pop, &vfs.StatOptions{})
fd, err := c.k.VFS().OpenAt(ctx, creds, &pop, &vfs.OpenOptions{Flags: linux.O_RDONLY | linux.O_DIRECTORY})
switch err {
case nil:
// Found '/tmp' in filesystem, check if it's empty.
if linux.FileMode(statx.Mode).FileType() != linux.ModeDirectory {
// Not a dir?! Leave it be.
defer fd.DecRef(ctx)
err := fd.IterDirents(ctx, vfs.IterDirentsCallbackFunc(func(dirent vfs.Dirent) error {
if dirent.Name != "." && dirent.Name != ".." {
return syserror.ENOTEMPTY
}
return nil
}
if statx.Nlink > 2 {
}))
switch err {
case nil:
log.Infof(`Mounting internal tmpfs on top of empty "/tmp"`)
case syserror.ENOTEMPTY:
// If more than "." and ".." is found, skip internal tmpfs to prevent
// hiding existing files.
log.Infof(`Skipping internal tmpfs mount for "/tmp" because it's not empty`)
return nil
default:
return err
}
log.Infof(`Mounting internal tmpfs on top of empty "/tmp"`)
fallthrough
case syserror.ENOENT:
@@ -429,8 +436,12 @@ func (c *containerMounter) mountTmpVFS2(ctx context.Context, conf *Config, creds
}
return c.mountSubmountVFS2(ctx, conf, mns, creds, &mountAndFD{Mount: tmpMount})
case syserror.ENOTDIR:
// Not a dir?! Let it be.
return nil
default:
return fmt.Errorf(`stating "/tmp" inside container: %w`, err)
return fmt.Errorf(`opening "/tmp" inside container: %w`, err)
}
}