// Copyright 2020 The gVisor Authors. // // 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 vfs2 import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/sentry/arch" "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/limits" "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/pkg/syserror" "gvisor.dev/gvisor/pkg/usermem" ) // Link implements Linux syscall link(2). func Link(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { oldpathAddr := args[0].Pointer() newpathAddr := args[1].Pointer() return 0, nil, linkat(t, linux.AT_FDCWD, oldpathAddr, linux.AT_FDCWD, newpathAddr, 0 /* flags */) } // Linkat implements Linux syscall linkat(2). func Linkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { olddirfd := args[0].Int() oldpathAddr := args[1].Pointer() newdirfd := args[2].Int() newpathAddr := args[3].Pointer() flags := args[4].Int() return 0, nil, linkat(t, olddirfd, oldpathAddr, newdirfd, newpathAddr, flags) } func linkat(t *kernel.Task, olddirfd int32, oldpathAddr usermem.Addr, newdirfd int32, newpathAddr usermem.Addr, flags int32) error { if flags&^(linux.AT_EMPTY_PATH|linux.AT_SYMLINK_FOLLOW) != 0 { return syserror.EINVAL } if flags&linux.AT_EMPTY_PATH != 0 && !t.HasCapability(linux.CAP_DAC_READ_SEARCH) { return syserror.ENOENT } oldpath, err := copyInPath(t, oldpathAddr) if err != nil { return err } oldtpop, err := getTaskPathOperation(t, olddirfd, oldpath, shouldAllowEmptyPath(flags&linux.AT_EMPTY_PATH != 0), shouldFollowFinalSymlink(flags&linux.AT_SYMLINK_FOLLOW != 0)) if err != nil { return err } defer oldtpop.Release() newpath, err := copyInPath(t, newpathAddr) if err != nil { return err } newtpop, err := getTaskPathOperation(t, newdirfd, newpath, disallowEmptyPath, nofollowFinalSymlink) if err != nil { return err } defer newtpop.Release() return t.Kernel().VFS().LinkAt(t, t.Credentials(), &oldtpop.pop, &newtpop.pop) } // Mkdir implements Linux syscall mkdir(2). func Mkdir(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() mode := args[1].ModeT() return 0, nil, mkdirat(t, linux.AT_FDCWD, addr, mode) } // Mkdirat implements Linux syscall mkdirat(2). func Mkdirat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() addr := args[1].Pointer() mode := args[2].ModeT() return 0, nil, mkdirat(t, dirfd, addr, mode) } func mkdirat(t *kernel.Task, dirfd int32, addr usermem.Addr, mode uint) error { path, err := copyInPath(t, addr) if err != nil { return err } tpop, err := getTaskPathOperation(t, dirfd, path, disallowEmptyPath, nofollowFinalSymlink) if err != nil { return err } defer tpop.Release() return t.Kernel().VFS().MkdirAt(t, t.Credentials(), &tpop.pop, &vfs.MkdirOptions{ Mode: linux.FileMode(mode & (0777 | linux.S_ISVTX) &^ t.FSContext().Umask()), }) } // Mknod implements Linux syscall mknod(2). func Mknod(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() mode := args[1].ModeT() dev := args[2].Uint() return 0, nil, mknodat(t, linux.AT_FDCWD, addr, linux.FileMode(mode), dev) } // Mknodat implements Linux syscall mknodat(2). func Mknodat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() addr := args[1].Pointer() mode := args[2].ModeT() dev := args[3].Uint() return 0, nil, mknodat(t, dirfd, addr, linux.FileMode(mode), dev) } func mknodat(t *kernel.Task, dirfd int32, addr usermem.Addr, mode linux.FileMode, dev uint32) error { path, err := copyInPath(t, addr) if err != nil { return err } tpop, err := getTaskPathOperation(t, dirfd, path, disallowEmptyPath, nofollowFinalSymlink) if err != nil { return err } defer tpop.Release() // "Zero file type is equivalent to type S_IFREG." - mknod(2) if mode.FileType() == 0 { mode |= linux.ModeRegular } major, minor := linux.DecodeDeviceID(dev) return t.Kernel().VFS().MknodAt(t, t.Credentials(), &tpop.pop, &vfs.MknodOptions{ Mode: mode &^ linux.FileMode(t.FSContext().Umask()), DevMajor: uint32(major), DevMinor: minor, }) } // Open implements Linux syscall open(2). func Open(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() flags := args[1].Uint() mode := args[2].ModeT() return openat(t, linux.AT_FDCWD, addr, flags, mode) } // Openat implements Linux syscall openat(2). func Openat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() addr := args[1].Pointer() flags := args[2].Uint() mode := args[3].ModeT() return openat(t, dirfd, addr, flags, mode) } // Creat implements Linux syscall creat(2). func Creat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { addr := args[0].Pointer() mode := args[1].ModeT() return openat(t, linux.AT_FDCWD, addr, linux.O_WRONLY|linux.O_CREAT|linux.O_TRUNC, mode) } func openat(t *kernel.Task, dirfd int32, pathAddr usermem.Addr, flags uint32, mode uint) (uintptr, *kernel.SyscallControl, error) { path, err := copyInPath(t, pathAddr) if err != nil { return 0, nil, err } tpop, err := getTaskPathOperation(t, dirfd, path, disallowEmptyPath, shouldFollowFinalSymlink(flags&linux.O_NOFOLLOW == 0)) if err != nil { return 0, nil, err } defer tpop.Release() file, err := t.Kernel().VFS().OpenAt(t, t.Credentials(), &tpop.pop, &vfs.OpenOptions{ Flags: flags | linux.O_LARGEFILE, Mode: linux.FileMode(mode & (0777 | linux.S_ISUID | linux.S_ISGID | linux.S_ISVTX) &^ t.FSContext().Umask()), }) if err != nil { return 0, nil, err } defer file.DecRef() fd, err := t.NewFDFromVFS2(0, file, kernel.FDFlags{ CloseOnExec: flags&linux.O_CLOEXEC != 0, }) return uintptr(fd), nil, err } // Rename implements Linux syscall rename(2). func Rename(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { oldpathAddr := args[0].Pointer() newpathAddr := args[1].Pointer() return 0, nil, renameat(t, linux.AT_FDCWD, oldpathAddr, linux.AT_FDCWD, newpathAddr, 0 /* flags */) } // Renameat implements Linux syscall renameat(2). func Renameat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { olddirfd := args[0].Int() oldpathAddr := args[1].Pointer() newdirfd := args[2].Int() newpathAddr := args[3].Pointer() return 0, nil, renameat(t, olddirfd, oldpathAddr, newdirfd, newpathAddr, 0 /* flags */) } // Renameat2 implements Linux syscall renameat2(2). func Renameat2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { olddirfd := args[0].Int() oldpathAddr := args[1].Pointer() newdirfd := args[2].Int() newpathAddr := args[3].Pointer() flags := args[4].Uint() return 0, nil, renameat(t, olddirfd, oldpathAddr, newdirfd, newpathAddr, flags) } func renameat(t *kernel.Task, olddirfd int32, oldpathAddr usermem.Addr, newdirfd int32, newpathAddr usermem.Addr, flags uint32) error { oldpath, err := copyInPath(t, oldpathAddr) if err != nil { return err } // "If oldpath refers to a symbolic link, the link is renamed" - rename(2) oldtpop, err := getTaskPathOperation(t, olddirfd, oldpath, disallowEmptyPath, nofollowFinalSymlink) if err != nil { return err } defer oldtpop.Release() newpath, err := copyInPath(t, newpathAddr) if err != nil { return err } newtpop, err := getTaskPathOperation(t, newdirfd, newpath, disallowEmptyPath, nofollowFinalSymlink) if err != nil { return err } defer newtpop.Release() return t.Kernel().VFS().RenameAt(t, t.Credentials(), &oldtpop.pop, &newtpop.pop, &vfs.RenameOptions{ Flags: flags, }) } // Fallocate implements linux system call fallocate(2). func Fallocate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { fd := args[0].Int() mode := args[1].Uint64() offset := args[2].Int64() length := args[3].Int64() file := t.GetFileVFS2(fd) if file == nil { return 0, nil, syserror.EBADF } defer file.DecRef() if !file.IsWritable() { return 0, nil, syserror.EBADF } if mode != 0 { return 0, nil, syserror.ENOTSUP } if offset < 0 || length <= 0 { return 0, nil, syserror.EINVAL } size := offset + length if size < 0 { return 0, nil, syserror.EFBIG } limit := limits.FromContext(t).Get(limits.FileSize).Cur if uint64(size) >= limit { t.SendSignal(&arch.SignalInfo{ Signo: int32(linux.SIGXFSZ), Code: arch.SignalInfoUser, }) return 0, nil, syserror.EFBIG } return 0, nil, file.Impl().Allocate(t, mode, uint64(offset), uint64(length)) // File length modified, generate notification. // TODO(gvisor.dev/issue/1479): Reenable when Inotify is ported. // file.Dirent.InotifyEvent(linux.IN_MODIFY, 0) } // Rmdir implements Linux syscall rmdir(2). func Rmdir(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pathAddr := args[0].Pointer() return 0, nil, rmdirat(t, linux.AT_FDCWD, pathAddr) } func rmdirat(t *kernel.Task, dirfd int32, pathAddr usermem.Addr) error { path, err := copyInPath(t, pathAddr) if err != nil { return err } tpop, err := getTaskPathOperation(t, dirfd, path, disallowEmptyPath, nofollowFinalSymlink) if err != nil { return err } defer tpop.Release() return t.Kernel().VFS().RmdirAt(t, t.Credentials(), &tpop.pop) } // Unlink implements Linux syscall unlink(2). func Unlink(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { pathAddr := args[0].Pointer() return 0, nil, unlinkat(t, linux.AT_FDCWD, pathAddr) } func unlinkat(t *kernel.Task, dirfd int32, pathAddr usermem.Addr) error { path, err := copyInPath(t, pathAddr) if err != nil { return err } tpop, err := getTaskPathOperation(t, dirfd, path, disallowEmptyPath, nofollowFinalSymlink) if err != nil { return err } defer tpop.Release() return t.Kernel().VFS().UnlinkAt(t, t.Credentials(), &tpop.pop) } // Unlinkat implements Linux syscall unlinkat(2). func Unlinkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { dirfd := args[0].Int() pathAddr := args[1].Pointer() flags := args[2].Int() if flags&^linux.AT_REMOVEDIR != 0 { return 0, nil, syserror.EINVAL } if flags&linux.AT_REMOVEDIR != 0 { return 0, nil, rmdirat(t, dirfd, pathAddr) } return 0, nil, unlinkat(t, dirfd, pathAddr) } // Symlink implements Linux syscall symlink(2). func Symlink(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { targetAddr := args[0].Pointer() linkpathAddr := args[1].Pointer() return 0, nil, symlinkat(t, targetAddr, linux.AT_FDCWD, linkpathAddr) } // Symlinkat implements Linux syscall symlinkat(2). func Symlinkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { targetAddr := args[0].Pointer() newdirfd := args[1].Int() linkpathAddr := args[2].Pointer() return 0, nil, symlinkat(t, targetAddr, newdirfd, linkpathAddr) } func symlinkat(t *kernel.Task, targetAddr usermem.Addr, newdirfd int32, linkpathAddr usermem.Addr) error { target, err := t.CopyInString(targetAddr, linux.PATH_MAX) if err != nil { return err } if len(target) == 0 { return syserror.ENOENT } linkpath, err := copyInPath(t, linkpathAddr) if err != nil { return err } tpop, err := getTaskPathOperation(t, newdirfd, linkpath, disallowEmptyPath, nofollowFinalSymlink) if err != nil { return err } defer tpop.Release() return t.Kernel().VFS().SymlinkAt(t, t.Credentials(), &tpop.pop, target) }