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" { 37*ff4fbdf5SAlan Somers #include <sys/types.h> 38*ff4fbdf5SAlan Somers #include <sys/extattr.h> 39*ff4fbdf5SAlan 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() { 52*ff4fbdf5SAlan Somers m_default_permissions = true; 539821f1d3SAlan Somers FuseTest::SetUp(); 54*ff4fbdf5SAlan Somers if (HasFatalFailure() || IsSkipped()) 55*ff4fbdf5SAlan Somers return; 569821f1d3SAlan Somers 579821f1d3SAlan Somers if (geteuid() == 0) { 589821f1d3SAlan Somers GTEST_SKIP() << "This test requires an unprivileged user"; 599821f1d3SAlan Somers } 60*ff4fbdf5SAlan Somers 61*ff4fbdf5SAlan Somers /* With -o default_permissions, FUSE_ACCESS should never be called */ 62*ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 63*ff4fbdf5SAlan Somers ResultOf([=](auto in) { 64*ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_ACCESS); 65*ff4fbdf5SAlan Somers }, Eq(true)), 66*ff4fbdf5SAlan Somers _) 67*ff4fbdf5SAlan Somers ).Times(0); 689821f1d3SAlan Somers } 699821f1d3SAlan Somers 709821f1d3SAlan Somers public: 71*ff4fbdf5SAlan Somers void expect_getattr(uint64_t ino, mode_t mode, uint64_t attr_valid, int times, 72*ff4fbdf5SAlan Somers uid_t uid = 0) 739821f1d3SAlan Somers { 74*ff4fbdf5SAlan Somers /* Until the attr cache is working, we may send an additional GETATTR */ 75*ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 76*ff4fbdf5SAlan Somers ResultOf([=](auto in) { 77*ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_GETATTR && 78*ff4fbdf5SAlan Somers in->header.nodeid == ino); 79*ff4fbdf5SAlan Somers }, Eq(true)), 80*ff4fbdf5SAlan Somers _) 81*ff4fbdf5SAlan Somers ).Times(times) 82*ff4fbdf5SAlan Somers .WillRepeatedly(Invoke(ReturnImmediate([=](auto i __unused, auto out) { 83*ff4fbdf5SAlan Somers SET_OUT_HEADER_LEN(out, attr); 84*ff4fbdf5SAlan Somers out->body.attr.attr.ino = ino; // Must match nodeid 85*ff4fbdf5SAlan Somers out->body.attr.attr.mode = mode; 86*ff4fbdf5SAlan Somers out->body.attr.attr.size = 0; 87*ff4fbdf5SAlan Somers out->body.attr.attr.uid = uid; 88*ff4fbdf5SAlan Somers out->body.attr.attr_valid = attr_valid; 89*ff4fbdf5SAlan Somers }))); 90*ff4fbdf5SAlan Somers } 91*ff4fbdf5SAlan Somers 92*ff4fbdf5SAlan Somers void expect_lookup(const char *relpath, uint64_t ino, mode_t mode, 93*ff4fbdf5SAlan Somers uint64_t attr_valid, uid_t uid = 0) 94*ff4fbdf5SAlan Somers { 95*ff4fbdf5SAlan Somers FuseTest::expect_lookup(relpath, ino, mode, 0, 1, attr_valid, uid); 969821f1d3SAlan Somers } 979821f1d3SAlan Somers 989821f1d3SAlan Somers }; 999821f1d3SAlan Somers 1009821f1d3SAlan Somers class Access: public DefaultPermissions {}; 101*ff4fbdf5SAlan Somers class Lookup: public DefaultPermissions {}; 1029821f1d3SAlan Somers class Open: public DefaultPermissions {}; 103*ff4fbdf5SAlan Somers class Setattr: public DefaultPermissions {}; 104*ff4fbdf5SAlan Somers class Unlink: public DefaultPermissions {}; 1059821f1d3SAlan Somers 106*ff4fbdf5SAlan Somers /* 107*ff4fbdf5SAlan Somers * Test permission handling during create, mkdir, mknod, link, symlink, and 108*ff4fbdf5SAlan Somers * rename vops (they all share a common path for permission checks in 109*ff4fbdf5SAlan Somers * VOP_LOOKUP) 110*ff4fbdf5SAlan Somers */ 111*ff4fbdf5SAlan Somers class Create: public DefaultPermissions { 112*ff4fbdf5SAlan Somers public: 113*ff4fbdf5SAlan Somers 114*ff4fbdf5SAlan Somers void expect_create(const char *relpath, uint64_t ino) 115*ff4fbdf5SAlan Somers { 116*ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 117*ff4fbdf5SAlan Somers ResultOf([=](auto in) { 118*ff4fbdf5SAlan Somers const char *name = (const char*)in->body.bytes + 119*ff4fbdf5SAlan Somers sizeof(fuse_open_in); 120*ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_CREATE && 121*ff4fbdf5SAlan Somers (0 == strcmp(relpath, name))); 122*ff4fbdf5SAlan Somers }, Eq(true)), 123*ff4fbdf5SAlan Somers _) 124*ff4fbdf5SAlan Somers ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto out) { 125*ff4fbdf5SAlan Somers SET_OUT_HEADER_LEN(out, create); 126*ff4fbdf5SAlan Somers out->body.create.entry.attr.mode = S_IFREG | 0644; 127*ff4fbdf5SAlan Somers out->body.create.entry.nodeid = ino; 128*ff4fbdf5SAlan Somers out->body.create.entry.entry_valid = UINT64_MAX; 129*ff4fbdf5SAlan Somers out->body.create.entry.attr_valid = UINT64_MAX; 130*ff4fbdf5SAlan Somers }))); 131*ff4fbdf5SAlan Somers } 132*ff4fbdf5SAlan Somers 133*ff4fbdf5SAlan Somers }; 134*ff4fbdf5SAlan Somers 135*ff4fbdf5SAlan Somers class Deleteextattr: public DefaultPermissions { 136*ff4fbdf5SAlan Somers public: 137*ff4fbdf5SAlan Somers void expect_removexattr() 138*ff4fbdf5SAlan Somers { 139*ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 140*ff4fbdf5SAlan Somers ResultOf([=](auto in) { 141*ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_REMOVEXATTR); 142*ff4fbdf5SAlan Somers }, Eq(true)), 143*ff4fbdf5SAlan Somers _) 144*ff4fbdf5SAlan Somers ).WillOnce(Invoke(ReturnErrno(0))); 145*ff4fbdf5SAlan Somers } 146*ff4fbdf5SAlan Somers }; 147*ff4fbdf5SAlan Somers 148*ff4fbdf5SAlan Somers class Getextattr: public DefaultPermissions { 149*ff4fbdf5SAlan Somers public: 150*ff4fbdf5SAlan Somers void expect_getxattr(ProcessMockerT r) 151*ff4fbdf5SAlan Somers { 152*ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 153*ff4fbdf5SAlan Somers ResultOf([=](auto in) { 154*ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_GETXATTR); 155*ff4fbdf5SAlan Somers }, Eq(true)), 156*ff4fbdf5SAlan Somers _) 157*ff4fbdf5SAlan Somers ).WillOnce(Invoke(r)); 158*ff4fbdf5SAlan Somers } 159*ff4fbdf5SAlan Somers }; 160*ff4fbdf5SAlan Somers 161*ff4fbdf5SAlan Somers class Listextattr: public DefaultPermissions { 162*ff4fbdf5SAlan Somers public: 163*ff4fbdf5SAlan Somers void expect_listxattr() 164*ff4fbdf5SAlan Somers { 165*ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 166*ff4fbdf5SAlan Somers ResultOf([=](auto in) { 167*ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_LISTXATTR); 168*ff4fbdf5SAlan Somers }, Eq(true)), 169*ff4fbdf5SAlan Somers _) 170*ff4fbdf5SAlan Somers ).WillOnce(Invoke(ReturnImmediate([](auto i __unused, auto out) { 171*ff4fbdf5SAlan Somers out->body.listxattr.size = 0; 172*ff4fbdf5SAlan Somers SET_OUT_HEADER_LEN(out, listxattr); 173*ff4fbdf5SAlan Somers }))); 174*ff4fbdf5SAlan Somers } 175*ff4fbdf5SAlan Somers }; 176*ff4fbdf5SAlan Somers 177*ff4fbdf5SAlan Somers class Rename: public DefaultPermissions { 178*ff4fbdf5SAlan Somers public: 179*ff4fbdf5SAlan Somers /* 180*ff4fbdf5SAlan Somers * Expect a rename and respond with the given error. Don't both to 181*ff4fbdf5SAlan Somers * validate arguments; the tests in rename.cc do that. 182*ff4fbdf5SAlan Somers */ 183*ff4fbdf5SAlan Somers void expect_rename(int error) 184*ff4fbdf5SAlan Somers { 185*ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 186*ff4fbdf5SAlan Somers ResultOf([=](auto in) { 187*ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_RENAME); 188*ff4fbdf5SAlan Somers }, Eq(true)), 189*ff4fbdf5SAlan Somers _) 190*ff4fbdf5SAlan Somers ).WillOnce(Invoke(ReturnErrno(error))); 191*ff4fbdf5SAlan Somers } 192*ff4fbdf5SAlan Somers }; 193*ff4fbdf5SAlan Somers 194*ff4fbdf5SAlan Somers class Setextattr: public DefaultPermissions { 195*ff4fbdf5SAlan Somers public: 196*ff4fbdf5SAlan Somers void expect_setxattr(int error) 197*ff4fbdf5SAlan Somers { 198*ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 199*ff4fbdf5SAlan Somers ResultOf([=](auto in) { 200*ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_SETXATTR); 201*ff4fbdf5SAlan Somers }, Eq(true)), 202*ff4fbdf5SAlan Somers _) 203*ff4fbdf5SAlan Somers ).WillOnce(Invoke(ReturnErrno(error))); 204*ff4fbdf5SAlan Somers } 205*ff4fbdf5SAlan Somers }; 206*ff4fbdf5SAlan Somers 207*ff4fbdf5SAlan Somers TEST_F(Access, eacces) 2089821f1d3SAlan Somers { 2099821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2109821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 2119821f1d3SAlan Somers uint64_t ino = 42; 2129821f1d3SAlan Somers mode_t access_mode = X_OK; 2139821f1d3SAlan Somers 214*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 215*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX); 216*ff4fbdf5SAlan Somers 217*ff4fbdf5SAlan Somers ASSERT_NE(0, access(FULLPATH, access_mode)); 218*ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 219*ff4fbdf5SAlan Somers } 220*ff4fbdf5SAlan Somers 221*ff4fbdf5SAlan Somers TEST_F(Access, eacces_no_cached_attrs) 222*ff4fbdf5SAlan Somers { 223*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 224*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 225*ff4fbdf5SAlan Somers uint64_t ino = 42; 226*ff4fbdf5SAlan Somers mode_t access_mode = X_OK; 227*ff4fbdf5SAlan Somers 228*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, 0, 1); 229*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, 0); 230*ff4fbdf5SAlan Somers expect_getattr(ino, S_IFREG | 0644, 0, 1); 2319821f1d3SAlan Somers /* 2329821f1d3SAlan Somers * Once default_permissions is properly implemented, there might be 2339821f1d3SAlan Somers * another FUSE_GETATTR or something in here. But there should not be 2349821f1d3SAlan Somers * a FUSE_ACCESS 2359821f1d3SAlan Somers */ 2369821f1d3SAlan Somers 2379821f1d3SAlan Somers ASSERT_NE(0, access(FULLPATH, access_mode)); 2389821f1d3SAlan Somers ASSERT_EQ(EACCES, errno); 2399821f1d3SAlan Somers } 2409821f1d3SAlan Somers 241*ff4fbdf5SAlan Somers TEST_F(Access, ok) 2429821f1d3SAlan Somers { 2439821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 2449821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 2459821f1d3SAlan Somers uint64_t ino = 42; 2469821f1d3SAlan Somers mode_t access_mode = R_OK; 2479821f1d3SAlan Somers 248*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 249*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX); 2509821f1d3SAlan Somers /* 2519821f1d3SAlan Somers * Once default_permissions is properly implemented, there might be 25291ff3a0dSAlan Somers * another FUSE_GETATTR or something in here. 2539821f1d3SAlan Somers */ 2549821f1d3SAlan Somers 2559821f1d3SAlan Somers ASSERT_EQ(0, access(FULLPATH, access_mode)) << strerror(errno); 2569821f1d3SAlan Somers } 2579821f1d3SAlan Somers 258*ff4fbdf5SAlan Somers TEST_F(Create, ok) 259*ff4fbdf5SAlan Somers { 260*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 261*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 262*ff4fbdf5SAlan Somers uint64_t ino = 42; 263*ff4fbdf5SAlan Somers int fd; 264*ff4fbdf5SAlan Somers 265*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1); 266*ff4fbdf5SAlan Somers EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); 267*ff4fbdf5SAlan Somers expect_create(RELPATH, ino); 268*ff4fbdf5SAlan Somers 269*ff4fbdf5SAlan Somers fd = open(FULLPATH, O_CREAT | O_EXCL, 0644); 270*ff4fbdf5SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 271*ff4fbdf5SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 272*ff4fbdf5SAlan Somers } 273*ff4fbdf5SAlan Somers 274*ff4fbdf5SAlan Somers TEST_F(Create, eacces) 275*ff4fbdf5SAlan Somers { 276*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 277*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 278*ff4fbdf5SAlan Somers 279*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 280*ff4fbdf5SAlan Somers EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT))); 281*ff4fbdf5SAlan Somers 282*ff4fbdf5SAlan Somers EXPECT_EQ(-1, open(FULLPATH, O_CREAT | O_EXCL, 0644)); 283*ff4fbdf5SAlan Somers EXPECT_EQ(EACCES, errno); 284*ff4fbdf5SAlan Somers } 285*ff4fbdf5SAlan Somers 286*ff4fbdf5SAlan Somers TEST_F(Deleteextattr, eacces) 287*ff4fbdf5SAlan Somers { 288*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 289*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 290*ff4fbdf5SAlan Somers uint64_t ino = 42; 291*ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 292*ff4fbdf5SAlan Somers 293*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 294*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0); 295*ff4fbdf5SAlan Somers 296*ff4fbdf5SAlan Somers ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); 297*ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 298*ff4fbdf5SAlan Somers } 299*ff4fbdf5SAlan Somers 300*ff4fbdf5SAlan Somers TEST_F(Deleteextattr, ok) 301*ff4fbdf5SAlan Somers { 302*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 303*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 304*ff4fbdf5SAlan Somers uint64_t ino = 42; 305*ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 306*ff4fbdf5SAlan Somers 307*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 308*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid()); 309*ff4fbdf5SAlan Somers expect_removexattr(); 310*ff4fbdf5SAlan Somers 311*ff4fbdf5SAlan Somers ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo")) 312*ff4fbdf5SAlan Somers << strerror(errno); 313*ff4fbdf5SAlan Somers } 314*ff4fbdf5SAlan Somers 315*ff4fbdf5SAlan Somers /* Delete system attributes requires superuser privilege */ 316*ff4fbdf5SAlan Somers TEST_F(Deleteextattr, system) 317*ff4fbdf5SAlan Somers { 318*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 319*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 320*ff4fbdf5SAlan Somers uint64_t ino = 42; 321*ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_SYSTEM; 322*ff4fbdf5SAlan Somers 323*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 324*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, geteuid()); 325*ff4fbdf5SAlan Somers 326*ff4fbdf5SAlan Somers ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); 327*ff4fbdf5SAlan Somers ASSERT_EQ(EPERM, errno); 328*ff4fbdf5SAlan Somers } 329*ff4fbdf5SAlan Somers 330*ff4fbdf5SAlan Somers /* Deleting user attributes merely requires WRITE privilege */ 331*ff4fbdf5SAlan Somers TEST_F(Deleteextattr, user) 332*ff4fbdf5SAlan Somers { 333*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 334*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 335*ff4fbdf5SAlan Somers uint64_t ino = 42; 336*ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 337*ff4fbdf5SAlan Somers 338*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 339*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, 0); 340*ff4fbdf5SAlan Somers expect_removexattr(); 341*ff4fbdf5SAlan Somers 342*ff4fbdf5SAlan Somers ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo")) 343*ff4fbdf5SAlan Somers << strerror(errno); 344*ff4fbdf5SAlan Somers } 345*ff4fbdf5SAlan Somers 346*ff4fbdf5SAlan Somers TEST_F(Getextattr, eacces) 347*ff4fbdf5SAlan Somers { 348*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 349*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 350*ff4fbdf5SAlan Somers uint64_t ino = 42; 351*ff4fbdf5SAlan Somers char data[80]; 352*ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 353*ff4fbdf5SAlan Somers 354*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 355*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0600, UINT64_MAX, 0); 356*ff4fbdf5SAlan Somers 357*ff4fbdf5SAlan Somers ASSERT_EQ(-1, 358*ff4fbdf5SAlan Somers extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data))); 359*ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 360*ff4fbdf5SAlan Somers } 361*ff4fbdf5SAlan Somers 362*ff4fbdf5SAlan Somers TEST_F(Getextattr, ok) 363*ff4fbdf5SAlan Somers { 364*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 365*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 366*ff4fbdf5SAlan Somers uint64_t ino = 42; 367*ff4fbdf5SAlan Somers char data[80]; 368*ff4fbdf5SAlan Somers const char value[] = "whatever"; 369*ff4fbdf5SAlan Somers ssize_t value_len = strlen(value) + 1; 370*ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 371*ff4fbdf5SAlan Somers ssize_t r; 372*ff4fbdf5SAlan Somers 373*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 374*ff4fbdf5SAlan Somers /* Getting user attributes only requires read access */ 375*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0444, UINT64_MAX, 0); 376*ff4fbdf5SAlan Somers expect_getxattr( 377*ff4fbdf5SAlan Somers ReturnImmediate([&](auto in __unused, auto out) { 378*ff4fbdf5SAlan Somers memcpy((void*)out->body.bytes, value, value_len); 379*ff4fbdf5SAlan Somers out->header.len = sizeof(out->header) + value_len; 380*ff4fbdf5SAlan Somers }) 381*ff4fbdf5SAlan Somers ); 382*ff4fbdf5SAlan Somers 383*ff4fbdf5SAlan Somers r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); 384*ff4fbdf5SAlan Somers ASSERT_EQ(value_len, r) << strerror(errno); 385*ff4fbdf5SAlan Somers EXPECT_STREQ(value, data); 386*ff4fbdf5SAlan Somers } 387*ff4fbdf5SAlan Somers 388*ff4fbdf5SAlan Somers /* Getting system attributes requires superuser privileges */ 389*ff4fbdf5SAlan Somers TEST_F(Getextattr, system) 390*ff4fbdf5SAlan Somers { 391*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 392*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 393*ff4fbdf5SAlan Somers uint64_t ino = 42; 394*ff4fbdf5SAlan Somers char data[80]; 395*ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_SYSTEM; 396*ff4fbdf5SAlan Somers 397*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 398*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, geteuid()); 399*ff4fbdf5SAlan Somers 400*ff4fbdf5SAlan Somers ASSERT_EQ(-1, 401*ff4fbdf5SAlan Somers extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data))); 402*ff4fbdf5SAlan Somers ASSERT_EQ(EPERM, errno); 403*ff4fbdf5SAlan Somers } 404*ff4fbdf5SAlan Somers 405*ff4fbdf5SAlan Somers TEST_F(Listextattr, eacces) 406*ff4fbdf5SAlan Somers { 407*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 408*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 409*ff4fbdf5SAlan Somers uint64_t ino = 42; 410*ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 411*ff4fbdf5SAlan Somers 412*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1); 413*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0600, UINT64_MAX, 0); 414*ff4fbdf5SAlan Somers 415*ff4fbdf5SAlan Somers ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); 416*ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 417*ff4fbdf5SAlan Somers } 418*ff4fbdf5SAlan Somers 419*ff4fbdf5SAlan Somers TEST_F(Listextattr, ok) 420*ff4fbdf5SAlan Somers { 421*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 422*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 423*ff4fbdf5SAlan Somers uint64_t ino = 42; 424*ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 425*ff4fbdf5SAlan Somers 426*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1); 427*ff4fbdf5SAlan Somers /* Listing user extended attributes merely requires read access */ 428*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0); 429*ff4fbdf5SAlan Somers expect_listxattr(); 430*ff4fbdf5SAlan Somers 431*ff4fbdf5SAlan Somers ASSERT_EQ(0, extattr_list_file(FULLPATH, ns, NULL, 0)) 432*ff4fbdf5SAlan Somers << strerror(errno); 433*ff4fbdf5SAlan Somers } 434*ff4fbdf5SAlan Somers 435*ff4fbdf5SAlan Somers /* Listing system xattrs requires superuser privileges */ 436*ff4fbdf5SAlan Somers TEST_F(Listextattr, system) 437*ff4fbdf5SAlan Somers { 438*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 439*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 440*ff4fbdf5SAlan Somers uint64_t ino = 42; 441*ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_SYSTEM; 442*ff4fbdf5SAlan Somers 443*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1); 444*ff4fbdf5SAlan Somers /* Listing user extended attributes merely requires read access */ 445*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid()); 446*ff4fbdf5SAlan Somers 447*ff4fbdf5SAlan Somers ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); 448*ff4fbdf5SAlan Somers ASSERT_EQ(EPERM, errno); 449*ff4fbdf5SAlan Somers } 450*ff4fbdf5SAlan Somers 451*ff4fbdf5SAlan Somers /* A component of the search path lacks execute permissions */ 452*ff4fbdf5SAlan Somers TEST_F(Lookup, eacces) 453*ff4fbdf5SAlan Somers { 454*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_dir/some_file.txt"; 455*ff4fbdf5SAlan Somers const char RELDIRPATH[] = "some_dir"; 456*ff4fbdf5SAlan Somers //const char FINALPATH[] = "some_file.txt"; 457*ff4fbdf5SAlan Somers uint64_t dir_ino = 42; 458*ff4fbdf5SAlan Somers 459*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 460*ff4fbdf5SAlan Somers expect_lookup(RELDIRPATH, dir_ino, S_IFDIR | 0700, UINT64_MAX, 0); 461*ff4fbdf5SAlan Somers 462*ff4fbdf5SAlan Somers EXPECT_EQ(-1, access(FULLPATH, F_OK)); 463*ff4fbdf5SAlan Somers EXPECT_EQ(EACCES, errno); 464*ff4fbdf5SAlan Somers } 465*ff4fbdf5SAlan Somers 466*ff4fbdf5SAlan Somers TEST_F(Open, eacces) 467*ff4fbdf5SAlan Somers { 468*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 469*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 470*ff4fbdf5SAlan Somers uint64_t ino = 42; 471*ff4fbdf5SAlan Somers 472*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 473*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX); 474*ff4fbdf5SAlan Somers 475*ff4fbdf5SAlan Somers EXPECT_NE(0, open(FULLPATH, O_RDWR)); 476*ff4fbdf5SAlan Somers EXPECT_EQ(EACCES, errno); 477*ff4fbdf5SAlan Somers } 478*ff4fbdf5SAlan Somers 4799821f1d3SAlan Somers TEST_F(Open, ok) 4809821f1d3SAlan Somers { 4819821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 4829821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 4839821f1d3SAlan Somers uint64_t ino = 42; 4849821f1d3SAlan Somers int fd; 4859821f1d3SAlan Somers 486*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 487*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX); 4889821f1d3SAlan Somers expect_open(ino, 0, 1); 4899821f1d3SAlan Somers 4909821f1d3SAlan Somers fd = open(FULLPATH, O_RDONLY); 4919821f1d3SAlan Somers EXPECT_LE(0, fd) << strerror(errno); 4929821f1d3SAlan Somers /* Deliberately leak fd. close(2) will be tested in release.cc */ 4939821f1d3SAlan Somers } 4949821f1d3SAlan Somers 495*ff4fbdf5SAlan Somers TEST_F(Rename, eacces_on_srcdir) 496*ff4fbdf5SAlan Somers { 497*ff4fbdf5SAlan Somers const char FULLDST[] = "mountpoint/d/dst"; 498*ff4fbdf5SAlan Somers const char RELDST[] = "d/dst"; 499*ff4fbdf5SAlan Somers const char FULLSRC[] = "mountpoint/src"; 500*ff4fbdf5SAlan Somers const char RELSRC[] = "src"; 501*ff4fbdf5SAlan Somers uint64_t ino = 42; 502*ff4fbdf5SAlan Somers 503*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1, 0); 504*ff4fbdf5SAlan Somers expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX); 505*ff4fbdf5SAlan Somers EXPECT_LOOKUP(1, RELDST) 506*ff4fbdf5SAlan Somers .Times(AnyNumber()) 507*ff4fbdf5SAlan Somers .WillRepeatedly(Invoke(ReturnErrno(ENOENT))); 508*ff4fbdf5SAlan Somers 509*ff4fbdf5SAlan Somers ASSERT_EQ(-1, rename(FULLSRC, FULLDST)); 510*ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 511*ff4fbdf5SAlan Somers } 512*ff4fbdf5SAlan Somers 513*ff4fbdf5SAlan Somers TEST_F(Rename, eacces_on_dstdir_for_creating) 514*ff4fbdf5SAlan Somers { 515*ff4fbdf5SAlan Somers const char FULLDST[] = "mountpoint/d/dst"; 516*ff4fbdf5SAlan Somers const char RELDSTDIR[] = "d"; 517*ff4fbdf5SAlan Somers const char RELDST[] = "dst"; 518*ff4fbdf5SAlan Somers const char FULLSRC[] = "mountpoint/src"; 519*ff4fbdf5SAlan Somers const char RELSRC[] = "src"; 520*ff4fbdf5SAlan Somers uint64_t src_ino = 42; 521*ff4fbdf5SAlan Somers uint64_t dstdir_ino = 43; 522*ff4fbdf5SAlan Somers 523*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, 0); 524*ff4fbdf5SAlan Somers expect_lookup(RELSRC, src_ino, S_IFREG | 0644, UINT64_MAX); 525*ff4fbdf5SAlan Somers expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 0755, UINT64_MAX); 526*ff4fbdf5SAlan Somers EXPECT_LOOKUP(dstdir_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT))); 527*ff4fbdf5SAlan Somers 528*ff4fbdf5SAlan Somers ASSERT_EQ(-1, rename(FULLSRC, FULLDST)); 529*ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 530*ff4fbdf5SAlan Somers } 531*ff4fbdf5SAlan Somers 532*ff4fbdf5SAlan Somers TEST_F(Rename, eacces_on_dstdir_for_removing) 533*ff4fbdf5SAlan Somers { 534*ff4fbdf5SAlan Somers const char FULLDST[] = "mountpoint/d/dst"; 535*ff4fbdf5SAlan Somers const char RELDSTDIR[] = "d"; 536*ff4fbdf5SAlan Somers const char RELDST[] = "dst"; 537*ff4fbdf5SAlan Somers const char FULLSRC[] = "mountpoint/src"; 538*ff4fbdf5SAlan Somers const char RELSRC[] = "src"; 539*ff4fbdf5SAlan Somers uint64_t src_ino = 42; 540*ff4fbdf5SAlan Somers uint64_t dstdir_ino = 43; 541*ff4fbdf5SAlan Somers 542*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, 0); 543*ff4fbdf5SAlan Somers expect_lookup(RELSRC, src_ino, S_IFREG | 0644, UINT64_MAX); 544*ff4fbdf5SAlan Somers expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 0755, UINT64_MAX); 545*ff4fbdf5SAlan Somers EXPECT_LOOKUP(dstdir_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT))); 546*ff4fbdf5SAlan Somers 547*ff4fbdf5SAlan Somers ASSERT_EQ(-1, rename(FULLSRC, FULLDST)); 548*ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 549*ff4fbdf5SAlan Somers } 550*ff4fbdf5SAlan Somers 5519821f1d3SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=216391 */ 552*ff4fbdf5SAlan Somers TEST_F(Rename, DISABLED_eperm_on_sticky_srcdir) 553*ff4fbdf5SAlan Somers { 554*ff4fbdf5SAlan Somers const char FULLDST[] = "mountpoint/d/dst"; 555*ff4fbdf5SAlan Somers const char RELDSTDIR[] = "d"; 556*ff4fbdf5SAlan Somers const char RELDST[] = "dst"; 557*ff4fbdf5SAlan Somers const char FULLSRC[] = "mountpoint/src"; 558*ff4fbdf5SAlan Somers const char RELSRC[] = "src"; 559*ff4fbdf5SAlan Somers uint64_t ino = 42; 560*ff4fbdf5SAlan Somers uint64_t dstdir_ino = 43; 561*ff4fbdf5SAlan Somers 562*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 01777, UINT64_MAX, 1, 0); 563*ff4fbdf5SAlan Somers expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX); 564*ff4fbdf5SAlan Somers expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 0777, UINT64_MAX); 565*ff4fbdf5SAlan Somers EXPECT_LOOKUP(dstdir_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT))); 566*ff4fbdf5SAlan Somers 567*ff4fbdf5SAlan Somers ASSERT_EQ(-1, rename(FULLSRC, FULLDST)); 568*ff4fbdf5SAlan Somers ASSERT_EQ(EPERM, errno); 569*ff4fbdf5SAlan Somers } 570*ff4fbdf5SAlan Somers 571*ff4fbdf5SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=216391 */ 572*ff4fbdf5SAlan Somers TEST_F(Rename, DISABLED_eperm_on_sticky_dstdir) 573*ff4fbdf5SAlan Somers { 574*ff4fbdf5SAlan Somers const char FULLDST[] = "mountpoint/d/dst"; 575*ff4fbdf5SAlan Somers const char RELDSTDIR[] = "d"; 576*ff4fbdf5SAlan Somers const char RELDST[] = "d/dst"; 577*ff4fbdf5SAlan Somers const char FULLSRC[] = "mountpoint/src"; 578*ff4fbdf5SAlan Somers const char RELSRC[] = "src"; 579*ff4fbdf5SAlan Somers uint64_t src_ino = 42; 580*ff4fbdf5SAlan Somers uint64_t dstdir_ino = 43; 581*ff4fbdf5SAlan Somers uint64_t dst_ino = 44; 582*ff4fbdf5SAlan Somers 583*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, 0); 584*ff4fbdf5SAlan Somers expect_lookup(RELSRC, src_ino, S_IFREG | 0644, UINT64_MAX); 585*ff4fbdf5SAlan Somers expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 01777, UINT64_MAX); 586*ff4fbdf5SAlan Somers expect_lookup(RELDST, dst_ino, S_IFREG | 0644, UINT64_MAX, 0); 587*ff4fbdf5SAlan Somers 588*ff4fbdf5SAlan Somers ASSERT_EQ(-1, rename(FULLSRC, FULLDST)); 589*ff4fbdf5SAlan Somers ASSERT_EQ(EPERM, errno); 590*ff4fbdf5SAlan Somers } 591*ff4fbdf5SAlan Somers 592*ff4fbdf5SAlan Somers /* Successfully rename a file, overwriting the destination */ 593*ff4fbdf5SAlan Somers TEST_F(Rename, ok) 594*ff4fbdf5SAlan Somers { 595*ff4fbdf5SAlan Somers const char FULLDST[] = "mountpoint/dst"; 596*ff4fbdf5SAlan Somers const char RELDST[] = "dst"; 597*ff4fbdf5SAlan Somers const char FULLSRC[] = "mountpoint/src"; 598*ff4fbdf5SAlan Somers const char RELSRC[] = "src"; 599*ff4fbdf5SAlan Somers // The inode of the already-existing destination file 600*ff4fbdf5SAlan Somers uint64_t dst_ino = 2; 601*ff4fbdf5SAlan Somers uint64_t ino = 42; 602*ff4fbdf5SAlan Somers 603*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, geteuid()); 604*ff4fbdf5SAlan Somers expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX); 605*ff4fbdf5SAlan Somers expect_lookup(RELDST, dst_ino, S_IFREG | 0644, UINT64_MAX); 606*ff4fbdf5SAlan Somers expect_rename(0); 607*ff4fbdf5SAlan Somers 608*ff4fbdf5SAlan Somers ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno); 609*ff4fbdf5SAlan Somers } 610*ff4fbdf5SAlan Somers 611*ff4fbdf5SAlan Somers TEST_F(Rename, ok_to_remove_src_because_of_stickiness) 612*ff4fbdf5SAlan Somers { 613*ff4fbdf5SAlan Somers const char FULLDST[] = "mountpoint/dst"; 614*ff4fbdf5SAlan Somers const char RELDST[] = "dst"; 615*ff4fbdf5SAlan Somers const char FULLSRC[] = "mountpoint/src"; 616*ff4fbdf5SAlan Somers const char RELSRC[] = "src"; 617*ff4fbdf5SAlan Somers uint64_t ino = 42; 618*ff4fbdf5SAlan Somers 619*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 01777, UINT64_MAX, 1, 0); 620*ff4fbdf5SAlan Somers expect_lookup(RELSRC, ino, S_IFREG | 0644, UINT64_MAX, geteuid()); 621*ff4fbdf5SAlan Somers EXPECT_LOOKUP(1, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT))); 622*ff4fbdf5SAlan Somers expect_rename(0); 623*ff4fbdf5SAlan Somers 624*ff4fbdf5SAlan Somers ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno); 625*ff4fbdf5SAlan Somers } 626*ff4fbdf5SAlan Somers 627*ff4fbdf5SAlan Somers TEST_F(Setattr, ok) 628*ff4fbdf5SAlan Somers { 629*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 630*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 631*ff4fbdf5SAlan Somers const uint64_t ino = 42; 632*ff4fbdf5SAlan Somers const mode_t oldmode = 0755; 633*ff4fbdf5SAlan Somers const mode_t newmode = 0644; 634*ff4fbdf5SAlan Somers 635*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 636*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, geteuid()); 637*ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 638*ff4fbdf5SAlan Somers ResultOf([](auto in) { 639*ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_SETATTR && 640*ff4fbdf5SAlan Somers in->header.nodeid == ino && 641*ff4fbdf5SAlan Somers in->body.setattr.mode == newmode); 642*ff4fbdf5SAlan Somers }, Eq(true)), 643*ff4fbdf5SAlan Somers _) 644*ff4fbdf5SAlan Somers ).WillOnce(Invoke(ReturnImmediate([](auto in __unused, auto out) { 645*ff4fbdf5SAlan Somers SET_OUT_HEADER_LEN(out, attr); 646*ff4fbdf5SAlan Somers out->body.attr.attr.mode = S_IFREG | newmode; 647*ff4fbdf5SAlan Somers }))); 648*ff4fbdf5SAlan Somers 649*ff4fbdf5SAlan Somers EXPECT_EQ(0, chmod(FULLPATH, newmode)) << strerror(errno); 650*ff4fbdf5SAlan Somers } 651*ff4fbdf5SAlan Somers 652*ff4fbdf5SAlan Somers TEST_F(Setattr, eacces) 653*ff4fbdf5SAlan Somers { 654*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 655*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 656*ff4fbdf5SAlan Somers const uint64_t ino = 42; 657*ff4fbdf5SAlan Somers const mode_t oldmode = 0755; 658*ff4fbdf5SAlan Somers const mode_t newmode = 0644; 659*ff4fbdf5SAlan Somers 660*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 661*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | oldmode, UINT64_MAX, 0); 662*ff4fbdf5SAlan Somers EXPECT_CALL(*m_mock, process( 663*ff4fbdf5SAlan Somers ResultOf([](auto in) { 664*ff4fbdf5SAlan Somers return (in->header.opcode == FUSE_SETATTR); 665*ff4fbdf5SAlan Somers }, Eq(true)), 666*ff4fbdf5SAlan Somers _) 667*ff4fbdf5SAlan Somers ).Times(0); 668*ff4fbdf5SAlan Somers 669*ff4fbdf5SAlan Somers EXPECT_NE(0, chmod(FULLPATH, newmode)); 670*ff4fbdf5SAlan Somers EXPECT_EQ(EPERM, errno); 671*ff4fbdf5SAlan Somers } 672*ff4fbdf5SAlan Somers 673*ff4fbdf5SAlan Somers TEST_F(Setextattr, ok) 674*ff4fbdf5SAlan Somers { 675*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 676*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 677*ff4fbdf5SAlan Somers uint64_t ino = 42; 678*ff4fbdf5SAlan Somers const char value[] = "whatever"; 679*ff4fbdf5SAlan Somers ssize_t value_len = strlen(value) + 1; 680*ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 681*ff4fbdf5SAlan Somers ssize_t r; 682*ff4fbdf5SAlan Somers 683*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 684*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid()); 685*ff4fbdf5SAlan Somers expect_setxattr(0); 686*ff4fbdf5SAlan Somers 687*ff4fbdf5SAlan Somers r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); 688*ff4fbdf5SAlan Somers ASSERT_EQ(value_len, r) << strerror(errno); 689*ff4fbdf5SAlan Somers } 690*ff4fbdf5SAlan Somers 691*ff4fbdf5SAlan Somers TEST_F(Setextattr, eacces) 692*ff4fbdf5SAlan Somers { 693*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 694*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 695*ff4fbdf5SAlan Somers uint64_t ino = 42; 696*ff4fbdf5SAlan Somers const char value[] = "whatever"; 697*ff4fbdf5SAlan Somers ssize_t value_len = strlen(value) + 1; 698*ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 699*ff4fbdf5SAlan Somers 700*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 701*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0); 702*ff4fbdf5SAlan Somers 703*ff4fbdf5SAlan Somers ASSERT_EQ(-1, 704*ff4fbdf5SAlan Somers extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len)); 705*ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 706*ff4fbdf5SAlan Somers } 707*ff4fbdf5SAlan Somers 708*ff4fbdf5SAlan Somers // Setting system attributes requires superuser privileges 709*ff4fbdf5SAlan Somers TEST_F(Setextattr, system) 710*ff4fbdf5SAlan Somers { 711*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 712*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 713*ff4fbdf5SAlan Somers uint64_t ino = 42; 714*ff4fbdf5SAlan Somers const char value[] = "whatever"; 715*ff4fbdf5SAlan Somers ssize_t value_len = strlen(value) + 1; 716*ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_SYSTEM; 717*ff4fbdf5SAlan Somers 718*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 719*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, geteuid()); 720*ff4fbdf5SAlan Somers 721*ff4fbdf5SAlan Somers ASSERT_EQ(-1, 722*ff4fbdf5SAlan Somers extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len)); 723*ff4fbdf5SAlan Somers ASSERT_EQ(EPERM, errno); 724*ff4fbdf5SAlan Somers } 725*ff4fbdf5SAlan Somers 726*ff4fbdf5SAlan Somers // Setting user attributes merely requires write privileges 727*ff4fbdf5SAlan Somers TEST_F(Setextattr, user) 728*ff4fbdf5SAlan Somers { 729*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 730*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 731*ff4fbdf5SAlan Somers uint64_t ino = 42; 732*ff4fbdf5SAlan Somers const char value[] = "whatever"; 733*ff4fbdf5SAlan Somers ssize_t value_len = strlen(value) + 1; 734*ff4fbdf5SAlan Somers int ns = EXTATTR_NAMESPACE_USER; 735*ff4fbdf5SAlan Somers ssize_t r; 736*ff4fbdf5SAlan Somers 737*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 738*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0666, UINT64_MAX, 0); 739*ff4fbdf5SAlan Somers expect_setxattr(0); 740*ff4fbdf5SAlan Somers 741*ff4fbdf5SAlan Somers r = extattr_set_file(FULLPATH, ns, "foo", (void*)value, value_len); 742*ff4fbdf5SAlan Somers ASSERT_EQ(value_len, r) << strerror(errno); 743*ff4fbdf5SAlan Somers } 744*ff4fbdf5SAlan Somers 745*ff4fbdf5SAlan Somers TEST_F(Unlink, ok) 7469821f1d3SAlan Somers { 7479821f1d3SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 7489821f1d3SAlan Somers const char RELPATH[] = "some_file.txt"; 7499821f1d3SAlan Somers uint64_t ino = 42; 7509821f1d3SAlan Somers 751*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1); 752*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid()); 753*ff4fbdf5SAlan Somers expect_unlink(1, RELPATH, 0); 7549821f1d3SAlan Somers 755*ff4fbdf5SAlan Somers ASSERT_EQ(0, unlink(FULLPATH)) << strerror(errno); 756*ff4fbdf5SAlan Somers } 757*ff4fbdf5SAlan Somers 758*ff4fbdf5SAlan Somers TEST_F(Unlink, unwritable_directory) 759*ff4fbdf5SAlan Somers { 760*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 761*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 762*ff4fbdf5SAlan Somers uint64_t ino = 42; 763*ff4fbdf5SAlan Somers 764*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 0755, UINT64_MAX, 1); 765*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, geteuid()); 766*ff4fbdf5SAlan Somers 767*ff4fbdf5SAlan Somers ASSERT_EQ(-1, unlink(FULLPATH)); 768*ff4fbdf5SAlan Somers ASSERT_EQ(EACCES, errno); 769*ff4fbdf5SAlan Somers } 770*ff4fbdf5SAlan Somers 771*ff4fbdf5SAlan Somers /* https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=216391 */ 772*ff4fbdf5SAlan Somers TEST_F(Unlink, DISABLED_sticky_directory) 773*ff4fbdf5SAlan Somers { 774*ff4fbdf5SAlan Somers const char FULLPATH[] = "mountpoint/some_file.txt"; 775*ff4fbdf5SAlan Somers const char RELPATH[] = "some_file.txt"; 776*ff4fbdf5SAlan Somers uint64_t ino = 42; 777*ff4fbdf5SAlan Somers 778*ff4fbdf5SAlan Somers expect_getattr(1, S_IFDIR | 01777, UINT64_MAX, 1); 779*ff4fbdf5SAlan Somers expect_lookup(RELPATH, ino, S_IFREG | 0644, UINT64_MAX, 0); 780*ff4fbdf5SAlan Somers 781*ff4fbdf5SAlan Somers ASSERT_EQ(-1, unlink(FULLPATH)); 782*ff4fbdf5SAlan Somers ASSERT_EQ(EPERM, errno); 7839821f1d3SAlan Somers } 784