Fixes #7086,#6964,#3413,#7001.

Also adds fuse fsync, rename, flock support.
This commit is contained in:
Yaroslav Litvinov
2022-01-27 13:07:42 +02:00
parent 6d15b0ee64
commit b7ccfa5084
13 changed files with 423 additions and 44 deletions
+116
View File
@@ -232,6 +232,43 @@ type FUSEInitOut struct {
_ [8]uint32
}
// FUSEStatfsOut is the reply sent by the daemon to the kernel
// for FUSE_STATFS.
// from https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/fuse.h#L252
//
// +marshal
type FUSEStatfsOut struct {
// Blocks is the maximum number of data blocks the filesystem may store, in
// units of BlockSize.
Blocks uint64
// BlocksFree is the number of free data blocks, in units of BlockSize.
BlocksFree uint64
// BlocksAvailable is the number of data blocks free for use by
// unprivileged users, in units of BlockSize.
BlocksAvailable uint64
// Files is the number of used file nodes on the filesystem.
Files uint64
// FileFress is the number of free file nodes on the filesystem.
FilesFree uint64
// BlockSize is the optimal transfer block size in bytes.
BlockSize uint32
// NameLength is the maximum file name length.
NameLength uint32
// FragmentSize is equivalent to BlockSize.
FragmentSize uint32
_ uint32
Spare [6]uint32
}
// FUSE_GETATTR_FH is currently the only flag of FUSEGetAttrIn.GetAttrFlags.
// If it is set, the file handle (FUSEGetAttrIn.Fh) is used to indicate the
// object instead of the node id attribute in the request header.
@@ -436,6 +473,15 @@ type FUSEOpenOut struct {
_ uint32
}
// FUSECreateOut is the reply sent by the daemon to the kernel
// for FUSECreateMeta.
//
// +marshal
type FUSECreateOut struct {
FUSEEntryOut
FUSEOpenOut
}
// FUSE_READ flags, consistent with the ones in include/uapi/linux/fuse.h.
const (
FUSE_READ_LOCKOWNER = 1 << 1
@@ -474,6 +520,7 @@ type FUSEReadIn struct {
//
// The second part of the payload is the
// binary bytes of the data to be written.
// See FUSEWritePayloadIn that combines header & payload.
//
// +marshal
type FUSEWriteIn struct {
@@ -498,6 +545,36 @@ type FUSEWriteIn struct {
_ uint32
}
// FUSEWritePayloadIn combines header - FUSEWriteIn and payload
// in a single marshallable struct when sending request by the
// kernel to the daemon
//
// +marshal dynamic
type FUSEWritePayloadIn struct {
Header FUSEWriteIn
Payload primitive.ByteSlice
}
// SizeBytes implements marshal.Marshallable.SizeBytes.
func (r *FUSEWritePayloadIn) SizeBytes() int {
if r == nil {
return (*FUSEWriteIn)(nil).SizeBytes()
}
return r.Header.SizeBytes() + r.Payload.SizeBytes()
}
// MarshalBytes implements marshal.Marshallable.MarshalBytes.
func (r *FUSEWritePayloadIn) MarshalBytes(dst []byte) []byte {
dst = r.Header.MarshalUnsafe(dst)
dst = r.Payload.MarshalUnsafe(dst)
return dst
}
// UnmarshalBytes implements marshal.Marshallable.UnmarshalBytes.
func (r *FUSEWritePayloadIn) UnmarshalBytes(src []byte) []byte {
panic("Unimplemented, FUSEWritePayloadIn is never unmarshalled")
}
// FUSEWriteOut is the payload of the reply sent by the daemon to the kernel
// for a FUSE_WRITE request.
//
@@ -543,6 +620,32 @@ type FUSECreateMeta struct {
_ uint32
}
// FUSERenameIn sent by the kernel for FUSE_RENAME
//
// +marshal dynamic
type FUSERenameIn struct {
Newdir primitive.Uint64
Oldname CString
Newname CString
}
// MarshalBytes implements marshal.Marshallable.MarshalBytes.
func (r *FUSERenameIn) MarshalBytes(dst []byte) []byte {
dst = r.Newdir.MarshalBytes(dst)
dst = r.Oldname.MarshalBytes(dst)
return r.Newname.MarshalBytes(dst)
}
// UnmarshalBytes implements marshal.Marshallable.UnmarshalBytes.
func (r *FUSERenameIn) UnmarshalBytes(buf []byte) []byte {
panic("Unimplemented, FUSERmDirIn is never unmarshalled")
}
// SizeBytes implements marshal.Marshallable.SizeBytes.
func (r *FUSERenameIn) SizeBytes() int {
return r.Newdir.SizeBytes() + r.Oldname.SizeBytes() + r.Newname.SizeBytes()
}
// FUSECreateIn contains all the arguments sent by the kernel to the daemon, to
// atomically create and open a new regular file.
//
@@ -929,3 +1032,16 @@ func (r *FUSEUnlinkIn) UnmarshalBytes(buf []byte) []byte {
func (r *FUSEUnlinkIn) SizeBytes() int {
return r.Name.SizeBytes()
}
// FUSEFsyncIn is the request sent by the kernel to the daemon
// when trying to fsync a file.
//
// +marshal
type FUSEFsyncIn struct {
Fh uint64
FsyncFlags uint32
// padding
_ uint32
}
+1
View File
@@ -51,6 +51,7 @@ go_library(
"//pkg/hostarch",
"//pkg/log",
"//pkg/marshal",
"//pkg/marshal/primitive",
"//pkg/refs",
"//pkg/refsvfs2",
"//pkg/safemem",
+1 -12
View File
@@ -168,7 +168,7 @@ func (fd *DeviceFD) readLocked(ctx context.Context, dst usermem.IOSequence, opts
for !fd.queue.Empty() {
req = fd.queue.Front()
if int64(req.hdr.Len)+int64(len(req.payload)) <= dst.NumBytes() {
if int64(req.hdr.Len) <= dst.NumBytes() {
break
}
@@ -207,17 +207,6 @@ func (fd *DeviceFD) readLocked(ctx context.Context, dst usermem.IOSequence, opts
return 0, linuxerr.EIO
}
if req.hdr.Opcode == linux.FUSE_WRITE {
written, err := dst.DropFirst(n).CopyOut(ctx, req.payload)
if err != nil {
return 0, err
}
if written != len(req.payload) {
return 0, linuxerr.EIO
}
n += int(written)
}
// Fully done with this req, remove it from the queue.
fd.queue.Remove(req)
+25 -1
View File
@@ -17,6 +17,7 @@ package fuse
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -29,7 +30,7 @@ type fileDescription struct {
vfsfd vfs.FileDescription
vfs.FileDescriptionDefaultImpl
vfs.DentryMetadataFileDescriptionImpl
vfs.NoLockFD
vfs.LockFD
// the file handle used in userspace.
Fh uint64
@@ -127,3 +128,26 @@ func (fd *fileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions)
creds := auth.CredentialsFromContext(ctx)
return fd.inode().setAttr(ctx, fs, creds, opts, true, fd.Fh)
}
// Sync implements vfs.FileDescriptionImpl.Sync.
func (fd *fileDescription) Sync(ctx context.Context) error {
if fd.inode().Mode().IsDir() {
return linuxerr.EPERM
}
conn := fd.inode().fs.conn
// no need to proceed if FUSE server doesn't implement Open.
if conn.noOpen {
return linuxerr.EINVAL
}
kernelTask := kernel.TaskFromContext(ctx)
in := linux.FUSEFsyncIn{
Fh: fd.Fh,
FsyncFlags: fd.statusFlags(),
}
// Ignoring errors and FUSE server reply is analogous to Linux's behavior.
req := conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(kernelTask.ThreadID()), fd.inode().nodeID, linux.FUSE_FSYNC, &in)
// The reply will be ignored since no callback is defined in asyncCallBack().
conn.CallAsync(kernelTask, req)
return nil
}
+99 -10
View File
@@ -26,6 +26,7 @@ import (
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/marshal"
"gvisor.dev/gvisor/pkg/marshal/primitive"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -299,6 +300,15 @@ func (fs *filesystem) MountOptions() string {
return fs.opts.mopts
}
// Fh data returned by newEntry
type NewFhData struct {
// file handler
fh uint64
// Flags of the file.
flags uint32
}
// inode implements kernfs.Inode.
//
// +stateify savable
@@ -334,6 +344,10 @@ type inode struct {
// link is result of following a symbolic link.
link string
// if newEntry got a new Fh from server it saves it here, until returned by Open
isNewFh bool
newFhData NewFhData
}
func (fs *filesystem) newRoot(ctx context.Context, creds *auth.Credentials, mode linux.FileMode) *kernfs.Dentry {
@@ -410,11 +424,17 @@ func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentr
fd = &(regularFD.fileDescription)
fdImpl = regularFD
}
fd.LockFD.Init(&i.locks)
// FOPEN_KEEP_CACHE is the defualt flag for noOpen.
fd.OpenFlag = linux.FOPEN_KEEP_CACHE
// Only send open request when FUSE server support open or is opening a directory.
if !i.fs.conn.noOpen || isDir {
if i.isNewFh {
// use Fh from NewEntry
fd.OpenFlag = i.newFhData.flags
fd.Fh = i.newFhData.fh
i.isNewFh = false
} else if !i.fs.conn.noOpen || isDir {
kernelTask := kernel.TaskFromContext(ctx)
if kernelTask == nil {
log.Warningf("fusefs.Inode.Open: couldn't get kernel task from context")
@@ -452,13 +472,12 @@ func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentr
// Process the reply.
fd.OpenFlag = out.OpenFlag
if isDir {
fd.OpenFlag &= ^uint32(linux.FOPEN_DIRECT_IO)
}
fd.Fh = out.Fh
}
}
if isDir {
fd.OpenFlag &= ^uint32(linux.FOPEN_DIRECT_IO)
}
// TODO(gvisor.dev/issue/3234): invalidate mmap after implemented it for FUSE Inode
fd.DirectIO = fd.OpenFlag&linux.FOPEN_DIRECT_IO != 0
@@ -590,6 +609,28 @@ func (i *inode) RmDir(ctx context.Context, name string, child kernfs.Inode) erro
return res.Error()
}
func (i *inode) Rename(ctx context.Context, oldname, newname string, child, dstDir kernfs.Inode) error {
fusefs := i.fs
task, creds := kernel.TaskFromContext(ctx), auth.CredentialsFromContext(ctx)
dstDirInode, ok := dstDir.(*inode)
if !ok {
return linuxerr.EXDEV
}
in := linux.FUSERenameIn{
Newdir: primitive.Uint64(dstDirInode.nodeID),
Oldname: linux.CString(oldname),
Newname: linux.CString(newname),
}
req := fusefs.conn.NewRequest(creds, uint32(task.ThreadID()), i.nodeID, linux.FUSE_RENAME, &in)
res, err := i.fs.conn.Call(task, req)
if err != nil {
return err
}
return res.Error()
}
// newEntry calls FUSE server for entry creation and allocates corresponding entry according to response.
// Shared by FUSE_MKNOD, FUSE_MKDIR, FUSE_SYMLINK, FUSE_LINK and FUSE_LOOKUP.
func (i *inode) newEntry(ctx context.Context, name string, fileType linux.FileMode, opcode linux.FUSEOpcode, payload marshal.Marshallable) (kernfs.Inode, error) {
@@ -606,14 +647,31 @@ func (i *inode) newEntry(ctx context.Context, name string, fileType linux.FileMo
if err := res.Error(); err != nil {
return nil, err
}
out := linux.FUSEEntryOut{}
if err := res.UnmarshalPayload(&out); err != nil {
return nil, err
out := linux.FUSECreateOut{}
if opcode == linux.FUSE_CREATE {
if err := res.UnmarshalPayload(&out); err != nil {
return nil, err
}
} else {
if err := res.UnmarshalPayload(&out.FUSEEntryOut); err != nil {
return nil, err
}
}
if opcode != linux.FUSE_LOOKUP && ((out.Attr.Mode&linux.S_IFMT)^uint32(fileType) != 0 || out.NodeID == 0 || out.NodeID == linux.FUSE_ROOT_ID) {
return nil, linuxerr.EIO
}
child := i.fs.newInode(ctx, out.NodeID, out.Attr)
if opcode == linux.FUSE_CREATE {
// File handler is returned by fuse server at a time of file create.
// Save it temporary in a created child, so Open could return it when invoked
// to be sure after fh is consumed reset 'isNewFh' flag of inode
childI, ok := child.(*inode)
if ok {
childI.isNewFh = true
childI.newFhData.fh = out.FUSEOpenOut.Fh
childI.newFhData.flags = out.FUSEOpenOut.OpenFlag
}
}
return child, nil
}
@@ -798,8 +856,39 @@ func (i *inode) DecRef(ctx context.Context) {
// StatFS implements kernfs.Inode.StatFS.
func (i *inode) StatFS(ctx context.Context, fs *vfs.Filesystem) (linux.Statfs, error) {
// TODO(gvisor.dev/issues/3413): Complete the implementation of statfs.
return vfs.GenericStatFS(linux.FUSE_SUPER_MAGIC), nil
task := kernel.TaskFromContext(ctx)
if task == nil {
log.Warningf("couldn't get kernel task from context")
return linux.Statfs{}, linuxerr.EINVAL
}
req := i.fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(task.ThreadID()), i.nodeID,
linux.FUSE_STATFS, &linux.FUSEEmptyIn{},
)
res, err := i.fs.conn.Call(task, req)
if err != nil {
return linux.Statfs{}, err
}
if err := res.Error(); err != nil {
return linux.Statfs{}, err
}
var out linux.FUSEStatfsOut
if err := res.UnmarshalPayload(&out); err != nil {
return linux.Statfs{}, err
}
return linux.Statfs{
Type: linux.FUSE_SUPER_MAGIC,
Blocks: uint64(out.Blocks),
BlocksFree: out.BlocksFree,
BlocksAvailable: out.BlocksAvailable,
Files: out.Files,
FilesFree: out.FilesFree,
BlockSize: int64(out.BlockSize),
NameLength: uint64(out.NameLength),
FragmentSize: int64(out.FragmentSize),
}, nil
}
// fattrMaskFromStats converts vfs.SetStatOptions.Stat.Mask to linux stats mask
+13 -11
View File
@@ -165,14 +165,16 @@ func (fs *filesystem) Write(ctx context.Context, fd *regularFileFD, off uint64,
}
// Reuse the same struct for unmarshalling to avoid unnecessary memory allocation.
in := linux.FUSEWriteIn{
Fh: fd.Fh,
// TODO(gvisor.dev/issue/3245): file lock
LockOwner: 0,
// TODO(gvisor.dev/issue/3245): |= linux.FUSE_READ_LOCKOWNER
// TODO(gvisor.dev/issue/3237): |= linux.FUSE_WRITE_CACHE (not added yet)
WriteFlags: 0,
Flags: fd.statusFlags(),
in := linux.FUSEWritePayloadIn {
Header: linux.FUSEWriteIn{
Fh: fd.Fh,
// TODO(gvisor.dev/issue/3245): file lock
LockOwner: 0,
// TODO(gvisor.dev/issue/3245): |= linux.FUSE_READ_LOCKOWNER
// TODO(gvisor.dev/issue/3237): |= linux.FUSE_WRITE_CACHE (not added yet)
WriteFlags: 0,
Flags: fd.statusFlags(),
},
}
inode := fd.inode()
@@ -197,11 +199,11 @@ func (fs *filesystem) Write(ctx context.Context, fd *regularFileFD, off uint64,
toWrite = maxWrite
}
in.Offset = off + uint64(written)
in.Size = toWrite
in.Header.Offset = off + uint64(written)
in.Header.Size = toWrite
in.Payload = data[written : written+toWrite]
req := fs.conn.NewRequest(auth.CredentialsFromContext(ctx), uint32(t.ThreadID()), inode.nodeID, linux.FUSE_WRITE, &in)
req.payload = data[written : written+toWrite]
// TODO(gvisor.dev/issue/3247): support async write.
@@ -95,10 +95,6 @@ type Request struct {
hdr *linux.FUSEHeaderIn
data []byte
// payload for this request: extra bytes to write after
// the data slice. Used by FUSE_WRITE.
payload []byte
// If this request is async.
async bool
// If we don't care its response.
+8 -1
View File
@@ -17,6 +17,7 @@ package vfs
import (
"bytes"
"fmt"
"strings"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
@@ -102,7 +103,13 @@ func (vfs *VirtualFilesystem) MustRegisterFilesystemType(name string, fsType Fil
func (vfs *VirtualFilesystem) getFilesystemType(name string) *registeredFilesystemType {
vfs.fsTypesMu.RLock()
defer vfs.fsTypesMu.RUnlock()
return vfs.fsTypes[name]
fsname := name
// Fetch a meaningful part of name if there is a dot in the name
// and use left part of a string as fname.
if strings.Index(name, ".") != -1 {
fsname = strings.Split(name, ".")[0]
}
return vfs.fsTypes[fsname]
}
// GenerateProcFilesystems emits the contents of /proc/filesystems for vfs to
+4
View File
@@ -8,6 +8,10 @@ package(licenses = ["notice"])
# fuse = "True",
# test = "//test/fuse/linux:stat_test",
# )
# syscall_test(
# fuse = "True",
# test = "//test/fuse/linux:statfs_test",
# )
#
# syscall_test(
# fuse = "True",
+15
View File
@@ -20,6 +20,21 @@ cc_binary(
],
)
cc_binary(
name = "statfs_test",
testonly = 1,
srcs = ["statfs_test.cc"],
deps = [
gtest,
":fuse_fd_util",
"//test/util:cleanup",
"//test/util:fs_util",
"//test/util:fuse_util",
"//test/util:test_main",
"//test/util:test_util",
],
)
cc_binary(
name = "open_test",
testonly = 1,
+13
View File
@@ -40,6 +40,19 @@ TEST(FuseMount, Success) {
ASSERT_NO_ERRNO_AND_VALUE(Mount("", dir.path(), "fuse", 0, mopts, 0));
}
TEST(FuseMount, SuccessFstype) {
const FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/fuse", O_WRONLY));
std::string mopts =
absl::StrFormat("fd=%d,user_id=%d,group_id=%d,rootmode=0777", fd.get(),
getuid(), getgid());
const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
const auto mount =
ASSERT_NO_ERRNO_AND_VALUE(Mount("", dir.path(), "fuse.testfs", 0, mopts, 0));
}
TEST(FuseMount, FDNotParsable) {
int devfd;
EXPECT_THAT(devfd = open("/dev/fuse", O_RDWR), SyscallSucceeds());
+123
View File
@@ -0,0 +1,123 @@
// 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 <linux/magic.h>
#include <sys/statfs.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <vector>
#include <iostream>
#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 {
#define FUSE_SUPER_MAGIC 0x65735546
class StatfsTest : public FuseFdTest {
public:
void SetUp() override {
FuseFdTest::SetUp();
}
protected:
const mode_t dir_mode_ = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
bool StatsfsAreEqual(struct statfs expected, struct statfs actual) {
return memcmp(&expected, &actual, sizeof(struct statfs)) == 0;
}
const mode_t expected_mode = S_IFREG | S_IRUSR | S_IWUSR;
const uint64_t fh = 23;
};
TEST_F(StatfsTest, StatfsNormal) {
SetServerInodeLookup(mount_point_.path(), dir_mode_);
struct fuse_out_header out_header = {
.len = sizeof(struct fuse_out_header) + sizeof(struct fuse_statfs_out),
};
struct fuse_statfs_out out_payload = {
.st = fuse_kstatfs {
.blocks = 0x6000,
.bfree = 0x6000,
.bavail = 0x6000,
.bsize = 4096,
.namelen = 0x10000,
},
};
auto iov_out = FuseGenerateIovecs(out_header, out_payload);
SetServerResponse(FUSE_STATFS, iov_out);
// Make syscall.
struct statfs st;
EXPECT_THAT(statfs(mount_point_.path().c_str(), &st), SyscallSucceeds());
// Check filesystem operation result.
struct statfs expected_stat = {
.f_type = FUSE_SUPER_MAGIC,
.f_bsize = out_payload.st.bsize,
.f_blocks = out_payload.st.blocks,
.f_bfree = out_payload.st.bfree,
.f_bavail = out_payload.st.bavail,
.f_namelen = out_payload.st.namelen,
};
EXPECT_TRUE(StatsfsAreEqual(st, expected_stat));
// Check FUSE request.
struct fuse_in_header in_header;
auto iov_in = FuseGenerateIovecs(in_header);
GetServerActualRequest(iov_in);
EXPECT_EQ(in_header.opcode, FUSE_STATFS);
}
TEST_F(StatfsTest, NotFound) {
struct fuse_out_header out_header = {
.len = sizeof(struct fuse_out_header),
.error = -ENOENT,
};
auto iov_out = FuseGenerateIovecs(out_header);
SetServerResponse(FUSE_STATFS, iov_out);
// Make syscall.
struct statfs statfs_buf;
EXPECT_THAT(statfs(mount_point_.path().c_str(), &statfs_buf),
SyscallFailsWithErrno(ENOENT));
// Check FUSE request.
struct fuse_in_header in_header;
auto iov_in = FuseGenerateIovecs(in_header);
GetServerActualRequest(iov_in);
EXPECT_EQ(in_header.opcode, FUSE_STATFS);
}
} // namespace
} // namespace testing
} // namespace gvisor
+5 -5
View File
@@ -120,7 +120,7 @@ TEST_F(WriteTest, WriteNormal) {
EXPECT_EQ(in_payload_write.fh, test_fh_);
EXPECT_EQ(in_header_write.len,
sizeof(in_header_write) + sizeof(in_payload_write));
sizeof(in_header_write) + sizeof(in_payload_write) + n_write);
EXPECT_EQ(in_header_write.opcode, FUSE_WRITE);
EXPECT_EQ(in_payload_write.offset, 0);
EXPECT_EQ(in_payload_write.size, n_write);
@@ -157,7 +157,7 @@ TEST_F(WriteTest, WriteShort) {
EXPECT_EQ(in_payload_write.fh, test_fh_);
EXPECT_EQ(in_header_write.len,
sizeof(in_header_write) + sizeof(in_payload_write));
sizeof(in_header_write) + sizeof(in_payload_write) + n_write);
EXPECT_EQ(in_header_write.opcode, FUSE_WRITE);
EXPECT_EQ(in_payload_write.offset, 0);
EXPECT_EQ(in_payload_write.size, n_write);
@@ -193,7 +193,7 @@ TEST_F(WriteTest, WriteShortZero) {
EXPECT_EQ(in_payload_write.fh, test_fh_);
EXPECT_EQ(in_header_write.len,
sizeof(in_header_write) + sizeof(in_payload_write));
sizeof(in_header_write) + sizeof(in_payload_write) + n_write);
EXPECT_EQ(in_header_write.opcode, FUSE_WRITE);
EXPECT_EQ(in_payload_write.offset, 0);
EXPECT_EQ(in_payload_write.size, n_write);
@@ -240,7 +240,7 @@ TEST_F(WriteTest, PWrite) {
EXPECT_EQ(in_payload_write.fh, test_fh_);
EXPECT_EQ(in_header_write.len,
sizeof(in_header_write) + sizeof(in_payload_write));
sizeof(in_header_write) + sizeof(in_payload_write) + n_write);
EXPECT_EQ(in_header_write.opcode, FUSE_WRITE);
EXPECT_EQ(in_payload_write.offset, offset_write);
EXPECT_EQ(in_payload_write.size, n_write);
@@ -287,7 +287,7 @@ TEST_F(WriteTestSmallMaxWrite, WriteSmallMaxWrie) {
EXPECT_EQ(in_payload_write.fh, test_fh_);
EXPECT_EQ(in_header_write.len,
sizeof(in_header_write) + sizeof(in_payload_write));
sizeof(in_header_write) + sizeof(in_payload_write) + size_fragment);
EXPECT_EQ(in_header_write.opcode, FUSE_WRITE);
EXPECT_EQ(in_payload_write.offset, i * size_fragment);
EXPECT_EQ(in_payload_write.size, size_fragment);