1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2019 The FreeBSD Foundation 5 * 6 * This software was developed by BFF Storage Systems, LLC under sponsorship 7 * from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* Tests for all things relating to extended attributes and FUSE */ 32 33 extern "C" { 34 #include <sys/types.h> 35 #include <sys/extattr.h> 36 #include <string.h> 37 } 38 39 #include "mockfs.hh" 40 #include "utils.hh" 41 42 using namespace testing; 43 44 const char FULLPATH[] = "mountpoint/some_file.txt"; 45 const char RELPATH[] = "some_file.txt"; 46 47 class Xattr: public FuseTest { 48 public: 49 void expect_getxattr(uint64_t ino, const char *attr, ProcessMockerT r) 50 { 51 EXPECT_CALL(*m_mock, process( 52 ResultOf([=](auto in) { 53 const char *a = (const char*)in.body.bytes + 54 sizeof(fuse_getxattr_in); 55 return (in.header.opcode == FUSE_GETXATTR && 56 in.header.nodeid == ino && 57 0 == strcmp(attr, a)); 58 }, Eq(true)), 59 _) 60 ).WillOnce(Invoke(r)); 61 } 62 63 void expect_listxattr(uint64_t ino, uint32_t size, ProcessMockerT r) 64 { 65 EXPECT_CALL(*m_mock, process( 66 ResultOf([=](auto in) { 67 return (in.header.opcode == FUSE_LISTXATTR && 68 in.header.nodeid == ino && 69 in.body.listxattr.size == size); 70 }, Eq(true)), 71 _) 72 ).WillOnce(Invoke(r)) 73 .RetiresOnSaturation(); 74 } 75 76 void expect_removexattr(uint64_t ino, const char *attr, int error) 77 { 78 EXPECT_CALL(*m_mock, process( 79 ResultOf([=](auto in) { 80 const char *a = (const char*)in.body.bytes; 81 return (in.header.opcode == FUSE_REMOVEXATTR && 82 in.header.nodeid == ino && 83 0 == strcmp(attr, a)); 84 }, Eq(true)), 85 _) 86 ).WillOnce(Invoke(ReturnErrno(error))); 87 } 88 89 void expect_setxattr(uint64_t ino, const char *attr, const char *value, 90 ProcessMockerT r) 91 { 92 EXPECT_CALL(*m_mock, process( 93 ResultOf([=](auto in) { 94 const char *a = (const char*)in.body.bytes + 95 sizeof(fuse_setxattr_in); 96 const char *v = a + strlen(a) + 1; 97 return (in.header.opcode == FUSE_SETXATTR && 98 in.header.nodeid == ino && 99 0 == strcmp(attr, a) && 100 0 == strcmp(value, v)); 101 }, Eq(true)), 102 _) 103 ).WillOnce(Invoke(r)); 104 } 105 106 }; 107 108 class Getxattr: public Xattr {}; 109 class Listxattr: public Xattr {}; 110 class Removexattr: public Xattr {}; 111 class Setxattr: public Xattr {}; 112 class RofsXattr: public Xattr { 113 public: 114 virtual void SetUp() { 115 m_ro = true; 116 Xattr::SetUp(); 117 } 118 }; 119 120 /* 121 * If the extended attribute does not exist on this file, the daemon should 122 * return ENOATTR (ENODATA on Linux, but it's up to the daemon to choose the 123 * correct errror code) 124 */ 125 TEST_F(Getxattr, enoattr) 126 { 127 char data[80]; 128 uint64_t ino = 42; 129 int ns = EXTATTR_NAMESPACE_USER; 130 ssize_t r; 131 132 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 133 expect_getxattr(ino, "user.foo", ReturnErrno(ENOATTR)); 134 135 r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); 136 ASSERT_EQ(-1, r); 137 ASSERT_EQ(ENOATTR, errno); 138 } 139 140 /* 141 * If the filesystem returns ENOSYS, then it will be treated as a permanent 142 * failure and all future VOP_GETEXTATTR calls will fail with EOPNOTSUPP 143 * without querying the filesystem daemon 144 */ 145 TEST_F(Getxattr, enosys) 146 { 147 char data[80]; 148 uint64_t ino = 42; 149 int ns = EXTATTR_NAMESPACE_USER; 150 ssize_t r; 151 152 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2); 153 expect_getxattr(ino, "user.foo", ReturnErrno(ENOSYS)); 154 155 r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); 156 ASSERT_EQ(-1, r); 157 EXPECT_EQ(EOPNOTSUPP, errno); 158 159 /* Subsequent attempts should not query the filesystem at all */ 160 r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); 161 ASSERT_EQ(-1, r); 162 EXPECT_EQ(EOPNOTSUPP, errno); 163 } 164 165 /* 166 * On FreeBSD, if the user passes an insufficiently large buffer then the 167 * filesystem is supposed to copy as much of the attribute's value as will fit. 168 * 169 * On Linux, however, the filesystem is supposed to return ERANGE. 170 * 171 * libfuse specifies the Linux behavior. However, that's probably an error. 172 * It would probably be correct for the filesystem to use platform-dependent 173 * behavior. 174 * 175 * This test case covers a filesystem that uses the Linux behavior 176 */ 177 TEST_F(Getxattr, erange) 178 { 179 char data[10]; 180 uint64_t ino = 42; 181 int ns = EXTATTR_NAMESPACE_USER; 182 ssize_t r; 183 184 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 185 expect_getxattr(ino, "user.foo", ReturnErrno(ERANGE)); 186 187 r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); 188 ASSERT_EQ(-1, r); 189 ASSERT_EQ(ERANGE, errno); 190 } 191 192 /* 193 * If the user passes a 0-length buffer, then the daemon should just return the 194 * size of the attribute 195 */ 196 TEST_F(Getxattr, size_only) 197 { 198 uint64_t ino = 42; 199 int ns = EXTATTR_NAMESPACE_USER; 200 201 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 202 expect_getxattr(ino, "user.foo", 203 ReturnImmediate([](auto in __unused, auto& out) { 204 SET_OUT_HEADER_LEN(out, getxattr); 205 out.body.getxattr.size = 99; 206 }) 207 ); 208 209 ASSERT_EQ(99, extattr_get_file(FULLPATH, ns, "foo", NULL, 0)) 210 << strerror(errno);; 211 } 212 213 /* 214 * Successfully get an attribute from the system namespace 215 */ 216 TEST_F(Getxattr, system) 217 { 218 uint64_t ino = 42; 219 char data[80]; 220 const char value[] = "whatever"; 221 ssize_t value_len = strlen(value) + 1; 222 int ns = EXTATTR_NAMESPACE_SYSTEM; 223 ssize_t r; 224 225 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 226 expect_getxattr(ino, "system.foo", 227 ReturnImmediate([&](auto in __unused, auto& out) { 228 memcpy((void*)out.body.bytes, value, value_len); 229 out.header.len = sizeof(out.header) + value_len; 230 }) 231 ); 232 233 r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); 234 ASSERT_EQ(value_len, r) << strerror(errno); 235 EXPECT_STREQ(value, data); 236 } 237 238 /* 239 * Successfully get an attribute from the user namespace 240 */ 241 TEST_F(Getxattr, user) 242 { 243 uint64_t ino = 42; 244 char data[80]; 245 const char value[] = "whatever"; 246 ssize_t value_len = strlen(value) + 1; 247 int ns = EXTATTR_NAMESPACE_USER; 248 ssize_t r; 249 250 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 251 expect_getxattr(ino, "user.foo", 252 ReturnImmediate([&](auto in __unused, auto& out) { 253 memcpy((void*)out.body.bytes, value, value_len); 254 out.header.len = sizeof(out.header) + value_len; 255 }) 256 ); 257 258 r = extattr_get_file(FULLPATH, ns, "foo", data, sizeof(data)); 259 ASSERT_EQ(value_len, r) << strerror(errno); 260 EXPECT_STREQ(value, data); 261 } 262 263 /* 264 * If the filesystem returns ENOSYS, then it will be treated as a permanent 265 * failure and all future VOP_LISTEXTATTR calls will fail with EOPNOTSUPP 266 * without querying the filesystem daemon 267 */ 268 TEST_F(Listxattr, enosys) 269 { 270 uint64_t ino = 42; 271 int ns = EXTATTR_NAMESPACE_USER; 272 273 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2); 274 expect_listxattr(ino, 0, ReturnErrno(ENOSYS)); 275 276 ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); 277 EXPECT_EQ(EOPNOTSUPP, errno); 278 279 /* Subsequent attempts should not query the filesystem at all */ 280 ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); 281 EXPECT_EQ(EOPNOTSUPP, errno); 282 } 283 284 /* 285 * Listing extended attributes failed because they aren't configured on this 286 * filesystem 287 */ 288 TEST_F(Listxattr, enotsup) 289 { 290 uint64_t ino = 42; 291 int ns = EXTATTR_NAMESPACE_USER; 292 293 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 294 expect_listxattr(ino, 0, ReturnErrno(ENOTSUP)); 295 296 ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); 297 ASSERT_EQ(ENOTSUP, errno); 298 } 299 300 /* 301 * On FreeBSD, if the user passes an insufficiently large buffer then the 302 * filesystem is supposed to copy as much of the attribute's value as will fit. 303 * 304 * On Linux, however, the filesystem is supposed to return ERANGE. 305 * 306 * libfuse specifies the Linux behavior. However, that's probably an error. 307 * It would probably be correct for the filesystem to use platform-dependent 308 * behavior. 309 * 310 * This test case covers a filesystem that uses the Linux behavior 311 */ 312 TEST_F(Listxattr, erange) 313 { 314 uint64_t ino = 42; 315 int ns = EXTATTR_NAMESPACE_USER; 316 317 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 318 expect_listxattr(ino, 0, ReturnErrno(ERANGE)); 319 320 ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, NULL, 0)); 321 ASSERT_EQ(ERANGE, errno); 322 } 323 324 /* 325 * Get the size of the list that it would take to list no extended attributes 326 */ 327 TEST_F(Listxattr, size_only_empty) 328 { 329 uint64_t ino = 42; 330 int ns = EXTATTR_NAMESPACE_USER; 331 332 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 333 expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) { 334 out.body.listxattr.size = 0; 335 SET_OUT_HEADER_LEN(out, listxattr); 336 })); 337 338 ASSERT_EQ(0, extattr_list_file(FULLPATH, ns, NULL, 0)) 339 << strerror(errno); 340 } 341 342 /* 343 * Get the size of the list that it would take to list some extended 344 * attributes. Due to the format differences between a FreeBSD and a 345 * Linux/FUSE extended attribute list, fuse(4) will actually allocate a buffer 346 * and get the whole list, then convert it, just to figure out its size. 347 */ 348 TEST_F(Listxattr, size_only_nonempty) 349 { 350 uint64_t ino = 42; 351 int ns = EXTATTR_NAMESPACE_USER; 352 353 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 354 expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) { 355 out.body.listxattr.size = 45; 356 SET_OUT_HEADER_LEN(out, listxattr); 357 })); 358 359 // TODO: fix the expected size after fixing the size calculation bug in 360 // fuse_vnop_listextattr. It should be exactly 45. 361 expect_listxattr(ino, 53, 362 ReturnImmediate([](auto in __unused, auto& out) { 363 const char l[] = "user.foo"; 364 strlcpy((char*)out.body.bytes, l, 365 sizeof(out.body.bytes)); 366 out.header.len = sizeof(fuse_out_header) + sizeof(l); 367 }) 368 ); 369 370 ASSERT_EQ(4, extattr_list_file(FULLPATH, ns, NULL, 0)) 371 << strerror(errno); 372 } 373 374 TEST_F(Listxattr, size_only_really_big) 375 { 376 uint64_t ino = 42; 377 int ns = EXTATTR_NAMESPACE_USER; 378 379 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 380 expect_listxattr(ino, 0, ReturnImmediate([](auto i __unused, auto& out) { 381 out.body.listxattr.size = 16000; 382 SET_OUT_HEADER_LEN(out, listxattr); 383 })); 384 385 // TODO: fix the expected size after fixing the size calculation bug in 386 // fuse_vnop_listextattr. It should be exactly 16000. 387 expect_listxattr(ino, 16008, 388 ReturnImmediate([](auto in __unused, auto& out) { 389 const char l[16] = "user.foobarbang"; 390 for (int i=0; i < 1000; i++) { 391 memcpy(&out.body.bytes[16 * i], l, 16); 392 } 393 out.header.len = sizeof(fuse_out_header) + 16000; 394 }) 395 ); 396 397 ASSERT_EQ(11000, extattr_list_file(FULLPATH, ns, NULL, 0)) 398 << strerror(errno); 399 } 400 401 /* 402 * List all of the user attributes of a file which has both user and system 403 * attributes 404 */ 405 TEST_F(Listxattr, user) 406 { 407 uint64_t ino = 42; 408 int ns = EXTATTR_NAMESPACE_USER; 409 char data[80]; 410 char expected[9] = {3, 'f', 'o', 'o', 4, 'b', 'a', 'n', 'g'}; 411 char attrs[28] = "user.foo\0system.x\0user.bang"; 412 413 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 414 expect_listxattr(ino, 0, 415 ReturnImmediate([&](auto in __unused, auto& out) { 416 out.body.listxattr.size = sizeof(attrs); 417 SET_OUT_HEADER_LEN(out, listxattr); 418 }) 419 ); 420 421 // TODO: fix the expected size after fixing the size calculation bug in 422 // fuse_vnop_listextattr. 423 expect_listxattr(ino, sizeof(attrs) + 8, 424 ReturnImmediate([&](auto in __unused, auto& out) { 425 memcpy((void*)out.body.bytes, attrs, sizeof(attrs)); 426 out.header.len = sizeof(fuse_out_header) + sizeof(attrs); 427 })); 428 429 ASSERT_EQ(static_cast<ssize_t>(sizeof(expected)), 430 extattr_list_file(FULLPATH, ns, data, sizeof(data))) 431 << strerror(errno); 432 ASSERT_EQ(0, memcmp(expected, data, sizeof(expected))); 433 } 434 435 /* 436 * List all of the system attributes of a file which has both user and system 437 * attributes 438 */ 439 TEST_F(Listxattr, system) 440 { 441 uint64_t ino = 42; 442 int ns = EXTATTR_NAMESPACE_SYSTEM; 443 char data[80]; 444 char expected[2] = {1, 'x'}; 445 char attrs[28] = "user.foo\0system.x\0user.bang"; 446 447 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 448 expect_listxattr(ino, 0, 449 ReturnImmediate([&](auto in __unused, auto& out) { 450 out.body.listxattr.size = sizeof(attrs); 451 SET_OUT_HEADER_LEN(out, listxattr); 452 }) 453 ); 454 455 // TODO: fix the expected size after fixing the size calculation bug in 456 // fuse_vnop_listextattr. 457 expect_listxattr(ino, sizeof(attrs) + 8, 458 ReturnImmediate([&](auto in __unused, auto& out) { 459 memcpy((void*)out.body.bytes, attrs, sizeof(attrs)); 460 out.header.len = sizeof(fuse_out_header) + sizeof(attrs); 461 })); 462 463 ASSERT_EQ(static_cast<ssize_t>(sizeof(expected)), 464 extattr_list_file(FULLPATH, ns, data, sizeof(data))) 465 << strerror(errno); 466 ASSERT_EQ(0, memcmp(expected, data, sizeof(expected))); 467 } 468 469 /* Fail to remove a nonexistent attribute */ 470 TEST_F(Removexattr, enoattr) 471 { 472 uint64_t ino = 42; 473 int ns = EXTATTR_NAMESPACE_USER; 474 475 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 476 expect_removexattr(ino, "user.foo", ENOATTR); 477 478 ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); 479 ASSERT_EQ(ENOATTR, errno); 480 } 481 482 /* 483 * If the filesystem returns ENOSYS, then it will be treated as a permanent 484 * failure and all future VOP_DELETEEXTATTR calls will fail with EOPNOTSUPP 485 * without querying the filesystem daemon 486 */ 487 TEST_F(Removexattr, enosys) 488 { 489 uint64_t ino = 42; 490 int ns = EXTATTR_NAMESPACE_USER; 491 492 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2); 493 expect_removexattr(ino, "user.foo", ENOSYS); 494 495 ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); 496 EXPECT_EQ(EOPNOTSUPP, errno); 497 498 /* Subsequent attempts should not query the filesystem at all */ 499 ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); 500 EXPECT_EQ(EOPNOTSUPP, errno); 501 } 502 503 /* Successfully remove a user xattr */ 504 TEST_F(Removexattr, user) 505 { 506 uint64_t ino = 42; 507 int ns = EXTATTR_NAMESPACE_USER; 508 509 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 510 expect_removexattr(ino, "user.foo", 0); 511 512 ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo")) 513 << strerror(errno); 514 } 515 516 /* Successfully remove a system xattr */ 517 TEST_F(Removexattr, system) 518 { 519 uint64_t ino = 42; 520 int ns = EXTATTR_NAMESPACE_SYSTEM; 521 522 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 523 expect_removexattr(ino, "system.foo", 0); 524 525 ASSERT_EQ(0, extattr_delete_file(FULLPATH, ns, "foo")) 526 << strerror(errno); 527 } 528 529 /* 530 * If the filesystem returns ENOSYS, then it will be treated as a permanent 531 * failure and all future VOP_SETEXTATTR calls will fail with EOPNOTSUPP 532 * without querying the filesystem daemon 533 */ 534 TEST_F(Setxattr, enosys) 535 { 536 uint64_t ino = 42; 537 const char value[] = "whatever"; 538 ssize_t value_len = strlen(value) + 1; 539 int ns = EXTATTR_NAMESPACE_USER; 540 ssize_t r; 541 542 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 2); 543 expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOSYS)); 544 545 r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value, 546 value_len); 547 ASSERT_EQ(-1, r); 548 EXPECT_EQ(EOPNOTSUPP, errno); 549 550 /* Subsequent attempts should not query the filesystem at all */ 551 r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value, 552 value_len); 553 ASSERT_EQ(-1, r); 554 EXPECT_EQ(EOPNOTSUPP, errno); 555 } 556 557 /* 558 * SETXATTR will return ENOTSUP if the namespace is invalid or the filesystem 559 * as currently configured doesn't support extended attributes. 560 */ 561 TEST_F(Setxattr, enotsup) 562 { 563 uint64_t ino = 42; 564 const char value[] = "whatever"; 565 ssize_t value_len = strlen(value) + 1; 566 int ns = EXTATTR_NAMESPACE_USER; 567 ssize_t r; 568 569 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 570 expect_setxattr(ino, "user.foo", value, ReturnErrno(ENOTSUP)); 571 572 r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value, 573 value_len); 574 ASSERT_EQ(-1, r); 575 EXPECT_EQ(ENOTSUP, errno); 576 } 577 578 /* 579 * Successfully set a user attribute. 580 */ 581 TEST_F(Setxattr, user) 582 { 583 uint64_t ino = 42; 584 const char value[] = "whatever"; 585 ssize_t value_len = strlen(value) + 1; 586 int ns = EXTATTR_NAMESPACE_USER; 587 ssize_t r; 588 589 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 590 expect_setxattr(ino, "user.foo", value, ReturnErrno(0)); 591 592 r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value, 593 value_len); 594 ASSERT_EQ(value_len, r) << strerror(errno); 595 } 596 597 /* 598 * Successfully set a system attribute. 599 */ 600 TEST_F(Setxattr, system) 601 { 602 uint64_t ino = 42; 603 const char value[] = "whatever"; 604 ssize_t value_len = strlen(value) + 1; 605 int ns = EXTATTR_NAMESPACE_SYSTEM; 606 ssize_t r; 607 608 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 609 expect_setxattr(ino, "system.foo", value, ReturnErrno(0)); 610 611 r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value, 612 value_len); 613 ASSERT_EQ(value_len, r) << strerror(errno); 614 } 615 616 TEST_F(RofsXattr, deleteextattr_erofs) 617 { 618 uint64_t ino = 42; 619 int ns = EXTATTR_NAMESPACE_USER; 620 621 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 622 623 ASSERT_EQ(-1, extattr_delete_file(FULLPATH, ns, "foo")); 624 ASSERT_EQ(EROFS, errno); 625 } 626 627 TEST_F(RofsXattr, setextattr_erofs) 628 { 629 uint64_t ino = 42; 630 const char value[] = "whatever"; 631 ssize_t value_len = strlen(value) + 1; 632 int ns = EXTATTR_NAMESPACE_USER; 633 ssize_t r; 634 635 expect_lookup(RELPATH, ino, S_IFREG | 0644, 0, 1); 636 637 r = extattr_set_file(FULLPATH, ns, "foo", (const void*)value, 638 value_len); 639 ASSERT_EQ(-1, r); 640 EXPECT_EQ(EROFS, errno); 641 } 642