Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F163546961
D55047.id182275.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
4 KB
Referenced Files
None
Subscribers
None
D55047.id182275.diff
View Options
diff --git a/sys/fs/fuse/fuse_internal.c b/sys/fs/fuse/fuse_internal.c
--- a/sys/fs/fuse/fuse_internal.c
+++ b/sys/fs/fuse/fuse_internal.c
@@ -257,6 +257,7 @@
struct fuse_vnode_data *fvdat;
struct fuse_data *data;
struct vattr *vp_cache_at;
+ off_t size;
mp = vnode_mount(vp);
fvdat = VTOFUD(vp);
@@ -264,13 +265,25 @@
ASSERT_CACHED_ATTRS_LOCKED(vp);
+ size = attr->size;
+
fuse_validity_2_bintime(attr_valid, attr_valid_nsec,
&fvdat->attr_cache_timeout);
+ /*
+ * Check if the file has been changed from under us. We
+ * can only reliably check whether the server has grown
+ * the file unilaterally, not shrunk. This is because
+ * unflushed writes on our end can lead us to report
+ * larger sizes compared to the result of a getattr
+ * to the server even if we have set FN_SIZECHANGE.
+ * The opposite isn't true, because we clear the
+ * FN_SIZECHANGE flag if we truncate the file.
+ */
if (vnode_isreg(vp) &&
fvdat->cached_attrs.va_size != VNOVAL &&
fvdat->flag & FN_SIZECHANGE &&
- attr->size != fvdat->cached_attrs.va_size)
+ attr->size > fvdat->cached_attrs.va_size)
{
if (data->cache_mode == FUSE_CACHE_WB)
{
@@ -313,11 +326,16 @@
}
/* Fix our buffers if the filesize changed without us knowing */
- if (vnode_isreg(vp) && attr->size != fvdat->cached_attrs.va_size) {
+ if (vnode_isreg(vp) && attr->size != fvdat->cached_attrs.va_size &&
+ !(fvdat->flag & FN_SIZECHANGE)) {
(void)fuse_vnode_setsize(vp, attr->size, from_server);
fvdat->cached_attrs.va_size = attr->size;
}
+ /* If we have a local dirty copy of the size, use it. */
+ if (fvdat->flag & FN_SIZECHANGE && fvdat->cached_attrs.va_size != VNOVAL)
+ size = fvdat->cached_attrs.va_size;
+
if (attr_valid > 0 || attr_valid_nsec > 0)
vp_cache_at = &(fvdat->cached_attrs);
else if (vap != NULL)
@@ -332,7 +350,7 @@
vp_cache_at->va_uid = attr->uid;
vp_cache_at->va_gid = attr->gid;
vp_cache_at->va_rdev = attr->rdev;
- vp_cache_at->va_size = attr->size;
+ vp_cache_at->va_size = size;
/* XXX on i386, seconds are truncated to 32 bits */
vp_cache_at->va_atime.tv_sec = attr->atime;
vp_cache_at->va_atime.tv_nsec = attr->atimensec;
diff --git a/tests/sys/fs/fusefs/write.cc b/tests/sys/fs/fusefs/write.cc
--- a/tests/sys/fs/fusefs/write.cc
+++ b/tests/sys/fs/fusefs/write.cc
@@ -1532,6 +1532,68 @@
leak(fd);
}
+/*
+ * When deciding whether to report the cached file size or that of the server, FUSE
+ * can report either the cached value or a new value from the server. If the file
+ * has been modified by a write locally that has not been flushed to the server, it
+ * should pick the local cached value. Ensure that the local file size takes precedence
+ * then over the server's (the alternative would trigger spurious file truncations).
+ */
+TEST_F(WriteBackAsync, lookup_dont_clobber_pending_size)
+{
+ const char FULLPATH[] = "mountpoint/some_file.txt";
+ const char RELPATH[] = "some_file.txt";
+ const char *CONTENTS = "abcdefgh";
+ ssize_t bufsize = strlen(CONTENTS);
+ uint64_t ino = 42;
+ uint64_t server_size = 0;
+ mode_t mode = S_IFREG | 0644;
+ int fd;
+ struct stat sb;
+
+ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH)
+ .WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto in __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, entry);
+ out.body.entry.attr.mode = mode;
+ out.body.entry.nodeid = ino;
+ out.body.entry.attr.nlink = 1;
+ out.body.entry.attr.size = server_size;
+ out.body.entry.attr_valid = UINT64_MAX;
+ })));
+ expect_open(ino, 0, 1);
+
+ EXPECT_CALL(*m_mock, process(
+ ResultOf([=](auto in) {
+ return (in.header.opcode == FUSE_GETATTR &&
+ in.header.nodeid == ino);
+ }, Eq(true)),
+ _)
+ ).WillRepeatedly(Invoke(
+ ReturnImmediate([=](auto i __unused, auto& out) {
+ SET_OUT_HEADER_LEN(out, attr);
+ out.body.attr.attr.ino = ino;
+ out.body.attr.attr.mode = mode;
+ out.body.attr.attr_valid = UINT64_MAX;
+ out.body.attr.attr.size = server_size;
+ })));
+
+ fd = open(FULLPATH, O_RDWR);
+ ASSERT_LE(0, fd) << strerror(errno);
+
+ /* Extend the file. */
+ ASSERT_EQ(bufsize, write(fd, CONTENTS, bufsize)) << strerror(errno);
+
+ /* If the write and stat are in the same tick the test doesn't trigger. */
+ usleep(10000);
+
+ /* Do another getattr call and ensure the file wasn't clobbered. */
+ ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
+ EXPECT_EQ(bufsize, sb.st_size);
+
+ leak(fd);
+}
+
/* Any dirty timestamp fields should be flushed during a SETATTR */
TEST_F(WriteBackAsync, timestamps_during_setattr)
{
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Jul 25, 8:36 AM (13 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35468909
Default Alt Text
D55047.id182275.diff (4 KB)
Attached To
Mode
D55047: fuse: Do not unconditionally overwrite local vnode size with remote
Attached
Detach File
Event Timeline
Log In to Comment