diff options
Diffstat (limited to 'pkg/sentry/fsimpl/kernfs/filesystem.go')
-rw-r--r-- | pkg/sentry/fsimpl/kernfs/filesystem.go | 788 |
1 files changed, 0 insertions, 788 deletions
diff --git a/pkg/sentry/fsimpl/kernfs/filesystem.go b/pkg/sentry/fsimpl/kernfs/filesystem.go deleted file mode 100644 index 3288de290..000000000 --- a/pkg/sentry/fsimpl/kernfs/filesystem.go +++ /dev/null @@ -1,788 +0,0 @@ -// 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 - -// This file implements vfs.FilesystemImpl for kernfs. - -import ( - "fmt" - - "gvisor.dev/gvisor/pkg/abi/linux" - "gvisor.dev/gvisor/pkg/context" - "gvisor.dev/gvisor/pkg/fspath" - "gvisor.dev/gvisor/pkg/sentry/kernel/auth" - "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? - if err := d.inode.CheckPermissions(ctx, rp.Credentials(), vfs.MayExec); err != nil { - return nil, err - } -afterSymlink: - 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 - } - d.dirMu.Lock() - nextVFSD, err := rp.ResolveChild(vfsd, name) - if err != nil { - d.dirMu.Unlock() - return nil, err - } - next, err := fs.revalidateChildLocked(ctx, rp.VirtualFilesystem(), d, name, nextVFSD) - d.dirMu.Unlock() - if err != nil { - return nil, err - } - // Resolve any symlink at current path component. - if rp.ShouldFollowSymlink() && next.isSymlink() { - // 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() - 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. - // - // 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. - var err error - childVFSD, err = parent.inode.Lookup(ctx, name) - if err != nil { - return nil, err - } - // Reference on childVFSD dropped by a corresponding Valid. - parent.insertChildLocked(name, childVFSD) - } - return childVFSD.Impl().(*Dentry), nil -} - -// 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. -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 { - 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. -func checkDeleteLocked(ctx context.Context, rp *vfs.ResolvingPath, vfsd *vfs.Dentry) error { - parentVFSD := vfsd.Parent() - if parentVFSD == nil { - return syserror.EBUSY - } - if parentVFSD.IsDisowned() { - return syserror.ENOENT - } - if err := parentVFSD.Impl().(*Dentry).inode.CheckPermissions(ctx, rp.Credentials(), vfs.MayWrite|vfs.MayExec); err != nil { - 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 -} - -// 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) -} - -// 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 - } - if err := inode.CheckPermissions(ctx, rp.Credentials(), vfs.MayExec); err != nil { - return nil, err - } - } - vfsd.IncRef() // Ownership transferred to caller. - return vfsd, nil -} - -// 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 -} - -// 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 - } - pc, err := checkCreateLocked(ctx, rp, parentVFSD, parentInode) - 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 - } - pc, err := checkCreateLocked(ctx, rp, parentVFSD, parentInode) - 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 - } - pc, err := checkCreateLocked(ctx, rp, parentVFSD, parentInode) - 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 - ats := vfs.AccessTypesForOpenFlags(&opts) - - // 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 - } - if err := inode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil { - return nil, err - } - return inode.Open(rp, vfsd, opts) - } - - // 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 - } - if err := inode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil { - return nil, err - } - return inode.Open(rp, vfsd, opts) - } -afterTrailingSymlink: - parentVFSD, parentInode, err := fs.walkParentDirLocked(ctx, rp) - fs.processDeferredDecRefsLocked() - if err != nil { - return nil, err - } - // Check for search permission in the parent directory. - if err := parentInode.CheckPermissions(ctx, rp.Credentials(), vfs.MayExec); err != nil { - 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. - if err := parentInode.CheckPermissions(ctx, rp.Credentials(), vfs.MayWrite); err != nil { - 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) - return child.Impl().(*Dentry).inode.Open(rp, child, opts) - } - // 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 - } - } - if err := childInode.CheckPermissions(ctx, rp.Credentials(), ats); err != nil { - return nil, err - } - return childInode.Open(rp, childVFSD, opts) -} - -// 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. -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 { - return syserror.EINVAL - } - noReplace := opts.Flags&linux.RENAME_NOREPLACE != 0 - - fs.mu.Lock() - defer fs.mu.Lock() - - // 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 - } - mnt := rp.Mount() - if mnt != oldParentVD.Mount() { - return syserror.EXDEV - } - if err := mnt.CheckBeginWrite(); err != nil { - return err - } - defer mnt.EndWrite() - - 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() - fs.processDeferredDecRefsLocked() - if err != nil { - return err - } - srcVFSD := &src.vfsd - - // Can we remove the src dentry? - if err := checkDeleteLocked(ctx, rp, srcVFSD); err != nil { - return err - } - - // Can we create the dst dentry? - var dstVFSD *vfs.Dentry - pc, err := checkCreateLocked(ctx, rp, dstDirVFSD, dstDirInode) - 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) - defer mntns.DecRef() - 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() - if err := checkDeleteLocked(ctx, rp, vfsd); err != nil { - 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() - - mntns := vfs.MountNamespaceFromContext(ctx) - defer mntns.DecRef() - if err := virtfs.PrepareDeleteDentry(mntns, vfsd); err != nil { - 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 - } - return inode.SetStat(fs.VFSFilesystem(), opts) -} - -// 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 - } - return inode.Stat(fs.VFSFilesystem(), opts) -} - -// 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 - } - pc, err := checkCreateLocked(ctx, rp, parentVFSD, parentInode) - 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() - if err := checkDeleteLocked(ctx, rp, vfsd); err != nil { - 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() - mntns := vfs.MountNamespaceFromContext(ctx) - defer mntns.DecRef() - if err := virtfs.PrepareDeleteDentry(mntns, vfsd); err != nil { - return err - } - if err := parentDentry.inode.Unlink(ctx, rp.Component(), vfsd); err != nil { - virtfs.AbortDeleteDentry(vfsd) - return err - } - virtfs.CommitDeleteDentry(vfsd) - return nil -} - -// 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 -} - -// 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) -} |