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> 33*435ecf40SAlan Somers #include <semaphore.h> 349821f1d3SAlan Somers } 359821f1d3SAlan Somers 369821f1d3SAlan Somers #include "mockfs.hh" 379821f1d3SAlan Somers #include "utils.hh" 389821f1d3SAlan Somers 399821f1d3SAlan Somers using namespace testing; 409821f1d3SAlan Somers 419821f1d3SAlan Somers class Rmdir: public FuseTest { 429821f1d3SAlan Somers public: 436124fd71SAlan Somers void expect_getattr(uint64_t ino, mode_t mode) 446124fd71SAlan Somers { 456124fd71SAlan Somers EXPECT_CALL(*m_mock, process( 466124fd71SAlan Somers ResultOf([=](auto in) { 4729edc611SAlan Somers return (in.header.opcode == FUSE_GETATTR && 4829edc611SAlan Somers in.header.nodeid == ino); 496124fd71SAlan Somers }, Eq(true)), 506124fd71SAlan Somers _) 5129edc611SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 526124fd71SAlan Somers SET_OUT_HEADER_LEN(out, attr); 5329edc611SAlan Somers out.body.attr.attr.ino = ino; // Must match nodeid 5429edc611SAlan Somers out.body.attr.attr.mode = mode; 5529edc611SAlan Somers out.body.attr.attr_valid = UINT64_MAX; 566124fd71SAlan Somers }))); 576124fd71SAlan Somers } 586124fd71SAlan Somers 59*435ecf40SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, int times=1) 609821f1d3SAlan Somers { 61a34cdd26SAlan Somers EXPECT_LOOKUP(FUSE_ROOT_ID, relpath) 62*435ecf40SAlan Somers .Times(times) 63*435ecf40SAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 649821f1d3SAlan Somers SET_OUT_HEADER_LEN(out, entry); 65*435ecf40SAlan Somers out.body.entry.attr_valid = UINT64_MAX; 6629edc611SAlan Somers out.body.entry.attr.mode = S_IFDIR | 0755; 6729edc611SAlan Somers out.body.entry.nodeid = ino; 6829edc611SAlan Somers out.body.entry.attr.nlink = 2; 699821f1d3SAlan Somers }))); 709821f1d3SAlan Somers } 71002e54b0SAlan Somers 72002e54b0SAlan Somers void expect_rmdir(uint64_t parent, const char *relpath, int error) 73002e54b0SAlan Somers { 74002e54b0SAlan Somers EXPECT_CALL(*m_mock, process( 75002e54b0SAlan Somers ResultOf([=](auto in) { 7629edc611SAlan Somers return (in.header.opcode == FUSE_RMDIR && 7729edc611SAlan Somers 0 == strcmp(relpath, in.body.rmdir) && 7829edc611SAlan Somers in.header.nodeid == parent); 79002e54b0SAlan Somers }, Eq(true)), 80002e54b0SAlan Somers _) 81002e54b0SAlan Somers ).WillOnce(Invoke(ReturnErrno(error))); 82002e54b0SAlan Somers } 839821f1d3SAlan Somers }; 849821f1d3SAlan Somers 85002e54b0SAlan Somers /* 86002e54b0SAlan Somers * A successful rmdir should clear the parent directory's attribute cache, 87002e54b0SAlan Somers * because the fuse daemon should update its mtime and ctime 88002e54b0SAlan Somers */ 89*435ecf40SAlan Somers TEST_F(Rmdir, parent_attr_cache) 90002e54b0SAlan Somers { 91002e54b0SAlan Somers const char FULLPATH[] = "mountpoint/some_dir"; 92002e54b0SAlan Somers const char RELPATH[] = "some_dir"; 93002e54b0SAlan Somers struct stat sb; 94*435ecf40SAlan Somers sem_t sem; 95002e54b0SAlan Somers uint64_t ino = 42; 96002e54b0SAlan Somers 97*435ecf40SAlan Somers ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno); 98*435ecf40SAlan Somers 99002e54b0SAlan Somers EXPECT_CALL(*m_mock, process( 100002e54b0SAlan Somers ResultOf([=](auto in) { 10129edc611SAlan Somers return (in.header.opcode == FUSE_GETATTR && 102a34cdd26SAlan Somers in.header.nodeid == FUSE_ROOT_ID); 103002e54b0SAlan Somers }, Eq(true)), 104002e54b0SAlan Somers _) 105002e54b0SAlan Somers ).Times(2) 10629edc611SAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto& out) { 107002e54b0SAlan Somers SET_OUT_HEADER_LEN(out, attr); 10829edc611SAlan Somers out.body.attr.attr.ino = ino; // Must match nodeid 10929edc611SAlan Somers out.body.attr.attr.mode = S_IFDIR | 0755; 11029edc611SAlan Somers out.body.attr.attr_valid = UINT64_MAX; 111002e54b0SAlan Somers }))); 112002e54b0SAlan Somers expect_lookup(RELPATH, ino); 113a34cdd26SAlan Somers expect_rmdir(FUSE_ROOT_ID, RELPATH, 0); 114*435ecf40SAlan Somers expect_forget(ino, 1, &sem); 115002e54b0SAlan Somers 116002e54b0SAlan Somers ASSERT_EQ(0, rmdir(FULLPATH)) << strerror(errno); 117002e54b0SAlan Somers EXPECT_EQ(0, stat("mountpoint", &sb)) << strerror(errno); 118*435ecf40SAlan Somers sem_wait(&sem); 119*435ecf40SAlan Somers sem_destroy(&sem); 120002e54b0SAlan Somers } 121002e54b0SAlan Somers 1229821f1d3SAlan Somers TEST_F(Rmdir, enotempty) 1239821f1d3SAlan Somers { 1249821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_dir"; 1259821f1d3SAlan Somers const char RELPATH[] = "some_dir"; 1269821f1d3SAlan Somers uint64_t ino = 42; 1279821f1d3SAlan Somers 128a34cdd26SAlan Somers expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755); 1299821f1d3SAlan Somers expect_lookup(RELPATH, ino); 130a34cdd26SAlan Somers expect_rmdir(FUSE_ROOT_ID, RELPATH, ENOTEMPTY); 1319821f1d3SAlan Somers 1329821f1d3SAlan Somers ASSERT_NE(0, rmdir(FULLPATH)); 1339821f1d3SAlan Somers ASSERT_EQ(ENOTEMPTY, errno); 1349821f1d3SAlan Somers } 1359821f1d3SAlan Somers 136*435ecf40SAlan Somers /* Removing a directory should expire its entry cache */ 137*435ecf40SAlan Somers TEST_F(Rmdir, entry_cache) 138*435ecf40SAlan Somers { 139*435ecf40SAlan Somers const char FULLPATH[] = "mountpoint/some_dir"; 140*435ecf40SAlan Somers const char RELPATH[] = "some_dir"; 141*435ecf40SAlan Somers sem_t sem; 142*435ecf40SAlan Somers uint64_t ino = 42; 143*435ecf40SAlan Somers 144*435ecf40SAlan Somers expect_getattr(1, S_IFDIR | 0755); 145*435ecf40SAlan Somers expect_lookup(RELPATH, ino, 2); 146*435ecf40SAlan Somers expect_rmdir(FUSE_ROOT_ID, RELPATH, 0); 147*435ecf40SAlan Somers expect_forget(ino, 1, &sem); 148*435ecf40SAlan Somers 149*435ecf40SAlan Somers ASSERT_EQ(0, rmdir(FULLPATH)) << strerror(errno); 150*435ecf40SAlan Somers ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); 151*435ecf40SAlan Somers sem_wait(&sem); 152*435ecf40SAlan Somers sem_destroy(&sem); 153*435ecf40SAlan Somers } 154*435ecf40SAlan Somers 1559821f1d3SAlan Somers TEST_F(Rmdir, ok) 1569821f1d3SAlan Somers { 1579821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_dir"; 1589821f1d3SAlan Somers const char RELPATH[] = "some_dir"; 159*435ecf40SAlan Somers sem_t sem; 1609821f1d3SAlan Somers uint64_t ino = 42; 1619821f1d3SAlan Somers 162*435ecf40SAlan Somers ASSERT_EQ(0, sem_init(&sem, 0, 0)) << strerror(errno); 163*435ecf40SAlan Somers 164a34cdd26SAlan Somers expect_getattr(FUSE_ROOT_ID, S_IFDIR | 0755); 1659821f1d3SAlan Somers expect_lookup(RELPATH, ino); 166a34cdd26SAlan Somers expect_rmdir(FUSE_ROOT_ID, RELPATH, 0); 167*435ecf40SAlan Somers expect_forget(ino, 1, &sem); 1689821f1d3SAlan Somers 1699821f1d3SAlan Somers ASSERT_EQ(0, rmdir(FULLPATH)) << strerror(errno); 170*435ecf40SAlan Somers sem_wait(&sem); 171*435ecf40SAlan Somers sem_destroy(&sem); 1729821f1d3SAlan Somers } 173