diff options
Diffstat (limited to 'pkg/sentry/syscalls/linux/sys_stat.go')
-rw-r--r-- | pkg/sentry/syscalls/linux/sys_stat.go | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/pkg/sentry/syscalls/linux/sys_stat.go b/pkg/sentry/syscalls/linux/sys_stat.go new file mode 100644 index 000000000..6e21b34fd --- /dev/null +++ b/pkg/sentry/syscalls/linux/sys_stat.go @@ -0,0 +1,209 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package linux + +import ( + "syscall" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/sentry/arch" + "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" + "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs" + "gvisor.googlesource.com/gvisor/pkg/sentry/usermem" + "gvisor.googlesource.com/gvisor/pkg/syserror" +) + +// Stat implements linux syscall stat(2). +func Stat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + addr := args[0].Pointer() + statAddr := args[1].Pointer() + + path, dirPath, err := copyInPath(t, addr, false /* allowEmpty */) + if err != nil { + return 0, nil, err + } + + return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent) error { + return stat(t, d, dirPath, statAddr) + }) +} + +// Fstatat implements linux syscall newfstatat, i.e. fstatat(2). +func Fstatat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + fd := kdefs.FD(args[0].Int()) + addr := args[1].Pointer() + statAddr := args[2].Pointer() + flags := args[3].Int() + + path, dirPath, err := copyInPath(t, addr, flags&linux.AT_EMPTY_PATH != 0) + if err != nil { + return 0, nil, err + } + + if path == "" { + // Annoying. What's wrong with fstat? + file := t.FDMap().GetFile(fd) + if file == nil { + return 0, nil, syserror.EBADF + } + defer file.DecRef() + + return 0, nil, stat(t, file.Dirent, false, statAddr) + } + + return 0, nil, fileOpOn(t, fd, path, flags&linux.AT_SYMLINK_NOFOLLOW == 0, func(root *fs.Dirent, d *fs.Dirent) error { + return stat(t, d, dirPath, statAddr) + }) +} + +// Lstat implements linux syscall lstat(2). +func Lstat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + addr := args[0].Pointer() + statAddr := args[1].Pointer() + + path, dirPath, err := copyInPath(t, addr, false /* allowEmpty */) + if err != nil { + return 0, nil, err + } + + return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, false /* resolve */, func(root *fs.Dirent, d *fs.Dirent) error { + return stat(t, d, dirPath, statAddr) + }) +} + +// Fstat implements linux syscall fstat(2). +func Fstat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + fd := kdefs.FD(args[0].Int()) + statAddr := args[1].Pointer() + + file := t.FDMap().GetFile(fd) + if file == nil { + return 0, nil, syserror.EBADF + } + defer file.DecRef() + + return 0, nil, stat(t, file.Dirent, false /* dirPath */, statAddr) +} + +// stat implements stat from the given *fs.Dirent. +func stat(t *kernel.Task, d *fs.Dirent, dirPath bool, statAddr usermem.Addr) error { + if dirPath && !fs.IsDir(d.Inode.StableAttr) { + return syserror.ENOTDIR + } + uattr, err := d.Inode.UnstableAttr(t) + if err != nil { + return err + } + + var mode uint32 + switch d.Inode.StableAttr.Type { + case fs.RegularFile, fs.SpecialFile: + mode |= linux.ModeRegular + case fs.Symlink: + mode |= linux.ModeSymlink + case fs.Directory, fs.SpecialDirectory: + mode |= linux.ModeDirectory + case fs.Pipe: + mode |= linux.ModeNamedPipe + case fs.CharacterDevice: + mode |= linux.ModeCharacterDevice + case fs.BlockDevice: + mode |= linux.ModeBlockDevice + case fs.Socket: + mode |= linux.ModeSocket + } + + _, err = t.CopyOut(statAddr, linux.Stat{ + Dev: uint64(d.Inode.StableAttr.DeviceID), + Rdev: uint64(linux.MakeDeviceID(d.Inode.StableAttr.DeviceFileMajor, d.Inode.StableAttr.DeviceFileMinor)), + Ino: uint64(d.Inode.StableAttr.InodeID), + Nlink: uattr.Links, + Mode: mode | uint32(uattr.Perms.LinuxMode()), + UID: uint32(uattr.Owner.UID.In(t.UserNamespace()).OrOverflow()), + GID: uint32(uattr.Owner.GID.In(t.UserNamespace()).OrOverflow()), + Size: uattr.Size, + Blksize: d.Inode.StableAttr.BlockSize, + Blocks: uattr.Usage / 512, + ATime: uattr.AccessTime.Timespec(), + MTime: uattr.ModificationTime.Timespec(), + CTime: uattr.StatusChangeTime.Timespec(), + }) + return err +} + +// Statfs implements linux syscall statfs(2). +func Statfs(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + addr := args[0].Pointer() + statfsAddr := args[1].Pointer() + + path, _, err := copyInPath(t, addr, false /* allowEmpty */) + if err != nil { + return 0, nil, err + } + + return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent) error { + return statfsImpl(t, d, statfsAddr) + }) +} + +// Fstatfs implements linux syscall fstatfs(2). +func Fstatfs(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + fd := kdefs.FD(args[0].Int()) + statfsAddr := args[1].Pointer() + + file := t.FDMap().GetFile(fd) + if file == nil { + return 0, nil, syserror.EBADF + } + defer file.DecRef() + + return 0, nil, statfsImpl(t, file.Dirent, statfsAddr) +} + +// statfsImpl implements the linux syscall statfs and fstatfs based on a Dirent, +// copying the statfs structure out to addr on success, otherwise an error is +// returned. +func statfsImpl(t *kernel.Task, d *fs.Dirent, addr usermem.Addr) error { + info, err := d.Inode.StatFS(t) + if err != nil { + return err + } + // Construct the statfs structure and copy it out. + statfs := linux.Statfs{ + Type: info.Type, + // Treat block size and fragment size as the same, as + // most consumers of this structure will expect one + // or the other to be filled in. + BlockSize: d.Inode.StableAttr.BlockSize, + Blocks: info.TotalBlocks, + // We don't have the concept of reserved blocks, so + // report blocks free the same as available blocks. + // This is a normal thing for filesystems, to do, see + // udf, hugetlbfs, tmpfs, among others. + BlocksFree: info.FreeBlocks, + BlocksAvailable: info.FreeBlocks, + Files: info.TotalFiles, + FilesFree: info.FreeFiles, + // Same as Linux for simple_statfs, see fs/libfs.c. + NameLength: syscall.PathMax, + FragmentSize: d.Inode.StableAttr.BlockSize, + // Leave other fields 0 like simple_statfs does. + } + if _, err := t.CopyOut(addr, &statfs); err != nil { + return err + } + return nil +} |