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 /* 329821f1d3SAlan Somers * Tests for the "default_permissions" mount option. They must be in their own 339821f1d3SAlan Somers * file so they can be run as an unprivileged user 349821f1d3SAlan Somers */ 359821f1d3SAlan Somers 369821f1d3SAlan Somers extern "C" { 37ff4fbdf5SAlan Somers #include <sys/types.h> 38ff4fbdf5SAlan Somers #include <sys/extattr.h> 39ff4fbdf5SAlan Somers 409821f1d3SAlan Somers #include <fcntl.h> 419821f1d3SAlan Somers #include <unistd.h> 429821f1d3SAlan Somers } 439821f1d3SAlan Somers 449821f1d3SAlan Somers #include "mockfs.hh" 459821f1d3SAlan Somers #include "utils.hh" 469821f1d3SAlan Somers 479821f1d3SAlan Somers using namespace testing; 489821f1d3SAlan Somers 499821f1d3SAlan Somers class DefaultPermissions: public FuseTest { 509821f1d3SAlan Somers 519821f1d3SAlan Somers virtual void SetUp() { 52ff4fbdf5SAlan Somers m_default_permissions = true; 539821f1d3SAlan Somers FuseTest::SetUp(); 54ff4fbdf5SAlan Somers if (HasFatalFailure() || IsSkipped()) 55ff4fbdf5SAlan Somers return; 569821f1d3SAlan Somers 579821f1d3SAlan Somers if (geteuid() == 0) { 589821f1d3SAlan Somers GTEST_SKIP() << "This test requires an unprivileged user"; 599821f1d3SAlan Somers } 60ff4fbdf5SAlan Somers 61ff4fbdf5SAlan Somers /* With -o default_permissions, FUSE_ACCESS should never be called */ 62ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 63ff4fbdf5SAlan Somers ResultOf([=](auto in) { 64ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_ACCESS); 65ff4fbdf5SAlan Somers }, Eq(true)), 66ff4fbdf5SAlan Somers _) 67ff4fbdf5SAlan Somers ).Times(0); 689821f1d3SAlan Somers } 699821f1d3SAlan Somers 709821f1d3SAlan Somers public: 71a90e32deSAlan Somers void expect_chmod(uint64_t ino, mode_t mode) 72a90e32deSAlan Somers { 73a90e32deSAlan Somers EXPECT_CALL(*m_mock, process( 74a90e32deSAlan Somers ResultOf([=](auto in) { 75a90e32deSAlan Somers return (in->header.opcode == FUSE_SETATTR && 76a90e32deSAlan Somers in->header.nodeid == ino && 77a90e32deSAlan Somers in->body.setattr.valid == FATTR_MODE && 78a90e32deSAlan Somers in->body.setattr.mode == mode); 79a90e32deSAlan Somers }, Eq(true)), 80a90e32deSAlan Somers _) 81a90e32deSAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { 82a90e32deSAlan Somers SET_OUT_HEADER_LEN(out, attr); 83a90e32deSAlan Somers out->body.attr.attr.ino = ino; // Must match nodeid 84a90e32deSAlan Somers out->body.attr.attr.mode = S_IFREG | mode; 85a90e32deSAlan Somers out->body.attr.attr_valid = UINT64_MAX; 86a90e32deSAlan Somers }))); 87a90e32deSAlan Somers } 88a90e32deSAlan Somers 89*3fa12789SAlan Somers void expect_create(const char *relpath, uint64_t ino) 90*3fa12789SAlan Somers { 91*3fa12789SAlan Somers EXPECT_CALL(*m_mock, process( 92*3fa12789SAlan Somers ResultOf([=](auto in) { 93*3fa12789SAlan Somers const char *name = (const char*)in->body.bytes + 94*3fa12789SAlan Somers sizeof(fuse_open_in); 95*3fa12789SAlan Somers return (in->header.opcode == FUSE_CREATE && 96*3fa12789SAlan Somers (0 == strcmp(relpath, name))); 97*3fa12789SAlan Somers }, Eq(true)), 98*3fa12789SAlan Somers _) 99*3fa12789SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { 100*3fa12789SAlan Somers SET_OUT_HEADER_LEN(out, create); 101*3fa12789SAlan Somers out->body.create.entry.attr.mode = S_IFREG | 0644; 102*3fa12789SAlan Somers out->body.create.entry.nodeid = ino; 103*3fa12789SAlan Somers out->body.create.entry.entry_valid = UINT64_MAX; 104*3fa12789SAlan Somers out->body.create.entry.attr_valid = UINT64_MAX; 105*3fa12789SAlan Somers }))); 106*3fa12789SAlan Somers } 107*3fa12789SAlan Somers 108ff4fbdf5SAlan Somers void expect_getattr(uint64_t ino, mode_t mode, uint64_t attr_valid, int times, 109474ba6faSAlan Somers uid_t uid = 0, gid_t gid = 0) 1109821f1d3SAlan Somers { 111ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 112ff4fbdf5SAlan Somers ResultOf([=](auto in) { 113ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_GETATTR && 114ff4fbdf5SAlan Somers in->header.nodeid == ino); 115ff4fbdf5SAlan Somers }, Eq(true)), 116ff4fbdf5SAlan Somers _) 117ff4fbdf5SAlan Somers ).Times(times) 118ff4fbdf5SAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) { 119ff4fbdf5SAlan Somers SET_OUT_HEADER_LEN(out, attr); 120ff4fbdf5SAlan Somers out->body.attr.attr.ino = ino; // Must match nodeid 121ff4fbdf5SAlan Somers out->body.attr.attr.mode = mode; 122ff4fbdf5SAlan Somers out->body.attr.attr.size = 0; 123ff4fbdf5SAlan Somers out->body.attr.attr.uid = uid; 124474ba6faSAlan Somers out->body.attr.attr.uid = gid; 125ff4fbdf5SAlan Somers out->body.attr.attr_valid = attr_valid; 126ff4fbdf5SAlan Somers }))); 127ff4fbdf5SAlan Somers } 128ff4fbdf5SAlan Somers 129ff4fbdf5SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, mode_t mode, 130474ba6faSAlan Somers uint64_t attr_valid, uid_t uid = 0, gid_t gid = 0) 131ff4fbdf5SAlan Somers { 132474ba6faSAlan Somers FuseTest::expect_lookup(relpath, ino, mode, 0, 1, attr_valid, uid, gid); 1339821f1d3SAlan Somers } 1349821f1d3SAlan Somers 1359821f1d3SAlan Somers }; 1369821f1d3SAlan Somers 1379821f1d3SAlan Somers class Access: public DefaultPermissions {}; 138474ba6faSAlan Somers class Chown: public DefaultPermissions {}; 139474ba6faSAlan Somers class Chgrp: public DefaultPermissions {}; 140ff4fbdf5SAlan Somers class Lookup: public DefaultPermissions {}; 1419821f1d3SAlan Somers class Open: public DefaultPermissions {}; 142ff4fbdf5SAlan Somers class Setattr: public DefaultPermissions {}; 143ff4fbdf5SAlan Somers class Unlink: public DefaultPermissions {}; 144a90e32deSAlan Somers class Write: public DefaultPermissions {}; 1459821f1d3SAlan Somers 146ff4fbdf5SAlan Somers /* 147ff4fbdf5SAlan Somers * Test permission handling during create, mkdir, mknod, link, symlink, and 148ff4fbdf5SAlan Somers * rename vops (they all share a common path for permission checks in 149ff4fbdf5SAlan Somers * VOP_LOOKUP) 150ff4fbdf5SAlan Somers */ 151*3fa12789SAlan Somers class Create: public DefaultPermissions {}; 152ff4fbdf5SAlan Somers 153ff4fbdf5SAlan Somers class Deleteextattr: public DefaultPermissions { 154ff4fbdf5SAlan Somers public: 155ff4fbdf5SAlan Somers void expect_removexattr() 156ff4fbdf5SAlan Somers { 157ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 158ff4fbdf5SAlan Somers ResultOf([=](auto in) { 159ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_REMOVEXATTR); 160ff4fbdf5SAlan Somers }, Eq(true)), 161ff4fbdf5SAlan Somers _) 162ff4fbdf5SAlan Somers ).WillOnce(Invoke(ReturnErrno(0))); 163ff4fbdf5SAlan Somers } 164ff4fbdf5SAlan Somers }; 165ff4fbdf5SAlan Somers 166ff4fbdf5SAlan Somers class Getextattr: public DefaultPermissions { 167ff4fbdf5SAlan Somers public: 168ff4fbdf5SAlan Somers void expect_getxattr(ProcessMockerT r) 169ff4fbdf5SAlan Somers { 170ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 171ff4fbdf5SAlan Somers ResultOf([=](auto in) { 172ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_GETXATTR); 173ff4fbdf5SAlan Somers }, Eq(true)), 174ff4fbdf5SAlan Somers _) 175ff4fbdf5SAlan Somers ).WillOnce(Invoke(r)); 176ff4fbdf5SAlan Somers } 177ff4fbdf5SAlan Somers }; 178ff4fbdf5SAlan Somers 179ff4fbdf5SAlan Somers class Listextattr: public DefaultPermissions { 180ff4fbdf5SAlan Somers public: 181ff4fbdf5SAlan Somers void expect_listxattr() 182ff4fbdf5SAlan Somers { 183ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 184ff4fbdf5SAlan Somers ResultOf([=](auto in) { 185ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_LISTXATTR); 186ff4fbdf5SAlan Somers }, Eq(true)), 187ff4fbdf5SAlan Somers _) 188ff4fbdf5SAlan Somers ).WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto out) { 189ff4fbdf5SAlan Somers out->body.listxattr.size = 0; 190ff4fbdf5SAlan Somers SET_OUT_HEADER_LEN(out, listxattr); 191ff4fbdf5SAlan Somers }))); 192ff4fbdf5SAlan Somers } 193ff4fbdf5SAlan Somers }; 194ff4fbdf5SAlan Somers 195ff4fbdf5SAlan Somers class Rename: public DefaultPermissions { 196ff4fbdf5SAlan Somers public: 197ff4fbdf5SAlan Somers /* 198ff4fbdf5SAlan Somers * Expect a rename and respond with the given error. Don't both to 199ff4fbdf5SAlan Somers * validate arguments; the tests in rename.cc do that. 200ff4fbdf5SAlan Somers */ 201ff4fbdf5SAlan Somers void expect_rename(int error) 202ff4fbdf5SAlan Somers { 203ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 204ff4fbdf5SAlan Somers ResultOf([=](auto in) { 205ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_RENAME); 206ff4fbdf5SAlan Somers }, Eq(true)), 207ff4fbdf5SAlan Somers _) 208ff4fbdf5SAlan Somers ).WillOnce(Invoke(ReturnErrno(error))); 209ff4fbdf5SAlan Somers } 210ff4fbdf5SAlan Somers }; 211ff4fbdf5SAlan Somers 212ff4fbdf5SAlan Somers class Setextattr: public DefaultPermissions { 213ff4fbdf5SAlan Somers public: 214ff4fbdf5SAlan Somers void expect_setxattr(int error) 215ff4fbdf5SAlan Somers { 216ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 217ff4fbdf5SAlan Somers ResultOf([=](auto in) { 218ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_SETXATTR); 219ff4fbdf5SAlan Somers }, Eq(true)), 220ff4fbdf5SAlan Somers _) 221ff4fbdf5SAlan Somers ).WillOnce(Invoke(ReturnErrno(error))); 222ff4fbdf5SAlan Somers } 223ff4fbdf5SAlan Somers }; 224ff4fbdf5SAlan Somers 2258cfb4431SAlan Somers /* Return a group to which this user does not belong */ 2268cfb4431SAlan Somers static gid_t excluded_group() 2278cfb4431SAlan Somers { 2288cfb4431SAlan Somers int i, ngroups = 64; 2298cfb4431SAlan Somers gid_t newgid, groups[ngroups]; 2308cfb4431SAlan Somers 2318cfb4431SAlan Somers getgrouplist(getlogin(), getegid(), groups, &ngroups); 2328cfb4431SAlan Somers for (newgid = 0; newgid >= 0; newgid++) { 2338cfb4431SAlan Somers bool belongs = false; 2348cfb4431SAlan Somers 2358cfb4431SAlan Somers for (i = 0; i < ngroups; i++) { 2368cfb4431SAlan Somers if (groups[i] == newgid) 2378cfb4431SAlan Somers belongs = true; 2388cfb4431SAlan Somers } 2398cfb4431SAlan Somers if (!belongs) 2408cfb4431SAlan Somers break; 2418cfb4431SAlan Somers } 2428cfb4431SAlan Somers /* newgid is now a group to which the current user does not belong */ 2438cfb4431SAlan Somers return newgid; 2448cfb4431SAlan Somers } 2458cfb4431SAlan Somers 246ff4fbdf5SAlan Somers TEST_F(Access, eacces) 2479821f1d3SAlan Somers { 2489821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2499821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 2509821f1d3SAlan Somers uint64_t ino = 42; 2519821f1d3SAlan Somers mode_t access_mode = X_OK; 2529821f1d3SAlan Somers 253ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 254ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX); 255ff4fbdf5SAlan Somers 256ff4fbdf5SAlan Somers ASSERT_NE(0, access(FULLPATH, access_mode)); 257ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 258ff4fbdf5SAlan Somers } 259ff4fbdf5SAlan Somers 260ff4fbdf5SAlan Somers TEST_F(Access, eacces_no_cached_attrs) 261ff4fbdf5SAlan Somers { 262ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 263ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 264ff4fbdf5SAlan Somers uint64_t ino = 42; 265ff4fbdf5SAlan Somers mode_t access_mode = X_OK; 266ff4fbdf5SAlan Somers 267ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, 0, 1); 268ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0); 269ff4fbdf5SAlan Somers expect_getattr(ino, S_IFREG | 0644, 0, 1); 2709821f1d3SAlan Somers /* 2719821f1d3SAlan Somers * Once default_permissions is properly implemented, there might be 2729821f1d3SAlan Somers * another FUSE_GETATTR or something in here. But there should not be 2739821f1d3SAlan Somers * a FUSE_ACCESS 2749821f1d3SAlan Somers */ 2759821f1d3SAlan Somers 2769821f1d3SAlan Somers ASSERT_NE(0, access(FULLPATH, access_mode)); 2779821f1d3SAlan Somers ASSERT_EQ(EACCES, errno); 2789821f1d3SAlan Somers } 2799821f1d3SAlan Somers 280ff4fbdf5SAlan Somers TEST_F(Access, ok) 2819821f1d3SAlan Somers { 2829821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2839821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 2849821f1d3SAlan Somers uint64_t ino = 42; 2859821f1d3SAlan Somers mode_t access_mode = R_OK; 2869821f1d3SAlan Somers 287ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 288ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX); 2899821f1d3SAlan Somers /* 2909821f1d3SAlan Somers * Once default_permissions is properly implemented, there might be 29191ff3a0dSAlan Somers * another FUSE_GETATTR or something in here. 2929821f1d3SAlan Somers */ 2939821f1d3SAlan Somers 2949821f1d3SAlan Somers ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno); 2959821f1d3SAlan Somers } 2969821f1d3SAlan Somers 297474ba6faSAlan Somers /* Only root may change a file's owner */ 298474ba6faSAlan Somers TEST_F(Chown, eperm) 299474ba6faSAlan Somers { 300474ba6faSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 301474ba6faSAlan Somers const char RELPATH[] = "some_file.txt"; 302474ba6faSAlan Somers const uint64_t ino = 42; 303474ba6faSAlan Somers const mode_t mode = 0755; 304474ba6faSAlan Somers 305474ba6faSAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1, geteuid()); 306474ba6faSAlan Somers expect_lookup(RELPATH, ino, S_IFREG | mode, UINT64_MAX, geteuid()); 307474ba6faSAlan Somers EXPECT_CALL(*m_mock, process( 308474ba6faSAlan Somers ResultOf([](auto in) { 309474ba6faSAlan Somers return (in->header.opcode == FUSE_SETATTR); 310474ba6faSAlan Somers }, Eq(true)), 311474ba6faSAlan Somers _) 312474ba6faSAlan Somers ).Times(0); 313474ba6faSAlan Somers 314474ba6faSAlan Somers EXPECT_NE(0, chown(FULLPATH, 0, -1)); 315474ba6faSAlan Somers EXPECT_EQ(EPERM, errno); 316474ba6faSAlan Somers } 317474ba6faSAlan Somers 318474ba6faSAlan Somers /* non-root users may only chgrp a file to a group they belong to */ 319474ba6faSAlan Somers TEST_F(Chgrp, eperm) 320474ba6faSAlan Somers { 321474ba6faSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 322474ba6faSAlan Somers const char RELPATH[] = "some_file.txt"; 323474ba6faSAlan Somers const uint64_t ino = 42; 324474ba6faSAlan Somers const mode_t mode = 0755; 325474ba6faSAlan Somers uid_t uid; 326474ba6faSAlan Somers gid_t gid, newgid; 327474ba6faSAlan Somers 328474ba6faSAlan Somers uid = geteuid(); 329474ba6faSAlan Somers gid = getegid(); 3308cfb4431SAlan Somers newgid = excluded_group(); 331474ba6faSAlan Somers 332474ba6faSAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1, uid, gid); 333474ba6faSAlan Somers expect_lookup(RELPATH, ino, S_IFREG | mode, UINT64_MAX, uid, gid); 334474ba6faSAlan Somers EXPECT_CALL(*m_mock, process( 335474ba6faSAlan Somers ResultOf([](auto in) { 336474ba6faSAlan Somers return (in->header.opcode == FUSE_SETATTR); 337474ba6faSAlan Somers }, Eq(true)), 338474ba6faSAlan Somers _) 339474ba6faSAlan Somers ).Times(0); 340474ba6faSAlan Somers 341474ba6faSAlan Somers EXPECT_NE(0, chown(FULLPATH, -1, newgid)); 342474ba6faSAlan Somers EXPECT_EQ(EPERM, errno); 343474ba6faSAlan Somers } 344474ba6faSAlan Somers 345474ba6faSAlan Somers TEST_F(Chgrp, ok) 346474ba6faSAlan Somers { 347474ba6faSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 348474ba6faSAlan Somers const char RELPATH[] = "some_file.txt"; 349474ba6faSAlan Somers const uint64_t ino = 42; 350474ba6faSAlan Somers const mode_t mode = 0755; 351474ba6faSAlan Somers uid_t uid; 352474ba6faSAlan Somers gid_t gid, newgid; 353474ba6faSAlan Somers 354474ba6faSAlan Somers uid = geteuid(); 355474ba6faSAlan Somers gid = 0; 356474ba6faSAlan Somers newgid = getegid(); 357474ba6faSAlan Somers 358474ba6faSAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1, uid, gid); 359474ba6faSAlan Somers expect_lookup(RELPATH, ino, S_IFREG | mode, UINT64_MAX, uid, gid); 360474ba6faSAlan Somers EXPECT_CALL(*m_mock, process( 361474ba6faSAlan Somers ResultOf([](auto in) { 362474ba6faSAlan Somers return (in->header.opcode == FUSE_SETATTR); 363474ba6faSAlan Somers }, Eq(true)), 364474ba6faSAlan Somers _) 365474ba6faSAlan Somers ).Times(0); 366474ba6faSAlan Somers EXPECT_CALL(*m_mock, process( 367474ba6faSAlan Somers ResultOf([](auto in) { 368474ba6faSAlan Somers return (in->header.opcode == FUSE_SETATTR && 369474ba6faSAlan Somers in->header.nodeid == ino); 370474ba6faSAlan Somers }, Eq(true)), 371474ba6faSAlan Somers _) 372474ba6faSAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { 373474ba6faSAlan Somers SET_OUT_HEADER_LEN(out, attr); 374474ba6faSAlan Somers out->body.attr.attr.mode = S_IFREG | mode; 375474ba6faSAlan Somers out->body.attr.attr.uid = uid; 376474ba6faSAlan Somers out->body.attr.attr.gid = newgid; 377474ba6faSAlan Somers }))); 378474ba6faSAlan Somers 379474ba6faSAlan Somers EXPECT_EQ(0, chown(FULLPATH, -1, newgid)) << strerror(errno); 380474ba6faSAlan Somers } 381474ba6faSAlan Somers 382ff4fbdf5SAlan Somers TEST_F(Create, ok) 383ff4fbdf5SAlan Somers { 384ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 385ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 386ff4fbdf5SAlan Somers uint64_t ino = 42; 387ff4fbdf5SAlan Somers int fd; 388ff4fbdf5SAlan Somers 389ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1); 390ff4fbdf5SAlan Somers EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); 391ff4fbdf5SAlan Somers expect_create(RELPATH, ino); 392ff4fbdf5SAlan Somers 393ff4fbdf5SAlan Somers fd = open(FULLPATH, O_CREAT | O_EXCL, 0644); 394ff4fbdf5SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 395ff4fbdf5SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 396ff4fbdf5SAlan Somers } 397ff4fbdf5SAlan Somers 398ff4fbdf5SAlan Somers TEST_F(Create, eacces) 399ff4fbdf5SAlan Somers { 400ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 401ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 402ff4fbdf5SAlan Somers 403ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 404ff4fbdf5SAlan Somers EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); 405ff4fbdf5SAlan Somers 406ff4fbdf5SAlan Somers EXPECT_EQ(-1, open(FULLPATH, O_CREAT | O_EXCL, 0644)); 407ff4fbdf5SAlan Somers EXPECT_EQ(EACCES, errno); 408ff4fbdf5SAlan Somers } 409ff4fbdf5SAlan Somers 410ff4fbdf5SAlan Somers TEST_F(Deleteextattr, eacces) 411ff4fbdf5SAlan Somers { 412ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 413ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 414ff4fbdf5SAlan Somers uint64_t ino = 42; 415ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 416ff4fbdf5SAlan Somers 417ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 418ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0); 419ff4fbdf5SAlan Somers 420ff4fbdf5SAlan Somers ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); 421ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 422ff4fbdf5SAlan Somers } 423ff4fbdf5SAlan Somers 424ff4fbdf5SAlan Somers TEST_F(Deleteextattr, ok) 425ff4fbdf5SAlan Somers { 426ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 427ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 428ff4fbdf5SAlan Somers uint64_t ino = 42; 429ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 430ff4fbdf5SAlan Somers 431ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 432ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid()); 433ff4fbdf5SAlan Somers expect_removexattr(); 434ff4fbdf5SAlan Somers 435ff4fbdf5SAlan Somers ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo")) 436ff4fbdf5SAlan Somers << strerror(errno); 437ff4fbdf5SAlan Somers } 438ff4fbdf5SAlan Somers 439ff4fbdf5SAlan Somers /* Delete system attributes requires superuser privilege */ 440ff4fbdf5SAlan Somers TEST_F(Deleteextattr, system) 441ff4fbdf5SAlan Somers { 442ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 443ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 444ff4fbdf5SAlan Somers uint64_t ino = 42; 445ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_SYSTEM; 446ff4fbdf5SAlan Somers 447ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 448ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, geteuid()); 449ff4fbdf5SAlan Somers 450ff4fbdf5SAlan Somers ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); 451ff4fbdf5SAlan Somers ASSERT_EQ(EPERM, errno); 452ff4fbdf5SAlan Somers } 453ff4fbdf5SAlan Somers 454ff4fbdf5SAlan Somers /* Deleting user attributes merely requires WRITE privilege */ 455ff4fbdf5SAlan Somers TEST_F(Deleteextattr, user) 456ff4fbdf5SAlan Somers { 457ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 458ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 459ff4fbdf5SAlan Somers uint64_t ino = 42; 460ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 461ff4fbdf5SAlan Somers 462ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 463ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, 0); 464ff4fbdf5SAlan Somers expect_removexattr(); 465ff4fbdf5SAlan Somers 466ff4fbdf5SAlan Somers ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo")) 467ff4fbdf5SAlan Somers << strerror(errno); 468ff4fbdf5SAlan Somers } 469ff4fbdf5SAlan Somers 470ff4fbdf5SAlan Somers TEST_F(Getextattr, eacces) 471ff4fbdf5SAlan Somers { 472ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 473ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 474ff4fbdf5SAlan Somers uint64_t ino = 42; 475ff4fbdf5SAlan Somers char data[80]; 476ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 477ff4fbdf5SAlan Somers 478ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 479ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0600, UINT64_MAX, 0); 480ff4fbdf5SAlan Somers 481ff4fbdf5SAlan Somers ASSERT_EQ(-1, 482ff4fbdf5SAlan Somers extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data))); 483ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 484ff4fbdf5SAlan Somers } 485ff4fbdf5SAlan Somers 486ff4fbdf5SAlan Somers TEST_F(Getextattr, ok) 487ff4fbdf5SAlan Somers { 488ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 489ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 490ff4fbdf5SAlan Somers uint64_t ino = 42; 491ff4fbdf5SAlan Somers char data[80]; 492ff4fbdf5SAlan Somers const char value[] = "whatever"; 493ff4fbdf5SAlan Somers ssize_t value_len = strlen(value) + 1; 494ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 495ff4fbdf5SAlan Somers ssize_t r; 496ff4fbdf5SAlan Somers 497ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 498ff4fbdf5SAlan Somers /* Getting user attributes only requires read access */ 499ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0444, UINT64_MAX, 0); 500ff4fbdf5SAlan Somers expect_getxattr( 501ff4fbdf5SAlan Somers ReturnImmediate([&](auto in __unused, auto out) { 502ff4fbdf5SAlan Somers memcpy((void*)out->body.bytes, value, value_len); 503ff4fbdf5SAlan Somers out->header.len = sizeof(out->header) + value_len; 504ff4fbdf5SAlan Somers }) 505ff4fbdf5SAlan Somers ); 506ff4fbdf5SAlan Somers 507ff4fbdf5SAlan Somers r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); 508ff4fbdf5SAlan Somers ASSERT_EQ(value_len, r) << strerror(errno); 509ff4fbdf5SAlan Somers EXPECT_STREQ(value, data); 510ff4fbdf5SAlan Somers } 511ff4fbdf5SAlan Somers 512ff4fbdf5SAlan Somers /* Getting system attributes requires superuser privileges */ 513ff4fbdf5SAlan Somers TEST_F(Getextattr, system) 514ff4fbdf5SAlan Somers { 515ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 516ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 517ff4fbdf5SAlan Somers uint64_t ino = 42; 518ff4fbdf5SAlan Somers char data[80]; 519ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_SYSTEM; 520ff4fbdf5SAlan Somers 521ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 522ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, geteuid()); 523ff4fbdf5SAlan Somers 524ff4fbdf5SAlan Somers ASSERT_EQ(-1, 525ff4fbdf5SAlan Somers extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data))); 526ff4fbdf5SAlan Somers ASSERT_EQ(EPERM, errno); 527ff4fbdf5SAlan Somers } 528ff4fbdf5SAlan Somers 529ff4fbdf5SAlan Somers TEST_F(Listextattr, eacces) 530ff4fbdf5SAlan Somers { 531ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 532ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 533ff4fbdf5SAlan Somers uint64_t ino = 42; 534ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 535ff4fbdf5SAlan Somers 536ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1); 537ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0600, UINT64_MAX, 0); 538ff4fbdf5SAlan Somers 539ff4fbdf5SAlan Somers ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); 540ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 541ff4fbdf5SAlan Somers } 542ff4fbdf5SAlan Somers 543ff4fbdf5SAlan Somers TEST_F(Listextattr, ok) 544ff4fbdf5SAlan Somers { 545ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 546ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 547ff4fbdf5SAlan Somers uint64_t ino = 42; 548ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 549ff4fbdf5SAlan Somers 550ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1); 551ff4fbdf5SAlan Somers /* Listing user extended attributes merely requires read access */ 552ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0); 553ff4fbdf5SAlan Somers expect_listxattr(); 554ff4fbdf5SAlan Somers 555ff4fbdf5SAlan Somers ASSERT_EQ(0, extattr_list_file(FULLPATH, ns, NULL, 0)) 556ff4fbdf5SAlan Somers << strerror(errno); 557ff4fbdf5SAlan Somers } 558ff4fbdf5SAlan Somers 559ff4fbdf5SAlan Somers /* Listing system xattrs requires superuser privileges */ 560ff4fbdf5SAlan Somers TEST_F(Listextattr, system) 561ff4fbdf5SAlan Somers { 562ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 563ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 564ff4fbdf5SAlan Somers uint64_t ino = 42; 565ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_SYSTEM; 566ff4fbdf5SAlan Somers 567ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1); 568ff4fbdf5SAlan Somers /* Listing user extended attributes merely requires read access */ 569ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid()); 570ff4fbdf5SAlan Somers 571ff4fbdf5SAlan Somers ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); 572ff4fbdf5SAlan Somers ASSERT_EQ(EPERM, errno); 573ff4fbdf5SAlan Somers } 574ff4fbdf5SAlan Somers 575ff4fbdf5SAlan Somers /* A component of the search path lacks execute permissions */ 576ff4fbdf5SAlan Somers TEST_F(Lookup, eacces) 577ff4fbdf5SAlan Somers { 578ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_dir/some_file.txt"; 579ff4fbdf5SAlan Somers const char RELDIRPATH[] = "some_dir"; 580ff4fbdf5SAlan Somers uint64_t dir_ino = 42; 581ff4fbdf5SAlan Somers 582ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 583ff4fbdf5SAlan Somers expect_lookup(RELDIRPATH, dir_ino, S_IFDIR | 0700, UINT64_MAX, 0); 584ff4fbdf5SAlan Somers 585ff4fbdf5SAlan Somers EXPECT_EQ(-1, access(FULLPATH, F_OK)); 586ff4fbdf5SAlan Somers EXPECT_EQ(EACCES, errno); 587ff4fbdf5SAlan Somers } 588ff4fbdf5SAlan Somers 589ff4fbdf5SAlan Somers TEST_F(Open, eacces) 590ff4fbdf5SAlan Somers { 591ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 592ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 593ff4fbdf5SAlan Somers uint64_t ino = 42; 594ff4fbdf5SAlan Somers 595ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 596ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX); 597ff4fbdf5SAlan Somers 598ff4fbdf5SAlan Somers EXPECT_NE(0, open(FULLPATH, O_RDWR)); 599ff4fbdf5SAlan Somers EXPECT_EQ(EACCES, errno); 600ff4fbdf5SAlan Somers } 601ff4fbdf5SAlan Somers 6029821f1d3SAlan Somers TEST_F(Open, ok) 6039821f1d3SAlan Somers { 6049821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 6059821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 6069821f1d3SAlan Somers uint64_t ino = 42; 6079821f1d3SAlan Somers int fd; 6089821f1d3SAlan Somers 609ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 610ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX); 6119821f1d3SAlan Somers expect_open(ino, 0, 1); 6129821f1d3SAlan Somers 6139821f1d3SAlan Somers fd = open(FULLPATH, O_RDONLY); 6149821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 6159821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 6169821f1d3SAlan Somers } 6179821f1d3SAlan Somers 618ff4fbdf5SAlan Somers TEST_F(Rename, eacces_on_srcdir) 619ff4fbdf5SAlan Somers { 620ff4fbdf5SAlan Somers const char FULLDST[] = "mountpoint/d/dst"; 621ff4fbdf5SAlan Somers const char RELDST[] = "d/dst"; 622ff4fbdf5SAlan Somers const char FULLSRC[] = "mountpoint/src"; 623ff4fbdf5SAlan Somers const char RELSRC[] = "src"; 624ff4fbdf5SAlan Somers uint64_t ino = 42; 625ff4fbdf5SAlan Somers 626ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1, 0); 627ff4fbdf5SAlan Somers expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX); 628ff4fbdf5SAlan Somers EXPECT_LOOKUP(1, RELDST) 629ff4fbdf5SAlan Somers .Times(AnyNumber()) 630ff4fbdf5SAlan Somers .WillRepeatedly(Invoke(ReturnErrno(ENOENT))); 631ff4fbdf5SAlan Somers 632ff4fbdf5SAlan Somers ASSERT_EQ(-1, rename(FULLSRC, FULLDST)); 633ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 634ff4fbdf5SAlan Somers } 635ff4fbdf5SAlan Somers 636ff4fbdf5SAlan Somers TEST_F(Rename, eacces_on_dstdir_for_creating) 637ff4fbdf5SAlan Somers { 638ff4fbdf5SAlan Somers const char FULLDST[] = "mountpoint/d/dst"; 639ff4fbdf5SAlan Somers const char RELDSTDIR[] = "d"; 640ff4fbdf5SAlan Somers const char RELDST[] = "dst"; 641ff4fbdf5SAlan Somers const char FULLSRC[] = "mountpoint/src"; 642ff4fbdf5SAlan Somers const char RELSRC[] = "src"; 643ff4fbdf5SAlan Somers uint64_t src_ino = 42; 644ff4fbdf5SAlan Somers uint64_t dstdir_ino = 43; 645ff4fbdf5SAlan Somers 646ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, 0); 647ff4fbdf5SAlan Somers expect_lookup(RELSRC, src_ino, S_IFREG | 0644, UINT64_MAX); 648ff4fbdf5SAlan Somers expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 0755, UINT64_MAX); 649ff4fbdf5SAlan Somers EXPECT_LOOKUP(dstdir_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT))); 650ff4fbdf5SAlan Somers 651ff4fbdf5SAlan Somers ASSERT_EQ(-1, rename(FULLSRC, FULLDST)); 652ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 653ff4fbdf5SAlan Somers } 654ff4fbdf5SAlan Somers 655ff4fbdf5SAlan Somers TEST_F(Rename, eacces_on_dstdir_for_removing) 656ff4fbdf5SAlan Somers { 657ff4fbdf5SAlan Somers const char FULLDST[] = "mountpoint/d/dst"; 658ff4fbdf5SAlan Somers const char RELDSTDIR[] = "d"; 659ff4fbdf5SAlan Somers const char RELDST[] = "dst"; 660ff4fbdf5SAlan Somers const char FULLSRC[] = "mountpoint/src"; 661ff4fbdf5SAlan Somers const char RELSRC[] = "src"; 662ff4fbdf5SAlan Somers uint64_t src_ino = 42; 663ff4fbdf5SAlan Somers uint64_t dstdir_ino = 43; 664ff4fbdf5SAlan Somers 665ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, 0); 666ff4fbdf5SAlan Somers expect_lookup(RELSRC, src_ino, S_IFREG | 0644, UINT64_MAX); 667ff4fbdf5SAlan Somers expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 0755, UINT64_MAX); 668ff4fbdf5SAlan Somers EXPECT_LOOKUP(dstdir_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT))); 669ff4fbdf5SAlan Somers 670ff4fbdf5SAlan Somers ASSERT_EQ(-1, rename(FULLSRC, FULLDST)); 671ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 672ff4fbdf5SAlan Somers } 673ff4fbdf5SAlan Somers 6746124fd71SAlan Somers TEST_F(Rename, eperm_on_sticky_srcdir) 675ff4fbdf5SAlan Somers { 676ff4fbdf5SAlan Somers const char FULLDST[] = "mountpoint/d/dst"; 677ff4fbdf5SAlan Somers const char FULLSRC[] = "mountpoint/src"; 678ff4fbdf5SAlan Somers const char RELSRC[] = "src"; 679ff4fbdf5SAlan Somers uint64_t ino = 42; 680ff4fbdf5SAlan Somers 681ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 01777, UINT64_MAX, 1, 0); 682ff4fbdf5SAlan Somers expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX); 683ff4fbdf5SAlan Somers 684ff4fbdf5SAlan Somers ASSERT_EQ(-1, rename(FULLSRC, FULLDST)); 685ff4fbdf5SAlan Somers ASSERT_EQ(EPERM, errno); 686ff4fbdf5SAlan Somers } 687ff4fbdf5SAlan Somers 6886124fd71SAlan Somers TEST_F(Rename, eperm_on_sticky_dstdir) 689ff4fbdf5SAlan Somers { 690ff4fbdf5SAlan Somers const char FULLDST[] = "mountpoint/d/dst"; 691ff4fbdf5SAlan Somers const char RELDSTDIR[] = "d"; 6926124fd71SAlan Somers const char RELDST[] = "dst"; 693ff4fbdf5SAlan Somers const char FULLSRC[] = "mountpoint/src"; 694ff4fbdf5SAlan Somers const char RELSRC[] = "src"; 695ff4fbdf5SAlan Somers uint64_t src_ino = 42; 696ff4fbdf5SAlan Somers uint64_t dstdir_ino = 43; 697ff4fbdf5SAlan Somers uint64_t dst_ino = 44; 698ff4fbdf5SAlan Somers 699ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, 0); 700ff4fbdf5SAlan Somers expect_lookup(RELSRC, src_ino, S_IFREG | 0644, UINT64_MAX); 701ff4fbdf5SAlan Somers expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 01777, UINT64_MAX); 7026124fd71SAlan Somers EXPECT_LOOKUP(dstdir_ino, RELDST) 7036124fd71SAlan Somers .WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { 7046124fd71SAlan Somers SET_OUT_HEADER_LEN(out, entry); 7056124fd71SAlan Somers out->body.entry.attr.mode = S_IFREG | 0644; 7066124fd71SAlan Somers out->body.entry.nodeid = dst_ino; 7076124fd71SAlan Somers out->body.entry.attr_valid = UINT64_MAX; 7086124fd71SAlan Somers out->body.entry.entry_valid = UINT64_MAX; 7096124fd71SAlan Somers out->body.entry.attr.uid = 0; 7106124fd71SAlan Somers }))); 711ff4fbdf5SAlan Somers 712ff4fbdf5SAlan Somers ASSERT_EQ(-1, rename(FULLSRC, FULLDST)); 713ff4fbdf5SAlan Somers ASSERT_EQ(EPERM, errno); 714ff4fbdf5SAlan Somers } 715ff4fbdf5SAlan Somers 716ff4fbdf5SAlan Somers /* Successfully rename a file, overwriting the destination */ 717ff4fbdf5SAlan Somers TEST_F(Rename, ok) 718ff4fbdf5SAlan Somers { 719ff4fbdf5SAlan Somers const char FULLDST[] = "mountpoint/dst"; 720ff4fbdf5SAlan Somers const char RELDST[] = "dst"; 721ff4fbdf5SAlan Somers const char FULLSRC[] = "mountpoint/src"; 722ff4fbdf5SAlan Somers const char RELSRC[] = "src"; 723ff4fbdf5SAlan Somers // The inode of the already-existing destination file 724ff4fbdf5SAlan Somers uint64_t dst_ino = 2; 725ff4fbdf5SAlan Somers uint64_t ino = 42; 726ff4fbdf5SAlan Somers 727ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, geteuid()); 728ff4fbdf5SAlan Somers expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX); 729ff4fbdf5SAlan Somers expect_lookup(RELDST, dst_ino, S_IFREG | 0644, UINT64_MAX); 730ff4fbdf5SAlan Somers expect_rename(0); 731ff4fbdf5SAlan Somers 732ff4fbdf5SAlan Somers ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno); 733ff4fbdf5SAlan Somers } 734ff4fbdf5SAlan Somers 735ff4fbdf5SAlan Somers TEST_F(Rename, ok_to_remove_src_because_of_stickiness) 736ff4fbdf5SAlan Somers { 737ff4fbdf5SAlan Somers const char FULLDST[] = "mountpoint/dst"; 738ff4fbdf5SAlan Somers const char RELDST[] = "dst"; 739ff4fbdf5SAlan Somers const char FULLSRC[] = "mountpoint/src"; 740ff4fbdf5SAlan Somers const char RELSRC[] = "src"; 741ff4fbdf5SAlan Somers uint64_t ino = 42; 742ff4fbdf5SAlan Somers 743ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 01777, UINT64_MAX, 1, 0); 744ff4fbdf5SAlan Somers expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX, geteuid()); 745ff4fbdf5SAlan Somers EXPECT_LOOKUP(1, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT))); 746ff4fbdf5SAlan Somers expect_rename(0); 747ff4fbdf5SAlan Somers 748ff4fbdf5SAlan Somers ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno); 749ff4fbdf5SAlan Somers } 750ff4fbdf5SAlan Somers 751ff4fbdf5SAlan Somers TEST_F(Setattr, ok) 752ff4fbdf5SAlan Somers { 753ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 754ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 755ff4fbdf5SAlan Somers const uint64_t ino = 42; 756ff4fbdf5SAlan Somers const mode_t oldmode = 0755; 757ff4fbdf5SAlan Somers const mode_t newmode = 0644; 758ff4fbdf5SAlan Somers 759ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 760ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, geteuid()); 761ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 762ff4fbdf5SAlan Somers ResultOf([](auto in) { 763ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_SETATTR && 764ff4fbdf5SAlan Somers in->header.nodeid == ino && 765ff4fbdf5SAlan Somers in->body.setattr.mode == newmode); 766ff4fbdf5SAlan Somers }, Eq(true)), 767ff4fbdf5SAlan Somers _) 768ff4fbdf5SAlan Somers ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto out) { 769ff4fbdf5SAlan Somers SET_OUT_HEADER_LEN(out, attr); 770ff4fbdf5SAlan Somers out->body.attr.attr.mode = S_IFREG | newmode; 771ff4fbdf5SAlan Somers }))); 772ff4fbdf5SAlan Somers 773ff4fbdf5SAlan Somers EXPECT_EQ(0, chmod(FULLPATH, newmode)) << strerror(errno); 774ff4fbdf5SAlan Somers } 775ff4fbdf5SAlan Somers 776ff4fbdf5SAlan Somers TEST_F(Setattr, eacces) 777ff4fbdf5SAlan Somers { 778ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 779ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 780ff4fbdf5SAlan Somers const uint64_t ino = 42; 781ff4fbdf5SAlan Somers const mode_t oldmode = 0755; 782ff4fbdf5SAlan Somers const mode_t newmode = 0644; 783ff4fbdf5SAlan Somers 784ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 785ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, 0); 786ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 787ff4fbdf5SAlan Somers ResultOf([](auto in) { 788ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_SETATTR); 789ff4fbdf5SAlan Somers }, Eq(true)), 790ff4fbdf5SAlan Somers _) 791ff4fbdf5SAlan Somers ).Times(0); 792ff4fbdf5SAlan Somers 793ff4fbdf5SAlan Somers EXPECT_NE(0, chmod(FULLPATH, newmode)); 794ff4fbdf5SAlan Somers EXPECT_EQ(EPERM, errno); 795ff4fbdf5SAlan Somers } 796ff4fbdf5SAlan Somers 7978cfb4431SAlan Somers /* 798*3fa12789SAlan Somers * ftruncate() of a file without writable permissions should succeed as long as 799*3fa12789SAlan Somers * the file descriptor is writable. This is important when combined with 800*3fa12789SAlan Somers * O_CREAT 801*3fa12789SAlan Somers */ 802*3fa12789SAlan Somers TEST_F(Setattr, ftruncate_of_newly_created_file) 803*3fa12789SAlan Somers { 804*3fa12789SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 805*3fa12789SAlan Somers const char RELPATH[] = "some_file.txt"; 806*3fa12789SAlan Somers const uint64_t ino = 42; 807*3fa12789SAlan Somers const mode_t mode = 0000; 808*3fa12789SAlan Somers int fd; 809*3fa12789SAlan Somers 810*3fa12789SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1); 811*3fa12789SAlan Somers EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); 812*3fa12789SAlan Somers expect_create(RELPATH, ino); 813*3fa12789SAlan Somers EXPECT_CALL(*m_mock, process( 814*3fa12789SAlan Somers ResultOf([](auto in) { 815*3fa12789SAlan Somers return (in->header.opcode == FUSE_SETATTR && 816*3fa12789SAlan Somers in->header.nodeid == ino && 817*3fa12789SAlan Somers (in->body.setattr.valid & FATTR_SIZE)); 818*3fa12789SAlan Somers }, Eq(true)), 819*3fa12789SAlan Somers _) 820*3fa12789SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { 821*3fa12789SAlan Somers SET_OUT_HEADER_LEN(out, attr); 822*3fa12789SAlan Somers out->body.attr.attr.ino = ino; 823*3fa12789SAlan Somers out->body.attr.attr.mode = S_IFREG | mode; 824*3fa12789SAlan Somers out->body.attr.attr_valid = UINT64_MAX; 825*3fa12789SAlan Somers }))); 826*3fa12789SAlan Somers 827*3fa12789SAlan Somers fd = open(FULLPATH, O_CREAT | O_RDWR, 0); 828*3fa12789SAlan Somers ASSERT_LE(0, fd) << strerror(errno); 829*3fa12789SAlan Somers ASSERT_EQ(0, ftruncate(fd, 100)) << strerror(errno); 830*3fa12789SAlan Somers /* Deliberately leak fd */ 831*3fa12789SAlan Somers } 832*3fa12789SAlan Somers 833*3fa12789SAlan Somers /* 8348cfb4431SAlan Somers * Setting the sgid bit should fail for an unprivileged user who doesn't belong 8358cfb4431SAlan Somers * to the file's group 8368cfb4431SAlan Somers */ 8378cfb4431SAlan Somers TEST_F(Setattr, sgid_by_non_group_member) 8388cfb4431SAlan Somers { 8398cfb4431SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 8408cfb4431SAlan Somers const char RELPATH[] = "some_file.txt"; 8418cfb4431SAlan Somers const uint64_t ino = 42; 8428cfb4431SAlan Somers const mode_t oldmode = 0755; 8438cfb4431SAlan Somers const mode_t newmode = 02755; 8448cfb4431SAlan Somers uid_t uid = geteuid(); 8458cfb4431SAlan Somers gid_t gid = excluded_group(); 8468cfb4431SAlan Somers 8478cfb4431SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 8488cfb4431SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, uid, gid); 8498cfb4431SAlan Somers EXPECT_CALL(*m_mock, process( 8508cfb4431SAlan Somers ResultOf([](auto in) { 8518cfb4431SAlan Somers return (in->header.opcode == FUSE_SETATTR); 8528cfb4431SAlan Somers }, Eq(true)), 8538cfb4431SAlan Somers _) 8548cfb4431SAlan Somers ).Times(0); 8558cfb4431SAlan Somers 8568cfb4431SAlan Somers EXPECT_NE(0, chmod(FULLPATH, newmode)); 8578cfb4431SAlan Somers EXPECT_EQ(EPERM, errno); 8588cfb4431SAlan Somers } 8598cfb4431SAlan Somers 860e5ff3a7eSAlan Somers /* Only the superuser may set the sticky bit on a non-directory */ 861e5ff3a7eSAlan Somers TEST_F(Setattr, sticky_regular_file) 862e5ff3a7eSAlan Somers { 863e5ff3a7eSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 864e5ff3a7eSAlan Somers const char RELPATH[] = "some_file.txt"; 865e5ff3a7eSAlan Somers const uint64_t ino = 42; 866e5ff3a7eSAlan Somers const mode_t oldmode = 0644; 867e5ff3a7eSAlan Somers const mode_t newmode = 01644; 868e5ff3a7eSAlan Somers 869e5ff3a7eSAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 870e5ff3a7eSAlan Somers expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, geteuid()); 871e5ff3a7eSAlan Somers EXPECT_CALL(*m_mock, process( 872e5ff3a7eSAlan Somers ResultOf([](auto in) { 873e5ff3a7eSAlan Somers return (in->header.opcode == FUSE_SETATTR); 874e5ff3a7eSAlan Somers }, Eq(true)), 875e5ff3a7eSAlan Somers _) 876e5ff3a7eSAlan Somers ).Times(0); 877e5ff3a7eSAlan Somers 878e5ff3a7eSAlan Somers EXPECT_NE(0, chmod(FULLPATH, newmode)); 879e5ff3a7eSAlan Somers EXPECT_EQ(EFTYPE, errno); 880e5ff3a7eSAlan Somers } 881e5ff3a7eSAlan Somers 882ff4fbdf5SAlan Somers TEST_F(Setextattr, ok) 883ff4fbdf5SAlan Somers { 884ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 885ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 886ff4fbdf5SAlan Somers uint64_t ino = 42; 887ff4fbdf5SAlan Somers const char value[] = "whatever"; 888ff4fbdf5SAlan Somers ssize_t value_len = strlen(value) + 1; 889ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 890ff4fbdf5SAlan Somers ssize_t r; 891ff4fbdf5SAlan Somers 892ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 893ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid()); 894ff4fbdf5SAlan Somers expect_setxattr(0); 895ff4fbdf5SAlan Somers 896ff4fbdf5SAlan Somers r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); 897ff4fbdf5SAlan Somers ASSERT_EQ(value_len, r) << strerror(errno); 898ff4fbdf5SAlan Somers } 899ff4fbdf5SAlan Somers 900ff4fbdf5SAlan Somers TEST_F(Setextattr, eacces) 901ff4fbdf5SAlan Somers { 902ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 903ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 904ff4fbdf5SAlan Somers uint64_t ino = 42; 905ff4fbdf5SAlan Somers const char value[] = "whatever"; 906ff4fbdf5SAlan Somers ssize_t value_len = strlen(value) + 1; 907ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 908ff4fbdf5SAlan Somers 909ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 910ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0); 911ff4fbdf5SAlan Somers 912ff4fbdf5SAlan Somers ASSERT_EQ(-1, 913ff4fbdf5SAlan Somers extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len)); 914ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 915ff4fbdf5SAlan Somers } 916ff4fbdf5SAlan Somers 917ff4fbdf5SAlan Somers // Setting system attributes requires superuser privileges 918ff4fbdf5SAlan Somers TEST_F(Setextattr, system) 919ff4fbdf5SAlan Somers { 920ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 921ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 922ff4fbdf5SAlan Somers uint64_t ino = 42; 923ff4fbdf5SAlan Somers const char value[] = "whatever"; 924ff4fbdf5SAlan Somers ssize_t value_len = strlen(value) + 1; 925ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_SYSTEM; 926ff4fbdf5SAlan Somers 927ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 928ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, geteuid()); 929ff4fbdf5SAlan Somers 930ff4fbdf5SAlan Somers ASSERT_EQ(-1, 931ff4fbdf5SAlan Somers extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len)); 932ff4fbdf5SAlan Somers ASSERT_EQ(EPERM, errno); 933ff4fbdf5SAlan Somers } 934ff4fbdf5SAlan Somers 935ff4fbdf5SAlan Somers // Setting user attributes merely requires write privileges 936ff4fbdf5SAlan Somers TEST_F(Setextattr, user) 937ff4fbdf5SAlan Somers { 938ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 939ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 940ff4fbdf5SAlan Somers uint64_t ino = 42; 941ff4fbdf5SAlan Somers const char value[] = "whatever"; 942ff4fbdf5SAlan Somers ssize_t value_len = strlen(value) + 1; 943ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 944ff4fbdf5SAlan Somers ssize_t r; 945ff4fbdf5SAlan Somers 946ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 947ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, 0); 948ff4fbdf5SAlan Somers expect_setxattr(0); 949ff4fbdf5SAlan Somers 950ff4fbdf5SAlan Somers r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); 951ff4fbdf5SAlan Somers ASSERT_EQ(value_len, r) << strerror(errno); 952ff4fbdf5SAlan Somers } 953ff4fbdf5SAlan Somers 954ff4fbdf5SAlan Somers TEST_F(Unlink, ok) 9559821f1d3SAlan Somers { 9569821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 9579821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 9589821f1d3SAlan Somers uint64_t ino = 42; 9599821f1d3SAlan Somers 960ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1); 961ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid()); 962ff4fbdf5SAlan Somers expect_unlink(1, RELPATH, 0); 9639821f1d3SAlan Somers 964ff4fbdf5SAlan Somers ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); 965ff4fbdf5SAlan Somers } 966ff4fbdf5SAlan Somers 9676124fd71SAlan Somers /* 9686124fd71SAlan Somers * Ensure that a cached name doesn't cause unlink to bypass permission checks 9696124fd71SAlan Somers * in VOP_LOOKUP. 9706124fd71SAlan Somers * 9716124fd71SAlan Somers * This test should pass because lookup(9) purges the namecache entry by doing 9726124fd71SAlan Somers * a vfs_cache_lookup with ~MAKEENTRY when nameiop == DELETE. 9736124fd71SAlan Somers */ 9746124fd71SAlan Somers TEST_F(Unlink, cached_unwritable_directory) 9756124fd71SAlan Somers { 9766124fd71SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 9776124fd71SAlan Somers const char RELPATH[] = "some_file.txt"; 9786124fd71SAlan Somers uint64_t ino = 42; 9796124fd71SAlan Somers 9806124fd71SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 9816124fd71SAlan Somers EXPECT_LOOKUP(1, RELPATH) 9826124fd71SAlan Somers .Times(AnyNumber()) 9836124fd71SAlan Somers .WillRepeatedly(Invoke( 9846124fd71SAlan Somers ReturnImmediate([=](auto i __unused, auto out) { 9856124fd71SAlan Somers SET_OUT_HEADER_LEN(out, entry); 9866124fd71SAlan Somers out->body.entry.attr.mode = S_IFREG | 0644; 9876124fd71SAlan Somers out->body.entry.nodeid = ino; 9886124fd71SAlan Somers out->body.entry.entry_valid = UINT64_MAX; 9896124fd71SAlan Somers })) 9906124fd71SAlan Somers ); 9916124fd71SAlan Somers 9926124fd71SAlan Somers /* Fill name cache */ 9936124fd71SAlan Somers ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno); 9946124fd71SAlan Somers /* Despite cached name , unlink should fail */ 9956124fd71SAlan Somers ASSERT_EQ(-1, unlink(FULLPATH)); 9966124fd71SAlan Somers ASSERT_EQ(EACCES, errno); 9976124fd71SAlan Somers } 9986124fd71SAlan Somers 999ff4fbdf5SAlan Somers TEST_F(Unlink, unwritable_directory) 1000ff4fbdf5SAlan Somers { 1001ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1002ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 1003ff4fbdf5SAlan Somers uint64_t ino = 42; 1004ff4fbdf5SAlan Somers 1005ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 1006ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid()); 1007ff4fbdf5SAlan Somers 1008ff4fbdf5SAlan Somers ASSERT_EQ(-1, unlink(FULLPATH)); 1009ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 1010ff4fbdf5SAlan Somers } 1011ff4fbdf5SAlan Somers 10126124fd71SAlan Somers TEST_F(Unlink, sticky_directory) 1013ff4fbdf5SAlan Somers { 1014ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1015ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 1016ff4fbdf5SAlan Somers uint64_t ino = 42; 1017ff4fbdf5SAlan Somers 1018ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 01777, UINT64_MAX, 1); 1019ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0); 1020ff4fbdf5SAlan Somers 1021ff4fbdf5SAlan Somers ASSERT_EQ(-1, unlink(FULLPATH)); 1022ff4fbdf5SAlan Somers ASSERT_EQ(EPERM, errno); 10239821f1d3SAlan Somers } 1024a90e32deSAlan Somers 1025a90e32deSAlan Somers /* A write by a non-owner should clear a file's SUID bit */ 1026a90e32deSAlan Somers TEST_F(Write, clear_suid) 1027a90e32deSAlan Somers { 1028a90e32deSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1029a90e32deSAlan Somers const char RELPATH[] = "some_file.txt"; 1030a90e32deSAlan Somers struct stat sb; 1031a90e32deSAlan Somers uint64_t ino = 42; 1032a90e32deSAlan Somers mode_t oldmode = 04777; 1033a90e32deSAlan Somers mode_t newmode = 0777; 1034a90e32deSAlan Somers char wbuf[1] = {'x'}; 1035a90e32deSAlan Somers int fd; 1036a90e32deSAlan Somers 1037a90e32deSAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 1038a90e32deSAlan Somers expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX); 1039a90e32deSAlan Somers expect_open(ino, 0, 1); 1040a90e32deSAlan Somers expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, wbuf); 1041a90e32deSAlan Somers expect_chmod(ino, newmode); 1042a90e32deSAlan Somers 1043a90e32deSAlan Somers fd = open(FULLPATH, O_WRONLY); 1044a90e32deSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1045a90e32deSAlan Somers ASSERT_EQ(1, write(fd, wbuf, sizeof(wbuf))) << strerror(errno); 1046a90e32deSAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 1047a90e32deSAlan Somers EXPECT_EQ(S_IFREG | newmode, sb.st_mode); 1048a90e32deSAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 1049a90e32deSAlan Somers } 1050a90e32deSAlan Somers 1051a90e32deSAlan Somers /* A write by a non-owner should clear a file's SGID bit */ 1052a90e32deSAlan Somers TEST_F(Write, clear_sgid) 1053a90e32deSAlan Somers { 1054a90e32deSAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 1055a90e32deSAlan Somers const char RELPATH[] = "some_file.txt"; 1056a90e32deSAlan Somers struct stat sb; 1057a90e32deSAlan Somers uint64_t ino = 42; 1058a90e32deSAlan Somers mode_t oldmode = 02777; 1059a90e32deSAlan Somers mode_t newmode = 0777; 1060a90e32deSAlan Somers char wbuf[1] = {'x'}; 1061a90e32deSAlan Somers int fd; 1062a90e32deSAlan Somers 1063a90e32deSAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 1064a90e32deSAlan Somers expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX); 1065a90e32deSAlan Somers expect_open(ino, 0, 1); 1066a90e32deSAlan Somers expect_write(ino, 0, sizeof(wbuf), sizeof(wbuf), 0, wbuf); 1067a90e32deSAlan Somers expect_chmod(ino, newmode); 1068a90e32deSAlan Somers 1069a90e32deSAlan Somers fd = open(FULLPATH, O_WRONLY); 1070a90e32deSAlan Somers ASSERT_LE(0, fd) << strerror(errno); 1071a90e32deSAlan Somers ASSERT_EQ(1, write(fd, wbuf, sizeof(wbuf))) << strerror(errno); 1072a90e32deSAlan Somers ASSERT_EQ(0, fstat(fd, &sb)) << strerror(errno); 1073a90e32deSAlan Somers EXPECT_EQ(S_IFREG | newmode, sb.st_mode); 1074a90e32deSAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 1075a90e32deSAlan Somers } 1076