19821f1d3SAlan Somers /*- 29821f1d3SAlan Somers * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 39821f1d3SAlan Somers * 49821f1d3SAlan Somers * Copyright (c) 2019 The FreeBSD Foundation 59821f1d3SAlan Somers * 69821f1d3SAlan Somers * This software was developed by BFF Storage Systems, LLC under sponsorship 79821f1d3SAlan Somers * from the FreeBSD Foundation. 89821f1d3SAlan Somers * 99821f1d3SAlan Somers * Redistribution and use in source and binary forms, with or without 109821f1d3SAlan Somers * modification, are permitted provided that the following conditions 119821f1d3SAlan Somers * are met: 129821f1d3SAlan Somers * 1. Redistributions of source code must retain the above copyright 139821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer. 149821f1d3SAlan Somers * 2. Redistributions in binary form must reproduce the above copyright 159821f1d3SAlan Somers * notice, this list of conditions and the following disclaimer in the 169821f1d3SAlan Somers * documentation and/or other materials provided with the distribution. 179821f1d3SAlan Somers * 189821f1d3SAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 199821f1d3SAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 209821f1d3SAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 219821f1d3SAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 229821f1d3SAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 239821f1d3SAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 249821f1d3SAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 259821f1d3SAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 269821f1d3SAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 279821f1d3SAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 289821f1d3SAlan Somers * SUCH DAMAGE. 299821f1d3SAlan Somers */ 309821f1d3SAlan Somers 319821f1d3SAlan Somers extern "C" { 329821f1d3SAlan Somers #include <fcntl.h> 339821f1d3SAlan Somers } 349821f1d3SAlan Somers 359821f1d3SAlan Somers #include "mockfs.hh" 369821f1d3SAlan Somers #include "utils.hh" 379821f1d3SAlan Somers 389821f1d3SAlan Somers using namespace testing; 399821f1d3SAlan Somers 402d445be1SAlan Somers class Create: public FuseTest { 412d445be1SAlan Somers public: 422d445be1SAlan Somers 43ede571e4SAlan Somers void expect_create(const char *relpath, mode_t mode, ProcessMockerT r) 442d445be1SAlan Somers { 45a4856c96SAlan Somers mode_t mask = umask(0); 46a4856c96SAlan Somers (void)umask(mask); 47a4856c96SAlan Somers 482d445be1SAlan Somers EXPECT_CALL(*m_mock, process( 492d445be1SAlan Somers ResultOf([=](auto in) { 5029edc611SAlan Somers const char *name = (const char*)in.body.bytes + 51a4856c96SAlan Somers sizeof(fuse_create_in); 5229edc611SAlan Somers return (in.header.opcode == FUSE_CREATE && 53a4856c96SAlan Somers in.body.create.mode == mode && 54a4856c96SAlan Somers in.body.create.umask == mask && 552d445be1SAlan Somers (0 == strcmp(relpath, name))); 562d445be1SAlan Somers }, Eq(true)), 572d445be1SAlan Somers _) 582d445be1SAlan Somers ).WillOnce(Invoke(r)); 592d445be1SAlan Somers } 602d445be1SAlan Somers 612d445be1SAlan Somers }; 629821f1d3SAlan Somers 6316bd2d47SAlan Somers /* FUSE_CREATE operations for a protocol 7.8 server */ 6416bd2d47SAlan Somers class Create_7_8: public Create { 6516bd2d47SAlan Somers public: 6616bd2d47SAlan Somers virtual void SetUp() { 6716bd2d47SAlan Somers m_kernel_minor_version = 8; 6816bd2d47SAlan Somers Create::SetUp(); 6916bd2d47SAlan Somers } 70a4856c96SAlan Somers 71a4856c96SAlan Somers void expect_create(const char *relpath, mode_t mode, ProcessMockerT r) 72a4856c96SAlan Somers { 73a4856c96SAlan Somers EXPECT_CALL(*m_mock, process( 74a4856c96SAlan Somers ResultOf([=](auto in) { 75a4856c96SAlan Somers const char *name = (const char*)in.body.bytes + 76a4856c96SAlan Somers sizeof(fuse_open_in); 77a4856c96SAlan Somers return (in.header.opcode == FUSE_CREATE && 78a4856c96SAlan Somers in.body.create.mode == mode && 79a4856c96SAlan Somers (0 == strcmp(relpath, name))); 80a4856c96SAlan Somers }, Eq(true)), 81a4856c96SAlan Somers _) 82a4856c96SAlan Somers ).WillOnce(Invoke(r)); 83a4856c96SAlan Somers } 84a4856c96SAlan Somers 85a4856c96SAlan Somers }; 86a4856c96SAlan Somers 87a4856c96SAlan Somers /* FUSE_CREATE operations for a server built at protocol <= 7.11 */ 88a4856c96SAlan Somers class Create_7_11: public FuseTest { 89a4856c96SAlan Somers public: 90a4856c96SAlan Somers virtual void SetUp() { 91a4856c96SAlan Somers m_kernel_minor_version = 11; 92a4856c96SAlan Somers FuseTest::SetUp(); 93a4856c96SAlan Somers } 94a4856c96SAlan Somers 95a4856c96SAlan Somers void expect_create(const char *relpath, mode_t mode, ProcessMockerT r) 96a4856c96SAlan Somers { 97a4856c96SAlan Somers EXPECT_CALL(*m_mock, process( 98a4856c96SAlan Somers ResultOf([=](auto in) { 99a4856c96SAlan Somers const char *name = (const char*)in.body.bytes + 100a4856c96SAlan Somers sizeof(fuse_open_in); 101a4856c96SAlan Somers return (in.header.opcode == FUSE_CREATE && 102a4856c96SAlan Somers in.body.create.mode == mode && 103a4856c96SAlan Somers (0 == strcmp(relpath, name))); 104a4856c96SAlan Somers }, Eq(true)), 105a4856c96SAlan Somers _) 106a4856c96SAlan Somers ).WillOnce(Invoke(r)); 107a4856c96SAlan Somers } 108a4856c96SAlan Somers 10916bd2d47SAlan Somers }; 11016bd2d47SAlan Somers 11116bd2d47SAlan Somers 1129821f1d3SAlan Somers /* 113002e54b0SAlan Somers * If FUSE_CREATE sets attr_valid, then subsequent GETATTRs should use the 1149821f1d3SAlan Somers * attribute cache 1159821f1d3SAlan Somers */ 116cad67791SAlan Somers TEST_F(Create, attr_cache) 1179821f1d3SAlan Somers { 1189821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1199821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 120ede571e4SAlan Somers mode_t mode = S_IFREG | 0755; 1219821f1d3SAlan Somers uint64_t ino = 42; 1229821f1d3SAlan Somers int fd; 1239821f1d3SAlan Somers 124a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 125a34cdd26SAlan Somers .WillOnce(Invoke(ReturnErrno(ENOENT))); 126ede571e4SAlan Somers expect_create(RELPATH, mode, 12729edc611SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 1289821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, create); 12929edc611SAlan Somers out.body.create.entry.attr.mode = mode; 13029edc611SAlan Somers out.body.create.entry.nodeid = ino; 13129edc611SAlan Somers out.body.create.entry.entry_valid = UINT64_MAX; 13229edc611SAlan Somers out.body.create.entry.attr_valid = UINT64_MAX; 1332d445be1SAlan Somers })); 1349821f1d3SAlan Somers 1359821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 1369821f1d3SAlan Somers ResultOf([=](auto in) { 13729edc611SAlan Somers return (in.header.opcode == FUSE_GETATTR && 13829edc611SAlan Somers in.header.nodeid == ino); 1399821f1d3SAlan Somers }, Eq(true)), 1409821f1d3SAlan Somers _) 1419821f1d3SAlan Somers ).Times(0); 1429821f1d3SAlan Somers 1439821f1d3SAlan Somers fd = open(FULLPATH, O_CREAT | O_EXCL, mode); 1449821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 145*7fc0921dSAlan Somers leak(fd); 1469821f1d3SAlan Somers } 1479821f1d3SAlan Somers 148002e54b0SAlan Somers /* A successful CREATE operation should purge the parent dir's attr cache */ 149002e54b0SAlan Somers TEST_F(Create, clear_attr_cache) 150002e54b0SAlan Somers { 151002e54b0SAlan Somers const char FULLPATH[] = "mountpoint/src"; 152002e54b0SAlan Somers const char RELPATH[] = "src"; 153002e54b0SAlan Somers mode_t mode = S_IFREG | 0755; 154002e54b0SAlan Somers uint64_t ino = 42; 155002e54b0SAlan Somers int fd; 156002e54b0SAlan Somers struct stat sb; 157002e54b0SAlan Somers 158a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 159a34cdd26SAlan Somers .WillOnce(Invoke(ReturnErrno(ENOENT))); 160002e54b0SAlan Somers EXPECT_CALL(*m_mock, process( 161002e54b0SAlan Somers ResultOf([=](auto in) { 16229edc611SAlan Somers return (in.header.opcode == FUSE_GETATTR && 163a34cdd26SAlan Somers in.header.nodeid == FUSE_ROOT_ID); 164002e54b0SAlan Somers }, Eq(true)), 165002e54b0SAlan Somers _) 166002e54b0SAlan Somers ).Times(2) 16729edc611SAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 168002e54b0SAlan Somers SET_OUT_HEADER_LEN(out, attr); 169a34cdd26SAlan Somers out.body.attr.attr.ino = FUSE_ROOT_ID; 17029edc611SAlan Somers out.body.attr.attr.mode = S_IFDIR | 0755; 17129edc611SAlan Somers out.body.attr.attr_valid = UINT64_MAX; 172002e54b0SAlan Somers }))); 173002e54b0SAlan Somers 174002e54b0SAlan Somers expect_create(RELPATH, mode, 17529edc611SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 176002e54b0SAlan Somers SET_OUT_HEADER_LEN(out, create); 17729edc611SAlan Somers out.body.create.entry.attr.mode = mode; 17829edc611SAlan Somers out.body.create.entry.nodeid = ino; 17929edc611SAlan Somers out.body.create.entry.entry_valid = UINT64_MAX; 18029edc611SAlan Somers out.body.create.entry.attr_valid = UINT64_MAX; 181002e54b0SAlan Somers })); 182002e54b0SAlan Somers 183002e54b0SAlan Somers EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno); 184002e54b0SAlan Somers fd = open(FULLPATH, O_CREAT | O_EXCL, mode); 185002e54b0SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 186002e54b0SAlan Somers EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno); 187002e54b0SAlan Somers 188*7fc0921dSAlan Somers leak(fd); 189002e54b0SAlan Somers } 190002e54b0SAlan Somers 1919821f1d3SAlan Somers /* 1929821f1d3SAlan Somers * The fuse daemon fails the request with EEXIST. This usually indicates a 1939821f1d3SAlan Somers * race condition: some other FUSE client created the file in between when the 1949821f1d3SAlan Somers * kernel checked for it with lookup and tried to create it with create 1959821f1d3SAlan Somers */ 1969821f1d3SAlan Somers TEST_F(Create, eexist) 1979821f1d3SAlan Somers { 1989821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1999821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 200ede571e4SAlan Somers mode_t mode = S_IFREG | 0755; 2019821f1d3SAlan Somers 202a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 203a34cdd26SAlan Somers .WillOnce(Invoke(ReturnErrno(ENOENT))); 204ede571e4SAlan Somers expect_create(RELPATH, mode, ReturnErrno(EEXIST)); 2059821f1d3SAlan Somers EXPECT_NE(0, open(FULLPATH, O_CREAT | O_EXCL, mode)); 2069821f1d3SAlan Somers EXPECT_EQ(EEXIST, errno); 2079821f1d3SAlan Somers } 2089821f1d3SAlan Somers 2099821f1d3SAlan Somers /* 2109821f1d3SAlan Somers * If the daemon doesn't implement FUSE_CREATE, then the kernel should fallback 2119821f1d3SAlan Somers * to FUSE_MKNOD/FUSE_OPEN 2129821f1d3SAlan Somers */ 21319ef317dSAlan Somers TEST_F(Create, Enosys) 2149821f1d3SAlan Somers { 2159821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2169821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 217ede571e4SAlan Somers mode_t mode = S_IFREG | 0755; 2189821f1d3SAlan Somers uint64_t ino = 42; 2199821f1d3SAlan Somers int fd; 2209821f1d3SAlan Somers 221a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 222a34cdd26SAlan Somers .WillOnce(Invoke(ReturnErrno(ENOENT))); 223ede571e4SAlan Somers expect_create(RELPATH, mode, ReturnErrno(ENOSYS)); 2249821f1d3SAlan Somers 2259821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 2269821f1d3SAlan Somers ResultOf([=](auto in) { 22729edc611SAlan Somers const char *name = (const char*)in.body.bytes + 2289821f1d3SAlan Somers sizeof(fuse_mknod_in); 22929edc611SAlan Somers return (in.header.opcode == FUSE_MKNOD && 23029edc611SAlan Somers in.body.mknod.mode == (S_IFREG | mode) && 23129edc611SAlan Somers in.body.mknod.rdev == 0 && 2329821f1d3SAlan Somers (0 == strcmp(RELPATH, name))); 2339821f1d3SAlan Somers }, Eq(true)), 2349821f1d3SAlan Somers _) 23529edc611SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 23619ef317dSAlan Somers SET_OUT_HEADER_LEN(out, entry); 23729edc611SAlan Somers out.body.entry.attr.mode = mode; 23829edc611SAlan Somers out.body.entry.nodeid = ino; 23929edc611SAlan Somers out.body.entry.entry_valid = UINT64_MAX; 24029edc611SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 2419821f1d3SAlan Somers }))); 2429821f1d3SAlan Somers 2439821f1d3SAlan Somers EXPECT_CALL(*m_mock, process( 2449821f1d3SAlan Somers ResultOf([=](auto in) { 24529edc611SAlan Somers return (in.header.opcode == FUSE_OPEN && 24629edc611SAlan Somers in.header.nodeid == ino); 2479821f1d3SAlan Somers }, Eq(true)), 2489821f1d3SAlan Somers _) 24929edc611SAlan Somers ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto& out) { 25029edc611SAlan Somers out.header.len = sizeof(out.header); 2519821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, open); 2529821f1d3SAlan Somers }))); 2539821f1d3SAlan Somers 2549821f1d3SAlan Somers fd = open(FULLPATH, O_CREAT | O_EXCL, mode); 2559821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 256*7fc0921dSAlan Somers leak(fd); 2579821f1d3SAlan Somers } 2589821f1d3SAlan Somers 2599821f1d3SAlan Somers /* 2609821f1d3SAlan Somers * Creating a new file after FUSE_LOOKUP returned a negative cache entry 2619821f1d3SAlan Somers */ 2626248288eSAlan Somers TEST_F(Create, entry_cache_negative) 2639821f1d3SAlan Somers { 2649821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2659821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 266ede571e4SAlan Somers mode_t mode = S_IFREG | 0755; 2679821f1d3SAlan Somers uint64_t ino = 42; 2689821f1d3SAlan Somers int fd; 2699821f1d3SAlan Somers /* 2709821f1d3SAlan Somers * Set entry_valid = 0 because this test isn't concerned with whether 2719821f1d3SAlan Somers * or not we actually cache negative entries, only with whether we 2729821f1d3SAlan Somers * interpret negative cache responses correctly. 2739821f1d3SAlan Somers */ 2749821f1d3SAlan Somers struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0}; 2759821f1d3SAlan Somers 2769821f1d3SAlan Somers /* create will first do a LOOKUP, adding a negative cache entry */ 277a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 278a34cdd26SAlan Somers .WillOnce(ReturnNegativeCache(&entry_valid)); 279ede571e4SAlan Somers expect_create(RELPATH, mode, 28029edc611SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 2819821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, create); 28229edc611SAlan Somers out.body.create.entry.attr.mode = mode; 28329edc611SAlan Somers out.body.create.entry.nodeid = ino; 28429edc611SAlan Somers out.body.create.entry.entry_valid = UINT64_MAX; 28529edc611SAlan Somers out.body.create.entry.attr_valid = UINT64_MAX; 2862d445be1SAlan Somers })); 2879821f1d3SAlan Somers 2889821f1d3SAlan Somers fd = open(FULLPATH, O_CREAT | O_EXCL, mode); 2899821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 290*7fc0921dSAlan Somers leak(fd); 2919821f1d3SAlan Somers } 2929821f1d3SAlan Somers 2939821f1d3SAlan Somers /* 2949821f1d3SAlan Somers * Creating a new file should purge any negative namecache entries 2959821f1d3SAlan Somers */ 2966248288eSAlan Somers TEST_F(Create, entry_cache_negative_purge) 2979821f1d3SAlan Somers { 2989821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2999821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 300ede571e4SAlan Somers mode_t mode = S_IFREG | 0755; 3019821f1d3SAlan Somers uint64_t ino = 42; 3029821f1d3SAlan Somers int fd; 3039821f1d3SAlan Somers struct timespec entry_valid = {.tv_sec = TIME_T_MAX, .tv_nsec = 0}; 3049821f1d3SAlan Somers 3059821f1d3SAlan Somers /* create will first do a LOOKUP, adding a negative cache entry */ 306a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH).Times(1) 3079821f1d3SAlan Somers .WillOnce(Invoke(ReturnNegativeCache(&entry_valid))) 3089821f1d3SAlan Somers .RetiresOnSaturation(); 3099821f1d3SAlan Somers 3109821f1d3SAlan Somers /* Then the CREATE should purge the negative cache entry */ 311ede571e4SAlan Somers expect_create(RELPATH, mode, 31229edc611SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 3139821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, create); 31429edc611SAlan Somers out.body.create.entry.attr.mode = mode; 31529edc611SAlan Somers out.body.create.entry.nodeid = ino; 31629edc611SAlan Somers out.body.create.entry.attr_valid = UINT64_MAX; 3172d445be1SAlan Somers })); 3189821f1d3SAlan Somers 3199821f1d3SAlan Somers fd = open(FULLPATH, O_CREAT | O_EXCL, mode); 3209821f1d3SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 3219821f1d3SAlan Somers 3229821f1d3SAlan Somers /* Finally, a subsequent lookup should query the daemon */ 3239821f1d3SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | mode, 0, 1); 3249821f1d3SAlan Somers 3259821f1d3SAlan Somers ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); 326*7fc0921dSAlan Somers leak(fd); 3279821f1d3SAlan Somers } 3289821f1d3SAlan Somers 3299821f1d3SAlan Somers /* 3309821f1d3SAlan Somers * The daemon is responsible for checking file permissions (unless the 3319821f1d3SAlan Somers * default_permissions mount option was used) 3329821f1d3SAlan Somers */ 3339821f1d3SAlan Somers TEST_F(Create, eperm) 3349821f1d3SAlan Somers { 3359821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 3369821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 337ede571e4SAlan Somers mode_t mode = S_IFREG | 0755; 3389821f1d3SAlan Somers 339a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 340a34cdd26SAlan Somers .WillOnce(Invoke(ReturnErrno(ENOENT))); 341ede571e4SAlan Somers expect_create(RELPATH, mode, ReturnErrno(EPERM)); 3429821f1d3SAlan Somers 3439821f1d3SAlan Somers EXPECT_NE(0, open(FULLPATH, O_CREAT | O_EXCL, mode)); 3449821f1d3SAlan Somers EXPECT_EQ(EPERM, errno); 3459821f1d3SAlan Somers } 3469821f1d3SAlan Somers 3479821f1d3SAlan Somers TEST_F(Create, ok) 3489821f1d3SAlan Somers { 3499821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 3509821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 351ede571e4SAlan Somers mode_t mode = S_IFREG | 0755; 3529821f1d3SAlan Somers uint64_t ino = 42; 3539821f1d3SAlan Somers int fd; 3549821f1d3SAlan Somers 355a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 356a34cdd26SAlan Somers .WillOnce(Invoke(ReturnErrno(ENOENT))); 357ede571e4SAlan Somers expect_create(RELPATH, mode, 35829edc611SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 3599821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, create); 36029edc611SAlan Somers out.body.create.entry.attr.mode = mode; 36129edc611SAlan Somers out.body.create.entry.nodeid = ino; 36229edc611SAlan Somers out.body.create.entry.entry_valid = UINT64_MAX; 36329edc611SAlan Somers out.body.create.entry.attr_valid = UINT64_MAX; 3642d445be1SAlan Somers })); 3659821f1d3SAlan Somers 3669821f1d3SAlan Somers fd = open(FULLPATH, O_CREAT | O_EXCL, mode); 3679821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 368*7fc0921dSAlan Somers leak(fd); 3699821f1d3SAlan Somers } 3702d445be1SAlan Somers 3712d445be1SAlan Somers /* 3722d445be1SAlan Somers * A regression test for a bug that affected old FUSE implementations: 3732d445be1SAlan Somers * open(..., O_WRONLY | O_CREAT, 0444) should work despite the seeming 3742d445be1SAlan Somers * contradiction between O_WRONLY and 0444 3752d445be1SAlan Somers * 3762d445be1SAlan Somers * For example: 3772d445be1SAlan Somers * https://bugs.launchpad.net/ubuntu/+source/sshfs-fuse/+bug/44886 3782d445be1SAlan Somers */ 3792d445be1SAlan Somers TEST_F(Create, wronly_0444) 3802d445be1SAlan Somers { 3812d445be1SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 3822d445be1SAlan Somers const char RELPATH[] = "some_file.txt"; 383ede571e4SAlan Somers mode_t mode = S_IFREG | 0444; 3842d445be1SAlan Somers uint64_t ino = 42; 3852d445be1SAlan Somers int fd; 3862d445be1SAlan Somers 387a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 388a34cdd26SAlan Somers .WillOnce(Invoke(ReturnErrno(ENOENT))); 389ede571e4SAlan Somers expect_create(RELPATH, mode, 39029edc611SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 3912d445be1SAlan Somers SET_OUT_HEADER_LEN(out, create); 39229edc611SAlan Somers out.body.create.entry.attr.mode = mode; 39329edc611SAlan Somers out.body.create.entry.nodeid = ino; 39429edc611SAlan Somers out.body.create.entry.entry_valid = UINT64_MAX; 39529edc611SAlan Somers out.body.create.entry.attr_valid = UINT64_MAX; 3962d445be1SAlan Somers })); 3972d445be1SAlan Somers 3982d445be1SAlan Somers fd = open(FULLPATH, O_CREAT | O_WRONLY, mode); 3992d445be1SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 400*7fc0921dSAlan Somers leak(fd); 4012d445be1SAlan Somers } 40216bd2d47SAlan Somers 40316bd2d47SAlan Somers TEST_F(Create_7_8, ok) 40416bd2d47SAlan Somers { 40516bd2d47SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 40616bd2d47SAlan Somers const char RELPATH[] = "some_file.txt"; 40716bd2d47SAlan Somers mode_t mode = S_IFREG | 0755; 40816bd2d47SAlan Somers uint64_t ino = 42; 40916bd2d47SAlan Somers int fd; 41016bd2d47SAlan Somers 411a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 412a34cdd26SAlan Somers .WillOnce(Invoke(ReturnErrno(ENOENT))); 41316bd2d47SAlan Somers expect_create(RELPATH, mode, 41429edc611SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 41516bd2d47SAlan Somers SET_OUT_HEADER_LEN(out, create_7_8); 41629edc611SAlan Somers out.body.create.entry.attr.mode = mode; 41729edc611SAlan Somers out.body.create.entry.nodeid = ino; 41829edc611SAlan Somers out.body.create.entry.entry_valid = UINT64_MAX; 41929edc611SAlan Somers out.body.create.entry.attr_valid = UINT64_MAX; 42016bd2d47SAlan Somers })); 42116bd2d47SAlan Somers 42216bd2d47SAlan Somers fd = open(FULLPATH, O_CREAT | O_EXCL, mode); 42316bd2d47SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 424*7fc0921dSAlan Somers leak(fd); 42516bd2d47SAlan Somers } 42616bd2d47SAlan Somers 427a4856c96SAlan Somers TEST_F(Create_7_11, ok) 428a4856c96SAlan Somers { 429a4856c96SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 430a4856c96SAlan Somers const char RELPATH[] = "some_file.txt"; 431a4856c96SAlan Somers mode_t mode = S_IFREG | 0755; 432a4856c96SAlan Somers uint64_t ino = 42; 433a4856c96SAlan Somers int fd; 43416bd2d47SAlan Somers 435a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) 436a34cdd26SAlan Somers .WillOnce(Invoke(ReturnErrno(ENOENT))); 437a4856c96SAlan Somers expect_create(RELPATH, mode, 438a4856c96SAlan Somers ReturnImmediate([=](auto in __unused, auto& out) { 439a4856c96SAlan Somers SET_OUT_HEADER_LEN(out, create); 440a4856c96SAlan Somers out.body.create.entry.attr.mode = mode; 441a4856c96SAlan Somers out.body.create.entry.nodeid = ino; 442a4856c96SAlan Somers out.body.create.entry.entry_valid = UINT64_MAX; 443a4856c96SAlan Somers out.body.create.entry.attr_valid = UINT64_MAX; 444a4856c96SAlan Somers })); 445a4856c96SAlan Somers 446a4856c96SAlan Somers fd = open(FULLPATH, O_CREAT | O_EXCL, mode); 447a4856c96SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 448*7fc0921dSAlan Somers leak(fd); 449a4856c96SAlan Somers } 450