gvisor/pkg/sentry/syscalls/linux/vfs2/filesystem.go

327 lines
11 KiB
Go

// 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/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, 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, mode, dev)
}
func mknodat(t *kernel.Task, dirfd int32, addr usermem.Addr, mode uint, 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()
major, minor := linux.DecodeDeviceID(dev)
return t.Kernel().VFS().MknodAt(t, t.Credentials(), &tpop.pop, &vfs.MknodOptions{
Mode: linux.FileMode(mode &^ 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,
})
}
// 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
}
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)
}