mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement FUSE_SETATTR
This commit implements FUSE_SETATTR command. When a system call modifies the metadata of a regular file or a folder by chown(2), chmod(2), truncate(2), utime(2), or utimes(2), they should be translated to corresponding FUSE_SETATTR command and sent to the FUSE server. Fixes #3332
This commit is contained in:
@@ -749,3 +749,70 @@ func (r *FUSEDirent) UnmarshalBytes(src []byte) {
|
||||
name.UnmarshalBytes(src[:r.Meta.NameLen])
|
||||
r.Name = string(name)
|
||||
}
|
||||
|
||||
// FATTR_* consts are the attribute flags defined in include/uapi/linux/fuse.h.
|
||||
// These should be or-ed together for setattr to know what has been changed.
|
||||
const (
|
||||
FATTR_MODE = (1 << 0)
|
||||
FATTR_UID = (1 << 1)
|
||||
FATTR_GID = (1 << 2)
|
||||
FATTR_SIZE = (1 << 3)
|
||||
FATTR_ATIME = (1 << 4)
|
||||
FATTR_MTIME = (1 << 5)
|
||||
FATTR_FH = (1 << 6)
|
||||
FATTR_ATIME_NOW = (1 << 7)
|
||||
FATTR_MTIME_NOW = (1 << 8)
|
||||
FATTR_LOCKOWNER = (1 << 9)
|
||||
FATTR_CTIME = (1 << 10)
|
||||
)
|
||||
|
||||
// FUSESetAttrIn is the request sent by the kernel to the daemon,
|
||||
// to set the attribute(s) of a file.
|
||||
//
|
||||
// +marshal
|
||||
type FUSESetAttrIn struct {
|
||||
// Valid indicates which attributes are modified by this request.
|
||||
Valid uint32
|
||||
|
||||
_ uint32
|
||||
|
||||
// Fh is used to identify the file if FATTR_FH is set in Valid.
|
||||
Fh uint64
|
||||
|
||||
// Size is the size that the request wants to change to.
|
||||
Size uint64
|
||||
|
||||
// LockOwner is the owner of the lock that the request wants to change to.
|
||||
LockOwner uint64
|
||||
|
||||
// Atime is the access time that the request wants to change to.
|
||||
Atime uint64
|
||||
|
||||
// Mtime is the modification time that the request wants to change to.
|
||||
Mtime uint64
|
||||
|
||||
// Ctime is the status change time that the request wants to change to.
|
||||
Ctime uint64
|
||||
|
||||
// AtimeNsec is the nano second part of Atime.
|
||||
AtimeNsec uint32
|
||||
|
||||
// MtimeNsec is the nano second part of Mtime.
|
||||
MtimeNsec uint32
|
||||
|
||||
// CtimeNsec is the nano second part of Ctime.
|
||||
CtimeNsec uint32
|
||||
|
||||
// Mode is the file mode that the request wants to change to.
|
||||
Mode uint32
|
||||
|
||||
_ uint32
|
||||
|
||||
// UID is the user ID of the owner that the request wants to change to.
|
||||
UID uint32
|
||||
|
||||
// GID is the group ID of the owner that the request wants to change to.
|
||||
GID uint32
|
||||
|
||||
_ uint32
|
||||
}
|
||||
|
||||
@@ -123,5 +123,5 @@ func (fd *fileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linu
|
||||
// SetStat implements FileDescriptionImpl.SetStat.
|
||||
func (fd *fileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
|
||||
creds := auth.CredentialsFromContext(ctx)
|
||||
return fd.inode().SetStat(ctx, fd.inode().fs.VFSFilesystem(), creds, opts)
|
||||
return fd.inode().setAttr(ctx, fd.inode().fs.VFSFilesystem(), creds, opts, true, fd.Fh)
|
||||
}
|
||||
|
||||
@@ -660,7 +660,7 @@ func (i *inode) getAttr(ctx context.Context, fs *vfs.Filesystem, opts vfs.StatOp
|
||||
}
|
||||
|
||||
// Set the metadata of kernfs.InodeAttrs.
|
||||
if err := i.SetStat(ctx, fs, creds, vfs.SetStatOptions{
|
||||
if err := i.SetInodeStat(ctx, fs, creds, vfs.SetStatOptions{
|
||||
Stat: statFromFUSEAttr(out.Attr, linux.STATX_ALL, i.fs.devMinor),
|
||||
}); err != nil {
|
||||
return linux.FUSEAttr{}, err
|
||||
@@ -703,3 +703,84 @@ func (i *inode) StatFS(ctx context.Context, fs *vfs.Filesystem) (linux.Statfs, e
|
||||
// TODO(gvisor.dev/issues/3413): Complete the implementation of statfs.
|
||||
return vfs.GenericStatFS(linux.FUSE_SUPER_MAGIC), nil
|
||||
}
|
||||
|
||||
// fattrMaskFromStats converts vfs.SetStatOptions.Stat.Mask to linux stats mask
|
||||
// aligned with the attribute mask defined in include/linux/fs.h.
|
||||
func fattrMaskFromStats(mask uint32) uint32 {
|
||||
var fuseAttrMask uint32
|
||||
maskMap := map[uint32]uint32{
|
||||
linux.STATX_MODE: linux.FATTR_MODE,
|
||||
linux.STATX_UID: linux.FATTR_UID,
|
||||
linux.STATX_GID: linux.FATTR_GID,
|
||||
linux.STATX_SIZE: linux.FATTR_SIZE,
|
||||
linux.STATX_ATIME: linux.FATTR_ATIME,
|
||||
linux.STATX_MTIME: linux.FATTR_MTIME,
|
||||
linux.STATX_CTIME: linux.FATTR_CTIME,
|
||||
}
|
||||
for statxMask, fattrMask := range maskMap {
|
||||
if mask&statxMask != 0 {
|
||||
fuseAttrMask |= fattrMask
|
||||
}
|
||||
}
|
||||
return fuseAttrMask
|
||||
}
|
||||
|
||||
// SetStat implements kernfs.Inode.SetStat.
|
||||
func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error {
|
||||
return i.setAttr(ctx, fs, creds, opts, false, 0)
|
||||
}
|
||||
|
||||
func (i *inode) setAttr(ctx context.Context, fs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions, useFh bool, fh uint64) error {
|
||||
conn := i.fs.conn
|
||||
task := kernel.TaskFromContext(ctx)
|
||||
if task == nil {
|
||||
log.Warningf("couldn't get kernel task from context")
|
||||
return syserror.EINVAL
|
||||
}
|
||||
|
||||
// We should retain the original file type when assigning new mode.
|
||||
fileType := uint16(i.Mode()) & linux.S_IFMT
|
||||
fattrMask := fattrMaskFromStats(opts.Stat.Mask)
|
||||
if useFh {
|
||||
fattrMask |= linux.FATTR_FH
|
||||
}
|
||||
in := linux.FUSESetAttrIn{
|
||||
Valid: fattrMask,
|
||||
Fh: fh,
|
||||
Size: opts.Stat.Size,
|
||||
Atime: uint64(opts.Stat.Atime.Sec),
|
||||
Mtime: uint64(opts.Stat.Mtime.Sec),
|
||||
Ctime: uint64(opts.Stat.Ctime.Sec),
|
||||
AtimeNsec: opts.Stat.Atime.Nsec,
|
||||
MtimeNsec: opts.Stat.Mtime.Nsec,
|
||||
CtimeNsec: opts.Stat.Ctime.Nsec,
|
||||
Mode: uint32(fileType | opts.Stat.Mode),
|
||||
UID: opts.Stat.UID,
|
||||
GID: opts.Stat.GID,
|
||||
}
|
||||
req, err := conn.NewRequest(creds, uint32(task.ThreadID()), i.NodeID, linux.FUSE_SETATTR, &in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := conn.Call(task, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := res.Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
out := linux.FUSEGetAttrOut{}
|
||||
if err := res.UnmarshalPayload(&out); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the metadata of kernfs.InodeAttrs.
|
||||
if err := i.SetInodeStat(ctx, fs, creds, vfs.SetStatOptions{
|
||||
Stat: statFromFUSEAttr(out.Attr, linux.STATX_ALL, i.fs.devMinor),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -256,6 +256,13 @@ func (a *InodeAttrs) Stat(context.Context, *vfs.Filesystem, vfs.StatOptions) (li
|
||||
|
||||
// SetStat implements Inode.SetStat.
|
||||
func (a *InodeAttrs) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error {
|
||||
return a.SetInodeStat(ctx, fs, creds, opts)
|
||||
}
|
||||
|
||||
// SetInodeStat sets the corresponding attributes from opts to InodeAttrs.
|
||||
// This function can be used by other kernfs-based filesystem implementation to
|
||||
// sets the unexported attributes into kernfs.InodeAttrs.
|
||||
func (a *InodeAttrs) SetInodeStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error {
|
||||
if opts.Stat.Mask == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -62,6 +62,11 @@ syscall_test(
|
||||
test = "//test/fuse/linux:create_test",
|
||||
)
|
||||
|
||||
syscall_test(
|
||||
fuse = "True",
|
||||
test = "//test/fuse/linux:setstat_test",
|
||||
)
|
||||
|
||||
syscall_test(
|
||||
size = "large",
|
||||
add_overlay = True,
|
||||
|
||||
@@ -100,6 +100,22 @@ cc_binary(
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "setstat_test",
|
||||
testonly = 1,
|
||||
srcs = ["setstat_test.cc"],
|
||||
deps = [
|
||||
gtest,
|
||||
":fuse_fd_util",
|
||||
"//test/util:cleanup",
|
||||
"//test/util:fs_util",
|
||||
"//test/util:fuse_util",
|
||||
"//test/util:temp_umask",
|
||||
"//test/util:test_main",
|
||||
"//test/util:test_util",
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "rmdir_test",
|
||||
testonly = 1,
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
// 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/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/fuse/linux/fuse_fd_util.h"
|
||||
#include "test/util/cleanup.h"
|
||||
#include "test/util/fs_util.h"
|
||||
#include "test/util/fuse_util.h"
|
||||
#include "test/util/test_util.h"
|
||||
|
||||
namespace gvisor {
|
||||
namespace testing {
|
||||
|
||||
namespace {
|
||||
|
||||
class SetStatTest : public FuseFdTest {
|
||||
public:
|
||||
void SetUp() override {
|
||||
FuseFdTest::SetUp();
|
||||
test_dir_path_ = JoinPath(mount_point_.path(), test_dir_);
|
||||
test_file_path_ = JoinPath(mount_point_.path(), test_file_);
|
||||
}
|
||||
|
||||
protected:
|
||||
const uint64_t fh = 23;
|
||||
const std::string test_dir_ = "testdir";
|
||||
const std::string test_file_ = "testfile";
|
||||
const mode_t test_dir_mode_ = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR;
|
||||
const mode_t test_file_mode_ = S_IFREG | S_IRUSR | S_IWUSR | S_IXUSR;
|
||||
|
||||
std::string test_dir_path_;
|
||||
std::string test_file_path_;
|
||||
};
|
||||
|
||||
TEST_F(SetStatTest, ChmodDir) {
|
||||
// Set up fixture.
|
||||
SetServerInodeLookup(test_dir_, test_dir_mode_);
|
||||
struct fuse_out_header out_header = {
|
||||
.len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out),
|
||||
.error = 0,
|
||||
};
|
||||
mode_t set_mode = S_IRGRP | S_IWGRP | S_IXGRP;
|
||||
struct fuse_attr_out out_payload = {
|
||||
.attr = DefaultFuseAttr(set_mode, 2),
|
||||
};
|
||||
auto iov_out = FuseGenerateIovecs(out_header, out_payload);
|
||||
SetServerResponse(FUSE_SETATTR, iov_out);
|
||||
|
||||
// Make syscall.
|
||||
EXPECT_THAT(chmod(test_dir_path_.c_str(), set_mode), SyscallSucceeds());
|
||||
|
||||
// Check FUSE request.
|
||||
struct fuse_in_header in_header;
|
||||
struct fuse_setattr_in in_payload;
|
||||
auto iov_in = FuseGenerateIovecs(in_header, in_payload);
|
||||
|
||||
GetServerActualRequest(iov_in);
|
||||
EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload));
|
||||
EXPECT_EQ(in_header.opcode, FUSE_SETATTR);
|
||||
EXPECT_EQ(in_header.uid, 0);
|
||||
EXPECT_EQ(in_header.gid, 0);
|
||||
EXPECT_EQ(in_payload.valid, FATTR_MODE);
|
||||
EXPECT_EQ(in_payload.mode, S_IFDIR | set_mode);
|
||||
}
|
||||
|
||||
TEST_F(SetStatTest, ChownDir) {
|
||||
// Set up fixture.
|
||||
SetServerInodeLookup(test_dir_, test_dir_mode_);
|
||||
struct fuse_out_header out_header = {
|
||||
.len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out),
|
||||
.error = 0,
|
||||
};
|
||||
struct fuse_attr_out out_payload = {
|
||||
.attr = DefaultFuseAttr(test_dir_mode_, 2),
|
||||
};
|
||||
auto iov_out = FuseGenerateIovecs(out_header, out_payload);
|
||||
SetServerResponse(FUSE_SETATTR, iov_out);
|
||||
|
||||
// Make syscall.
|
||||
EXPECT_THAT(chown(test_dir_path_.c_str(), 1025, 1025), SyscallSucceeds());
|
||||
|
||||
// Check FUSE request.
|
||||
struct fuse_in_header in_header;
|
||||
struct fuse_setattr_in in_payload;
|
||||
auto iov_in = FuseGenerateIovecs(in_header, in_payload);
|
||||
|
||||
GetServerActualRequest(iov_in);
|
||||
EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload));
|
||||
EXPECT_EQ(in_header.opcode, FUSE_SETATTR);
|
||||
EXPECT_EQ(in_header.uid, 0);
|
||||
EXPECT_EQ(in_header.gid, 0);
|
||||
EXPECT_EQ(in_payload.valid, FATTR_UID | FATTR_GID);
|
||||
EXPECT_EQ(in_payload.uid, 1025);
|
||||
EXPECT_EQ(in_payload.gid, 1025);
|
||||
}
|
||||
|
||||
TEST_F(SetStatTest, TruncateFile) {
|
||||
// Set up fixture.
|
||||
SetServerInodeLookup(test_file_, test_file_mode_);
|
||||
struct fuse_out_header out_header = {
|
||||
.len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out),
|
||||
.error = 0,
|
||||
};
|
||||
struct fuse_attr_out out_payload = {
|
||||
.attr = DefaultFuseAttr(S_IFREG | S_IRUSR | S_IWUSR, 2),
|
||||
};
|
||||
auto iov_out = FuseGenerateIovecs(out_header, out_payload);
|
||||
SetServerResponse(FUSE_SETATTR, iov_out);
|
||||
|
||||
// Make syscall.
|
||||
EXPECT_THAT(truncate(test_file_path_.c_str(), 321), SyscallSucceeds());
|
||||
|
||||
// Check FUSE request.
|
||||
struct fuse_in_header in_header;
|
||||
struct fuse_setattr_in in_payload;
|
||||
auto iov_in = FuseGenerateIovecs(in_header, in_payload);
|
||||
|
||||
GetServerActualRequest(iov_in);
|
||||
EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload));
|
||||
EXPECT_EQ(in_header.opcode, FUSE_SETATTR);
|
||||
EXPECT_EQ(in_header.uid, 0);
|
||||
EXPECT_EQ(in_header.gid, 0);
|
||||
EXPECT_EQ(in_payload.valid, FATTR_SIZE);
|
||||
EXPECT_EQ(in_payload.size, 321);
|
||||
}
|
||||
|
||||
TEST_F(SetStatTest, UtimeFile) {
|
||||
// Set up fixture.
|
||||
SetServerInodeLookup(test_file_, test_file_mode_);
|
||||
struct fuse_out_header out_header = {
|
||||
.len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out),
|
||||
.error = 0,
|
||||
};
|
||||
struct fuse_attr_out out_payload = {
|
||||
.attr = DefaultFuseAttr(S_IFREG | S_IRUSR | S_IWUSR, 2),
|
||||
};
|
||||
auto iov_out = FuseGenerateIovecs(out_header, out_payload);
|
||||
SetServerResponse(FUSE_SETATTR, iov_out);
|
||||
|
||||
// Make syscall.
|
||||
time_t expected_atime = 1597159766, expected_mtime = 1597159765;
|
||||
struct utimbuf times = {
|
||||
.actime = expected_atime,
|
||||
.modtime = expected_mtime,
|
||||
};
|
||||
EXPECT_THAT(utime(test_file_path_.c_str(), ×), SyscallSucceeds());
|
||||
|
||||
// Check FUSE request.
|
||||
struct fuse_in_header in_header;
|
||||
struct fuse_setattr_in in_payload;
|
||||
auto iov_in = FuseGenerateIovecs(in_header, in_payload);
|
||||
|
||||
GetServerActualRequest(iov_in);
|
||||
EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload));
|
||||
EXPECT_EQ(in_header.opcode, FUSE_SETATTR);
|
||||
EXPECT_EQ(in_header.uid, 0);
|
||||
EXPECT_EQ(in_header.gid, 0);
|
||||
EXPECT_EQ(in_payload.valid, FATTR_ATIME | FATTR_MTIME);
|
||||
EXPECT_EQ(in_payload.atime, expected_atime);
|
||||
EXPECT_EQ(in_payload.mtime, expected_mtime);
|
||||
}
|
||||
|
||||
TEST_F(SetStatTest, UtimesFile) {
|
||||
// Set up fixture.
|
||||
SetServerInodeLookup(test_file_, test_file_mode_);
|
||||
struct fuse_out_header out_header = {
|
||||
.len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out),
|
||||
.error = 0,
|
||||
};
|
||||
struct fuse_attr_out out_payload = {
|
||||
.attr = DefaultFuseAttr(test_file_mode_, 2),
|
||||
};
|
||||
auto iov_out = FuseGenerateIovecs(out_header, out_payload);
|
||||
SetServerResponse(FUSE_SETATTR, iov_out);
|
||||
|
||||
// Make syscall.
|
||||
struct timeval expected_times[2] = {
|
||||
{
|
||||
.tv_sec = 1597159766,
|
||||
.tv_usec = 234945,
|
||||
},
|
||||
{
|
||||
.tv_sec = 1597159765,
|
||||
.tv_usec = 232341,
|
||||
},
|
||||
};
|
||||
EXPECT_THAT(utimes(test_file_path_.c_str(), expected_times),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Check FUSE request.
|
||||
struct fuse_in_header in_header;
|
||||
struct fuse_setattr_in in_payload;
|
||||
auto iov_in = FuseGenerateIovecs(in_header, in_payload);
|
||||
|
||||
GetServerActualRequest(iov_in);
|
||||
EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload));
|
||||
EXPECT_EQ(in_header.opcode, FUSE_SETATTR);
|
||||
EXPECT_EQ(in_header.uid, 0);
|
||||
EXPECT_EQ(in_header.gid, 0);
|
||||
EXPECT_EQ(in_payload.valid, FATTR_ATIME | FATTR_MTIME);
|
||||
EXPECT_EQ(in_payload.atime, expected_times[0].tv_sec);
|
||||
EXPECT_EQ(in_payload.atimensec, expected_times[0].tv_usec * 1000);
|
||||
EXPECT_EQ(in_payload.mtime, expected_times[1].tv_sec);
|
||||
EXPECT_EQ(in_payload.mtimensec, expected_times[1].tv_usec * 1000);
|
||||
}
|
||||
|
||||
TEST_F(SetStatTest, FtruncateFile) {
|
||||
// Set up fixture.
|
||||
SetServerInodeLookup(test_file_, test_file_mode_);
|
||||
auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenPath(test_file_path_, O_RDWR, fh));
|
||||
auto close_fd = CloseFD(fd);
|
||||
|
||||
struct fuse_out_header out_header = {
|
||||
.len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out),
|
||||
.error = 0,
|
||||
};
|
||||
struct fuse_attr_out out_payload = {
|
||||
.attr = DefaultFuseAttr(test_file_mode_, 2),
|
||||
};
|
||||
auto iov_out = FuseGenerateIovecs(out_header, out_payload);
|
||||
SetServerResponse(FUSE_SETATTR, iov_out);
|
||||
|
||||
// Make syscall.
|
||||
EXPECT_THAT(ftruncate(fd.get(), 321), SyscallSucceeds());
|
||||
|
||||
// Check FUSE request.
|
||||
struct fuse_in_header in_header;
|
||||
struct fuse_setattr_in in_payload;
|
||||
auto iov_in = FuseGenerateIovecs(in_header, in_payload);
|
||||
|
||||
GetServerActualRequest(iov_in);
|
||||
EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload));
|
||||
EXPECT_EQ(in_header.opcode, FUSE_SETATTR);
|
||||
EXPECT_EQ(in_header.uid, 0);
|
||||
EXPECT_EQ(in_header.gid, 0);
|
||||
EXPECT_EQ(in_payload.valid, FATTR_SIZE | FATTR_FH);
|
||||
EXPECT_EQ(in_payload.fh, fh);
|
||||
EXPECT_EQ(in_payload.size, 321);
|
||||
}
|
||||
|
||||
TEST_F(SetStatTest, FchmodFile) {
|
||||
// Set up fixture.
|
||||
SetServerInodeLookup(test_file_, test_file_mode_);
|
||||
auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenPath(test_file_path_, O_RDWR, fh));
|
||||
auto close_fd = CloseFD(fd);
|
||||
|
||||
struct fuse_out_header out_header = {
|
||||
.len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out),
|
||||
.error = 0,
|
||||
};
|
||||
mode_t set_mode = S_IROTH | S_IWOTH | S_IXOTH;
|
||||
struct fuse_attr_out out_payload = {
|
||||
.attr = DefaultFuseAttr(set_mode, 2),
|
||||
};
|
||||
auto iov_out = FuseGenerateIovecs(out_header, out_payload);
|
||||
SetServerResponse(FUSE_SETATTR, iov_out);
|
||||
|
||||
// Make syscall.
|
||||
EXPECT_THAT(fchmod(fd.get(), set_mode), SyscallSucceeds());
|
||||
|
||||
// Check FUSE request.
|
||||
struct fuse_in_header in_header;
|
||||
struct fuse_setattr_in in_payload;
|
||||
auto iov_in = FuseGenerateIovecs(in_header, in_payload);
|
||||
|
||||
GetServerActualRequest(iov_in);
|
||||
EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload));
|
||||
EXPECT_EQ(in_header.opcode, FUSE_SETATTR);
|
||||
EXPECT_EQ(in_header.uid, 0);
|
||||
EXPECT_EQ(in_header.gid, 0);
|
||||
EXPECT_EQ(in_payload.valid, FATTR_MODE | FATTR_FH);
|
||||
EXPECT_EQ(in_payload.fh, fh);
|
||||
EXPECT_EQ(in_payload.mode, S_IFREG | set_mode);
|
||||
}
|
||||
|
||||
TEST_F(SetStatTest, FchownFile) {
|
||||
// Set up fixture.
|
||||
SetServerInodeLookup(test_file_, test_file_mode_);
|
||||
auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenPath(test_file_path_, O_RDWR, fh));
|
||||
auto close_fd = CloseFD(fd);
|
||||
|
||||
struct fuse_out_header out_header = {
|
||||
.len = sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out),
|
||||
.error = 0,
|
||||
};
|
||||
struct fuse_attr_out out_payload = {
|
||||
.attr = DefaultFuseAttr(S_IFREG | S_IRUSR | S_IWUSR | S_IXUSR, 2),
|
||||
};
|
||||
auto iov_out = FuseGenerateIovecs(out_header, out_payload);
|
||||
SetServerResponse(FUSE_SETATTR, iov_out);
|
||||
|
||||
// Make syscall.
|
||||
EXPECT_THAT(fchown(fd.get(), 1025, 1025), SyscallSucceeds());
|
||||
|
||||
// Check FUSE request.
|
||||
struct fuse_in_header in_header;
|
||||
struct fuse_setattr_in in_payload;
|
||||
auto iov_in = FuseGenerateIovecs(in_header, in_payload);
|
||||
|
||||
GetServerActualRequest(iov_in);
|
||||
EXPECT_EQ(in_header.len, sizeof(in_header) + sizeof(in_payload));
|
||||
EXPECT_EQ(in_header.opcode, FUSE_SETATTR);
|
||||
EXPECT_EQ(in_header.uid, 0);
|
||||
EXPECT_EQ(in_header.gid, 0);
|
||||
EXPECT_EQ(in_payload.valid, FATTR_UID | FATTR_GID | FATTR_FH);
|
||||
EXPECT_EQ(in_payload.fh, fh);
|
||||
EXPECT_EQ(in_payload.uid, 1025);
|
||||
EXPECT_EQ(in_payload.gid, 1025);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
Reference in New Issue
Block a user