mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Port most syscalls to VFS2.
pipe and pipe2 aren't ported, pending a slight rework of pipe FDs for VFS2. mount and umount2 aren't ported out of temporary laziness. access and faccessat need additional FSImpl methods to implement properly, but are stubbed to prevent googletest from CHECK-failing. Other syscalls require additional plumbing. Updates #1623 PiperOrigin-RevId: 297188448
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
package linux
|
||||
|
||||
// EpollEvent is equivalent to struct epoll_event from epoll(2).
|
||||
//
|
||||
// +marshal
|
||||
type EpollEvent struct {
|
||||
Events uint32
|
||||
// Linux makes struct epoll_event::data a __u64. We represent it as
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package linux
|
||||
|
||||
// EpollEvent is equivalent to struct epoll_event from epoll(2).
|
||||
//
|
||||
// +marshal
|
||||
type EpollEvent struct {
|
||||
Events uint32
|
||||
// Linux makes struct epoll_event a __u64, necessitating 4 bytes of padding
|
||||
|
||||
@@ -241,6 +241,8 @@ const (
|
||||
)
|
||||
|
||||
// Statx represents struct statx.
|
||||
//
|
||||
// +marshal
|
||||
type Statx struct {
|
||||
Mask uint32
|
||||
Blksize uint32
|
||||
|
||||
@@ -38,6 +38,8 @@ const (
|
||||
)
|
||||
|
||||
// Statfs is struct statfs, from uapi/asm-generic/statfs.h.
|
||||
//
|
||||
// +marshal
|
||||
type Statfs struct {
|
||||
// Type is one of the filesystem magic values, defined above.
|
||||
Type uint64
|
||||
|
||||
@@ -115,6 +115,8 @@ const (
|
||||
)
|
||||
|
||||
// SignalSet is a signal mask with a bit corresponding to each signal.
|
||||
//
|
||||
// +marshal
|
||||
type SignalSet uint64
|
||||
|
||||
// SignalSetSize is the size in bytes of a SignalSet.
|
||||
|
||||
@@ -157,6 +157,8 @@ func DurationToTimespec(dur time.Duration) Timespec {
|
||||
const SizeOfTimeval = 16
|
||||
|
||||
// Timeval represents struct timeval in <time.h>.
|
||||
//
|
||||
// +marshal
|
||||
type Timeval struct {
|
||||
Sec int64
|
||||
Usec int64
|
||||
@@ -230,6 +232,8 @@ type Tms struct {
|
||||
type TimerID int32
|
||||
|
||||
// StatxTimestamp represents struct statx_timestamp.
|
||||
//
|
||||
// +marshal
|
||||
type StatxTimestamp struct {
|
||||
Sec int64
|
||||
Nsec uint32
|
||||
@@ -258,6 +262,8 @@ func NsecToStatxTimestamp(nsec int64) (ts StatxTimestamp) {
|
||||
}
|
||||
|
||||
// Utime represents struct utimbuf used by utimes(2).
|
||||
//
|
||||
// +marshal
|
||||
type Utime struct {
|
||||
Actime int64
|
||||
Modtime int64
|
||||
|
||||
@@ -18,6 +18,7 @@ package linux
|
||||
const (
|
||||
XATTR_NAME_MAX = 255
|
||||
XATTR_SIZE_MAX = 65536
|
||||
XATTR_LIST_MAX = 65536
|
||||
|
||||
XATTR_CREATE = 1
|
||||
XATTR_REPLACE = 2
|
||||
|
||||
+3
-1
@@ -8,9 +8,11 @@ go_library(
|
||||
name = "fspath",
|
||||
srcs = [
|
||||
"builder.go",
|
||||
"builder_unsafe.go",
|
||||
"fspath.go",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/gohacks",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
|
||||
@@ -16,6 +16,8 @@ package fspath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/gohacks"
|
||||
)
|
||||
|
||||
// Builder is similar to strings.Builder, but is used to produce pathnames
|
||||
@@ -102,3 +104,9 @@ func (b *Builder) AppendString(str string) {
|
||||
copy(b.buf[b.start:], b.buf[oldStart:])
|
||||
copy(b.buf[len(b.buf)-len(str):], str)
|
||||
}
|
||||
|
||||
// String returns the accumulated string. No other methods should be called
|
||||
// after String.
|
||||
func (b *Builder) String() string {
|
||||
return gohacks.StringFromImmutableBytes(b.buf[b.start:])
|
||||
}
|
||||
|
||||
@@ -67,7 +67,8 @@ func Parse(pathname string) Path {
|
||||
|
||||
// Path contains the information contained in a pathname string.
|
||||
//
|
||||
// Path is copyable by value.
|
||||
// Path is copyable by value. The zero value for Path is equivalent to
|
||||
// fspath.Parse(""), i.e. the empty path.
|
||||
type Path struct {
|
||||
// Begin is an iterator to the first path component in the relative part of
|
||||
// the path.
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
go_library(
|
||||
name = "gohacks",
|
||||
srcs = [
|
||||
"gohacks_unsafe.go",
|
||||
],
|
||||
visibility = ["//:sandbox"],
|
||||
)
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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 gohacks contains utilities for subverting the Go compiler.
|
||||
package gohacks
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Noescape hides a pointer from escape analysis. Noescape is the identity
|
||||
// function but escape analysis doesn't think the output depends on the input.
|
||||
// Noescape is inlined and currently compiles down to zero instructions.
|
||||
// USE CAREFULLY!
|
||||
//
|
||||
// (Noescape is copy/pasted from Go's runtime/stubs.go:noescape().)
|
||||
//
|
||||
//go:nosplit
|
||||
func Noescape(p unsafe.Pointer) unsafe.Pointer {
|
||||
x := uintptr(p)
|
||||
return unsafe.Pointer(x ^ 0)
|
||||
}
|
||||
|
||||
// ImmutableBytesFromString is equivalent to []byte(s), except that it uses the
|
||||
// same memory backing s instead of making a heap-allocated copy. This is only
|
||||
// valid if the returned slice is never mutated.
|
||||
func ImmutableBytesFromString(s string) []byte {
|
||||
shdr := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
||||
var bs []byte
|
||||
bshdr := (*reflect.SliceHeader)(unsafe.Pointer(&bs))
|
||||
bshdr.Data = shdr.Data
|
||||
bshdr.Len = shdr.Len
|
||||
bshdr.Cap = shdr.Len
|
||||
return bs
|
||||
}
|
||||
|
||||
// StringFromImmutableBytes is equivalent to string(bs), except that it uses
|
||||
// the same memory backing bs instead of making a heap-allocated copy. This is
|
||||
// only valid if bs is never mutated after StringFromImmutableBytes returns.
|
||||
func StringFromImmutableBytes(bs []byte) string {
|
||||
// This is cheaper than messing with reflect.StringHeader and
|
||||
// reflect.SliceHeader, which as of this writing produces many dead stores
|
||||
// of zeroes. Compare strings.Builder.String().
|
||||
return *(*string)(unsafe.Pointer(&bs))
|
||||
}
|
||||
@@ -117,15 +117,19 @@ func NewVFSLookup(mntns *vfs.MountNamespace, root, workingDir vfs.VirtualDentry)
|
||||
// default anyways.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/1623): Check mount has read and exec permission.
|
||||
func (l *vfsLookup) OpenPath(ctx context.Context, path string, opts vfs.OpenOptions, _ *uint, resolveFinal bool) (File, error) {
|
||||
func (l *vfsLookup) OpenPath(ctx context.Context, pathname string, opts vfs.OpenOptions, _ *uint, resolveFinal bool) (File, error) {
|
||||
vfsObj := l.mntns.Root().Mount().Filesystem().VirtualFilesystem()
|
||||
creds := auth.CredentialsFromContext(ctx)
|
||||
path := fspath.Parse(pathname)
|
||||
pop := &vfs.PathOperation{
|
||||
Root: l.root,
|
||||
Start: l.root,
|
||||
Path: fspath.Parse(path),
|
||||
Start: l.workingDir,
|
||||
Path: path,
|
||||
FollowFinalSymlink: resolveFinal,
|
||||
}
|
||||
if path.Absolute {
|
||||
pop.Start = l.root
|
||||
}
|
||||
fd, err := vfsObj.OpenAt(ctx, creds, pop, &opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -73,9 +73,9 @@ func newTasksInode(inoGen InoGenerator, k *kernel.Kernel, pidns *kernel.PIDNames
|
||||
"meminfo": newDentry(root, inoGen.NextIno(), 0444, &meminfoData{}),
|
||||
"mounts": kernfs.NewStaticSymlink(root, inoGen.NextIno(), "self/mounts"),
|
||||
"net": newNetDir(root, inoGen, k),
|
||||
"stat": newDentry(root, inoGen.NextIno(), 0444, &statData{}),
|
||||
"stat": newDentry(root, inoGen.NextIno(), 0444, &statData{k: k}),
|
||||
"uptime": newDentry(root, inoGen.NextIno(), 0444, &uptimeData{}),
|
||||
"version": newDentry(root, inoGen.NextIno(), 0444, &versionData{}),
|
||||
"version": newDentry(root, inoGen.NextIno(), 0444, &versionData{k: k}),
|
||||
}
|
||||
|
||||
inode := &tasksInode{
|
||||
|
||||
@@ -296,6 +296,50 @@ func (f *FDTable) NewFDs(ctx context.Context, fd int32, files []*fs.File, flags
|
||||
return fds, nil
|
||||
}
|
||||
|
||||
// NewFDVFS2 allocates a file descriptor greater than or equal to minfd for
|
||||
// the given file description. If it succeeds, it takes a reference on file.
|
||||
func (f *FDTable) NewFDVFS2(ctx context.Context, minfd int32, file *vfs.FileDescription, flags FDFlags) (int32, error) {
|
||||
if minfd < 0 {
|
||||
// Don't accept negative FDs.
|
||||
return -1, syscall.EINVAL
|
||||
}
|
||||
|
||||
// Default limit.
|
||||
end := int32(math.MaxInt32)
|
||||
|
||||
// Ensure we don't get past the provided limit.
|
||||
if limitSet := limits.FromContext(ctx); limitSet != nil {
|
||||
lim := limitSet.Get(limits.NumberOfFiles)
|
||||
if lim.Cur != limits.Infinity {
|
||||
end = int32(lim.Cur)
|
||||
}
|
||||
if minfd >= end {
|
||||
return -1, syscall.EMFILE
|
||||
}
|
||||
}
|
||||
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
// From f.next to find available fd.
|
||||
fd := minfd
|
||||
if fd < f.next {
|
||||
fd = f.next
|
||||
}
|
||||
for fd < end {
|
||||
if d, _, _ := f.get(fd); d == nil {
|
||||
f.setVFS2(fd, file, flags)
|
||||
if fd == f.next {
|
||||
// Update next search start position.
|
||||
f.next = fd + 1
|
||||
}
|
||||
return fd, nil
|
||||
}
|
||||
fd++
|
||||
}
|
||||
return -1, syscall.EMFILE
|
||||
}
|
||||
|
||||
// NewFDAt sets the file reference for the given FD. If there is an active
|
||||
// reference for that FD, the ref count for that existing reference is
|
||||
// decremented.
|
||||
@@ -316,9 +360,6 @@ func (f *FDTable) newFDAt(ctx context.Context, fd int32, file *fs.File, fileVFS2
|
||||
return syscall.EBADF
|
||||
}
|
||||
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
// Check the limit for the provided file.
|
||||
if limitSet := limits.FromContext(ctx); limitSet != nil {
|
||||
if lim := limitSet.Get(limits.NumberOfFiles); lim.Cur != limits.Infinity && uint64(fd) >= lim.Cur {
|
||||
@@ -327,6 +368,8 @@ func (f *FDTable) newFDAt(ctx context.Context, fd int32, file *fs.File, fileVFS2
|
||||
}
|
||||
|
||||
// Install the entry.
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
f.setAll(fd, file, fileVFS2, flags)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -244,6 +244,28 @@ func (f *FSContext) SetRootDirectory(d *fs.Dirent) {
|
||||
old.DecRef()
|
||||
}
|
||||
|
||||
// SetRootDirectoryVFS2 sets the root directory. It takes a reference on vd.
|
||||
//
|
||||
// This is not a valid call after free.
|
||||
func (f *FSContext) SetRootDirectoryVFS2(vd vfs.VirtualDentry) {
|
||||
if !vd.Ok() {
|
||||
panic("FSContext.SetRootDirectoryVFS2 called with zero-value VirtualDentry")
|
||||
}
|
||||
|
||||
f.mu.Lock()
|
||||
|
||||
if !f.rootVFS2.Ok() {
|
||||
f.mu.Unlock()
|
||||
panic(fmt.Sprintf("FSContext.SetRootDirectoryVFS2(%v)) called after destroy", vd))
|
||||
}
|
||||
|
||||
old := f.rootVFS2
|
||||
vd.IncRef()
|
||||
f.rootVFS2 = vd
|
||||
f.mu.Unlock()
|
||||
old.DecRef()
|
||||
}
|
||||
|
||||
// Umask returns the current umask.
|
||||
func (f *FSContext) Umask() uint {
|
||||
f.mu.Lock()
|
||||
|
||||
@@ -789,6 +789,15 @@ func (t *Task) NewFDFrom(fd int32, file *fs.File, flags FDFlags) (int32, error)
|
||||
return fds[0], nil
|
||||
}
|
||||
|
||||
// NewFDFromVFS2 is a convenience wrapper for t.FDTable().NewFDVFS2.
|
||||
//
|
||||
// This automatically passes the task as the context.
|
||||
//
|
||||
// Precondition: same as FDTable.Get.
|
||||
func (t *Task) NewFDFromVFS2(fd int32, file *vfs.FileDescription, flags FDFlags) (int32, error) {
|
||||
return t.fdTable.NewFDVFS2(t, fd, file, flags)
|
||||
}
|
||||
|
||||
// NewFDAt is a convenience wrapper for t.FDTable().NewFDAt.
|
||||
//
|
||||
// This automatically passes the task as the context.
|
||||
@@ -798,6 +807,15 @@ func (t *Task) NewFDAt(fd int32, file *fs.File, flags FDFlags) error {
|
||||
return t.fdTable.NewFDAt(t, fd, file, flags)
|
||||
}
|
||||
|
||||
// NewFDAtVFS2 is a convenience wrapper for t.FDTable().NewFDAtVFS2.
|
||||
//
|
||||
// This automatically passes the task as the context.
|
||||
//
|
||||
// Precondition: same as FDTable.
|
||||
func (t *Task) NewFDAtVFS2(fd int32, file *vfs.FileDescription, flags FDFlags) error {
|
||||
return t.fdTable.NewFDAtVFS2(t, fd, file, flags)
|
||||
}
|
||||
|
||||
// WithMuLocked executes f with t.mu locked.
|
||||
func (t *Task) WithMuLocked(f func(*Task)) {
|
||||
t.mu.Lock()
|
||||
|
||||
@@ -25,6 +25,8 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// EpollCreate1 implements the epoll_create1(2) linux syscall.
|
||||
func EpollCreate1(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
flags := args[0].Int()
|
||||
@@ -164,3 +166,5 @@ func EpollPwait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
|
||||
|
||||
return EpollWait(t, args)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/epoll.go)
|
||||
|
||||
@@ -130,6 +130,8 @@ func copyInPath(t *kernel.Task, addr usermem.Addr, allowEmpty bool) (path string
|
||||
return path, dirPath, nil
|
||||
}
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
func openAt(t *kernel.Task, dirFD int32, addr usermem.Addr, flags uint) (fd uintptr, err error) {
|
||||
path, dirPath, err := copyInPath(t, addr, false /* allowEmpty */)
|
||||
if err != nil {
|
||||
@@ -575,6 +577,10 @@ func Faccessat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
|
||||
return 0, nil, accessAt(t, dirFD, addr, flags&linux.AT_SYMLINK_NOFOLLOW == 0, mode)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/filesystem.go)
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// Ioctl implements linux syscall ioctl(2).
|
||||
func Ioctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
fd := args[0].Int()
|
||||
@@ -650,6 +656,10 @@ func Ioctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
}
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/ioctl.go)
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// Getcwd implements the linux syscall getcwd(2).
|
||||
func Getcwd(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
addr := args[0].Pointer()
|
||||
@@ -760,6 +770,10 @@ func Fchdir(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/fscontext.go)
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// Close implements linux syscall close(2).
|
||||
func Close(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
fd := args[0].Int()
|
||||
@@ -1094,6 +1108,8 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
}
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/fd.go)
|
||||
|
||||
const (
|
||||
_FADV_NORMAL = 0
|
||||
_FADV_RANDOM = 1
|
||||
@@ -1141,6 +1157,8 @@ func Fadvise64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
func mkdirAt(t *kernel.Task, dirFD int32, addr usermem.Addr, mode linux.FileMode) error {
|
||||
path, _, err := copyInPath(t, addr, false /* allowEmpty */)
|
||||
if err != nil {
|
||||
@@ -1421,6 +1439,10 @@ func Linkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
|
||||
return 0, nil, linkAt(t, oldDirFD, oldAddr, newDirFD, newAddr, resolve, allowEmpty)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/filesystem.go)
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
func readlinkAt(t *kernel.Task, dirFD int32, addr usermem.Addr, bufAddr usermem.Addr, size uint) (copied uintptr, err error) {
|
||||
path, dirPath, err := copyInPath(t, addr, false /* allowEmpty */)
|
||||
if err != nil {
|
||||
@@ -1480,6 +1502,10 @@ func Readlinkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sy
|
||||
return n, nil, err
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/stat.go)
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
func unlinkAt(t *kernel.Task, dirFD int32, addr usermem.Addr) error {
|
||||
path, dirPath, err := copyInPath(t, addr, false /* allowEmpty */)
|
||||
if err != nil {
|
||||
@@ -1516,6 +1542,10 @@ func Unlinkat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
return 0, nil, unlinkAt(t, dirFD, addr)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/filesystem.go)
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// Truncate implements linux syscall truncate(2).
|
||||
func Truncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
addr := args[0].Pointer()
|
||||
@@ -1614,6 +1644,8 @@ func Ftruncate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/setstat.go)
|
||||
|
||||
// Umask implements linux syscall umask(2).
|
||||
func Umask(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
mask := args[0].ModeT()
|
||||
@@ -1621,6 +1653,8 @@ func Umask(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
return uintptr(mask), nil, nil
|
||||
}
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// Change ownership of a file.
|
||||
//
|
||||
// uid and gid may be -1, in which case they will not be changed.
|
||||
@@ -1987,6 +2021,10 @@ func Futimesat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
|
||||
return 0, nil, utimes(t, dirFD, pathnameAddr, ts, true)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/setstat.go)
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
func renameAt(t *kernel.Task, oldDirFD int32, oldAddr usermem.Addr, newDirFD int32, newAddr usermem.Addr) error {
|
||||
newPath, _, err := copyInPath(t, newAddr, false /* allowEmpty */)
|
||||
if err != nil {
|
||||
@@ -2042,6 +2080,8 @@ func Renameat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
return 0, nil, renameAt(t, oldDirFD, oldPathAddr, newDirFD, newPathAddr)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/filesystem.go)
|
||||
|
||||
// Fallocate implements linux system call fallocate(2).
|
||||
func Fallocate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
fd := args[0].Int()
|
||||
|
||||
@@ -27,6 +27,8 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// Getdents implements linux syscall getdents(2) for 64bit systems.
|
||||
func Getdents(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
fd := args[0].Int()
|
||||
@@ -244,3 +246,5 @@ func (ds *direntSerializer) CopyOut(name string, attr fs.DentAttr) error {
|
||||
func (ds *direntSerializer) Written() int {
|
||||
return ds.written
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/getdents.go)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user