mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
@@ -846,3 +846,26 @@ type FUSESetAttrIn struct {
|
||||
|
||||
_ uint32
|
||||
}
|
||||
|
||||
// FUSEUnlinkIn is the request sent by the kernel to the daemon
|
||||
// when trying to unlink a node.
|
||||
//
|
||||
// Dynamically-sized objects cannot be marshalled.
|
||||
type FUSEUnlinkIn struct {
|
||||
marshal.StubMarshallable
|
||||
|
||||
// Name of the node to unlink.
|
||||
Name string
|
||||
}
|
||||
|
||||
// MarshalBytes serializes r.name to the dst buffer, which should
|
||||
// have size len(r.Name) + 1 and last byte set to 0.
|
||||
func (r *FUSEUnlinkIn) MarshalBytes(buf []byte) {
|
||||
copy(buf, r.Name)
|
||||
}
|
||||
|
||||
// SizeBytes is the size of the memory representation of FUSEUnlinkIn.
|
||||
// 1 extra byte for null-terminated Name string.
|
||||
func (r *FUSEUnlinkIn) SizeBytes() int {
|
||||
return len(r.Name) + 1
|
||||
}
|
||||
|
||||
@@ -455,6 +455,29 @@ func (i *inode) NewSymlink(ctx context.Context, name, target string) (*vfs.Dentr
|
||||
return i.newEntry(ctx, name, linux.S_IFLNK, linux.FUSE_SYMLINK, &in)
|
||||
}
|
||||
|
||||
// Unlink implements kernfs.Inode.Unlink.
|
||||
func (i *inode) Unlink(ctx context.Context, name string, child *vfs.Dentry) error {
|
||||
kernelTask := kernel.TaskFromContext(ctx)
|
||||
if kernelTask == nil {
|
||||
log.Warningf("fusefs.Inode.newEntry: couldn't get kernel task from context", i.NodeID)
|
||||
return syserror.EINVAL
|
||||
}
|
||||
in := linux.FUSEUnlinkIn{Name: name}
|
||||
req, err := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), i.NodeID, linux.FUSE_UNLINK, &in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := i.fs.conn.Call(kernelTask, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// only return error, discard res.
|
||||
if err := res.Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
return i.dentry.RemoveChildLocked(name, child)
|
||||
}
|
||||
|
||||
// NewDir implements kernfs.Inode.NewDir.
|
||||
func (i *inode) NewDir(ctx context.Context, name string, opts vfs.MkdirOptions) (*vfs.Dentry, error) {
|
||||
in := linux.FUSEMkdirIn{
|
||||
|
||||
@@ -770,6 +770,10 @@ func (fs *Filesystem) SymlinkAt(ctx context.Context, rp *vfs.ResolvingPath, targ
|
||||
func (fs *Filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error {
|
||||
fs.mu.Lock()
|
||||
defer fs.mu.Unlock()
|
||||
|
||||
// Store the name before walkExistingLocked as rp will be advanced past the
|
||||
// name in the following call.
|
||||
name := rp.Component()
|
||||
vfsd, _, err := fs.walkExistingLocked(ctx, rp)
|
||||
fs.processDeferredDecRefsLocked(ctx)
|
||||
if err != nil {
|
||||
@@ -795,7 +799,7 @@ func (fs *Filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error
|
||||
if err := virtfs.PrepareDeleteDentry(mntns, vfsd); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := parentDentry.inode.Unlink(ctx, rp.Component(), vfsd); err != nil {
|
||||
if err := parentDentry.inode.Unlink(ctx, name, vfsd); err != nil {
|
||||
virtfs.AbortDeleteDentry(vfsd)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
// Filesystem mostly implements vfs.FilesystemImpl for a generic in-memory
|
||||
@@ -267,6 +268,36 @@ func (d *Dentry) InsertChildLocked(name string, child *Dentry) {
|
||||
d.children[name] = child
|
||||
}
|
||||
|
||||
// RemoveChild removes child from the vfs dentry cache. This does not update the
|
||||
// directory inode or modify the inode to be unlinked. So calling this on its own
|
||||
// isn't sufficient to remove a child from a directory.
|
||||
//
|
||||
// Precondition: d must represent a directory inode.
|
||||
func (d *Dentry) RemoveChild(name string, child *vfs.Dentry) error {
|
||||
d.dirMu.Lock()
|
||||
defer d.dirMu.Unlock()
|
||||
return d.RemoveChildLocked(name, child)
|
||||
}
|
||||
|
||||
// RemoveChildLocked is equivalent to RemoveChild, with additional
|
||||
// preconditions.
|
||||
//
|
||||
// Precondition: d.dirMu must be locked.
|
||||
func (d *Dentry) RemoveChildLocked(name string, child *vfs.Dentry) error {
|
||||
if !d.isDir() {
|
||||
panic(fmt.Sprintf("RemoveChild called on non-directory Dentry: %+v.", d))
|
||||
}
|
||||
c, ok := d.children[name]
|
||||
if !ok {
|
||||
return syserror.ENOENT
|
||||
}
|
||||
if &c.vfsd != child {
|
||||
panic(fmt.Sprintf("Dentry hashed into inode doesn't match what vfs thinks! Child: %+v, vfs: %+v", c, child))
|
||||
}
|
||||
delete(d.children, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Inode returns the dentry's inode.
|
||||
func (d *Dentry) Inode() Inode {
|
||||
return d.inode
|
||||
|
||||
@@ -62,6 +62,11 @@ syscall_test(
|
||||
test = "//test/fuse/linux:create_test",
|
||||
)
|
||||
|
||||
syscall_test(
|
||||
fuse = "True",
|
||||
test = "//test/fuse/linux:unlink_test",
|
||||
)
|
||||
|
||||
syscall_test(
|
||||
fuse = "True",
|
||||
test = "//test/fuse/linux:setstat_test",
|
||||
|
||||
@@ -214,3 +214,17 @@ cc_binary(
|
||||
"//test/util:test_util",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "unlink_test",
|
||||
testonly = 1,
|
||||
srcs = ["unlink_test.cc"],
|
||||
deps = [
|
||||
gtest,
|
||||
":fuse_base",
|
||||
"//test/util:fuse_util",
|
||||
"//test/util:temp_umask",
|
||||
"//test/util:test_main",
|
||||
"//test/util:test_util",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/fuse.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/fuse/linux/fuse_base.h"
|
||||
#include "test/util/fuse_util.h"
|
||||
#include "test/util/test_util.h"
|
||||
|
||||
namespace gvisor {
|
||||
namespace testing {
|
||||
|
||||
namespace {
|
||||
|
||||
class UnlinkTest : public FuseTest {
|
||||
protected:
|
||||
const std::string test_file_ = "test_file";
|
||||
};
|
||||
|
||||
TEST_F(UnlinkTest, RegularFile) {
|
||||
const std::string test_file_path =
|
||||
JoinPath(mount_point_.path().c_str(), test_file_);
|
||||
SetServerInodeLookup(test_file_, S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
|
||||
struct fuse_out_header out_header = {
|
||||
.len = sizeof(struct fuse_out_header),
|
||||
};
|
||||
auto iov_out = FuseGenerateIovecs(out_header);
|
||||
SetServerResponse(FUSE_UNLINK, iov_out);
|
||||
|
||||
ASSERT_THAT(unlink(test_file_path.c_str()), SyscallSucceeds());
|
||||
struct fuse_in_header in_header;
|
||||
std::vector<char> unlinked_file(test_file_.length() + 1);
|
||||
auto iov_in = FuseGenerateIovecs(in_header, unlinked_file);
|
||||
GetServerActualRequest(iov_in);
|
||||
|
||||
EXPECT_EQ(in_header.len, sizeof(in_header) + test_file_.length() + 1);
|
||||
EXPECT_EQ(in_header.opcode, FUSE_UNLINK);
|
||||
EXPECT_EQ(std::string(unlinked_file.data()), test_file_);
|
||||
}
|
||||
|
||||
TEST_F(UnlinkTest, NoFile) {
|
||||
const std::string test_file_path =
|
||||
JoinPath(mount_point_.path().c_str(), test_file_);
|
||||
SetServerInodeLookup(test_file_, S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
|
||||
struct fuse_out_header out_header = {
|
||||
.len = sizeof(struct fuse_out_header),
|
||||
.error = -ENOENT,
|
||||
};
|
||||
auto iov_out = FuseGenerateIovecs(out_header);
|
||||
SetServerResponse(FUSE_UNLINK, iov_out);
|
||||
|
||||
ASSERT_THAT(unlink(test_file_path.c_str()), SyscallFailsWithErrno(ENOENT));
|
||||
SkipServerActualRequest();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
Reference in New Issue
Block a user