mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
VFS2 gofer client
Updates #1198 Opening host pipes (by spinning in fdpipe) and host sockets is not yet complete, and will be done in a future CL. Major differences from VFS1 gofer client (sentry/fs/gofer), with varying levels of backportability: - "Cache policies" are replaced by InteropMode, which control the behavior of timestamps in addition to caching. Under InteropModeExclusive (analogous to cacheAll) and InteropModeWritethrough (analogous to cacheAllWritethrough), client timestamps are *not* written back to the server (it is not possible in 9P or Linux for clients to set ctime, so writing back client-authoritative timestamps results in incoherence between atime/mtime and ctime). Under InteropModeShared (analogous to cacheRemoteRevalidating), client timestamps are not used at all (remote filesystem clocks are authoritative). cacheNone is translated to InteropModeShared + new option filesystemOptions.specialRegularFiles. - Under InteropModeShared, "unstable attribute" reloading for permission checks, lookup, and revalidation are fused, which is feasible in VFS2 since gofer.filesystem controls path resolution. This results in a ~33% reduction in RPCs for filesystem operations compared to cacheRemoteRevalidating. For example, consider stat("/foo/bar/baz") where "/foo/bar/baz" fails revalidation, resulting in the instantiation of a new dentry: VFS1 RPCs: getattr("/") // fs.MountNamespace.FindLink() => fs.Inode.CheckPermission() => gofer.inodeOperations.check() => gofer.inodeOperations.UnstableAttr() walkgetattr("/", "foo") = fid1 // fs.Dirent.walk() => gofer.session.Revalidate() => gofer.cachePolicy.Revalidate() clunk(fid1) getattr("/foo") // CheckPermission walkgetattr("/foo", "bar") = fid2 // Revalidate clunk(fid2) getattr("/foo/bar") // CheckPermission walkgetattr("/foo/bar", "baz") = fid3 // Revalidate clunk(fid3) walkgetattr("/foo/bar", "baz") = fid4 // fs.Dirent.walk() => gofer.inodeOperations.Lookup getattr("/foo/bar/baz") // linux.stat() => gofer.inodeOperations.UnstableAttr() VFS2 RPCs: getattr("/") // gofer.filesystem.walkExistingLocked() walkgetattr("/", "foo") = fid1 // gofer.filesystem.stepExistingLocked() clunk(fid1) // No getattr: walkgetattr already updated metadata for permission check walkgetattr("/foo", "bar") = fid2 clunk(fid2) walkgetattr("/foo/bar", "baz") = fid3 // No clunk: fid3 used for new gofer.dentry // No getattr: walkgetattr already updated metadata for stat() - gofer.filesystem.unlinkAt() does not require instantiation of a dentry that represents the file to be deleted. Updates #898. - gofer.regularFileFD.OnClose() skips Tflushf for regular files under InteropModeExclusive, as it's nonsensical to request a remote file flush without flushing locally-buffered writes to that remote file first. - Symlink targets are cached when InteropModeShared is not in effect. - p9.QID.Path (which is already required to be unique for each file within a server, and is accordingly already synthesized from device/inode numbers in all known gofers) is used as-is for inode numbers, rather than being mapped along with attr.RDev in the client to yet another synthetic inode number. - Relevant parts of fsutil.CachingInodeOperations are inlined directly into gofer package code. This avoids having to duplicate part of its functionality in fsutil.HostMappable. PiperOrigin-RevId: 293190213
This commit is contained in:
@@ -18,6 +18,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -297,3 +298,19 @@ func ZeroSeq(dsts BlockSeq) (uint64, error) {
|
||||
}
|
||||
return done, nil
|
||||
}
|
||||
|
||||
// IovecsFromBlockSeq returns a []syscall.Iovec representing seq.
|
||||
func IovecsFromBlockSeq(bs BlockSeq) []syscall.Iovec {
|
||||
iovs := make([]syscall.Iovec, 0, bs.NumBlocks())
|
||||
for ; !bs.IsEmpty(); bs = bs.Tail() {
|
||||
b := bs.Head()
|
||||
iovs = append(iovs, syscall.Iovec{
|
||||
Base: &b.ToSlice()[0],
|
||||
Len: uint64(b.Len()),
|
||||
})
|
||||
// We don't need to care about b.NeedSafecopy(), because the host
|
||||
// kernel will handle such address ranges just fine (by returning
|
||||
// EFAULT).
|
||||
}
|
||||
return iovs
|
||||
}
|
||||
|
||||
@@ -28,13 +28,13 @@ go_template_instance(
|
||||
"platform": "gvisor.dev/gvisor/pkg/sentry/platform",
|
||||
},
|
||||
package = "fsutil",
|
||||
prefix = "frameRef",
|
||||
prefix = "FrameRef",
|
||||
template = "//pkg/segment:generic_set",
|
||||
types = {
|
||||
"Key": "uint64",
|
||||
"Range": "platform.FileRange",
|
||||
"Value": "uint64",
|
||||
"Functions": "frameRefSetFunctions",
|
||||
"Functions": "FrameRefSetFunctions",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -20,24 +20,25 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/platform"
|
||||
)
|
||||
|
||||
type frameRefSetFunctions struct{}
|
||||
// FrameRefSetFunctions implements segment.Functions for FrameRefSet.
|
||||
type FrameRefSetFunctions struct{}
|
||||
|
||||
// MinKey implements segment.Functions.MinKey.
|
||||
func (frameRefSetFunctions) MinKey() uint64 {
|
||||
func (FrameRefSetFunctions) MinKey() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// MaxKey implements segment.Functions.MaxKey.
|
||||
func (frameRefSetFunctions) MaxKey() uint64 {
|
||||
func (FrameRefSetFunctions) MaxKey() uint64 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
|
||||
// ClearValue implements segment.Functions.ClearValue.
|
||||
func (frameRefSetFunctions) ClearValue(val *uint64) {
|
||||
func (FrameRefSetFunctions) ClearValue(val *uint64) {
|
||||
}
|
||||
|
||||
// Merge implements segment.Functions.Merge.
|
||||
func (frameRefSetFunctions) Merge(_ platform.FileRange, val1 uint64, _ platform.FileRange, val2 uint64) (uint64, bool) {
|
||||
func (FrameRefSetFunctions) Merge(_ platform.FileRange, val1 uint64, _ platform.FileRange, val2 uint64) (uint64, bool) {
|
||||
if val1 != val2 {
|
||||
return 0, false
|
||||
}
|
||||
@@ -45,6 +46,6 @@ func (frameRefSetFunctions) Merge(_ platform.FileRange, val1 uint64, _ platform.
|
||||
}
|
||||
|
||||
// Split implements segment.Functions.Split.
|
||||
func (frameRefSetFunctions) Split(_ platform.FileRange, val uint64, _ uint64) (uint64, uint64) {
|
||||
func (FrameRefSetFunctions) Split(_ platform.FileRange, val uint64, _ uint64) (uint64, uint64) {
|
||||
return val, val
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ type CachingInodeOperations struct {
|
||||
// refs tracks active references to data in the cache.
|
||||
//
|
||||
// refs is protected by dataMu.
|
||||
refs frameRefSet
|
||||
refs FrameRefSet
|
||||
}
|
||||
|
||||
// CachingInodeOperationsOptions configures a CachingInodeOperations.
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
load("//tools/go_generics:defs.bzl", "go_template_instance")
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
go_template_instance(
|
||||
name = "dentry_list",
|
||||
out = "dentry_list.go",
|
||||
package = "gofer",
|
||||
prefix = "dentry",
|
||||
template = "//pkg/ilist:generic_list",
|
||||
types = {
|
||||
"Element": "*dentry",
|
||||
"Linker": "*dentry",
|
||||
},
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "gofer",
|
||||
srcs = [
|
||||
"dentry_list.go",
|
||||
"directory.go",
|
||||
"filesystem.go",
|
||||
"gofer.go",
|
||||
"handle.go",
|
||||
"handle_unsafe.go",
|
||||
"p9file.go",
|
||||
"pagemath.go",
|
||||
"regular_file.go",
|
||||
"special_file.go",
|
||||
"symlink.go",
|
||||
"time.go",
|
||||
],
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/context",
|
||||
"//pkg/fd",
|
||||
"//pkg/fspath",
|
||||
"//pkg/log",
|
||||
"//pkg/p9",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/fs/fsutil",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/sentry/memmap",
|
||||
"//pkg/sentry/pgalloc",
|
||||
"//pkg/sentry/platform",
|
||||
"//pkg/sentry/usage",
|
||||
"//pkg/sentry/vfs",
|
||||
"//pkg/syserror",
|
||||
"//pkg/unet",
|
||||
"//pkg/usermem",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,190 @@
|
||||
// Copyright 2019 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 gofer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/p9"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
func (d *dentry) isDir() bool {
|
||||
return d.fileType() == linux.S_IFDIR
|
||||
}
|
||||
|
||||
// Preconditions: d.dirMu must be locked. d.isDir(). fs.opts.interop !=
|
||||
// InteropModeShared.
|
||||
func (d *dentry) cacheNegativeChildLocked(name string) {
|
||||
if d.negativeChildren == nil {
|
||||
d.negativeChildren = make(map[string]struct{})
|
||||
}
|
||||
d.negativeChildren[name] = struct{}{}
|
||||
}
|
||||
|
||||
type directoryFD struct {
|
||||
fileDescription
|
||||
vfs.DirectoryFileDescriptionDefaultImpl
|
||||
|
||||
mu sync.Mutex
|
||||
off int64
|
||||
dirents []vfs.Dirent
|
||||
}
|
||||
|
||||
// Release implements vfs.FileDescriptionImpl.Release.
|
||||
func (fd *directoryFD) Release() {
|
||||
}
|
||||
|
||||
// IterDirents implements vfs.FileDescriptionImpl.IterDirents.
|
||||
func (fd *directoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback) error {
|
||||
fd.mu.Lock()
|
||||
defer fd.mu.Unlock()
|
||||
|
||||
if fd.dirents == nil {
|
||||
ds, err := fd.dentry().getDirents(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fd.dirents = ds
|
||||
}
|
||||
|
||||
for fd.off < int64(len(fd.dirents)) {
|
||||
if !cb.Handle(fd.dirents[fd.off]) {
|
||||
return nil
|
||||
}
|
||||
fd.off++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Preconditions: d.isDir(). There exists at least one directoryFD representing d.
|
||||
func (d *dentry) getDirents(ctx context.Context) ([]vfs.Dirent, error) {
|
||||
// 9P2000.L's readdir does not specify behavior in the presence of
|
||||
// concurrent mutation of an iterated directory, so implementations may
|
||||
// duplicate or omit entries in this case, which violates POSIX semantics.
|
||||
// Thus we read all directory entries while holding d.dirMu to exclude
|
||||
// directory mutations. (Note that it is impossible for the client to
|
||||
// exclude concurrent mutation from other remote filesystem users. Since
|
||||
// there is no way to detect if the server has incorrectly omitted
|
||||
// directory entries, we simply assume that the server is well-behaved
|
||||
// under InteropModeShared.) This is inconsistent with Linux (which appears
|
||||
// to assume that directory fids have the correct semantics, and translates
|
||||
// struct file_operations::readdir calls directly to readdir RPCs), but is
|
||||
// consistent with VFS1.
|
||||
|
||||
d.fs.renameMu.RLock()
|
||||
defer d.fs.renameMu.RUnlock()
|
||||
d.dirMu.Lock()
|
||||
defer d.dirMu.Unlock()
|
||||
if d.dirents != nil {
|
||||
return d.dirents, nil
|
||||
}
|
||||
|
||||
// It's not clear if 9P2000.L's readdir is expected to return "." and "..",
|
||||
// so we generate them here.
|
||||
parent := d.vfsd.ParentOrSelf().Impl().(*dentry)
|
||||
dirents := []vfs.Dirent{
|
||||
{
|
||||
Name: ".",
|
||||
Type: linux.DT_DIR,
|
||||
Ino: d.ino,
|
||||
NextOff: 1,
|
||||
},
|
||||
{
|
||||
Name: "..",
|
||||
Type: uint8(atomic.LoadUint32(&parent.mode) >> 12),
|
||||
Ino: parent.ino,
|
||||
NextOff: 2,
|
||||
},
|
||||
}
|
||||
off := uint64(0)
|
||||
const count = 64 * 1024 // for consistency with the vfs1 client
|
||||
d.handleMu.RLock()
|
||||
defer d.handleMu.RUnlock()
|
||||
if !d.handleReadable {
|
||||
// This should not be possible because a readable handle should have
|
||||
// been opened when the calling directoryFD was opened.
|
||||
panic("gofer.dentry.getDirents called without a readable handle")
|
||||
}
|
||||
for {
|
||||
p9ds, err := d.handle.file.readdir(ctx, off, count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(p9ds) == 0 {
|
||||
// Cache dirents for future directoryFDs if permitted.
|
||||
if d.fs.opts.interop != InteropModeShared {
|
||||
d.dirents = dirents
|
||||
}
|
||||
return dirents, nil
|
||||
}
|
||||
for _, p9d := range p9ds {
|
||||
if p9d.Name == "." || p9d.Name == ".." {
|
||||
continue
|
||||
}
|
||||
dirent := vfs.Dirent{
|
||||
Name: p9d.Name,
|
||||
Ino: p9d.QID.Path,
|
||||
NextOff: int64(len(dirents) + 1),
|
||||
}
|
||||
// p9 does not expose 9P2000.U's DMDEVICE, DMNAMEDPIPE, or
|
||||
// DMSOCKET.
|
||||
switch p9d.Type {
|
||||
case p9.TypeSymlink:
|
||||
dirent.Type = linux.DT_LNK
|
||||
case p9.TypeDir:
|
||||
dirent.Type = linux.DT_DIR
|
||||
default:
|
||||
dirent.Type = linux.DT_REG
|
||||
}
|
||||
dirents = append(dirents, dirent)
|
||||
}
|
||||
off = p9ds[len(p9ds)-1].Offset
|
||||
}
|
||||
}
|
||||
|
||||
// Seek implements vfs.FileDescriptionImpl.Seek.
|
||||
func (fd *directoryFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
|
||||
fd.mu.Lock()
|
||||
defer fd.mu.Unlock()
|
||||
|
||||
switch whence {
|
||||
case linux.SEEK_SET:
|
||||
if offset < 0 {
|
||||
return 0, syserror.EINVAL
|
||||
}
|
||||
if offset == 0 {
|
||||
// Ensure that the next call to fd.IterDirents() calls
|
||||
// fd.dentry().getDirents().
|
||||
fd.dirents = nil
|
||||
}
|
||||
fd.off = offset
|
||||
return fd.off, nil
|
||||
case linux.SEEK_CUR:
|
||||
offset += fd.off
|
||||
if offset < 0 {
|
||||
return 0, syserror.EINVAL
|
||||
}
|
||||
// Don't clear fd.dirents in this case, even if offset == 0.
|
||||
fd.off = offset
|
||||
return fd.off, nil
|
||||
default:
|
||||
return 0, syserror.EINVAL
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,135 @@
|
||||
// Copyright 2019 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 gofer
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/p9"
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
)
|
||||
|
||||
// handle represents a remote "open file descriptor", consisting of an opened
|
||||
// fid (p9.File) and optionally a host file descriptor.
|
||||
type handle struct {
|
||||
file p9file
|
||||
fd int32 // -1 if unavailable
|
||||
}
|
||||
|
||||
// Preconditions: read || write.
|
||||
func openHandle(ctx context.Context, file p9file, read, write, trunc bool) (handle, error) {
|
||||
_, newfile, err := file.walk(ctx, nil)
|
||||
if err != nil {
|
||||
return handle{fd: -1}, err
|
||||
}
|
||||
var flags p9.OpenFlags
|
||||
switch {
|
||||
case read && !write:
|
||||
flags = p9.ReadOnly
|
||||
case !read && write:
|
||||
flags = p9.WriteOnly
|
||||
case read && write:
|
||||
flags = p9.ReadWrite
|
||||
}
|
||||
if trunc {
|
||||
flags |= p9.OpenTruncate
|
||||
}
|
||||
fdobj, _, _, err := newfile.open(ctx, flags)
|
||||
if err != nil {
|
||||
newfile.close(ctx)
|
||||
return handle{fd: -1}, err
|
||||
}
|
||||
fd := int32(-1)
|
||||
if fdobj != nil {
|
||||
fd = int32(fdobj.Release())
|
||||
}
|
||||
return handle{
|
||||
file: newfile,
|
||||
fd: fd,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h *handle) close(ctx context.Context) {
|
||||
h.file.close(ctx)
|
||||
h.file = p9file{}
|
||||
if h.fd >= 0 {
|
||||
syscall.Close(int(h.fd))
|
||||
h.fd = -1
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handle) readToBlocksAt(ctx context.Context, dsts safemem.BlockSeq, offset uint64) (uint64, error) {
|
||||
if dsts.IsEmpty() {
|
||||
return 0, nil
|
||||
}
|
||||
if h.fd >= 0 {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
n, err := hostPreadv(h.fd, dsts, int64(offset))
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return n, err
|
||||
}
|
||||
if dsts.NumBlocks() == 1 && !dsts.Head().NeedSafecopy() {
|
||||
n, err := h.file.readAt(ctx, dsts.Head().ToSlice(), offset)
|
||||
return uint64(n), err
|
||||
}
|
||||
// Buffer the read since p9.File.ReadAt() takes []byte.
|
||||
buf := make([]byte, dsts.NumBytes())
|
||||
n, err := h.file.readAt(ctx, buf, offset)
|
||||
if n == 0 {
|
||||
return 0, err
|
||||
}
|
||||
if cp, cperr := safemem.CopySeq(dsts, safemem.BlockSeqOf(safemem.BlockFromSafeSlice(buf[:n]))); cperr != nil {
|
||||
return cp, cperr
|
||||
}
|
||||
return uint64(n), err
|
||||
}
|
||||
|
||||
func (h *handle) writeFromBlocksAt(ctx context.Context, srcs safemem.BlockSeq, offset uint64) (uint64, error) {
|
||||
if srcs.IsEmpty() {
|
||||
return 0, nil
|
||||
}
|
||||
if h.fd >= 0 {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
n, err := hostPwritev(h.fd, srcs, int64(offset))
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return n, err
|
||||
}
|
||||
if srcs.NumBlocks() == 1 && !srcs.Head().NeedSafecopy() {
|
||||
n, err := h.file.writeAt(ctx, srcs.Head().ToSlice(), offset)
|
||||
return uint64(n), err
|
||||
}
|
||||
// Buffer the write since p9.File.WriteAt() takes []byte.
|
||||
buf := make([]byte, srcs.NumBytes())
|
||||
cp, cperr := safemem.CopySeq(safemem.BlockSeqOf(safemem.BlockFromSafeSlice(buf)), srcs)
|
||||
if cp == 0 {
|
||||
return 0, cperr
|
||||
}
|
||||
n, err := h.file.writeAt(ctx, buf[:cp], offset)
|
||||
if err != nil {
|
||||
return uint64(n), err
|
||||
}
|
||||
return cp, cperr
|
||||
}
|
||||
|
||||
func (h *handle) sync(ctx context.Context) error {
|
||||
if h.fd >= 0 {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := syscall.Fsync(int(h.fd))
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return err
|
||||
}
|
||||
return h.file.fsync(ctx)
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2019 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 gofer
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
)
|
||||
|
||||
// Preconditions: !dsts.IsEmpty().
|
||||
func hostPreadv(fd int32, dsts safemem.BlockSeq, off int64) (uint64, error) {
|
||||
// No buffering is necessary regardless of safecopy; host syscalls will
|
||||
// return EFAULT if appropriate, instead of raising SIGBUS.
|
||||
if dsts.NumBlocks() == 1 {
|
||||
// Use pread() instead of preadv() to avoid iovec allocation and
|
||||
// copying.
|
||||
dst := dsts.Head()
|
||||
n, _, e := syscall.Syscall6(syscall.SYS_PREAD64, uintptr(fd), dst.Addr(), uintptr(dst.Len()), uintptr(off), 0, 0)
|
||||
if e != 0 {
|
||||
return 0, e
|
||||
}
|
||||
return uint64(n), nil
|
||||
}
|
||||
iovs := safemem.IovecsFromBlockSeq(dsts)
|
||||
n, _, e := syscall.Syscall6(syscall.SYS_PREADV, uintptr(fd), uintptr((unsafe.Pointer)(&iovs[0])), uintptr(len(iovs)), uintptr(off), 0, 0)
|
||||
if e != 0 {
|
||||
return 0, e
|
||||
}
|
||||
return uint64(n), nil
|
||||
}
|
||||
|
||||
// Preconditions: !srcs.IsEmpty().
|
||||
func hostPwritev(fd int32, srcs safemem.BlockSeq, off int64) (uint64, error) {
|
||||
// No buffering is necessary regardless of safecopy; host syscalls will
|
||||
// return EFAULT if appropriate, instead of raising SIGBUS.
|
||||
if srcs.NumBlocks() == 1 {
|
||||
// Use pwrite() instead of pwritev() to avoid iovec allocation and
|
||||
// copying.
|
||||
src := srcs.Head()
|
||||
n, _, e := syscall.Syscall6(syscall.SYS_PWRITE64, uintptr(fd), src.Addr(), uintptr(src.Len()), uintptr(off), 0, 0)
|
||||
if e != 0 {
|
||||
return 0, e
|
||||
}
|
||||
return uint64(n), nil
|
||||
}
|
||||
iovs := safemem.IovecsFromBlockSeq(srcs)
|
||||
n, _, e := syscall.Syscall6(syscall.SYS_PWRITEV, uintptr(fd), uintptr((unsafe.Pointer)(&iovs[0])), uintptr(len(iovs)), uintptr(off), 0, 0)
|
||||
if e != 0 {
|
||||
return 0, e
|
||||
}
|
||||
return uint64(n), nil
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
// Copyright 2019 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 gofer
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fd"
|
||||
"gvisor.dev/gvisor/pkg/p9"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
// p9file is a wrapper around p9.File that provides methods that are
|
||||
// Context-aware.
|
||||
type p9file struct {
|
||||
file p9.File
|
||||
}
|
||||
|
||||
func (f p9file) isNil() bool {
|
||||
return f.file == nil
|
||||
}
|
||||
|
||||
func (f p9file) walk(ctx context.Context, names []string) ([]p9.QID, p9file, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
qids, newfile, err := f.file.Walk(names)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return qids, p9file{newfile}, err
|
||||
}
|
||||
|
||||
func (f p9file) walkGetAttr(ctx context.Context, names []string) ([]p9.QID, p9file, p9.AttrMask, p9.Attr, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
qids, newfile, attrMask, attr, err := f.file.WalkGetAttr(names)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return qids, p9file{newfile}, attrMask, attr, err
|
||||
}
|
||||
|
||||
// walkGetAttrOne is a wrapper around p9.File.WalkGetAttr that takes a single
|
||||
// path component and returns a single qid.
|
||||
func (f p9file) walkGetAttrOne(ctx context.Context, name string) (p9.QID, p9file, p9.AttrMask, p9.Attr, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
qids, newfile, attrMask, attr, err := f.file.WalkGetAttr([]string{name})
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
if err != nil {
|
||||
return p9.QID{}, p9file{}, p9.AttrMask{}, p9.Attr{}, err
|
||||
}
|
||||
if len(qids) != 1 {
|
||||
ctx.Warningf("p9.File.WalkGetAttr returned %d qids (%v), wanted 1", len(qids), qids)
|
||||
if newfile != nil {
|
||||
p9file{newfile}.close(ctx)
|
||||
}
|
||||
return p9.QID{}, p9file{}, p9.AttrMask{}, p9.Attr{}, syserror.EIO
|
||||
}
|
||||
return qids[0], p9file{newfile}, attrMask, attr, nil
|
||||
}
|
||||
|
||||
func (f p9file) statFS(ctx context.Context) (p9.FSStat, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
fsstat, err := f.file.StatFS()
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return fsstat, err
|
||||
}
|
||||
|
||||
func (f p9file) getAttr(ctx context.Context, req p9.AttrMask) (p9.QID, p9.AttrMask, p9.Attr, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
qid, attrMask, attr, err := f.file.GetAttr(req)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return qid, attrMask, attr, err
|
||||
}
|
||||
|
||||
func (f p9file) setAttr(ctx context.Context, valid p9.SetAttrMask, attr p9.SetAttr) error {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.file.SetAttr(valid, attr)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f p9file) getXattr(ctx context.Context, name string, size uint64) (string, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
val, err := f.file.GetXattr(name, size)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return val, err
|
||||
}
|
||||
|
||||
func (f p9file) setXattr(ctx context.Context, name, value string, flags uint32) error {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.file.SetXattr(name, value, flags)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f p9file) allocate(ctx context.Context, mode p9.AllocateMode, offset, length uint64) error {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.file.Allocate(mode, offset, length)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f p9file) close(ctx context.Context) error {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.file.Close()
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f p9file) open(ctx context.Context, flags p9.OpenFlags) (*fd.FD, p9.QID, uint32, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
fdobj, qid, iounit, err := f.file.Open(flags)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return fdobj, qid, iounit, err
|
||||
}
|
||||
|
||||
func (f p9file) readAt(ctx context.Context, p []byte, offset uint64) (int, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
n, err := f.file.ReadAt(p, offset)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (f p9file) writeAt(ctx context.Context, p []byte, offset uint64) (int, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
n, err := f.file.WriteAt(p, offset)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (f p9file) fsync(ctx context.Context) error {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.file.FSync()
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f p9file) create(ctx context.Context, name string, flags p9.OpenFlags, permissions p9.FileMode, uid p9.UID, gid p9.GID) (*fd.FD, p9file, p9.QID, uint32, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
fdobj, newfile, qid, iounit, err := f.file.Create(name, flags, permissions, uid, gid)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return fdobj, p9file{newfile}, qid, iounit, err
|
||||
}
|
||||
|
||||
func (f p9file) mkdir(ctx context.Context, name string, permissions p9.FileMode, uid p9.UID, gid p9.GID) (p9.QID, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
qid, err := f.file.Mkdir(name, permissions, uid, gid)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return qid, err
|
||||
}
|
||||
|
||||
func (f p9file) symlink(ctx context.Context, oldName string, newName string, uid p9.UID, gid p9.GID) (p9.QID, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
qid, err := f.file.Symlink(oldName, newName, uid, gid)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return qid, err
|
||||
}
|
||||
|
||||
func (f p9file) link(ctx context.Context, target p9file, newName string) error {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.file.Link(target.file, newName)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f p9file) mknod(ctx context.Context, name string, mode p9.FileMode, major uint32, minor uint32, uid p9.UID, gid p9.GID) (p9.QID, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
qid, err := f.file.Mknod(name, mode, major, minor, uid, gid)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return qid, err
|
||||
}
|
||||
|
||||
func (f p9file) rename(ctx context.Context, newDir p9file, newName string) error {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.file.Rename(newDir.file, newName)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f p9file) unlinkAt(ctx context.Context, name string, flags uint32) error {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.file.UnlinkAt(name, flags)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f p9file) readdir(ctx context.Context, offset uint64, count uint32) ([]p9.Dirent, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
dirents, err := f.file.Readdir(offset, count)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return dirents, err
|
||||
}
|
||||
|
||||
func (f p9file) readlink(ctx context.Context) (string, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
target, err := f.file.Readlink()
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return target, err
|
||||
}
|
||||
|
||||
func (f p9file) flush(ctx context.Context) error {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
err := f.file.Flush()
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f p9file) connect(ctx context.Context, flags p9.ConnectFlags) (*fd.FD, error) {
|
||||
ctx.UninterruptibleSleepStart(false)
|
||||
fdobj, err := f.file.Connect(flags)
|
||||
ctx.UninterruptibleSleepFinish(false)
|
||||
return fdobj, err
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2019 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 gofer
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
|
||||
// This are equivalent to usermem.Addr.RoundDown/Up, but without the
|
||||
// potentially truncating conversion to usermem.Addr. This is necessary because
|
||||
// there is no way to define generic "PageRoundDown/Up" functions in Go.
|
||||
|
||||
func pageRoundDown(x uint64) uint64 {
|
||||
return x &^ (usermem.PageSize - 1)
|
||||
}
|
||||
|
||||
func pageRoundUp(x uint64) uint64 {
|
||||
return pageRoundDown(x + usermem.PageSize - 1)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,159 @@
|
||||
// Copyright 2019 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 gofer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/safemem"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
|
||||
// specialFileFD implements vfs.FileDescriptionImpl for files other than
|
||||
// regular files, directories, and symlinks: pipes, sockets, etc. It is also
|
||||
// used for regular files when filesystemOptions.specialRegularFiles is in
|
||||
// effect. specialFileFD differs from regularFileFD by using per-FD handles
|
||||
// instead of shared per-dentry handles, and never buffering I/O.
|
||||
type specialFileFD struct {
|
||||
fileDescription
|
||||
|
||||
// handle is immutable.
|
||||
handle handle
|
||||
|
||||
// off is the file offset. off is protected by mu. (POSIX 2.9.7 only
|
||||
// requires operations using the file offset to be atomic for regular files
|
||||
// and symlinks; however, since specialFileFD may be used for regular
|
||||
// files, we apply this atomicity unconditionally.)
|
||||
mu sync.Mutex
|
||||
off int64
|
||||
}
|
||||
|
||||
// Release implements vfs.FileDescriptionImpl.Release.
|
||||
func (fd *specialFileFD) Release() {
|
||||
fd.handle.close(context.Background())
|
||||
fs := fd.vfsfd.Mount().Filesystem().Impl().(*filesystem)
|
||||
fs.syncMu.Lock()
|
||||
delete(fs.specialFileFDs, fd)
|
||||
fs.syncMu.Unlock()
|
||||
}
|
||||
|
||||
// OnClose implements vfs.FileDescriptionImpl.OnClose.
|
||||
func (fd *specialFileFD) OnClose(ctx context.Context) error {
|
||||
if !fd.vfsfd.IsWritable() {
|
||||
return nil
|
||||
}
|
||||
return fd.handle.file.flush(ctx)
|
||||
}
|
||||
|
||||
// PRead implements vfs.FileDescriptionImpl.PRead.
|
||||
func (fd *specialFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
|
||||
if offset < 0 {
|
||||
return 0, syserror.EINVAL
|
||||
}
|
||||
if opts.Flags != 0 {
|
||||
return 0, syserror.EOPNOTSUPP
|
||||
}
|
||||
|
||||
// Going through dst.CopyOutFrom() holds MM locks around file operations of
|
||||
// unknown duration. For regularFileFD, doing so is necessary to support
|
||||
// mmap due to lock ordering; MM locks precede dentry.dataMu. That doesn't
|
||||
// hold here since specialFileFD doesn't client-cache data. Just buffer the
|
||||
// read instead.
|
||||
if d := fd.dentry(); d.fs.opts.interop != InteropModeShared {
|
||||
d.touchAtime(ctx, fd.vfsfd.Mount())
|
||||
}
|
||||
buf := make([]byte, dst.NumBytes())
|
||||
n, err := fd.handle.readToBlocksAt(ctx, safemem.BlockSeqOf(safemem.BlockFromSafeSlice(buf)), uint64(offset))
|
||||
if n == 0 {
|
||||
return 0, err
|
||||
}
|
||||
if cp, cperr := dst.CopyOut(ctx, buf[:n]); cperr != nil {
|
||||
return int64(cp), cperr
|
||||
}
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// Read implements vfs.FileDescriptionImpl.Read.
|
||||
func (fd *specialFileFD) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) {
|
||||
fd.mu.Lock()
|
||||
n, err := fd.PRead(ctx, dst, fd.off, opts)
|
||||
fd.off += n
|
||||
fd.mu.Unlock()
|
||||
return n, err
|
||||
}
|
||||
|
||||
// PWrite implements vfs.FileDescriptionImpl.PWrite.
|
||||
func (fd *specialFileFD) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
|
||||
if offset < 0 {
|
||||
return 0, syserror.EINVAL
|
||||
}
|
||||
if opts.Flags != 0 {
|
||||
return 0, syserror.EOPNOTSUPP
|
||||
}
|
||||
|
||||
// Do a buffered write. See rationale in PRead.
|
||||
if d := fd.dentry(); d.fs.opts.interop != InteropModeShared {
|
||||
d.touchCMtime(ctx)
|
||||
}
|
||||
buf := make([]byte, src.NumBytes())
|
||||
// Don't do partial writes if we get a partial read from src.
|
||||
if _, err := src.CopyIn(ctx, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err := fd.handle.writeFromBlocksAt(ctx, safemem.BlockSeqOf(safemem.BlockFromSafeSlice(buf)), uint64(offset))
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// Write implements vfs.FileDescriptionImpl.Write.
|
||||
func (fd *specialFileFD) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) {
|
||||
fd.mu.Lock()
|
||||
n, err := fd.PWrite(ctx, src, fd.off, opts)
|
||||
fd.off += n
|
||||
fd.mu.Unlock()
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Seek implements vfs.FileDescriptionImpl.Seek.
|
||||
func (fd *specialFileFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
|
||||
fd.mu.Lock()
|
||||
defer fd.mu.Unlock()
|
||||
switch whence {
|
||||
case linux.SEEK_SET:
|
||||
// Use offset as given.
|
||||
case linux.SEEK_CUR:
|
||||
offset += fd.off
|
||||
default:
|
||||
// SEEK_END, SEEK_DATA, and SEEK_HOLE aren't supported since it's not
|
||||
// clear that file size is even meaningful for these files.
|
||||
return 0, syserror.EINVAL
|
||||
}
|
||||
if offset < 0 {
|
||||
return 0, syserror.EINVAL
|
||||
}
|
||||
fd.off = offset
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
// Sync implements vfs.FileDescriptionImpl.Sync.
|
||||
func (fd *specialFileFD) Sync(ctx context.Context) error {
|
||||
if !fd.vfsfd.IsWritable() {
|
||||
return nil
|
||||
}
|
||||
return fd.handle.sync(ctx)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2019 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 gofer
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
func (d *dentry) isSymlink() bool {
|
||||
return d.fileType() == linux.S_IFLNK
|
||||
}
|
||||
|
||||
// Precondition: d.isSymlink().
|
||||
func (d *dentry) readlink(ctx context.Context, mnt *vfs.Mount) (string, error) {
|
||||
if d.fs.opts.interop != InteropModeShared {
|
||||
d.touchAtime(ctx, mnt)
|
||||
d.dataMu.Lock()
|
||||
if d.haveTarget {
|
||||
target := d.target
|
||||
d.dataMu.Unlock()
|
||||
return target, nil
|
||||
}
|
||||
}
|
||||
target, err := d.file.readlink(ctx)
|
||||
if d.fs.opts.interop != InteropModeShared {
|
||||
if err == nil {
|
||||
d.haveTarget = true
|
||||
d.target = target
|
||||
}
|
||||
d.dataMu.Unlock()
|
||||
}
|
||||
return target, err
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright 2019 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 gofer
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
func dentryTimestampFromP9(s, ns uint64) int64 {
|
||||
return int64(s*1e9 + ns)
|
||||
}
|
||||
|
||||
func dentryTimestampFromStatx(ts linux.StatxTimestamp) int64 {
|
||||
return ts.Sec*1e9 + int64(ts.Nsec)
|
||||
}
|
||||
|
||||
func statxTimestampFromDentry(ns int64) linux.StatxTimestamp {
|
||||
return linux.StatxTimestamp{
|
||||
Sec: ns / 1e9,
|
||||
Nsec: uint32(ns % 1e9),
|
||||
}
|
||||
}
|
||||
|
||||
func nowFromContext(ctx context.Context) (int64, bool) {
|
||||
if clock := ktime.RealtimeClockFromContext(ctx); clock != nil {
|
||||
return clock.Now().Nanoseconds(), true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Preconditions: fs.interop != InteropModeShared.
|
||||
func (d *dentry) touchAtime(ctx context.Context, mnt *vfs.Mount) {
|
||||
if err := mnt.CheckBeginWrite(); err != nil {
|
||||
return
|
||||
}
|
||||
now, ok := nowFromContext(ctx)
|
||||
if !ok {
|
||||
mnt.EndWrite()
|
||||
return
|
||||
}
|
||||
d.metadataMu.Lock()
|
||||
atomic.StoreInt64(&d.atime, now)
|
||||
d.metadataMu.Unlock()
|
||||
mnt.EndWrite()
|
||||
}
|
||||
|
||||
// Preconditions: fs.interop != InteropModeShared. The caller has successfully
|
||||
// called vfs.Mount.CheckBeginWrite().
|
||||
func (d *dentry) touchCMtime(ctx context.Context) {
|
||||
now, ok := nowFromContext(ctx)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
d.metadataMu.Lock()
|
||||
atomic.StoreInt64(&d.mtime, now)
|
||||
atomic.StoreInt64(&d.ctime, now)
|
||||
d.metadataMu.Unlock()
|
||||
}
|
||||
@@ -622,7 +622,7 @@ func (fs *filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error
|
||||
if child.inode.isDir() {
|
||||
return syserror.EISDIR
|
||||
}
|
||||
if !rp.MustBeDir() {
|
||||
if rp.MustBeDir() {
|
||||
return syserror.ENOTDIR
|
||||
}
|
||||
mnt := rp.Mount()
|
||||
|
||||
@@ -126,7 +126,7 @@ func (s *socketOperations) Read(ctx context.Context, _ *fs.File, dst usermem.IOS
|
||||
}
|
||||
return uint64(n), nil
|
||||
}
|
||||
return readv(s.fd, iovecsFromBlockSeq(dsts))
|
||||
return readv(s.fd, safemem.IovecsFromBlockSeq(dsts))
|
||||
}))
|
||||
return int64(n), err
|
||||
}
|
||||
@@ -149,7 +149,7 @@ func (s *socketOperations) Write(ctx context.Context, _ *fs.File, src usermem.IO
|
||||
}
|
||||
return uint64(n), nil
|
||||
}
|
||||
return writev(s.fd, iovecsFromBlockSeq(srcs))
|
||||
return writev(s.fd, safemem.IovecsFromBlockSeq(srcs))
|
||||
}))
|
||||
return int64(n), err
|
||||
}
|
||||
@@ -402,7 +402,7 @@ func (s *socketOperations) RecvMsg(t *kernel.Task, dst usermem.IOSequence, flags
|
||||
// We always do a non-blocking recv*().
|
||||
sysflags := flags | syscall.MSG_DONTWAIT
|
||||
|
||||
iovs := iovecsFromBlockSeq(dsts)
|
||||
iovs := safemem.IovecsFromBlockSeq(dsts)
|
||||
msg := syscall.Msghdr{
|
||||
Iov: &iovs[0],
|
||||
Iovlen: uint64(len(iovs)),
|
||||
@@ -522,7 +522,7 @@ func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []
|
||||
return uint64(n), nil
|
||||
}
|
||||
|
||||
iovs := iovecsFromBlockSeq(srcs)
|
||||
iovs := safemem.IovecsFromBlockSeq(srcs)
|
||||
msg := syscall.Msghdr{
|
||||
Iov: &iovs[0],
|
||||
Iovlen: uint64(len(iovs)),
|
||||
@@ -567,21 +567,6 @@ func (s *socketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []
|
||||
return int(n), syserr.FromError(err)
|
||||
}
|
||||
|
||||
func iovecsFromBlockSeq(bs safemem.BlockSeq) []syscall.Iovec {
|
||||
iovs := make([]syscall.Iovec, 0, bs.NumBlocks())
|
||||
for ; !bs.IsEmpty(); bs = bs.Tail() {
|
||||
b := bs.Head()
|
||||
iovs = append(iovs, syscall.Iovec{
|
||||
Base: &b.ToSlice()[0],
|
||||
Len: uint64(b.Len()),
|
||||
})
|
||||
// We don't need to care about b.NeedSafecopy(), because the host
|
||||
// kernel will handle such address ranges just fine (by returning
|
||||
// EFAULT).
|
||||
}
|
||||
return iovs
|
||||
}
|
||||
|
||||
func translateIOSyscallError(err error) error {
|
||||
if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK {
|
||||
return syserror.ErrWouldBlock
|
||||
|
||||
Reference in New Issue
Block a user