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