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