2019-12-12 19:19:18 +00:00
|
|
|
// 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 kernfs
|
|
|
|
|
2020-03-06 20:58:45 +00:00
|
|
|
// This file implements vfs.FilesystemImpl for kernfs.
|
|
|
|
|
2019-12-12 19:19:18 +00:00
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
2020-01-27 23:17:58 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/context"
|
2019-12-12 19:19:18 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/fspath"
|
2020-03-13 18:40:13 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
2019-12-12 19:19:18 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
|
|
|
"gvisor.dev/gvisor/pkg/syserror"
|
|
|
|
)
|
|
|
|
|
|
|
|
// stepExistingLocked resolves rp.Component() in parent directory vfsd.
|
|
|
|
//
|
|
|
|
// stepExistingLocked is loosely analogous to fs/namei.c:walk_component().
|
|
|
|
//
|
|
|
|
// Preconditions: Filesystem.mu must be locked for at least reading. !rp.Done().
|
|
|
|
//
|
|
|
|
// Postcondition: Caller must call fs.processDeferredDecRefs*.
|
|
|
|
func (fs *Filesystem) stepExistingLocked(ctx context.Context, rp *vfs.ResolvingPath, vfsd *vfs.Dentry) (*vfs.Dentry, error) {
|
|
|
|
d := vfsd.Impl().(*Dentry)
|
|
|
|
if !d.isDir() {
|
|
|
|
return nil, syserror.ENOTDIR
|
|
|
|
}
|
|
|
|
// Directory searchable?
|
2020-01-16 21:13:22 +00:00
|
|
|
if err := d.inode.CheckPermissions(ctx, rp.Credentials(), vfs.MayExec); err != nil {
|
2019-12-12 19:19:18 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
afterSymlink:
|
2019-12-23 21:17:29 +00:00
|
|
|
name := rp.Component()
|
|
|
|
// Revalidation must be skipped if name is "." or ".."; d or its parent
|
|
|
|
// respectively can't be expected to transition from invalidated back to
|
|
|
|
// valid, so detecting invalidation and retrying would loop forever. This
|
|
|
|
// is consistent with Linux: fs/namei.c:walk_component() => lookup_fast()
|
|
|
|
// calls d_revalidate(), but walk_component() => handle_dots() does not.
|
|
|
|
if name == "." {
|
|
|
|
rp.Advance()
|
|
|
|
return vfsd, nil
|
|
|
|
}
|
|
|
|
if name == ".." {
|
|
|
|
nextVFSD, err := rp.ResolveParent(vfsd)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
rp.Advance()
|
|
|
|
return nextVFSD, nil
|
|
|
|
}
|
2019-12-12 19:19:18 +00:00
|
|
|
d.dirMu.Lock()
|
2019-12-23 21:17:29 +00:00
|
|
|
nextVFSD, err := rp.ResolveChild(vfsd, name)
|
2019-12-12 19:19:18 +00:00
|
|
|
if err != nil {
|
2019-12-23 21:17:29 +00:00
|
|
|
d.dirMu.Unlock()
|
2019-12-12 19:19:18 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2019-12-23 21:17:29 +00:00
|
|
|
next, err := fs.revalidateChildLocked(ctx, rp.VirtualFilesystem(), d, name, nextVFSD)
|
|
|
|
d.dirMu.Unlock()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-12-12 19:19:18 +00:00
|
|
|
}
|
|
|
|
// Resolve any symlink at current path component.
|
2019-12-23 21:17:29 +00:00
|
|
|
if rp.ShouldFollowSymlink() && next.isSymlink() {
|
2019-12-12 19:19:18 +00:00
|
|
|
// TODO: VFS2 needs something extra for /proc/[pid]/fd/ "magic symlinks".
|
|
|
|
target, err := next.inode.Readlink(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := rp.HandleSymlink(target); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
goto afterSymlink
|
|
|
|
|
|
|
|
}
|
|
|
|
rp.Advance()
|
2019-12-23 21:17:29 +00:00
|
|
|
return &next.vfsd, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// revalidateChildLocked must be called after a call to parent.vfsd.Child(name)
|
|
|
|
// or vfs.ResolvingPath.ResolveChild(name) returns childVFSD (which may be
|
|
|
|
// nil) to verify that the returned child (or lack thereof) is correct.
|
|
|
|
//
|
|
|
|
// Preconditions: Filesystem.mu must be locked for at least reading.
|
|
|
|
// parent.dirMu must be locked. parent.isDir(). name is not "." or "..".
|
|
|
|
//
|
|
|
|
// Postconditions: Caller must call fs.processDeferredDecRefs*.
|
|
|
|
func (fs *Filesystem) revalidateChildLocked(ctx context.Context, vfsObj *vfs.VirtualFilesystem, parent *Dentry, name string, childVFSD *vfs.Dentry) (*Dentry, error) {
|
|
|
|
if childVFSD != nil {
|
|
|
|
// Cached dentry exists, revalidate.
|
|
|
|
child := childVFSD.Impl().(*Dentry)
|
|
|
|
if !child.inode.Valid(ctx) {
|
|
|
|
vfsObj.ForceDeleteDentry(childVFSD)
|
|
|
|
fs.deferDecRef(childVFSD) // Reference from Lookup.
|
|
|
|
childVFSD = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if childVFSD == nil {
|
|
|
|
// Dentry isn't cached; it either doesn't exist or failed
|
|
|
|
// revalidation. Attempt to resolve it via Lookup.
|
|
|
|
//
|
2020-02-10 20:02:47 +00:00
|
|
|
// FIXME(gvisor.dev/issue/1193): Inode.Lookup() should return
|
|
|
|
// *(kernfs.)Dentry, not *vfs.Dentry, since (kernfs.)Filesystem assumes
|
|
|
|
// that all dentries in the filesystem are (kernfs.)Dentry and performs
|
|
|
|
// vfs.DentryImpl casts accordingly.
|
2019-12-23 21:17:29 +00:00
|
|
|
var err error
|
|
|
|
childVFSD, err = parent.inode.Lookup(ctx, name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Reference on childVFSD dropped by a corresponding Valid.
|
2019-12-24 01:31:20 +00:00
|
|
|
parent.insertChildLocked(name, childVFSD)
|
2019-12-23 21:17:29 +00:00
|
|
|
}
|
|
|
|
return childVFSD.Impl().(*Dentry), nil
|
2019-12-12 19:19:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// walkExistingLocked resolves rp to an existing file.
|
|
|
|
//
|
|
|
|
// walkExistingLocked is loosely analogous to Linux's
|
|
|
|
// fs/namei.c:path_lookupat().
|
|
|
|
//
|
|
|
|
// Preconditions: Filesystem.mu must be locked for at least reading.
|
|
|
|
//
|
|
|
|
// Postconditions: Caller must call fs.processDeferredDecRefs*.
|
|
|
|
func (fs *Filesystem) walkExistingLocked(ctx context.Context, rp *vfs.ResolvingPath) (*vfs.Dentry, Inode, error) {
|
|
|
|
vfsd := rp.Start()
|
|
|
|
for !rp.Done() {
|
|
|
|
var err error
|
|
|
|
vfsd, err = fs.stepExistingLocked(ctx, rp, vfsd)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
d := vfsd.Impl().(*Dentry)
|
|
|
|
if rp.MustBeDir() && !d.isDir() {
|
|
|
|
return nil, nil, syserror.ENOTDIR
|
|
|
|
}
|
|
|
|
return vfsd, d.inode, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// walkParentDirLocked resolves all but the last path component of rp to an
|
|
|
|
// existing directory. It does not check that the returned directory is
|
|
|
|
// searchable by the provider of rp.
|
|
|
|
//
|
|
|
|
// walkParentDirLocked is loosely analogous to Linux's
|
|
|
|
// fs/namei.c:path_parentat().
|
|
|
|
//
|
|
|
|
// Preconditions: Filesystem.mu must be locked for at least reading. !rp.Done().
|
|
|
|
//
|
|
|
|
// Postconditions: Caller must call fs.processDeferredDecRefs*.
|
|
|
|
func (fs *Filesystem) walkParentDirLocked(ctx context.Context, rp *vfs.ResolvingPath) (*vfs.Dentry, Inode, error) {
|
|
|
|
vfsd := rp.Start()
|
|
|
|
for !rp.Final() {
|
|
|
|
var err error
|
|
|
|
vfsd, err = fs.stepExistingLocked(ctx, rp, vfsd)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
d := vfsd.Impl().(*Dentry)
|
|
|
|
if !d.isDir() {
|
|
|
|
return nil, nil, syserror.ENOTDIR
|
|
|
|
}
|
|
|
|
return vfsd, d.inode, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// checkCreateLocked checks that a file named rp.Component() may be created in
|
|
|
|
// directory parentVFSD, then returns rp.Component().
|
|
|
|
//
|
|
|
|
// Preconditions: Filesystem.mu must be locked for at least reading. parentInode
|
|
|
|
// == parentVFSD.Impl().(*Dentry).Inode. isDir(parentInode) == true.
|
2020-01-16 21:13:22 +00:00
|
|
|
func checkCreateLocked(ctx context.Context, rp *vfs.ResolvingPath, parentVFSD *vfs.Dentry, parentInode Inode) (string, error) {
|
|
|
|
if err := parentInode.CheckPermissions(ctx, rp.Credentials(), vfs.MayWrite|vfs.MayExec); err != nil {
|
2019-12-12 19:19:18 +00:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
pc := rp.Component()
|
|
|
|
if pc == "." || pc == ".." {
|
|
|
|
return "", syserror.EEXIST
|
|
|
|
}
|
|
|
|
childVFSD, err := rp.ResolveChild(parentVFSD, pc)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if childVFSD != nil {
|
|
|
|
return "", syserror.EEXIST
|
|
|
|
}
|
|
|
|
if parentVFSD.IsDisowned() {
|
|
|
|
return "", syserror.ENOENT
|
|
|
|
}
|
|
|
|
return pc, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// checkDeleteLocked checks that the file represented by vfsd may be deleted.
|
|
|
|
//
|
|
|
|
// Preconditions: Filesystem.mu must be locked for at least reading.
|
2020-01-16 21:13:22 +00:00
|
|
|
func checkDeleteLocked(ctx context.Context, rp *vfs.ResolvingPath, vfsd *vfs.Dentry) error {
|
2019-12-12 19:19:18 +00:00
|
|
|
parentVFSD := vfsd.Parent()
|
|
|
|
if parentVFSD == nil {
|
|
|
|
return syserror.EBUSY
|
|
|
|
}
|
|
|
|
if parentVFSD.IsDisowned() {
|
|
|
|
return syserror.ENOENT
|
|
|
|
}
|
2020-01-16 21:13:22 +00:00
|
|
|
if err := parentVFSD.Impl().(*Dentry).inode.CheckPermissions(ctx, rp.Credentials(), vfs.MayWrite|vfs.MayExec); err != nil {
|
2019-12-12 19:19:18 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Release implements vfs.FilesystemImpl.Release.
|
|
|
|
func (fs *Filesystem) Release() {
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sync implements vfs.FilesystemImpl.Sync.
|
|
|
|
func (fs *Filesystem) Sync(ctx context.Context) error {
|
|
|
|
// All filesystem state is in-memory.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-03-13 18:40:13 +00:00
|
|
|
// AccessAt implements vfs.Filesystem.Impl.AccessAt.
|
|
|
|
func (fs *Filesystem) AccessAt(ctx context.Context, rp *vfs.ResolvingPath, creds *auth.Credentials, ats vfs.AccessTypes) error {
|
|
|
|
fs.mu.RLock()
|
|
|
|
defer fs.mu.RUnlock()
|
|
|
|
defer fs.processDeferredDecRefs()
|
|
|
|
|
|
|
|
_, inode, err := fs.walkExistingLocked(ctx, rp)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return inode.CheckPermissions(ctx, creds, ats)
|
|
|
|
}
|
|
|
|
|
2019-12-12 19:19:18 +00:00
|
|
|
// GetDentryAt implements vfs.FilesystemImpl.GetDentryAt.
|
|
|
|
func (fs *Filesystem) GetDentryAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.GetDentryOptions) (*vfs.Dentry, error) {
|
|
|
|
fs.mu.RLock()
|
|
|
|
defer fs.processDeferredDecRefs()
|
|
|
|
defer fs.mu.RUnlock()
|
|
|
|
vfsd, inode, err := fs.walkExistingLocked(ctx, rp)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts.CheckSearchable {
|
|
|
|
d := vfsd.Impl().(*Dentry)
|
|
|
|
if !d.isDir() {
|
|
|
|
return nil, syserror.ENOTDIR
|
|
|
|
}
|
2020-01-16 21:13:22 +00:00
|
|
|
if err := inode.CheckPermissions(ctx, rp.Credentials(), vfs.MayExec); err != nil {
|
2019-12-12 19:19:18 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vfsd.IncRef() // Ownership transferred to caller.
|
|
|
|
return vfsd, nil
|
|
|
|
}
|
|
|
|
|
2019-12-23 21:17:29 +00:00
|
|
|
// GetParentDentryAt implements vfs.FilesystemImpl.GetParentDentryAt.
|
|
|
|
func (fs *Filesystem) GetParentDentryAt(ctx context.Context, rp *vfs.ResolvingPath) (*vfs.Dentry, error) {
|
|
|
|
fs.mu.RLock()
|
|
|
|
defer fs.processDeferredDecRefs()
|
|
|
|
defer fs.mu.RUnlock()
|
|
|
|
vfsd, _, err := fs.walkParentDirLocked(ctx, rp)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
vfsd.IncRef() // Ownership transferred to caller.
|
|
|
|
return vfsd, nil
|
|
|
|
}
|
|
|
|
|
2019-12-12 19:19:18 +00:00
|
|
|
// LinkAt implements vfs.FilesystemImpl.LinkAt.
|
|
|
|
func (fs *Filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.VirtualDentry) error {
|
|
|
|
if rp.Done() {
|
|
|
|
return syserror.EEXIST
|
|
|
|
}
|
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
parentVFSD, parentInode, err := fs.walkParentDirLocked(ctx, rp)
|
|
|
|
fs.processDeferredDecRefsLocked()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-01-16 21:13:22 +00:00
|
|
|
pc, err := checkCreateLocked(ctx, rp, parentVFSD, parentInode)
|
2019-12-12 19:19:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if rp.Mount() != vd.Mount() {
|
|
|
|
return syserror.EXDEV
|
|
|
|
}
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rp.Mount().EndWrite()
|
|
|
|
|
|
|
|
d := vd.Dentry().Impl().(*Dentry)
|
|
|
|
if d.isDir() {
|
|
|
|
return syserror.EPERM
|
|
|
|
}
|
|
|
|
|
|
|
|
child, err := parentInode.NewLink(ctx, pc, d.inode)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
parentVFSD.Impl().(*Dentry).InsertChild(pc, child)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MkdirAt implements vfs.FilesystemImpl.MkdirAt.
|
|
|
|
func (fs *Filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MkdirOptions) error {
|
|
|
|
if rp.Done() {
|
|
|
|
return syserror.EEXIST
|
|
|
|
}
|
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
parentVFSD, parentInode, err := fs.walkParentDirLocked(ctx, rp)
|
|
|
|
fs.processDeferredDecRefsLocked()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-01-16 21:13:22 +00:00
|
|
|
pc, err := checkCreateLocked(ctx, rp, parentVFSD, parentInode)
|
2019-12-12 19:19:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rp.Mount().EndWrite()
|
|
|
|
child, err := parentInode.NewDir(ctx, pc, opts)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
parentVFSD.Impl().(*Dentry).InsertChild(pc, child)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MknodAt implements vfs.FilesystemImpl.MknodAt.
|
|
|
|
func (fs *Filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MknodOptions) error {
|
|
|
|
if rp.Done() {
|
|
|
|
return syserror.EEXIST
|
|
|
|
}
|
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
parentVFSD, parentInode, err := fs.walkParentDirLocked(ctx, rp)
|
|
|
|
fs.processDeferredDecRefsLocked()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-01-16 21:13:22 +00:00
|
|
|
pc, err := checkCreateLocked(ctx, rp, parentVFSD, parentInode)
|
2019-12-12 19:19:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rp.Mount().EndWrite()
|
|
|
|
new, err := parentInode.NewNode(ctx, pc, opts)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
parentVFSD.Impl().(*Dentry).InsertChild(pc, new)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// OpenAt implements vfs.FilesystemImpl.OpenAt.
|
|
|
|
func (fs *Filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
|
|
|
|
// Filter out flags that are not supported by kernfs. O_DIRECTORY and
|
|
|
|
// O_NOFOLLOW have no effect here (they're handled by VFS by setting
|
|
|
|
// appropriate bits in rp), but are returned by
|
|
|
|
// FileDescriptionImpl.StatusFlags().
|
|
|
|
opts.Flags &= linux.O_ACCMODE | linux.O_CREAT | linux.O_EXCL | linux.O_TRUNC | linux.O_DIRECTORY | linux.O_NOFOLLOW
|
2020-02-14 01:56:34 +00:00
|
|
|
ats := vfs.AccessTypesForOpenFlags(&opts)
|
2019-12-12 19:19:18 +00:00
|
|
|
|
|
|
|
// Do not create new file.
|
|
|
|
if opts.Flags&linux.O_CREAT == 0 {
|
|
|
|
fs.mu.RLock()
|
|
|
|
defer fs.processDeferredDecRefs()
|
|
|
|
defer fs.mu.RUnlock()
|
|
|
|
vfsd, inode, err := fs.walkExistingLocked(ctx, rp)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-01-16 21:13:22 +00:00
|
|
|
if err := inode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil {
|
2019-12-12 19:19:18 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2020-02-14 01:56:34 +00:00
|
|
|
return inode.Open(rp, vfsd, opts)
|
2019-12-12 19:19:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// May create new file.
|
|
|
|
mustCreate := opts.Flags&linux.O_EXCL != 0
|
|
|
|
vfsd := rp.Start()
|
|
|
|
inode := vfsd.Impl().(*Dentry).inode
|
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
if rp.Done() {
|
|
|
|
if rp.MustBeDir() {
|
|
|
|
return nil, syserror.EISDIR
|
|
|
|
}
|
|
|
|
if mustCreate {
|
|
|
|
return nil, syserror.EEXIST
|
|
|
|
}
|
2020-01-16 21:13:22 +00:00
|
|
|
if err := inode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil {
|
2019-12-12 19:19:18 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2020-02-14 01:56:34 +00:00
|
|
|
return inode.Open(rp, vfsd, opts)
|
2019-12-12 19:19:18 +00:00
|
|
|
}
|
|
|
|
afterTrailingSymlink:
|
|
|
|
parentVFSD, parentInode, err := fs.walkParentDirLocked(ctx, rp)
|
|
|
|
fs.processDeferredDecRefsLocked()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Check for search permission in the parent directory.
|
2020-01-16 21:13:22 +00:00
|
|
|
if err := parentInode.CheckPermissions(ctx, rp.Credentials(), vfs.MayExec); err != nil {
|
2019-12-12 19:19:18 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Reject attempts to open directories with O_CREAT.
|
|
|
|
if rp.MustBeDir() {
|
|
|
|
return nil, syserror.EISDIR
|
|
|
|
}
|
|
|
|
pc := rp.Component()
|
|
|
|
if pc == "." || pc == ".." {
|
|
|
|
return nil, syserror.EISDIR
|
|
|
|
}
|
|
|
|
// Determine whether or not we need to create a file.
|
|
|
|
childVFSD, err := rp.ResolveChild(parentVFSD, pc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if childVFSD == nil {
|
|
|
|
// Already checked for searchability above; now check for writability.
|
2020-01-16 21:13:22 +00:00
|
|
|
if err := parentInode.CheckPermissions(ctx, rp.Credentials(), vfs.MayWrite); err != nil {
|
2019-12-12 19:19:18 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer rp.Mount().EndWrite()
|
|
|
|
// Create and open the child.
|
|
|
|
child, err := parentInode.NewFile(ctx, pc, opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
parentVFSD.Impl().(*Dentry).InsertChild(pc, child)
|
2020-02-14 01:56:34 +00:00
|
|
|
return child.Impl().(*Dentry).inode.Open(rp, child, opts)
|
2019-12-12 19:19:18 +00:00
|
|
|
}
|
|
|
|
// Open existing file or follow symlink.
|
|
|
|
if mustCreate {
|
|
|
|
return nil, syserror.EEXIST
|
|
|
|
}
|
|
|
|
childDentry := childVFSD.Impl().(*Dentry)
|
|
|
|
childInode := childDentry.inode
|
|
|
|
if rp.ShouldFollowSymlink() {
|
|
|
|
if childDentry.isSymlink() {
|
|
|
|
target, err := childInode.Readlink(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := rp.HandleSymlink(target); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// rp.Final() may no longer be true since we now need to resolve the
|
|
|
|
// symlink target.
|
|
|
|
goto afterTrailingSymlink
|
|
|
|
}
|
|
|
|
}
|
2020-01-16 21:13:22 +00:00
|
|
|
if err := childInode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil {
|
2019-12-12 19:19:18 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2020-02-14 01:56:34 +00:00
|
|
|
return childInode.Open(rp, childVFSD, opts)
|
2019-12-12 19:19:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ReadlinkAt implements vfs.FilesystemImpl.ReadlinkAt.
|
|
|
|
func (fs *Filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (string, error) {
|
|
|
|
fs.mu.RLock()
|
|
|
|
d, inode, err := fs.walkExistingLocked(ctx, rp)
|
|
|
|
fs.mu.RUnlock()
|
|
|
|
fs.processDeferredDecRefs()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if !d.Impl().(*Dentry).isSymlink() {
|
|
|
|
return "", syserror.EINVAL
|
|
|
|
}
|
|
|
|
return inode.Readlink(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
// RenameAt implements vfs.FilesystemImpl.RenameAt.
|
2019-12-23 21:17:29 +00:00
|
|
|
func (fs *Filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldParentVD vfs.VirtualDentry, oldName string, opts vfs.RenameOptions) error {
|
|
|
|
// Only RENAME_NOREPLACE is supported.
|
|
|
|
if opts.Flags&^linux.RENAME_NOREPLACE != 0 {
|
2019-12-12 19:19:18 +00:00
|
|
|
return syserror.EINVAL
|
|
|
|
}
|
2019-12-23 21:17:29 +00:00
|
|
|
noReplace := opts.Flags&linux.RENAME_NOREPLACE != 0
|
2019-12-12 19:19:18 +00:00
|
|
|
|
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Lock()
|
|
|
|
|
2019-12-23 21:17:29 +00:00
|
|
|
// Resolve the destination directory first to verify that it's on this
|
|
|
|
// Mount.
|
|
|
|
dstDirVFSD, dstDirInode, err := fs.walkParentDirLocked(ctx, rp)
|
|
|
|
fs.processDeferredDecRefsLocked()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-12-12 19:19:18 +00:00
|
|
|
mnt := rp.Mount()
|
2019-12-23 21:17:29 +00:00
|
|
|
if mnt != oldParentVD.Mount() {
|
2019-12-12 19:19:18 +00:00
|
|
|
return syserror.EXDEV
|
|
|
|
}
|
|
|
|
if err := mnt.CheckBeginWrite(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer mnt.EndWrite()
|
|
|
|
|
2019-12-23 21:17:29 +00:00
|
|
|
srcDirVFSD := oldParentVD.Dentry()
|
|
|
|
srcDir := srcDirVFSD.Impl().(*Dentry)
|
|
|
|
srcDir.dirMu.Lock()
|
|
|
|
src, err := fs.revalidateChildLocked(ctx, rp.VirtualFilesystem(), srcDir, oldName, srcDirVFSD.Child(oldName))
|
|
|
|
srcDir.dirMu.Unlock()
|
2019-12-12 19:19:18 +00:00
|
|
|
fs.processDeferredDecRefsLocked()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-12-23 21:17:29 +00:00
|
|
|
srcVFSD := &src.vfsd
|
2019-12-12 19:19:18 +00:00
|
|
|
|
|
|
|
// Can we remove the src dentry?
|
2020-01-16 21:13:22 +00:00
|
|
|
if err := checkDeleteLocked(ctx, rp, srcVFSD); err != nil {
|
2019-12-12 19:19:18 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Can we create the dst dentry?
|
|
|
|
var dstVFSD *vfs.Dentry
|
2020-01-16 21:13:22 +00:00
|
|
|
pc, err := checkCreateLocked(ctx, rp, dstDirVFSD, dstDirInode)
|
2019-12-12 19:19:18 +00:00
|
|
|
switch err {
|
|
|
|
case nil:
|
|
|
|
// Ok, continue with rename as replacement.
|
|
|
|
case syserror.EEXIST:
|
|
|
|
if noReplace {
|
|
|
|
// Won't overwrite existing node since RENAME_NOREPLACE was requested.
|
|
|
|
return syserror.EEXIST
|
|
|
|
}
|
|
|
|
dstVFSD, err = rp.ResolveChild(dstDirVFSD, pc)
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("Child %q for parent Dentry %+v disappeared inside atomic section?", pc, dstDirVFSD))
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
mntns := vfs.MountNamespaceFromContext(ctx)
|
2020-02-14 19:11:55 +00:00
|
|
|
defer mntns.DecRef()
|
2019-12-12 19:19:18 +00:00
|
|
|
virtfs := rp.VirtualFilesystem()
|
|
|
|
|
|
|
|
srcDirDentry := srcDirVFSD.Impl().(*Dentry)
|
|
|
|
dstDirDentry := dstDirVFSD.Impl().(*Dentry)
|
|
|
|
|
|
|
|
// We can't deadlock here due to lock ordering because we're protected from
|
|
|
|
// concurrent renames by fs.mu held for writing.
|
|
|
|
srcDirDentry.dirMu.Lock()
|
|
|
|
defer srcDirDentry.dirMu.Unlock()
|
|
|
|
dstDirDentry.dirMu.Lock()
|
|
|
|
defer dstDirDentry.dirMu.Unlock()
|
|
|
|
|
|
|
|
if err := virtfs.PrepareRenameDentry(mntns, srcVFSD, dstVFSD); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
srcDirInode := srcDirDentry.inode
|
|
|
|
replaced, err := srcDirInode.Rename(ctx, srcVFSD.Name(), pc, srcVFSD, dstDirVFSD)
|
|
|
|
if err != nil {
|
|
|
|
virtfs.AbortRenameDentry(srcVFSD, dstVFSD)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
virtfs.CommitRenameReplaceDentry(srcVFSD, dstDirVFSD, pc, replaced)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RmdirAt implements vfs.FilesystemImpl.RmdirAt.
|
|
|
|
func (fs *Filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error {
|
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
vfsd, inode, err := fs.walkExistingLocked(ctx, rp)
|
|
|
|
fs.processDeferredDecRefsLocked()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rp.Mount().EndWrite()
|
2020-01-16 21:13:22 +00:00
|
|
|
if err := checkDeleteLocked(ctx, rp, vfsd); err != nil {
|
2019-12-12 19:19:18 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !vfsd.Impl().(*Dentry).isDir() {
|
|
|
|
return syserror.ENOTDIR
|
|
|
|
}
|
|
|
|
if inode.HasChildren() {
|
|
|
|
return syserror.ENOTEMPTY
|
|
|
|
}
|
|
|
|
virtfs := rp.VirtualFilesystem()
|
|
|
|
parentDentry := vfsd.Parent().Impl().(*Dentry)
|
|
|
|
parentDentry.dirMu.Lock()
|
|
|
|
defer parentDentry.dirMu.Unlock()
|
2020-02-14 19:11:55 +00:00
|
|
|
|
|
|
|
mntns := vfs.MountNamespaceFromContext(ctx)
|
|
|
|
defer mntns.DecRef()
|
|
|
|
if err := virtfs.PrepareDeleteDentry(mntns, vfsd); err != nil {
|
2019-12-12 19:19:18 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := parentDentry.inode.RmDir(ctx, rp.Component(), vfsd); err != nil {
|
|
|
|
virtfs.AbortDeleteDentry(vfsd)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
virtfs.CommitDeleteDentry(vfsd)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetStatAt implements vfs.FilesystemImpl.SetStatAt.
|
|
|
|
func (fs *Filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetStatOptions) error {
|
|
|
|
fs.mu.RLock()
|
|
|
|
_, inode, err := fs.walkExistingLocked(ctx, rp)
|
|
|
|
fs.mu.RUnlock()
|
|
|
|
fs.processDeferredDecRefs()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if opts.Stat.Mask == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2020-03-16 20:28:00 +00:00
|
|
|
return inode.SetStat(fs.VFSFilesystem(), rp.Credentials(), opts)
|
2019-12-12 19:19:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// StatAt implements vfs.FilesystemImpl.StatAt.
|
|
|
|
func (fs *Filesystem) StatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.StatOptions) (linux.Statx, error) {
|
|
|
|
fs.mu.RLock()
|
|
|
|
_, inode, err := fs.walkExistingLocked(ctx, rp)
|
|
|
|
fs.mu.RUnlock()
|
|
|
|
fs.processDeferredDecRefs()
|
|
|
|
if err != nil {
|
|
|
|
return linux.Statx{}, err
|
|
|
|
}
|
2020-03-06 20:58:45 +00:00
|
|
|
return inode.Stat(fs.VFSFilesystem(), opts)
|
2019-12-12 19:19:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// StatFSAt implements vfs.FilesystemImpl.StatFSAt.
|
|
|
|
func (fs *Filesystem) StatFSAt(ctx context.Context, rp *vfs.ResolvingPath) (linux.Statfs, error) {
|
|
|
|
fs.mu.RLock()
|
|
|
|
_, _, err := fs.walkExistingLocked(ctx, rp)
|
|
|
|
fs.mu.RUnlock()
|
|
|
|
fs.processDeferredDecRefs()
|
|
|
|
if err != nil {
|
|
|
|
return linux.Statfs{}, err
|
|
|
|
}
|
|
|
|
// TODO: actually implement statfs
|
|
|
|
return linux.Statfs{}, syserror.ENOSYS
|
|
|
|
}
|
|
|
|
|
|
|
|
// SymlinkAt implements vfs.FilesystemImpl.SymlinkAt.
|
|
|
|
func (fs *Filesystem) SymlinkAt(ctx context.Context, rp *vfs.ResolvingPath, target string) error {
|
|
|
|
if rp.Done() {
|
|
|
|
return syserror.EEXIST
|
|
|
|
}
|
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
parentVFSD, parentInode, err := fs.walkParentDirLocked(ctx, rp)
|
|
|
|
fs.processDeferredDecRefsLocked()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-01-16 21:13:22 +00:00
|
|
|
pc, err := checkCreateLocked(ctx, rp, parentVFSD, parentInode)
|
2019-12-12 19:19:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rp.Mount().EndWrite()
|
|
|
|
child, err := parentInode.NewSymlink(ctx, pc, target)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
parentVFSD.Impl().(*Dentry).InsertChild(pc, child)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnlinkAt implements vfs.FilesystemImpl.UnlinkAt.
|
|
|
|
func (fs *Filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error {
|
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
vfsd, _, err := fs.walkExistingLocked(ctx, rp)
|
|
|
|
fs.processDeferredDecRefsLocked()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rp.Mount().EndWrite()
|
2020-01-16 21:13:22 +00:00
|
|
|
if err := checkDeleteLocked(ctx, rp, vfsd); err != nil {
|
2019-12-12 19:19:18 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if vfsd.Impl().(*Dentry).isDir() {
|
|
|
|
return syserror.EISDIR
|
|
|
|
}
|
|
|
|
virtfs := rp.VirtualFilesystem()
|
|
|
|
parentDentry := vfsd.Parent().Impl().(*Dentry)
|
|
|
|
parentDentry.dirMu.Lock()
|
|
|
|
defer parentDentry.dirMu.Unlock()
|
2020-02-14 19:11:55 +00:00
|
|
|
mntns := vfs.MountNamespaceFromContext(ctx)
|
|
|
|
defer mntns.DecRef()
|
|
|
|
if err := virtfs.PrepareDeleteDentry(mntns, vfsd); err != nil {
|
2019-12-12 19:19:18 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := parentDentry.inode.Unlink(ctx, rp.Component(), vfsd); err != nil {
|
|
|
|
virtfs.AbortDeleteDentry(vfsd)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
virtfs.CommitDeleteDentry(vfsd)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-12-18 23:47:24 +00:00
|
|
|
// ListxattrAt implements vfs.FilesystemImpl.ListxattrAt.
|
|
|
|
func (fs *Filesystem) ListxattrAt(ctx context.Context, rp *vfs.ResolvingPath) ([]string, error) {
|
|
|
|
fs.mu.RLock()
|
|
|
|
_, _, err := fs.walkExistingLocked(ctx, rp)
|
|
|
|
fs.mu.RUnlock()
|
|
|
|
fs.processDeferredDecRefs()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// kernfs currently does not support extended attributes.
|
|
|
|
return nil, syserror.ENOTSUP
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetxattrAt implements vfs.FilesystemImpl.GetxattrAt.
|
|
|
|
func (fs *Filesystem) GetxattrAt(ctx context.Context, rp *vfs.ResolvingPath, name string) (string, error) {
|
|
|
|
fs.mu.RLock()
|
|
|
|
_, _, err := fs.walkExistingLocked(ctx, rp)
|
|
|
|
fs.mu.RUnlock()
|
|
|
|
fs.processDeferredDecRefs()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
// kernfs currently does not support extended attributes.
|
|
|
|
return "", syserror.ENOTSUP
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetxattrAt implements vfs.FilesystemImpl.SetxattrAt.
|
|
|
|
func (fs *Filesystem) SetxattrAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetxattrOptions) error {
|
|
|
|
fs.mu.RLock()
|
|
|
|
_, _, err := fs.walkExistingLocked(ctx, rp)
|
|
|
|
fs.mu.RUnlock()
|
|
|
|
fs.processDeferredDecRefs()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// kernfs currently does not support extended attributes.
|
|
|
|
return syserror.ENOTSUP
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemovexattrAt implements vfs.FilesystemImpl.RemovexattrAt.
|
|
|
|
func (fs *Filesystem) RemovexattrAt(ctx context.Context, rp *vfs.ResolvingPath, name string) error {
|
|
|
|
fs.mu.RLock()
|
|
|
|
_, _, err := fs.walkExistingLocked(ctx, rp)
|
|
|
|
fs.mu.RUnlock()
|
|
|
|
fs.processDeferredDecRefs()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// kernfs currently does not support extended attributes.
|
|
|
|
return syserror.ENOTSUP
|
|
|
|
}
|
|
|
|
|
2019-12-12 19:19:18 +00:00
|
|
|
// PrependPath implements vfs.FilesystemImpl.PrependPath.
|
|
|
|
func (fs *Filesystem) PrependPath(ctx context.Context, vfsroot, vd vfs.VirtualDentry, b *fspath.Builder) error {
|
|
|
|
fs.mu.RLock()
|
|
|
|
defer fs.mu.RUnlock()
|
|
|
|
return vfs.GenericPrependPath(vfsroot, vd, b)
|
|
|
|
}
|