2019-07-18 22:09:14 +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 memfs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"sync/atomic"
|
|
|
|
|
|
|
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/context"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
|
|
|
"gvisor.dev/gvisor/pkg/syserror"
|
|
|
|
)
|
|
|
|
|
|
|
|
// stepLocked resolves rp.Component() in parent directory vfsd.
|
|
|
|
//
|
|
|
|
// stepLocked is loosely analogous to fs/namei.c:walk_component().
|
|
|
|
//
|
2019-08-08 18:45:33 +00:00
|
|
|
// Preconditions: filesystem.mu must be locked. !rp.Done(). inode ==
|
|
|
|
// vfsd.Impl().(*dentry).inode.
|
|
|
|
func stepLocked(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, inode *inode) (*vfs.Dentry, *inode, error) {
|
2019-07-18 22:09:14 +00:00
|
|
|
if !inode.isDir() {
|
|
|
|
return nil, nil, syserror.ENOTDIR
|
|
|
|
}
|
|
|
|
if err := inode.checkPermissions(rp.Credentials(), vfs.MayExec, true); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
afterSymlink:
|
|
|
|
nextVFSD, err := rp.ResolveComponent(vfsd)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
if nextVFSD == nil {
|
|
|
|
// Since the Dentry tree is the sole source of truth for memfs, if it's
|
|
|
|
// not in the Dentry tree, it doesn't exist.
|
|
|
|
return nil, nil, syserror.ENOENT
|
|
|
|
}
|
2019-08-08 18:45:33 +00:00
|
|
|
nextInode := nextVFSD.Impl().(*dentry).inode
|
2019-07-18 22:09:14 +00:00
|
|
|
if symlink, ok := nextInode.impl.(*symlink); ok && rp.ShouldFollowSymlink() {
|
|
|
|
// TODO: symlink traversals update access time
|
|
|
|
if err := rp.HandleSymlink(symlink.target); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
goto afterSymlink // don't check the current directory again
|
|
|
|
}
|
|
|
|
rp.Advance()
|
|
|
|
return nextVFSD, nextInode, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// walkExistingLocked resolves rp to an existing file.
|
|
|
|
//
|
|
|
|
// walkExistingLocked is loosely analogous to Linux's
|
|
|
|
// fs/namei.c:path_lookupat().
|
|
|
|
//
|
2019-08-08 18:45:33 +00:00
|
|
|
// Preconditions: filesystem.mu must be locked.
|
|
|
|
func walkExistingLocked(rp *vfs.ResolvingPath) (*vfs.Dentry, *inode, error) {
|
2019-07-18 22:09:14 +00:00
|
|
|
vfsd := rp.Start()
|
2019-08-08 18:45:33 +00:00
|
|
|
inode := vfsd.Impl().(*dentry).inode
|
2019-07-18 22:09:14 +00:00
|
|
|
for !rp.Done() {
|
|
|
|
var err error
|
|
|
|
vfsd, inode, err = stepLocked(rp, vfsd, inode)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if rp.MustBeDir() && !inode.isDir() {
|
|
|
|
return nil, nil, syserror.ENOTDIR
|
|
|
|
}
|
|
|
|
return vfsd, 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().
|
|
|
|
//
|
2019-08-08 18:45:33 +00:00
|
|
|
// Preconditions: filesystem.mu must be locked. !rp.Done().
|
|
|
|
func walkParentDirLocked(rp *vfs.ResolvingPath) (*vfs.Dentry, *inode, error) {
|
2019-07-18 22:09:14 +00:00
|
|
|
vfsd := rp.Start()
|
2019-08-08 18:45:33 +00:00
|
|
|
inode := vfsd.Impl().(*dentry).inode
|
2019-07-18 22:09:14 +00:00
|
|
|
for !rp.Final() {
|
|
|
|
var err error
|
|
|
|
vfsd, inode, err = stepLocked(rp, vfsd, inode)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !inode.isDir() {
|
|
|
|
return nil, nil, syserror.ENOTDIR
|
|
|
|
}
|
|
|
|
return vfsd, inode, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// checkCreateLocked checks that a file named rp.Component() may be created in
|
|
|
|
// directory parentVFSD, then returns rp.Component().
|
|
|
|
//
|
2019-08-08 18:45:33 +00:00
|
|
|
// Preconditions: filesystem.mu must be locked. parentInode ==
|
|
|
|
// parentVFSD.Impl().(*dentry).inode. parentInode.isDir() == true.
|
|
|
|
func checkCreateLocked(rp *vfs.ResolvingPath, parentVFSD *vfs.Dentry, parentInode *inode) (string, error) {
|
2019-07-18 22:09:14 +00:00
|
|
|
if err := parentInode.checkPermissions(rp.Credentials(), vfs.MayWrite|vfs.MayExec, true); 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.
|
|
|
|
func checkDeleteLocked(vfsd *vfs.Dentry) error {
|
|
|
|
parentVFSD := vfsd.Parent()
|
|
|
|
if parentVFSD == nil {
|
|
|
|
return syserror.EBUSY
|
|
|
|
}
|
|
|
|
if parentVFSD.IsDisowned() {
|
|
|
|
return syserror.ENOENT
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetDentryAt implements vfs.FilesystemImpl.GetDentryAt.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) GetDentryAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.GetDentryOptions) (*vfs.Dentry, error) {
|
2019-07-18 22:09:14 +00:00
|
|
|
fs.mu.RLock()
|
|
|
|
defer fs.mu.RUnlock()
|
|
|
|
vfsd, inode, err := walkExistingLocked(rp)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if opts.CheckSearchable {
|
|
|
|
if !inode.isDir() {
|
|
|
|
return nil, syserror.ENOTDIR
|
|
|
|
}
|
|
|
|
if err := inode.checkPermissions(rp.Credentials(), vfs.MayExec, true); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
inode.incRef() // vfsd.IncRef(&fs.vfsfs)
|
|
|
|
return vfsd, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// LinkAt implements vfs.FilesystemImpl.LinkAt.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.VirtualDentry) error {
|
2019-07-18 22:09:14 +00:00
|
|
|
if rp.Done() {
|
|
|
|
return syserror.EEXIST
|
|
|
|
}
|
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
parentVFSD, parentInode, err := walkParentDirLocked(rp)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
pc, err := checkCreateLocked(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()
|
2019-08-08 18:45:33 +00:00
|
|
|
d := vd.Dentry().Impl().(*dentry)
|
2019-07-18 22:09:14 +00:00
|
|
|
if d.inode.isDir() {
|
|
|
|
return syserror.EPERM
|
|
|
|
}
|
|
|
|
d.inode.incLinksLocked()
|
|
|
|
child := fs.newDentry(d.inode)
|
|
|
|
parentVFSD.InsertChild(&child.vfsd, pc)
|
|
|
|
parentInode.impl.(*directory).childList.PushBack(child)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MkdirAt implements vfs.FilesystemImpl.MkdirAt.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MkdirOptions) error {
|
2019-07-18 22:09:14 +00:00
|
|
|
if rp.Done() {
|
|
|
|
return syserror.EEXIST
|
|
|
|
}
|
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
parentVFSD, parentInode, err := walkParentDirLocked(rp)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
pc, err := checkCreateLocked(rp, parentVFSD, parentInode)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rp.Mount().EndWrite()
|
|
|
|
child := fs.newDentry(fs.newDirectory(rp.Credentials(), opts.Mode))
|
|
|
|
parentVFSD.InsertChild(&child.vfsd, pc)
|
|
|
|
parentInode.impl.(*directory).childList.PushBack(child)
|
|
|
|
parentInode.incLinksLocked() // from child's ".."
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MknodAt implements vfs.FilesystemImpl.MknodAt.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MknodOptions) error {
|
2019-07-18 22:09:14 +00:00
|
|
|
if rp.Done() {
|
|
|
|
return syserror.EEXIST
|
|
|
|
}
|
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
parentVFSD, parentInode, err := walkParentDirLocked(rp)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = checkCreateLocked(rp, parentVFSD, parentInode)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rp.Mount().EndWrite()
|
|
|
|
// TODO: actually implement mknod
|
|
|
|
return syserror.EPERM
|
|
|
|
}
|
|
|
|
|
|
|
|
// OpenAt implements vfs.FilesystemImpl.OpenAt.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
|
2019-07-18 22:09:14 +00:00
|
|
|
// Filter out flags that are not supported by memfs. 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
|
|
|
|
|
|
|
|
if opts.Flags&linux.O_CREAT == 0 {
|
|
|
|
fs.mu.RLock()
|
|
|
|
defer fs.mu.RUnlock()
|
|
|
|
vfsd, inode, err := walkExistingLocked(rp)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return inode.open(rp, vfsd, opts.Flags, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
mustCreate := opts.Flags&linux.O_EXCL != 0
|
|
|
|
vfsd := rp.Start()
|
2019-08-08 18:45:33 +00:00
|
|
|
inode := vfsd.Impl().(*dentry).inode
|
2019-07-18 22:09:14 +00:00
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
if rp.Done() {
|
|
|
|
if rp.MustBeDir() {
|
|
|
|
return nil, syserror.EISDIR
|
|
|
|
}
|
|
|
|
if mustCreate {
|
|
|
|
return nil, syserror.EEXIST
|
|
|
|
}
|
|
|
|
return inode.open(rp, vfsd, opts.Flags, false)
|
|
|
|
}
|
|
|
|
afterTrailingSymlink:
|
|
|
|
// Walk to the parent directory of the last path component.
|
|
|
|
for !rp.Final() {
|
|
|
|
var err error
|
|
|
|
vfsd, inode, err = stepLocked(rp, vfsd, inode)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !inode.isDir() {
|
|
|
|
return nil, syserror.ENOTDIR
|
|
|
|
}
|
|
|
|
// Check for search permission in the parent directory.
|
|
|
|
if err := inode.checkPermissions(rp.Credentials(), vfs.MayExec, true); 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(vfsd, pc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if childVFSD == nil {
|
|
|
|
// Already checked for searchability above; now check for writability.
|
|
|
|
if err := inode.checkPermissions(rp.Credentials(), vfs.MayWrite, true); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer rp.Mount().EndWrite()
|
|
|
|
// Create and open the child.
|
|
|
|
childInode := fs.newRegularFile(rp.Credentials(), opts.Mode)
|
|
|
|
child := fs.newDentry(childInode)
|
|
|
|
vfsd.InsertChild(&child.vfsd, pc)
|
|
|
|
inode.impl.(*directory).childList.PushBack(child)
|
|
|
|
return childInode.open(rp, &child.vfsd, opts.Flags, true)
|
|
|
|
}
|
|
|
|
// Open existing file or follow symlink.
|
|
|
|
if mustCreate {
|
|
|
|
return nil, syserror.EEXIST
|
|
|
|
}
|
2019-08-08 18:45:33 +00:00
|
|
|
childInode := childVFSD.Impl().(*dentry).inode
|
2019-07-18 22:09:14 +00:00
|
|
|
if symlink, ok := childInode.impl.(*symlink); ok && rp.ShouldFollowSymlink() {
|
|
|
|
// TODO: symlink traversals update access time
|
|
|
|
if err := rp.HandleSymlink(symlink.target); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// rp.Final() may no longer be true since we now need to resolve the
|
|
|
|
// symlink target.
|
|
|
|
goto afterTrailingSymlink
|
|
|
|
}
|
|
|
|
return childInode.open(rp, childVFSD, opts.Flags, false)
|
|
|
|
}
|
|
|
|
|
2019-08-08 18:45:33 +00:00
|
|
|
func (i *inode) open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, flags uint32, afterCreate bool) (*vfs.FileDescription, error) {
|
2019-07-18 22:09:14 +00:00
|
|
|
ats := vfs.AccessTypesForOpenFlags(flags)
|
|
|
|
if !afterCreate {
|
|
|
|
if err := i.checkPermissions(rp.Credentials(), ats, i.isDir()); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch impl := i.impl.(type) {
|
|
|
|
case *regularFile:
|
|
|
|
var fd regularFileFD
|
|
|
|
fd.flags = flags
|
|
|
|
fd.readable = vfs.MayReadFileWithOpenFlags(flags)
|
|
|
|
fd.writable = vfs.MayWriteFileWithOpenFlags(flags)
|
|
|
|
if fd.writable {
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Mount.EndWrite() is called by regularFileFD.Release().
|
|
|
|
}
|
|
|
|
fd.vfsfd.Init(&fd, rp.Mount(), vfsd)
|
|
|
|
if flags&linux.O_TRUNC != 0 {
|
|
|
|
impl.mu.Lock()
|
|
|
|
impl.data = impl.data[:0]
|
|
|
|
atomic.StoreInt64(&impl.dataLen, 0)
|
|
|
|
impl.mu.Unlock()
|
|
|
|
}
|
|
|
|
return &fd.vfsfd, nil
|
|
|
|
case *directory:
|
|
|
|
// Can't open directories writably.
|
|
|
|
if ats&vfs.MayWrite != 0 {
|
|
|
|
return nil, syserror.EISDIR
|
|
|
|
}
|
|
|
|
var fd directoryFD
|
|
|
|
fd.vfsfd.Init(&fd, rp.Mount(), vfsd)
|
|
|
|
fd.flags = flags
|
|
|
|
return &fd.vfsfd, nil
|
|
|
|
case *symlink:
|
|
|
|
// Can't open symlinks without O_PATH (which is unimplemented).
|
|
|
|
return nil, syserror.ELOOP
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("unknown inode type: %T", i.impl))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadlinkAt implements vfs.FilesystemImpl.ReadlinkAt.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (string, error) {
|
2019-07-18 22:09:14 +00:00
|
|
|
fs.mu.RLock()
|
|
|
|
_, inode, err := walkExistingLocked(rp)
|
|
|
|
fs.mu.RUnlock()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
symlink, ok := inode.impl.(*symlink)
|
|
|
|
if !ok {
|
|
|
|
return "", syserror.EINVAL
|
|
|
|
}
|
|
|
|
return symlink.target, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RenameAt implements vfs.FilesystemImpl.RenameAt.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.VirtualDentry, opts vfs.RenameOptions) error {
|
2019-07-18 22:09:14 +00:00
|
|
|
if rp.Done() {
|
|
|
|
return syserror.ENOENT
|
|
|
|
}
|
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
parentVFSD, parentInode, err := walkParentDirLocked(rp)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = checkCreateLocked(rp, parentVFSD, parentInode)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rp.Mount().EndWrite()
|
|
|
|
// TODO: actually implement RenameAt
|
|
|
|
return syserror.EPERM
|
|
|
|
}
|
|
|
|
|
|
|
|
// RmdirAt implements vfs.FilesystemImpl.RmdirAt.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error {
|
2019-07-18 22:09:14 +00:00
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
vfsd, inode, err := walkExistingLocked(rp)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rp.Mount().EndWrite()
|
|
|
|
if err := checkDeleteLocked(vfsd); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !inode.isDir() {
|
|
|
|
return syserror.ENOTDIR
|
|
|
|
}
|
|
|
|
if vfsd.HasChildren() {
|
|
|
|
return syserror.ENOTEMPTY
|
|
|
|
}
|
|
|
|
if err := rp.VirtualFilesystem().DeleteDentry(vfs.MountNamespaceFromContext(ctx), vfsd); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-08-08 18:45:33 +00:00
|
|
|
// Remove from parent directory's childList.
|
|
|
|
vfsd.Parent().Impl().(*dentry).inode.impl.(*directory).childList.Remove(vfsd.Impl().(*dentry))
|
2019-07-18 22:09:14 +00:00
|
|
|
inode.decRef()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetStatAt implements vfs.FilesystemImpl.SetStatAt.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetStatOptions) error {
|
2019-07-18 22:09:14 +00:00
|
|
|
fs.mu.RLock()
|
|
|
|
_, _, err := walkExistingLocked(rp)
|
|
|
|
fs.mu.RUnlock()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if opts.Stat.Mask == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2019-08-08 18:45:33 +00:00
|
|
|
// TODO: implement inode.setStat
|
2019-07-18 22:09:14 +00:00
|
|
|
return syserror.EPERM
|
|
|
|
}
|
|
|
|
|
|
|
|
// StatAt implements vfs.FilesystemImpl.StatAt.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) StatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.StatOptions) (linux.Statx, error) {
|
2019-07-18 22:09:14 +00:00
|
|
|
fs.mu.RLock()
|
|
|
|
_, inode, err := walkExistingLocked(rp)
|
|
|
|
fs.mu.RUnlock()
|
|
|
|
if err != nil {
|
|
|
|
return linux.Statx{}, err
|
|
|
|
}
|
|
|
|
var stat linux.Statx
|
|
|
|
inode.statTo(&stat)
|
|
|
|
return stat, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// StatFSAt implements vfs.FilesystemImpl.StatFSAt.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) StatFSAt(ctx context.Context, rp *vfs.ResolvingPath) (linux.Statfs, error) {
|
2019-07-18 22:09:14 +00:00
|
|
|
fs.mu.RLock()
|
|
|
|
_, _, err := walkExistingLocked(rp)
|
|
|
|
fs.mu.RUnlock()
|
|
|
|
if err != nil {
|
|
|
|
return linux.Statfs{}, err
|
|
|
|
}
|
|
|
|
// TODO: actually implement statfs
|
|
|
|
return linux.Statfs{}, syserror.ENOSYS
|
|
|
|
}
|
|
|
|
|
|
|
|
// SymlinkAt implements vfs.FilesystemImpl.SymlinkAt.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) SymlinkAt(ctx context.Context, rp *vfs.ResolvingPath, target string) error {
|
2019-07-18 22:09:14 +00:00
|
|
|
if rp.Done() {
|
|
|
|
return syserror.EEXIST
|
|
|
|
}
|
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
parentVFSD, parentInode, err := walkParentDirLocked(rp)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
pc, err := checkCreateLocked(rp, parentVFSD, parentInode)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rp.Mount().EndWrite()
|
|
|
|
child := fs.newDentry(fs.newSymlink(rp.Credentials(), target))
|
|
|
|
parentVFSD.InsertChild(&child.vfsd, pc)
|
|
|
|
parentInode.impl.(*directory).childList.PushBack(child)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnlinkAt implements vfs.FilesystemImpl.UnlinkAt.
|
2019-08-08 18:45:33 +00:00
|
|
|
func (fs *filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error {
|
2019-07-18 22:09:14 +00:00
|
|
|
fs.mu.Lock()
|
|
|
|
defer fs.mu.Unlock()
|
|
|
|
vfsd, inode, err := walkExistingLocked(rp)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := rp.Mount().CheckBeginWrite(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer rp.Mount().EndWrite()
|
|
|
|
if err := checkDeleteLocked(vfsd); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if inode.isDir() {
|
|
|
|
return syserror.EISDIR
|
|
|
|
}
|
|
|
|
if err := rp.VirtualFilesystem().DeleteDentry(vfs.MountNamespaceFromContext(ctx), vfsd); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-08-08 18:45:33 +00:00
|
|
|
// Remove from parent directory's childList.
|
|
|
|
vfsd.Parent().Impl().(*dentry).inode.impl.(*directory).childList.Remove(vfsd.Impl().(*dentry))
|
2019-07-18 22:09:14 +00:00
|
|
|
inode.decLinksLocked()
|
|
|
|
return nil
|
|
|
|
}
|