summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/syscalls/linux/sys_file.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/syscalls/linux/sys_file.go')
-rw-r--r--pkg/sentry/syscalls/linux/sys_file.go210
1 files changed, 135 insertions, 75 deletions
diff --git a/pkg/sentry/syscalls/linux/sys_file.go b/pkg/sentry/syscalls/linux/sys_file.go
index 19f579930..2776fdec7 100644
--- a/pkg/sentry/syscalls/linux/sys_file.go
+++ b/pkg/sentry/syscalls/linux/sys_file.go
@@ -17,31 +17,30 @@ package linux
import (
"syscall"
- "gvisor.googlesource.com/gvisor/pkg/abi/linux"
- "gvisor.googlesource.com/gvisor/pkg/sentry/arch"
- "gvisor.googlesource.com/gvisor/pkg/sentry/context"
- "gvisor.googlesource.com/gvisor/pkg/sentry/fs"
- "gvisor.googlesource.com/gvisor/pkg/sentry/fs/lock"
- "gvisor.googlesource.com/gvisor/pkg/sentry/fs/tmpfs"
- "gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
- "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth"
- "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/fasync"
- "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs"
- "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/pipe"
- ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
- "gvisor.googlesource.com/gvisor/pkg/sentry/limits"
- "gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
- "gvisor.googlesource.com/gvisor/pkg/syserror"
+ "gvisor.dev/gvisor/pkg/abi/linux"
+ "gvisor.dev/gvisor/pkg/sentry/arch"
+ "gvisor.dev/gvisor/pkg/sentry/context"
+ "gvisor.dev/gvisor/pkg/sentry/fs"
+ "gvisor.dev/gvisor/pkg/sentry/fs/lock"
+ "gvisor.dev/gvisor/pkg/sentry/fs/tmpfs"
+ "gvisor.dev/gvisor/pkg/sentry/kernel"
+ "gvisor.dev/gvisor/pkg/sentry/kernel/auth"
+ "gvisor.dev/gvisor/pkg/sentry/kernel/fasync"
+ "gvisor.dev/gvisor/pkg/sentry/kernel/kdefs"
+ ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
+ "gvisor.dev/gvisor/pkg/sentry/limits"
+ "gvisor.dev/gvisor/pkg/sentry/usermem"
+ "gvisor.dev/gvisor/pkg/syserror"
)
// fileOpAt performs an operation on the second last component in the path.
-func fileOpAt(t *kernel.Task, dirFD kdefs.FD, path string, fn func(root *fs.Dirent, d *fs.Dirent, name string) error) error {
+func fileOpAt(t *kernel.Task, dirFD kdefs.FD, path string, fn func(root *fs.Dirent, d *fs.Dirent, name string, remainingTraversals uint) error) error {
// Extract the last component.
dir, name := fs.SplitLast(path)
if dir == "/" {
// Common case: we are accessing a file in the root.
root := t.FSContext().RootDirectory()
- err := fn(root, root, name)
+ err := fn(root, root, name, linux.MaxSymlinkTraversals)
root.DecRef()
return err
} else if dir == "." && dirFD == linux.AT_FDCWD {
@@ -49,19 +48,19 @@ func fileOpAt(t *kernel.Task, dirFD kdefs.FD, path string, fn func(root *fs.Dire
// working directory; skip the look-up.
wd := t.FSContext().WorkingDirectory()
root := t.FSContext().RootDirectory()
- err := fn(root, wd, name)
+ err := fn(root, wd, name, linux.MaxSymlinkTraversals)
wd.DecRef()
root.DecRef()
return err
}
- return fileOpOn(t, dirFD, dir, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent) error {
- return fn(root, d, name)
+ return fileOpOn(t, dirFD, dir, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, remainingTraversals uint) error {
+ return fn(root, d, name, remainingTraversals)
})
}
// fileOpOn performs an operation on the last entry of the path.
-func fileOpOn(t *kernel.Task, dirFD kdefs.FD, path string, resolve bool, fn func(root *fs.Dirent, d *fs.Dirent) error) error {
+func fileOpOn(t *kernel.Task, dirFD kdefs.FD, path string, resolve bool, fn func(root *fs.Dirent, d *fs.Dirent, remainingTraversals uint) error) error {
var (
d *fs.Dirent // The file.
wd *fs.Dirent // The working directory (if required.)
@@ -110,7 +109,7 @@ func fileOpOn(t *kernel.Task, dirFD kdefs.FD, path string, resolve bool, fn func
return err
}
- err = fn(root, d)
+ err = fn(root, d, remainingTraversals)
d.DecRef()
return err
}
@@ -139,7 +138,7 @@ func openAt(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr, flags uint) (fd u
}
resolve := flags&linux.O_NOFOLLOW == 0
- err = fileOpOn(t, dirFD, path, resolve, func(root *fs.Dirent, d *fs.Dirent) error {
+ err = fileOpOn(t, dirFD, path, resolve, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
// First check a few things about the filesystem before trying to get the file
// reference.
//
@@ -211,7 +210,7 @@ func mknodAt(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr, mode linux.FileM
return syserror.ENOENT
}
- return fileOpAt(t, dirFD, path, func(root *fs.Dirent, d *fs.Dirent, name string) error {
+ return fileOpAt(t, dirFD, path, func(root *fs.Dirent, d *fs.Dirent, name string, _ uint) error {
if !fs.IsDir(d.Inode.StableAttr) {
return syserror.ENOTDIR
}
@@ -304,45 +303,105 @@ func createAt(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr, flags uint, mod
return 0, syserror.ENOENT
}
- err = fileOpAt(t, dirFD, path, func(root *fs.Dirent, d *fs.Dirent, name string) error {
- if !fs.IsDir(d.Inode.StableAttr) {
- return syserror.ENOTDIR
- }
-
- fileFlags := linuxToFlags(flags)
- // Linux always adds the O_LARGEFILE flag when running in 64-bit mode.
- fileFlags.LargeFile = true
+ fileFlags := linuxToFlags(flags)
+ // Linux always adds the O_LARGEFILE flag when running in 64-bit mode.
+ fileFlags.LargeFile = true
+
+ err = fileOpAt(t, dirFD, path, func(root *fs.Dirent, parent *fs.Dirent, name string, remainingTraversals uint) error {
+ // Resolve the name to see if it exists, and follow any
+ // symlinks along the way. We must do the symlink resolution
+ // manually because if the symlink target does not exist, we
+ // must create the target (and not the symlink itself).
+ var (
+ found *fs.Dirent
+ err error
+ )
+ for {
+ if !fs.IsDir(parent.Inode.StableAttr) {
+ return syserror.ENOTDIR
+ }
- // Does this file exist already?
- remainingTraversals := uint(linux.MaxSymlinkTraversals)
- targetDirent, err := t.MountNamespace().FindInode(t, root, d, name, &remainingTraversals)
- var newFile *fs.File
- switch err {
- case nil:
- // The file existed.
- defer targetDirent.DecRef()
+ // Start by looking up the dirent at 'name'.
+ found, err = t.MountNamespace().FindLink(t, root, parent, name, &remainingTraversals)
+ if err != nil {
+ break
+ }
+ defer found.DecRef()
- // Check if we wanted to create.
+ // We found something (possibly a symlink). If the
+ // O_EXCL flag was passed, then we can immediately
+ // return EEXIST.
if flags&linux.O_EXCL != 0 {
return syserror.EEXIST
}
+ // If we have a non-symlink, then we can proceed.
+ if !fs.IsSymlink(found.Inode.StableAttr) {
+ break
+ }
+
+ // If O_NOFOLLOW was passed, then don't try to resolve
+ // anything.
+ if flags&linux.O_NOFOLLOW != 0 {
+ return syserror.ELOOP
+ }
+
+ // Try to resolve the symlink directly to a Dirent.
+ var resolved *fs.Dirent
+ resolved, err = found.Inode.Getlink(t)
+ if err == nil {
+ // No more resolution necessary.
+ defer resolved.DecRef()
+ break
+ } else if err != fs.ErrResolveViaReadlink {
+ return err
+ }
+
+ // Are we able to resolve further?
+ if remainingTraversals == 0 {
+ return syscall.ELOOP
+ }
+
+ // Resolve the symlink to a path via Readlink.
+ path, err := found.Inode.Readlink(t)
+ if err != nil {
+ break
+ }
+ remainingTraversals--
+
+ // Get the new parent from the target path.
+ newParentPath, newName := fs.SplitLast(path)
+ newParent, err := t.MountNamespace().FindInode(t, root, parent, newParentPath, &remainingTraversals)
+ if err != nil {
+ break
+ }
+ defer newParent.DecRef()
+
+ // Repeat the process with the parent and name of the
+ // symlink target.
+ parent = newParent
+ name = newName
+ }
+
+ var newFile *fs.File
+ switch err {
+ case nil:
// Like sys_open, check for a few things about the
// filesystem before trying to get a reference to the
// fs.File. The same constraints on Check apply.
- if err := targetDirent.Inode.CheckPermission(t, flagsToPermissions(flags)); err != nil {
+ if err := found.Inode.CheckPermission(t, flagsToPermissions(flags)); err != nil {
return err
}
// Should we truncate the file?
if flags&linux.O_TRUNC != 0 {
- if err := targetDirent.Inode.Truncate(t, targetDirent, 0); err != nil {
+ if err := found.Inode.Truncate(t, found, 0); err != nil {
return err
}
}
// Create a new fs.File.
- newFile, err = targetDirent.Inode.GetFile(t, targetDirent, fileFlags)
+ newFile, err = found.Inode.GetFile(t, found, fileFlags)
if err != nil {
return syserror.ConvertIntr(err, kernel.ERESTARTSYS)
}
@@ -351,19 +410,19 @@ func createAt(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr, flags uint, mod
// File does not exist. Proceed with creation.
// Do we have write permissions on the parent?
- if err := d.Inode.CheckPermission(t, fs.PermMask{Write: true, Execute: true}); err != nil {
+ if err := parent.Inode.CheckPermission(t, fs.PermMask{Write: true, Execute: true}); err != nil {
return err
}
// Attempt a creation.
perms := fs.FilePermsFromMode(mode &^ linux.FileMode(t.FSContext().Umask()))
- newFile, err = d.Create(t, root, name, fileFlags, perms)
+ newFile, err = parent.Create(t, root, name, fileFlags, perms)
if err != nil {
// No luck, bail.
return err
}
defer newFile.DecRef()
- targetDirent = newFile.Dirent
+ found = newFile.Dirent
default:
return err
}
@@ -379,10 +438,10 @@ func createAt(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr, flags uint, mod
fd = uintptr(newFD)
// Queue the open inotify event. The creation event is
- // automatically queued when the dirent is targetDirent. The
- // open events are implemented at the syscall layer so we need
- // to manually queue one here.
- targetDirent.InotifyEvent(linux.IN_OPEN, 0)
+ // automatically queued when the dirent is found. The open
+ // events are implemented at the syscall layer so we need to
+ // manually queue one here.
+ found.InotifyEvent(linux.IN_OPEN, 0)
return nil
})
@@ -458,7 +517,7 @@ func accessAt(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr, resolve bool, m
return syserror.EINVAL
}
- return fileOpOn(t, dirFD, path, resolve, func(root *fs.Dirent, d *fs.Dirent) error {
+ return fileOpOn(t, dirFD, path, resolve, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
// access(2) and faccessat(2) check permissions using real
// UID/GID, not effective UID/GID.
//
@@ -572,7 +631,7 @@ func Ioctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
return 0, nil, err
default:
- ret, err := file.FileOperations.Ioctl(t, t.MemoryManager(), args)
+ ret, err := file.FileOperations.Ioctl(t, file, t.MemoryManager(), args)
if err != nil {
return 0, nil, err
}
@@ -626,7 +685,7 @@ func Chroot(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
return 0, nil, err
}
- return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent) error {
+ return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
// Is it a directory?
if !fs.IsDir(d.Inode.StableAttr) {
return syserror.ENOTDIR
@@ -651,7 +710,7 @@ func Chdir(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
return 0, nil, err
}
- return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent) error {
+ return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
// Is it a directory?
if !fs.IsDir(d.Inode.StableAttr) {
return syserror.ENOTDIR
@@ -945,17 +1004,18 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
err := tmpfs.AddSeals(file.Dirent.Inode, args[2].Uint())
return 0, nil, err
case linux.F_GETPIPE_SZ:
- sz, ok := file.FileOperations.(pipe.Sizer)
+ sz, ok := file.FileOperations.(fs.FifoSizer)
if !ok {
return 0, nil, syserror.EINVAL
}
- return uintptr(sz.PipeSize()), nil, nil
+ size, err := sz.FifoSize(t, file)
+ return uintptr(size), nil, err
case linux.F_SETPIPE_SZ:
- sz, ok := file.FileOperations.(pipe.Sizer)
+ sz, ok := file.FileOperations.(fs.FifoSizer)
if !ok {
return 0, nil, syserror.EINVAL
}
- n, err := sz.SetPipeSize(int64(args[2].Int()))
+ n, err := sz.SetFifoSize(int64(args[2].Int()))
return uintptr(n), nil, err
default:
// Everything else is not yet supported.
@@ -1017,7 +1077,7 @@ func mkdirAt(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr, mode linux.FileM
return err
}
- return fileOpAt(t, dirFD, path, func(root *fs.Dirent, d *fs.Dirent, name string) error {
+ return fileOpAt(t, dirFD, path, func(root *fs.Dirent, d *fs.Dirent, name string, _ uint) error {
if !fs.IsDir(d.Inode.StableAttr) {
return syserror.ENOTDIR
}
@@ -1074,7 +1134,7 @@ func rmdirAt(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr) error {
return syserror.EBUSY
}
- return fileOpAt(t, dirFD, path, func(root *fs.Dirent, d *fs.Dirent, name string) error {
+ return fileOpAt(t, dirFD, path, func(root *fs.Dirent, d *fs.Dirent, name string, _ uint) error {
if !fs.IsDir(d.Inode.StableAttr) {
return syserror.ENOTDIR
}
@@ -1122,7 +1182,7 @@ func symlinkAt(t *kernel.Task, dirFD kdefs.FD, newAddr usermem.Addr, oldAddr use
return syserror.ENOENT
}
- return fileOpAt(t, dirFD, newPath, func(root *fs.Dirent, d *fs.Dirent, name string) error {
+ return fileOpAt(t, dirFD, newPath, func(root *fs.Dirent, d *fs.Dirent, name string, _ uint) error {
if !fs.IsDir(d.Inode.StableAttr) {
return syserror.ENOTDIR
}
@@ -1212,7 +1272,7 @@ func linkAt(t *kernel.Task, oldDirFD kdefs.FD, oldAddr usermem.Addr, newDirFD kd
}
// Resolve the target directory.
- return fileOpAt(t, newDirFD, newPath, func(root *fs.Dirent, newParent *fs.Dirent, newName string) error {
+ return fileOpAt(t, newDirFD, newPath, func(root *fs.Dirent, newParent *fs.Dirent, newName string, _ uint) error {
if !fs.IsDir(newParent.Inode.StableAttr) {
return syserror.ENOTDIR
}
@@ -1227,13 +1287,13 @@ func linkAt(t *kernel.Task, oldDirFD kdefs.FD, oldAddr usermem.Addr, newDirFD kd
// Resolve oldDirFD and oldAddr to a dirent. The "resolve" argument
// only applies to this name.
- return fileOpOn(t, oldDirFD, oldPath, resolve, func(root *fs.Dirent, target *fs.Dirent) error {
+ return fileOpOn(t, oldDirFD, oldPath, resolve, func(root *fs.Dirent, target *fs.Dirent, _ uint) error {
if err := mayLinkAt(t, target.Inode); err != nil {
return err
}
// Next resolve newDirFD and newAddr to the parent dirent and name.
- return fileOpAt(t, newDirFD, newPath, func(root *fs.Dirent, newParent *fs.Dirent, newName string) error {
+ return fileOpAt(t, newDirFD, newPath, func(root *fs.Dirent, newParent *fs.Dirent, newName string, _ uint) error {
if !fs.IsDir(newParent.Inode.StableAttr) {
return syserror.ENOTDIR
}
@@ -1300,7 +1360,7 @@ func readlinkAt(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr, bufAddr userm
return 0, syserror.ENOENT
}
- err = fileOpOn(t, dirFD, path, false /* resolve */, func(root *fs.Dirent, d *fs.Dirent) error {
+ err = fileOpOn(t, dirFD, path, false /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
// Check for Read permission.
if err := d.Inode.CheckPermission(t, fs.PermMask{Read: true}); err != nil {
return err
@@ -1359,7 +1419,7 @@ func unlinkAt(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr) error {
return syserror.ENOENT
}
- return fileOpAt(t, dirFD, path, func(root *fs.Dirent, d *fs.Dirent, name string) error {
+ return fileOpAt(t, dirFD, path, func(root *fs.Dirent, d *fs.Dirent, name string, _ uint) error {
if !fs.IsDir(d.Inode.StableAttr) {
return syserror.ENOTDIR
}
@@ -1414,7 +1474,7 @@ func Truncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
return 0, nil, syserror.EFBIG
}
- return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent) error {
+ return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
if fs.IsDir(d.Inode.StableAttr) {
return syserror.EISDIR
}
@@ -1576,7 +1636,7 @@ func chownAt(t *kernel.Task, fd kdefs.FD, addr usermem.Addr, resolve, allowEmpty
return chown(t, file.Dirent, uid, gid)
}
- return fileOpOn(t, fd, path, resolve, func(root *fs.Dirent, d *fs.Dirent) error {
+ return fileOpOn(t, fd, path, resolve, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
return chown(t, d, uid, gid)
})
}
@@ -1652,7 +1712,7 @@ func chmodAt(t *kernel.Task, fd kdefs.FD, addr usermem.Addr, mode linux.FileMode
return err
}
- return fileOpOn(t, fd, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent) error {
+ return fileOpOn(t, fd, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
return chmod(t, d, mode)
})
}
@@ -1698,7 +1758,7 @@ func defaultSetToSystemTimeSpec() fs.TimeSpec {
}
func utimes(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr, ts fs.TimeSpec, resolve bool) error {
- setTimestamp := func(root *fs.Dirent, d *fs.Dirent) error {
+ setTimestamp := func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
// Does the task own the file?
if !d.Inode.CheckOwnership(t) {
// Trying to set a specific time? Must be owner.
@@ -1739,7 +1799,7 @@ func utimes(t *kernel.Task, dirFD kdefs.FD, addr usermem.Addr, ts fs.TimeSpec, r
root := t.FSContext().RootDirectory()
defer root.DecRef()
- return setTimestamp(root, f.Dirent)
+ return setTimestamp(root, f.Dirent, linux.MaxSymlinkTraversals)
}
path, _, err := copyInPath(t, addr, false /* allowEmpty */)
@@ -1867,7 +1927,7 @@ func renameAt(t *kernel.Task, oldDirFD kdefs.FD, oldAddr usermem.Addr, newDirFD
return err
}
- return fileOpAt(t, oldDirFD, oldPath, func(root *fs.Dirent, oldParent *fs.Dirent, oldName string) error {
+ return fileOpAt(t, oldDirFD, oldPath, func(root *fs.Dirent, oldParent *fs.Dirent, oldName string, _ uint) error {
if !fs.IsDir(oldParent.Inode.StableAttr) {
return syserror.ENOTDIR
}
@@ -1879,7 +1939,7 @@ func renameAt(t *kernel.Task, oldDirFD kdefs.FD, oldAddr usermem.Addr, newDirFD
return syserror.EBUSY
}
- return fileOpAt(t, newDirFD, newPath, func(root *fs.Dirent, newParent *fs.Dirent, newName string) error {
+ return fileOpAt(t, newDirFD, newPath, func(root *fs.Dirent, newParent *fs.Dirent, newName string, _ uint) error {
if !fs.IsDir(newParent.Inode.StableAttr) {
return syserror.ENOTDIR
}
@@ -2067,7 +2127,7 @@ func MemfdCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
name = memfdPrefix + name
inode := tmpfs.NewMemfdInode(t, allowSeals)
- dirent := fs.NewDirent(inode, name)
+ dirent := fs.NewDirent(t, inode, name)
// Per Linux, mm/shmem.c:__shmem_file_setup(), memfd files are set up with
// FMODE_READ | FMODE_WRITE.
file, err := inode.GetFile(t, dirent, fs.FileFlags{Read: true, Write: true})