1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2017, Fedor Uporov
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: releng/12.0/sys/fs/ext2fs/ext2_extattr.c 327977 2018-01-14 20:46:39Z fsu $
29 */
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/types.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/vnode.h>
37 #include <sys/bio.h>
38 #include <sys/buf.h>
39 #include <sys/endian.h>
40 #include <sys/conf.h>
41 #include <sys/extattr.h>
42
43 #include <fs/ext2fs/fs.h>
44 #include <fs/ext2fs/ext2fs.h>
45 #include <fs/ext2fs/inode.h>
46 #include <fs/ext2fs/ext2_dinode.h>
47 #include <fs/ext2fs/ext2_mount.h>
48 #include <fs/ext2fs/ext2_extattr.h>
49 #include <fs/ext2fs/ext2_extern.h>
50
51 static int
52 ext2_extattr_attrnamespace_to_bsd(int attrnamespace)
53 {
54
55 switch (attrnamespace) {
56 case EXT4_XATTR_INDEX_SYSTEM:
57 return (EXTATTR_NAMESPACE_SYSTEM);
58
59 case EXT4_XATTR_INDEX_USER:
60 return (EXTATTR_NAMESPACE_USER);
61
62 case EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT:
63 return (POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE);
64
65 case EXT4_XATTR_INDEX_POSIX_ACL_ACCESS:
66 return (POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE);
67 }
68
69 return (EXTATTR_NAMESPACE_EMPTY);
70 }
71
72 static const char *
73 ext2_extattr_name_to_bsd(int attrnamespace, const char *name, int* name_len)
74 {
75
76 if (attrnamespace == EXT4_XATTR_INDEX_SYSTEM)
77 return (name);
78 else if (attrnamespace == EXT4_XATTR_INDEX_USER)
79 return (name);
80 else if (attrnamespace == EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT) {
81 *name_len = strlen(POSIX1E_ACL_DEFAULT_EXTATTR_NAME);
82 return (POSIX1E_ACL_DEFAULT_EXTATTR_NAME);
83 } else if (attrnamespace == EXT4_XATTR_INDEX_POSIX_ACL_ACCESS) {
84 *name_len = strlen(POSIX1E_ACL_ACCESS_EXTATTR_NAME);
85 return (POSIX1E_ACL_ACCESS_EXTATTR_NAME);
86 }
87
88 /*
89 * XXX: Not all linux namespaces are mapped to bsd for now,
90 * return NULL, which will be converted to ENOTSUP on upper layer.
91 */
92 #ifdef EXT2FS_DEBUG
93 printf("can not convert ext2fs name to bsd: namespace=%d\n", attrnamespace);
94 #endif
95
96 return (NULL);
97 }
98
99 static int
100 ext2_extattr_attrnamespace_to_linux(int attrnamespace, const char *name)
101 {
102
103 if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE &&
104 !strcmp(name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME))
105 return (EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT);
106
107 if (attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE &&
108 !strcmp(name, POSIX1E_ACL_ACCESS_EXTATTR_NAME))
109 return (EXT4_XATTR_INDEX_POSIX_ACL_ACCESS);
110
111 switch (attrnamespace) {
112 case EXTATTR_NAMESPACE_SYSTEM:
113 return (EXT4_XATTR_INDEX_SYSTEM);
114
115 case EXTATTR_NAMESPACE_USER:
116 return (EXT4_XATTR_INDEX_USER);
117 }
118
119 /*
120 * In this case namespace conversion should be unique,
121 * so this point is unreachable.
122 */
123 return (-1);
124 }
125
126 static const char *
127 ext2_extattr_name_to_linux(int attrnamespace, const char *name)
128 {
129
130 if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE ||
131 attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE)
132 return ("");
133 else
134 return (name);
135 }
136
137 int
138 ext2_extattr_valid_attrname(int attrnamespace, const char *attrname)
139 {
140 if (attrnamespace == EXTATTR_NAMESPACE_EMPTY)
141 return (EINVAL);
142
143 if (strlen(attrname) == 0)
144 return (EINVAL);
145
146 if (strlen(attrname) + 1 > EXT2_EXTATTR_NAMELEN_MAX)
147 return (ENAMETOOLONG);
148
149 return (0);
150 }
151
152 static int
153 ext2_extattr_check(struct ext2fs_extattr_entry *entry, char *end)
154 {
155 struct ext2fs_extattr_entry *next;
156
157 while (!EXT2_IS_LAST_ENTRY(entry)) {
158 next = EXT2_EXTATTR_NEXT(entry);
159 if ((char *)next >= end)
160 return (EIO);
161
162 entry = next;
163 }
164
165 return (0);
166 }
167
168 static int
169 ext2_extattr_block_check(struct inode *ip, struct buf *bp)
170 {
171 struct ext2fs_extattr_header *header;
172 int error;
173
174 header = (struct ext2fs_extattr_header *)bp->b_data;
175
176 error = ext2_extattr_check(EXT2_IFIRST(header),
177 bp->b_data + bp->b_bufsize);
178 if (error)
179 return (error);
180
181 return (ext2_extattr_blk_csum_verify(ip, bp));
182 }
183
184 int
185 ext2_extattr_inode_list(struct inode *ip, int attrnamespace,
186 struct uio *uio, size_t *size)
187 {
188 struct m_ext2fs *fs;
189 struct buf *bp;
190 struct ext2fs_extattr_dinode_header *header;
191 struct ext2fs_extattr_entry *entry;
192 const char *attr_name;
193 int name_len;
194 int error;
195
196 fs = ip->i_e2fs;
197
198 if ((error = bread(ip->i_devvp,
199 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
200 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
201 brelse(bp);
202 return (error);
203 }
204
205 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
206 ((char *)bp->b_data +
207 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
208
209 /* Check attributes magic value */
210 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
211 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
212
213 if (header->h_magic != EXTATTR_MAGIC) {
214 brelse(bp);
215 return (0);
216 }
217
218 error = ext2_extattr_check(EXT2_IFIRST(header),
219 (char *)dinode + EXT2_INODE_SIZE(fs));
220 if (error) {
221 brelse(bp);
222 return (error);
223 }
224
225 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
226 entry = EXT2_EXTATTR_NEXT(entry)) {
227 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
228 attrnamespace)
229 continue;
230
231 name_len = entry->e_name_len;
232 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
233 entry->e_name, &name_len);
234 if (!attr_name) {
235 brelse(bp);
236 return (ENOTSUP);
237 }
238
239 if (size != NULL)
240 *size += name_len + 1;
241
242 if (uio != NULL) {
243 char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
244 name[0] = name_len;
245 memcpy(&name[1], attr_name, name_len);
246 error = uiomove(name, name_len + 1, uio);
247 free(name, M_TEMP);
248 if (error)
249 break;
250 }
251 }
252
253 brelse(bp);
254
255 return (error);
256 }
257
258 int
259 ext2_extattr_block_list(struct inode *ip, int attrnamespace,
260 struct uio *uio, size_t *size)
261 {
262 struct m_ext2fs *fs;
263 struct buf *bp;
264 struct ext2fs_extattr_header *header;
265 struct ext2fs_extattr_entry *entry;
266 const char *attr_name;
267 int name_len;
268 int error;
269
270 fs = ip->i_e2fs;
271
272 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
273 fs->e2fs_bsize, NOCRED, &bp);
274 if (error) {
275 brelse(bp);
276 return (error);
277 }
278
279 /* Check attributes magic value */
280 header = EXT2_HDR(bp);
281 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
282 brelse(bp);
283 return (EINVAL);
284 }
285
286 error = ext2_extattr_block_check(ip, bp);
287 if (error) {
288 brelse(bp);
289 return (error);
290 }
291
292 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
293 entry = EXT2_EXTATTR_NEXT(entry)) {
294 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
295 attrnamespace)
296 continue;
297
298 name_len = entry->e_name_len;
299 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
300 entry->e_name, &name_len);
301 if (!attr_name) {
302 brelse(bp);
303 return (ENOTSUP);
304 }
305
306 if (size != NULL)
307 *size += name_len + 1;
308
309 if (uio != NULL) {
310 char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
311 name[0] = name_len;
312 memcpy(&name[1], attr_name, name_len);
313 error = uiomove(name, name_len + 1, uio);
314 free(name, M_TEMP);
315 if (error)
316 break;
317 }
318 }
319
320 brelse(bp);
321
322 return (error);
323 }
324
325 int
326 ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
327 const char *name, struct uio *uio, size_t *size)
328 {
329 struct m_ext2fs *fs;
330 struct buf *bp;
331 struct ext2fs_extattr_dinode_header *header;
332 struct ext2fs_extattr_entry *entry;
333 const char *attr_name;
334 int name_len;
335 int error;
336
337 fs = ip->i_e2fs;
338
339 if ((error = bread(ip->i_devvp,
340 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
341 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
342 brelse(bp);
343 return (error);
344 }
345
346 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
347 ((char *)bp->b_data +
348 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
349
350 /* Check attributes magic value */
351 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
352 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
353
354 if (header->h_magic != EXTATTR_MAGIC) {
355 brelse(bp);
356 return (ENOATTR);
357 }
358
359 error = ext2_extattr_check(EXT2_IFIRST(header),
360 (char *)dinode + EXT2_INODE_SIZE(fs));
361 if (error) {
362 brelse(bp);
363 return (error);
364 }
365
366 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
367 entry = EXT2_EXTATTR_NEXT(entry)) {
368 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
369 attrnamespace)
370 continue;
371
372 name_len = entry->e_name_len;
373 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
374 entry->e_name, &name_len);
375 if (!attr_name) {
376 brelse(bp);
377 return (ENOTSUP);
378 }
379
380 if (strlen(name) == name_len &&
381 0 == strncmp(attr_name, name, name_len)) {
382 if (size != NULL)
383 *size += entry->e_value_size;
384
385 if (uio != NULL)
386 error = uiomove(((char *)EXT2_IFIRST(header)) +
387 entry->e_value_offs, entry->e_value_size, uio);
388
389 brelse(bp);
390 return (error);
391 }
392 }
393
394 brelse(bp);
395
396 return (ENOATTR);
397 }
398
399 int
400 ext2_extattr_block_get(struct inode *ip, int attrnamespace,
401 const char *name, struct uio *uio, size_t *size)
402 {
403 struct m_ext2fs *fs;
404 struct buf *bp;
405 struct ext2fs_extattr_header *header;
406 struct ext2fs_extattr_entry *entry;
407 const char *attr_name;
408 int name_len;
409 int error;
410
411 fs = ip->i_e2fs;
412
413 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
414 fs->e2fs_bsize, NOCRED, &bp);
415 if (error) {
416 brelse(bp);
417 return (error);
418 }
419
420 /* Check attributes magic value */
421 header = EXT2_HDR(bp);
422 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
423 brelse(bp);
424 return (EINVAL);
425 }
426
427 error = ext2_extattr_block_check(ip, bp);
428 if (error) {
429 brelse(bp);
430 return (error);
431 }
432
433 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
434 entry = EXT2_EXTATTR_NEXT(entry)) {
435 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
436 attrnamespace)
437 continue;
438
439 name_len = entry->e_name_len;
440 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
441 entry->e_name, &name_len);
442 if (!attr_name) {
443 brelse(bp);
444 return (ENOTSUP);
445 }
446
447 if (strlen(name) == name_len &&
448 0 == strncmp(attr_name, name, name_len)) {
449 if (size != NULL)
450 *size += entry->e_value_size;
451
452 if (uio != NULL)
453 error = uiomove(bp->b_data + entry->e_value_offs,
454 entry->e_value_size, uio);
455
456 brelse(bp);
457 return (error);
458 }
459 }
460
461 brelse(bp);
462
463 return (ENOATTR);
464 }
465
466 static uint16_t
467 ext2_extattr_delete_value(char *off,
468 struct ext2fs_extattr_entry *first_entry,
469 struct ext2fs_extattr_entry *entry, char *end)
470 {
471 uint16_t min_offs;
472 struct ext2fs_extattr_entry *next;
473
474 min_offs = end - off;
475 next = first_entry;
476 while (!EXT2_IS_LAST_ENTRY(next)) {
477 if (min_offs > next->e_value_offs && next->e_value_offs > 0)
478 min_offs = next->e_value_offs;
479
480 next = EXT2_EXTATTR_NEXT(next);
481 }
482
483 if (entry->e_value_size == 0)
484 return (min_offs);
485
486 memmove(off + min_offs + EXT2_EXTATTR_SIZE(entry->e_value_size),
487 off + min_offs, entry->e_value_offs - min_offs);
488
489 /* Adjust all value offsets */
490 next = first_entry;
491 while (!EXT2_IS_LAST_ENTRY(next))
492 {
493 if (next->e_value_offs > 0 &&
494 next->e_value_offs < entry->e_value_offs)
495 next->e_value_offs +=
496 EXT2_EXTATTR_SIZE(entry->e_value_size);
497
498 next = EXT2_EXTATTR_NEXT(next);
499 }
500
501 min_offs += EXT2_EXTATTR_SIZE(entry->e_value_size);
502
503 return (min_offs);
504 }
505
506 static void
507 ext2_extattr_delete_entry(char *off,
508 struct ext2fs_extattr_entry *first_entry,
509 struct ext2fs_extattr_entry *entry, char *end)
510 {
511 char *pad;
512 struct ext2fs_extattr_entry *next;
513
514 /* Clean entry value */
515 ext2_extattr_delete_value(off, first_entry, entry, end);
516
517 /* Clean the entry */
518 next = first_entry;
519 while (!EXT2_IS_LAST_ENTRY(next))
520 next = EXT2_EXTATTR_NEXT(next);
521
522 pad = (char*)next + sizeof(uint32_t);
523
524 memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len),
525 pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len)));
526 }
527
528 int
529 ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name)
530 {
531 struct m_ext2fs *fs;
532 struct buf *bp;
533 struct ext2fs_extattr_dinode_header *header;
534 struct ext2fs_extattr_entry *entry;
535 const char *attr_name;
536 int name_len;
537 int error;
538
539 fs = ip->i_e2fs;
540
541 if ((error = bread(ip->i_devvp,
542 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
543 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
544 brelse(bp);
545 return (error);
546 }
547
548 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
549 ((char *)bp->b_data +
550 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
551
552 /* Check attributes magic value */
553 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
554 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
555
556 if (header->h_magic != EXTATTR_MAGIC) {
557 brelse(bp);
558 return (ENOATTR);
559 }
560
561 error = ext2_extattr_check(EXT2_IFIRST(header),
562 (char *)dinode + EXT2_INODE_SIZE(fs));
563 if (error) {
564 brelse(bp);
565 return (error);
566 }
567
568 /* If I am last entry, just make magic zero */
569 entry = EXT2_IFIRST(header);
570 if ((EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) &&
571 (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
572 attrnamespace)) {
573
574 name_len = entry->e_name_len;
575 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
576 entry->e_name, &name_len);
577 if (!attr_name) {
578 brelse(bp);
579 return (ENOTSUP);
580 }
581
582 if (strlen(name) == name_len &&
583 0 == strncmp(attr_name, name, name_len)) {
584 memset(header, 0, sizeof(struct ext2fs_extattr_dinode_header));
585
586 return (bwrite(bp));
587 }
588 }
589
590 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
591 entry = EXT2_EXTATTR_NEXT(entry)) {
592 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
593 attrnamespace)
594 continue;
595
596 name_len = entry->e_name_len;
597 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
598 entry->e_name, &name_len);
599 if (!attr_name) {
600 brelse(bp);
601 return (ENOTSUP);
602 }
603
604 if (strlen(name) == name_len &&
605 0 == strncmp(attr_name, name, name_len)) {
606 ext2_extattr_delete_entry((char *)EXT2_IFIRST(header),
607 EXT2_IFIRST(header), entry,
608 (char *)dinode + EXT2_INODE_SIZE(fs));
609
610 return (bwrite(bp));
611 }
612 }
613
614 brelse(bp);
615
616 return (ENOATTR);
617 }
618
619 static int
620 ext2_extattr_block_clone(struct inode *ip, struct buf **bpp)
621 {
622 struct m_ext2fs *fs;
623 struct buf *sbp;
624 struct buf *cbp;
625 struct ext2fs_extattr_header *header;
626 uint64_t facl;
627
628 fs = ip->i_e2fs;
629 sbp = *bpp;
630
631 header = EXT2_HDR(sbp);
632 if (header->h_magic != EXTATTR_MAGIC || header->h_refcount == 1)
633 return (EINVAL);
634
635 facl = ext2_alloc_meta(ip);
636 if (!facl)
637 return (ENOSPC);
638
639 cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0);
640 if (!cbp) {
641 ext2_blkfree(ip, facl, fs->e2fs_bsize);
642 return (EIO);
643 }
644
645 memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize);
646 header->h_refcount--;
647 bwrite(sbp);
648
649 ip->i_facl = facl;
650 ext2_update(ip->i_vnode, 1);
651
652 header = EXT2_HDR(cbp);
653 header->h_refcount = 1;
654
655 *bpp = cbp;
656
657 return (0);
658 }
659
660 int
661 ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name)
662 {
663 struct m_ext2fs *fs;
664 struct buf *bp;
665 struct ext2fs_extattr_header *header;
666 struct ext2fs_extattr_entry *entry;
667 const char *attr_name;
668 int name_len;
669 int error;
670
671 fs = ip->i_e2fs;
672
673 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
674 fs->e2fs_bsize, NOCRED, &bp);
675 if (error) {
676 brelse(bp);
677 return (error);
678 }
679
680 /* Check attributes magic value */
681 header = EXT2_HDR(bp);
682 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
683 brelse(bp);
684 return (EINVAL);
685 }
686
687 error = ext2_extattr_block_check(ip, bp);
688 if (error) {
689 brelse(bp);
690 return (error);
691 }
692
693 if (header->h_refcount > 1) {
694 error = ext2_extattr_block_clone(ip, &bp);
695 if (error) {
696 brelse(bp);
697 return (error);
698 }
699 }
700
701 /* If I am last entry, clean me and free the block */
702 entry = EXT2_FIRST_ENTRY(bp);
703 if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry)) &&
704 (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
705 attrnamespace)) {
706
707 name_len = entry->e_name_len;
708 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
709 entry->e_name, &name_len);
710 if (!attr_name) {
711 brelse(bp);
712 return (ENOTSUP);
713 }
714
715 if (strlen(name) == name_len &&
716 0 == strncmp(attr_name, name, name_len)) {
717 ip->i_blocks -= btodb(fs->e2fs_bsize);
718 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
719 ip->i_facl = 0;
720 error = ext2_update(ip->i_vnode, 1);
721
722 brelse(bp);
723 return (error);
724 }
725 }
726
727 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
728 entry = EXT2_EXTATTR_NEXT(entry)) {
729 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
730 attrnamespace)
731 continue;
732
733 name_len = entry->e_name_len;
734 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
735 entry->e_name, &name_len);
736 if (!attr_name) {
737 brelse(bp);
738 return (ENOTSUP);
739 }
740
741 if (strlen(name) == name_len &&
742 0 == strncmp(attr_name, name, name_len)) {
743 ext2_extattr_delete_entry(bp->b_data,
744 EXT2_FIRST_ENTRY(bp), entry,
745 bp->b_data + bp->b_bufsize);
746
747 return (bwrite(bp));
748 }
749 }
750
751 brelse(bp);
752
753 return (ENOATTR);
754 }
755
756 static struct ext2fs_extattr_entry *
757 allocate_entry(const char *name, int attrnamespace, uint16_t offs,
758 uint32_t size, uint32_t hash)
759 {
760 const char *attr_name;
761 int name_len;
762 struct ext2fs_extattr_entry *entry;
763
764 attr_name = ext2_extattr_name_to_linux(attrnamespace, name);
765 name_len = strlen(attr_name);
766
767 entry = malloc(sizeof(struct ext2fs_extattr_entry) + name_len,
768 M_TEMP, M_WAITOK);
769
770 entry->e_name_len = name_len;
771 entry->e_name_index = ext2_extattr_attrnamespace_to_linux(attrnamespace, name);
772 entry->e_value_offs = offs;
773 entry->e_value_block = 0;
774 entry->e_value_size = size;
775 entry->e_hash = hash;
776 memcpy(entry->e_name, name, name_len);
777
778 return (entry);
779 }
780
781 static void
782 free_entry(struct ext2fs_extattr_entry *entry)
783 {
784
785 free(entry, M_TEMP);
786 }
787
788 static int
789 ext2_extattr_get_size(struct ext2fs_extattr_entry *first_entry,
790 struct ext2fs_extattr_entry *exist_entry, int header_size,
791 int name_len, int new_size)
792 {
793 struct ext2fs_extattr_entry *entry;
794 int size;
795
796 size = header_size;
797 size += sizeof(uint32_t);
798
799 if (NULL == exist_entry) {
800 size += EXT2_EXTATTR_LEN(name_len);
801 size += EXT2_EXTATTR_SIZE(new_size);
802 }
803
804 if (first_entry)
805 for (entry = first_entry; !EXT2_IS_LAST_ENTRY(entry);
806 entry = EXT2_EXTATTR_NEXT(entry)) {
807 if (entry != exist_entry)
808 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
809 EXT2_EXTATTR_SIZE(entry->e_value_size);
810 else
811 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
812 EXT2_EXTATTR_SIZE(new_size);
813 }
814
815 return (size);
816 }
817
818 static void
819 ext2_extattr_set_exist_entry(char *off,
820 struct ext2fs_extattr_entry *first_entry,
821 struct ext2fs_extattr_entry *entry,
822 char *end, struct uio *uio)
823 {
824 uint16_t min_offs;
825
826 min_offs = ext2_extattr_delete_value(off, first_entry, entry, end);
827
828 entry->e_value_size = uio->uio_resid;
829 if (entry->e_value_size)
830 entry->e_value_offs = min_offs -
831 EXT2_EXTATTR_SIZE(uio->uio_resid);
832 else
833 entry->e_value_offs = 0;
834
835 uiomove(off + entry->e_value_offs, entry->e_value_size, uio);
836 }
837
838 static struct ext2fs_extattr_entry *
839 ext2_extattr_set_new_entry(char *off, struct ext2fs_extattr_entry *first_entry,
840 const char *name, int attrnamespace, char *end, struct uio *uio)
841 {
842 int name_len;
843 char *pad;
844 uint16_t min_offs;
845 struct ext2fs_extattr_entry *entry;
846 struct ext2fs_extattr_entry *new_entry;
847
848 /* Find pad's */
849 min_offs = end - off;
850 entry = first_entry;
851 while (!EXT2_IS_LAST_ENTRY(entry)) {
852 if (min_offs > entry->e_value_offs && entry->e_value_offs > 0)
853 min_offs = entry->e_value_offs;
854
855 entry = EXT2_EXTATTR_NEXT(entry);
856 }
857
858 pad = (char*)entry + sizeof(uint32_t);
859
860 /* Find entry insert position */
861 name_len = strlen(name);
862 entry = first_entry;
863 while (!EXT2_IS_LAST_ENTRY(entry)) {
864 if (!(attrnamespace - entry->e_name_index) &&
865 !(name_len - entry->e_name_len))
866 if (memcmp(name, entry->e_name, name_len) <= 0)
867 break;
868
869 entry = EXT2_EXTATTR_NEXT(entry);
870 }
871
872 /* Create new entry and insert it */
873 new_entry = allocate_entry(name, attrnamespace, 0, uio->uio_resid, 0);
874 memmove((char *)entry + EXT2_EXTATTR_LEN(new_entry->e_name_len), entry,
875 pad - (char*)entry);
876
877 memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len));
878 free_entry(new_entry);
879
880 new_entry = entry;
881 if (new_entry->e_value_size > 0)
882 new_entry->e_value_offs = min_offs -
883 EXT2_EXTATTR_SIZE(new_entry->e_value_size);
884
885 uiomove(off + new_entry->e_value_offs, new_entry->e_value_size, uio);
886
887 return (new_entry);
888 }
889
890 int
891 ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
892 const char *name, struct uio *uio)
893 {
894 struct m_ext2fs *fs;
895 struct buf *bp;
896 struct ext2fs_extattr_dinode_header *header;
897 struct ext2fs_extattr_entry *entry;
898 const char *attr_name;
899 int name_len;
900 size_t size = 0, max_size;
901 int error;
902
903 fs = ip->i_e2fs;
904
905 if ((error = bread(ip->i_devvp,
906 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
907 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
908 brelse(bp);
909 return (error);
910 }
911
912 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
913 ((char *)bp->b_data +
914 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
915
916 /* Check attributes magic value */
917 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
918 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
919
920 if (header->h_magic != EXTATTR_MAGIC) {
921 brelse(bp);
922 return (ENOSPC);
923 }
924
925 error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode +
926 EXT2_INODE_SIZE(fs));
927 if (error) {
928 brelse(bp);
929 return (error);
930 }
931
932 /* Find if entry exist */
933 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
934 entry = EXT2_EXTATTR_NEXT(entry)) {
935 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
936 attrnamespace)
937 continue;
938
939 name_len = entry->e_name_len;
940 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
941 entry->e_name, &name_len);
942 if (!attr_name) {
943 brelse(bp);
944 return (ENOTSUP);
945 }
946
947 if (strlen(name) == name_len &&
948 0 == strncmp(attr_name, name, name_len))
949 break;
950 }
951
952 max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE -
953 dinode->e2di_extra_isize;
954
955 if (!EXT2_IS_LAST_ENTRY(entry)) {
956 size = ext2_extattr_get_size(EXT2_IFIRST(header), entry,
957 sizeof(struct ext2fs_extattr_dinode_header),
958 entry->e_name_len, uio->uio_resid);
959 if (size > max_size) {
960 brelse(bp);
961 return (ENOSPC);
962 }
963
964 ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(header),
965 EXT2_IFIRST(header), entry, (char *)header + max_size, uio);
966 } else {
967 /* Ensure that the same entry does not exist in the block */
968 if (ip->i_facl) {
969 error = ext2_extattr_block_get(ip, attrnamespace, name,
970 NULL, &size);
971 if (error != ENOATTR || size > 0) {
972 brelse(bp);
973 if (size > 0)
974 error = ENOSPC;
975
976 return (error);
977 }
978 }
979
980 size = ext2_extattr_get_size(EXT2_IFIRST(header), NULL,
981 sizeof(struct ext2fs_extattr_dinode_header),
982 entry->e_name_len, uio->uio_resid);
983 if (size > max_size) {
984 brelse(bp);
985 return (ENOSPC);
986 }
987
988 ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header),
989 EXT2_IFIRST(header), name, attrnamespace,
990 (char *)header + max_size, uio);
991 }
992
993 return (bwrite(bp));
994 }
995
996 static void
997 ext2_extattr_hash_entry(struct ext2fs_extattr_header *header,
998 struct ext2fs_extattr_entry *entry)
999 {
1000 uint32_t hash = 0;
1001 char *name = entry->e_name;
1002 int n;
1003
1004 for (n=0; n < entry->e_name_len; n++) {
1005 hash = (hash << EXT2_EXTATTR_NAME_HASH_SHIFT) ^
1006 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_NAME_HASH_SHIFT)) ^
1007 (*name++);
1008 }
1009
1010 if (entry->e_value_block == 0 && entry->e_value_size != 0) {
1011 uint32_t *value = (uint32_t *)((char *)header + entry->e_value_offs);
1012 for (n = (entry->e_value_size +
1013 EXT2_EXTATTR_ROUND) >> EXT2_EXTATTR_PAD_BITS; n; n--) {
1014 hash = (hash << EXT2_EXTATTR_VALUE_HASH_SHIFT) ^
1015 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_VALUE_HASH_SHIFT)) ^
1016 (*value++);
1017 }
1018 }
1019
1020 entry->e_hash = hash;
1021 }
1022
1023 static void
1024 ext2_extattr_rehash(struct ext2fs_extattr_header *header,
1025 struct ext2fs_extattr_entry *entry)
1026 {
1027 struct ext2fs_extattr_entry *here;
1028 uint32_t hash = 0;
1029
1030 ext2_extattr_hash_entry(header, entry);
1031
1032 here = EXT2_ENTRY(header+1);
1033 while (!EXT2_IS_LAST_ENTRY(here)) {
1034 if (!here->e_hash) {
1035 /* Block is not shared if an entry's hash value == 0 */
1036 hash = 0;
1037 break;
1038 }
1039
1040 hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^
1041 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^
1042 here->e_hash;
1043
1044 here = EXT2_EXTATTR_NEXT(here);
1045 }
1046
1047 header->h_hash = hash;
1048 }
1049
1050 int
1051 ext2_extattr_block_set(struct inode *ip, int attrnamespace,
1052 const char *name, struct uio *uio)
1053 {
1054 struct m_ext2fs *fs;
1055 struct buf *bp;
1056 struct ext2fs_extattr_header *header;
1057 struct ext2fs_extattr_entry *entry;
1058 const char *attr_name;
1059 int name_len;
1060 size_t size;
1061 int error;
1062
1063 fs = ip->i_e2fs;
1064
1065 if (ip->i_facl) {
1066 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1067 fs->e2fs_bsize, NOCRED, &bp);
1068 if (error) {
1069 brelse(bp);
1070 return (error);
1071 }
1072
1073 /* Check attributes magic value */
1074 header = EXT2_HDR(bp);
1075 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1076 brelse(bp);
1077 return (EINVAL);
1078 }
1079
1080 error = ext2_extattr_block_check(ip, bp);
1081 if (error) {
1082 brelse(bp);
1083 return (error);
1084 }
1085
1086 if (header->h_refcount > 1) {
1087 error = ext2_extattr_block_clone(ip, &bp);
1088 if (error) {
1089 brelse(bp);
1090 return (error);
1091 }
1092
1093 header = EXT2_HDR(bp);
1094 }
1095
1096 /* Find if entry exist */
1097 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
1098 entry = EXT2_EXTATTR_NEXT(entry)) {
1099 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
1100 attrnamespace)
1101 continue;
1102
1103 name_len = entry->e_name_len;
1104 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
1105 entry->e_name, &name_len);
1106 if (!attr_name) {
1107 brelse(bp);
1108 return (ENOTSUP);
1109 }
1110
1111 if (strlen(name) == name_len &&
1112 0 == strncmp(attr_name, name, name_len))
1113 break;
1114 }
1115
1116 if (!EXT2_IS_LAST_ENTRY(entry)) {
1117 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), entry,
1118 sizeof(struct ext2fs_extattr_header),
1119 entry->e_name_len, uio->uio_resid);
1120 if (size > bp->b_bufsize) {
1121 brelse(bp);
1122 return (ENOSPC);
1123 }
1124
1125 ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1126 entry, bp->b_data + bp->b_bufsize, uio);
1127 } else {
1128 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), NULL,
1129 sizeof(struct ext2fs_extattr_header),
1130 strlen(name), uio->uio_resid);
1131 if (size > bp->b_bufsize) {
1132 brelse(bp);
1133 return (ENOSPC);
1134 }
1135
1136 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1137 name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1138
1139 /* Clean the same entry in the inode */
1140 error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1141 if (error && error != ENOATTR) {
1142 brelse(bp);
1143 return (error);
1144 }
1145 }
1146
1147 ext2_extattr_rehash(header, entry);
1148 ext2_extattr_blk_csum_set(ip, bp);
1149
1150 return (bwrite(bp));
1151 }
1152
1153 size = ext2_extattr_get_size(NULL, NULL,
1154 sizeof(struct ext2fs_extattr_header),
1155 strlen(ext2_extattr_name_to_linux(attrnamespace, name)), uio->uio_resid);
1156 if (size > fs->e2fs_bsize)
1157 return (ENOSPC);
1158
1159 /* Allocate block, fill EA header and insert entry */
1160 ip->i_facl = ext2_alloc_meta(ip);
1161 if (0 == ip->i_facl)
1162 return (ENOSPC);
1163
1164 ip->i_blocks += btodb(fs->e2fs_bsize);
1165 ext2_update(ip->i_vnode, 1);
1166
1167 bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0);
1168 if (!bp) {
1169 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
1170 ip->i_blocks -= btodb(fs->e2fs_bsize);
1171 ip->i_facl = 0;
1172 ext2_update(ip->i_vnode, 1);
1173 return (EIO);
1174 }
1175
1176 header = EXT2_HDR(bp);
1177 header->h_magic = EXTATTR_MAGIC;
1178 header->h_refcount = 1;
1179 header->h_blocks = 1;
1180 header->h_hash = 0;
1181 memset(header->h_reserved, 0, sizeof(header->h_reserved));
1182 memcpy(bp->b_data, header, sizeof(struct ext2fs_extattr_header));
1183 memset(EXT2_FIRST_ENTRY(bp), 0, sizeof(uint32_t));
1184
1185 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1186 name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1187
1188 /* Clean the same entry in the inode */
1189 error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1190 if (error && error != ENOATTR) {
1191 brelse(bp);
1192 return (error);
1193 }
1194
1195 ext2_extattr_rehash(header, entry);
1196 ext2_extattr_blk_csum_set(ip, bp);
1197
1198 return (bwrite(bp));
1199 }
1200
1201 int ext2_extattr_free(struct inode *ip)
1202 {
1203 struct m_ext2fs *fs;
1204 struct buf *bp;
1205 struct ext2fs_extattr_header *header;
1206 int error;
1207
1208 fs = ip->i_e2fs;
1209
1210 if (!ip->i_facl)
1211 return (0);
1212
1213 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1214 fs->e2fs_bsize, NOCRED, &bp);
1215 if (error) {
1216 brelse(bp);
1217 return (error);
1218 }
1219
1220 /* Check attributes magic value */
1221 header = EXT2_HDR(bp);
1222 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1223 brelse(bp);
1224 return (EINVAL);
1225 }
1226
1227 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp),
1228 bp->b_data + bp->b_bufsize);
1229 if (error) {
1230 brelse(bp);
1231 return (error);
1232 }
1233
1234 if (header->h_refcount > 1) {
1235 header->h_refcount--;
1236 bwrite(bp);
1237 } else {
1238 ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize);
1239 brelse(bp);
1240 }
1241
1242 ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize);
1243 ip->i_facl = 0;
1244 ext2_update(ip->i_vnode, 1);
1245
1246 return (0);
1247 }
Cache object: 9784cfeea87af9cb339531fd398cc1b0
|