2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2017, Fedor Uporov
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
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>
39 #include <sys/endian.h>
41 #include <sys/extattr.h>
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>
52 ext2_extattr_attrnamespace_to_bsd(int attrnamespace)
55 switch (attrnamespace) {
56 case EXT4_XATTR_INDEX_SYSTEM:
57 return (EXTATTR_NAMESPACE_SYSTEM);
59 case EXT4_XATTR_INDEX_USER:
60 return (EXTATTR_NAMESPACE_USER);
62 case EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT:
63 return (POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE);
65 case EXT4_XATTR_INDEX_POSIX_ACL_ACCESS:
66 return (POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE);
69 return (EXTATTR_NAMESPACE_EMPTY);
73 ext2_extattr_name_to_bsd(int attrnamespace, const char *name, int* name_len)
76 if (attrnamespace == EXT4_XATTR_INDEX_SYSTEM)
78 else if (attrnamespace == EXT4_XATTR_INDEX_USER)
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);
89 * XXX: Not all linux namespaces are mapped to bsd for now,
90 * return NULL, which will be converted to ENOTSUP on upper layer.
93 printf("can not convert ext2fs name to bsd: namespace=%d\n", attrnamespace);
100 ext2_extattr_attrnamespace_to_linux(int attrnamespace, const char *name)
103 if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE &&
104 !strcmp(name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME))
105 return (EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT);
107 if (attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE &&
108 !strcmp(name, POSIX1E_ACL_ACCESS_EXTATTR_NAME))
109 return (EXT4_XATTR_INDEX_POSIX_ACL_ACCESS);
111 switch (attrnamespace) {
112 case EXTATTR_NAMESPACE_SYSTEM:
113 return (EXT4_XATTR_INDEX_SYSTEM);
115 case EXTATTR_NAMESPACE_USER:
116 return (EXT4_XATTR_INDEX_USER);
120 * In this case namespace conversion should be unique,
121 * so this point is unreachable.
127 ext2_extattr_name_to_linux(int attrnamespace, const char *name)
130 if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE ||
131 attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE)
138 ext2_extattr_valid_attrname(int attrnamespace, const char *attrname)
140 if (attrnamespace == EXTATTR_NAMESPACE_EMPTY)
143 if (strlen(attrname) == 0)
146 if (strlen(attrname) + 1 > EXT2_EXTATTR_NAMELEN_MAX)
147 return (ENAMETOOLONG);
153 ext2_extattr_check(struct ext2fs_extattr_entry *entry, char *end)
155 struct ext2fs_extattr_entry *next;
157 while (!EXT2_IS_LAST_ENTRY(entry)) {
158 next = EXT2_EXTATTR_NEXT(entry);
159 if ((char *)next >= end)
169 ext2_extattr_inode_list(struct inode *ip, int attrnamespace,
170 struct uio *uio, size_t *size)
174 struct ext2fs_extattr_dinode_header *header;
175 struct ext2fs_extattr_entry *entry;
176 const char *attr_name;
182 if ((error = bread(ip->i_devvp,
183 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
184 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
189 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
190 ((char *)bp->b_data +
191 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
193 /* Check attributes magic value */
194 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
195 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
197 if (header->h_magic != EXTATTR_MAGIC) {
202 error = ext2_extattr_check(EXT2_IFIRST(header),
203 (char *)dinode + EXT2_INODE_SIZE(fs));
209 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
210 entry = EXT2_EXTATTR_NEXT(entry)) {
211 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
215 name_len = entry->e_name_len;
216 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
217 entry->e_name, &name_len);
224 *size += name_len + 1;
227 char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
229 memcpy(&name[1], attr_name, name_len);
230 error = uiomove(name, name_len + 1, uio);
243 ext2_extattr_block_list(struct inode *ip, int attrnamespace,
244 struct uio *uio, size_t *size)
248 struct ext2fs_extattr_header *header;
249 struct ext2fs_extattr_entry *entry;
250 const char *attr_name;
256 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
257 fs->e2fs_bsize, NOCRED, &bp);
263 /* Check attributes magic value */
264 header = EXT2_HDR(bp);
265 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
270 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize);
276 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
277 entry = EXT2_EXTATTR_NEXT(entry)) {
278 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
282 name_len = entry->e_name_len;
283 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
284 entry->e_name, &name_len);
291 *size += name_len + 1;
294 char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
296 memcpy(&name[1], attr_name, name_len);
297 error = uiomove(name, name_len + 1, uio);
310 ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
311 const char *name, struct uio *uio, size_t *size)
315 struct ext2fs_extattr_dinode_header *header;
316 struct ext2fs_extattr_entry *entry;
317 const char *attr_name;
323 if ((error = bread(ip->i_devvp,
324 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
325 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
330 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
331 ((char *)bp->b_data +
332 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
334 /* Check attributes magic value */
335 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
336 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
338 if (header->h_magic != EXTATTR_MAGIC) {
343 error = ext2_extattr_check(EXT2_IFIRST(header),
344 (char *)dinode + EXT2_INODE_SIZE(fs));
350 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
351 entry = EXT2_EXTATTR_NEXT(entry)) {
352 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
356 name_len = entry->e_name_len;
357 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
358 entry->e_name, &name_len);
364 if (strlen(name) == name_len &&
365 0 == strncmp(attr_name, name, name_len)) {
367 *size += entry->e_value_size;
370 error = uiomove(((char *)EXT2_IFIRST(header)) +
371 entry->e_value_offs, entry->e_value_size, uio);
384 ext2_extattr_block_get(struct inode *ip, int attrnamespace,
385 const char *name, struct uio *uio, size_t *size)
389 struct ext2fs_extattr_header *header;
390 struct ext2fs_extattr_entry *entry;
391 const char *attr_name;
397 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
398 fs->e2fs_bsize, NOCRED, &bp);
404 /* Check attributes magic value */
405 header = EXT2_HDR(bp);
406 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
411 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize);
417 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
418 entry = EXT2_EXTATTR_NEXT(entry)) {
419 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
423 name_len = entry->e_name_len;
424 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
425 entry->e_name, &name_len);
431 if (strlen(name) == name_len &&
432 0 == strncmp(attr_name, name, name_len)) {
434 *size += entry->e_value_size;
437 error = uiomove(bp->b_data + entry->e_value_offs,
438 entry->e_value_size, uio);
451 ext2_extattr_delete_value(char *off,
452 struct ext2fs_extattr_entry *first_entry,
453 struct ext2fs_extattr_entry *entry, char *end)
456 struct ext2fs_extattr_entry *next;
458 min_offs = end - off;
460 while (!EXT2_IS_LAST_ENTRY(next)) {
461 if (min_offs > next->e_value_offs && next->e_value_offs > 0)
462 min_offs = next->e_value_offs;
464 next = EXT2_EXTATTR_NEXT(next);
467 if (entry->e_value_size == 0)
470 memmove(off + min_offs + EXT2_EXTATTR_SIZE(entry->e_value_size),
471 off + min_offs, entry->e_value_offs - min_offs);
473 /* Adjust all value offsets */
475 while (!EXT2_IS_LAST_ENTRY(next))
477 if (next->e_value_offs > 0 &&
478 next->e_value_offs < entry->e_value_offs)
479 next->e_value_offs +=
480 EXT2_EXTATTR_SIZE(entry->e_value_size);
482 next = EXT2_EXTATTR_NEXT(next);
485 min_offs += EXT2_EXTATTR_SIZE(entry->e_value_size);
491 ext2_extattr_delete_entry(char *off,
492 struct ext2fs_extattr_entry *first_entry,
493 struct ext2fs_extattr_entry *entry, char *end)
496 struct ext2fs_extattr_entry *next;
498 /* Clean entry value */
499 ext2_extattr_delete_value(off, first_entry, entry, end);
501 /* Clean the entry */
503 while (!EXT2_IS_LAST_ENTRY(next))
504 next = EXT2_EXTATTR_NEXT(next);
506 pad = (char*)next + sizeof(uint32_t);
508 memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len),
509 pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len)));
513 ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name)
517 struct ext2fs_extattr_dinode_header *header;
518 struct ext2fs_extattr_entry *entry;
519 const char *attr_name;
525 if ((error = bread(ip->i_devvp,
526 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
527 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
532 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
533 ((char *)bp->b_data +
534 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
536 /* Check attributes magic value */
537 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
538 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
540 if (header->h_magic != EXTATTR_MAGIC) {
545 error = ext2_extattr_check(EXT2_IFIRST(header),
546 (char *)dinode + EXT2_INODE_SIZE(fs));
552 /* If I am last entry, just make magic zero */
553 entry = EXT2_IFIRST(header);
554 if ((EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) &&
555 (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
558 name_len = entry->e_name_len;
559 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
560 entry->e_name, &name_len);
566 if (strlen(name) == name_len &&
567 0 == strncmp(attr_name, name, name_len)) {
568 memset(header, 0, sizeof(struct ext2fs_extattr_dinode_header));
574 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
575 entry = EXT2_EXTATTR_NEXT(entry)) {
576 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
580 name_len = entry->e_name_len;
581 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
582 entry->e_name, &name_len);
588 if (strlen(name) == name_len &&
589 0 == strncmp(attr_name, name, name_len)) {
590 ext2_extattr_delete_entry((char *)EXT2_IFIRST(header),
591 EXT2_IFIRST(header), entry,
592 (char *)dinode + EXT2_INODE_SIZE(fs));
604 ext2_extattr_block_clone(struct inode *ip, struct buf **bpp)
609 struct ext2fs_extattr_header *header;
615 header = EXT2_HDR(sbp);
616 if (header->h_magic != EXTATTR_MAGIC || header->h_refcount == 1)
619 facl = ext2_alloc_meta(ip);
623 cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0);
625 ext2_blkfree(ip, facl, fs->e2fs_bsize);
629 memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize);
630 header->h_refcount--;
634 ext2_update(ip->i_vnode, 1);
636 header = EXT2_HDR(cbp);
637 header->h_refcount = 1;
645 ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name)
649 struct ext2fs_extattr_header *header;
650 struct ext2fs_extattr_entry *entry;
651 const char *attr_name;
657 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
658 fs->e2fs_bsize, NOCRED, &bp);
664 /* Check attributes magic value */
665 header = EXT2_HDR(bp);
666 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
671 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize);
677 if (header->h_refcount > 1) {
678 error = ext2_extattr_block_clone(ip, &bp);
685 /* If I am last entry, clean me and free the block */
686 entry = EXT2_FIRST_ENTRY(bp);
687 if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry)) &&
688 (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
691 name_len = entry->e_name_len;
692 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
693 entry->e_name, &name_len);
699 if (strlen(name) == name_len &&
700 0 == strncmp(attr_name, name, name_len)) {
701 ip->i_blocks -= btodb(fs->e2fs_bsize);
702 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
704 error = ext2_update(ip->i_vnode, 1);
711 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
712 entry = EXT2_EXTATTR_NEXT(entry)) {
713 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
717 name_len = entry->e_name_len;
718 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
719 entry->e_name, &name_len);
725 if (strlen(name) == name_len &&
726 0 == strncmp(attr_name, name, name_len)) {
727 ext2_extattr_delete_entry(bp->b_data,
728 EXT2_FIRST_ENTRY(bp), entry,
729 bp->b_data + bp->b_bufsize);
740 static struct ext2fs_extattr_entry *
741 allocate_entry(const char *name, int attrnamespace, uint16_t offs,
742 uint32_t size, uint32_t hash)
744 const char *attr_name;
746 struct ext2fs_extattr_entry *entry;
748 attr_name = ext2_extattr_name_to_linux(attrnamespace, name);
749 name_len = strlen(attr_name);
751 entry = malloc(sizeof(struct ext2fs_extattr_entry) + name_len,
754 entry->e_name_len = name_len;
755 entry->e_name_index = ext2_extattr_attrnamespace_to_linux(attrnamespace, name);
756 entry->e_value_offs = offs;
757 entry->e_value_block = 0;
758 entry->e_value_size = size;
759 entry->e_hash = hash;
760 memcpy(entry->e_name, name, name_len);
766 free_entry(struct ext2fs_extattr_entry *entry)
773 ext2_extattr_get_size(struct ext2fs_extattr_entry *first_entry,
774 struct ext2fs_extattr_entry *exist_entry, int header_size,
775 int name_len, int new_size)
777 struct ext2fs_extattr_entry *entry;
781 size += sizeof(uint32_t);
783 if (NULL == exist_entry) {
784 size += EXT2_EXTATTR_LEN(name_len);
785 size += EXT2_EXTATTR_SIZE(new_size);
789 for (entry = first_entry; !EXT2_IS_LAST_ENTRY(entry);
790 entry = EXT2_EXTATTR_NEXT(entry)) {
791 if (entry != exist_entry)
792 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
793 EXT2_EXTATTR_SIZE(entry->e_value_size);
795 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
796 EXT2_EXTATTR_SIZE(new_size);
803 ext2_extattr_set_exist_entry(char *off,
804 struct ext2fs_extattr_entry *first_entry,
805 struct ext2fs_extattr_entry *entry,
806 char *end, struct uio *uio)
810 min_offs = ext2_extattr_delete_value(off, first_entry, entry, end);
812 entry->e_value_size = uio->uio_resid;
813 if (entry->e_value_size)
814 entry->e_value_offs = min_offs -
815 EXT2_EXTATTR_SIZE(uio->uio_resid);
817 entry->e_value_offs = 0;
819 uiomove(off + entry->e_value_offs, entry->e_value_size, uio);
822 static struct ext2fs_extattr_entry *
823 ext2_extattr_set_new_entry(char *off, struct ext2fs_extattr_entry *first_entry,
824 const char *name, int attrnamespace, char *end, struct uio *uio)
829 struct ext2fs_extattr_entry *entry;
830 struct ext2fs_extattr_entry *new_entry;
833 min_offs = end - off;
835 while (!EXT2_IS_LAST_ENTRY(entry)) {
836 if (min_offs > entry->e_value_offs && entry->e_value_offs > 0)
837 min_offs = entry->e_value_offs;
839 entry = EXT2_EXTATTR_NEXT(entry);
842 pad = (char*)entry + sizeof(uint32_t);
844 /* Find entry insert position */
845 name_len = strlen(name);
847 while (!EXT2_IS_LAST_ENTRY(entry)) {
848 if (!(attrnamespace - entry->e_name_index) &&
849 !(name_len - entry->e_name_len))
850 if (memcmp(name, entry->e_name, name_len) <= 0)
853 entry = EXT2_EXTATTR_NEXT(entry);
856 /* Create new entry and insert it */
857 new_entry = allocate_entry(name, attrnamespace, 0, uio->uio_resid, 0);
858 memmove((char *)entry + EXT2_EXTATTR_LEN(new_entry->e_name_len), entry,
861 memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len));
862 free_entry(new_entry);
865 if (new_entry->e_value_size > 0)
866 new_entry->e_value_offs = min_offs -
867 EXT2_EXTATTR_SIZE(new_entry->e_value_size);
869 uiomove(off + new_entry->e_value_offs, new_entry->e_value_size, uio);
875 ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
876 const char *name, struct uio *uio)
880 struct ext2fs_extattr_dinode_header *header;
881 struct ext2fs_extattr_entry *entry;
882 const char *attr_name;
884 size_t size = 0, max_size;
889 if ((error = bread(ip->i_devvp,
890 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
891 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
896 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
897 ((char *)bp->b_data +
898 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
900 /* Check attributes magic value */
901 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
902 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
904 if (header->h_magic != EXTATTR_MAGIC) {
909 error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode +
910 EXT2_INODE_SIZE(fs));
916 /* Find if entry exist */
917 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
918 entry = EXT2_EXTATTR_NEXT(entry)) {
919 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
923 name_len = entry->e_name_len;
924 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
925 entry->e_name, &name_len);
931 if (strlen(name) == name_len &&
932 0 == strncmp(attr_name, name, name_len))
936 max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE -
937 dinode->e2di_extra_isize;
939 if (!EXT2_IS_LAST_ENTRY(entry)) {
940 size = ext2_extattr_get_size(EXT2_IFIRST(header), entry,
941 sizeof(struct ext2fs_extattr_dinode_header),
942 entry->e_name_len, uio->uio_resid);
943 if (size > max_size) {
948 ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(header),
949 EXT2_IFIRST(header), entry, (char *)header + max_size, uio);
951 /* Ensure that the same entry does not exist in the block */
953 error = ext2_extattr_block_get(ip, attrnamespace, name,
955 if (error != ENOATTR || size > 0) {
964 size = ext2_extattr_get_size(EXT2_IFIRST(header), NULL,
965 sizeof(struct ext2fs_extattr_dinode_header),
966 entry->e_name_len, uio->uio_resid);
967 if (size > max_size) {
972 ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header),
973 EXT2_IFIRST(header), name, attrnamespace,
974 (char *)header + max_size, uio);
981 ext2_extattr_hash_entry(struct ext2fs_extattr_header *header,
982 struct ext2fs_extattr_entry *entry)
985 char *name = entry->e_name;
988 for (n=0; n < entry->e_name_len; n++) {
989 hash = (hash << EXT2_EXTATTR_NAME_HASH_SHIFT) ^
990 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_NAME_HASH_SHIFT)) ^
994 if (entry->e_value_block == 0 && entry->e_value_size != 0) {
995 uint32_t *value = (uint32_t *)((char *)header + entry->e_value_offs);
996 for (n = (entry->e_value_size +
997 EXT2_EXTATTR_ROUND) >> EXT2_EXTATTR_PAD_BITS; n; n--) {
998 hash = (hash << EXT2_EXTATTR_VALUE_HASH_SHIFT) ^
999 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_VALUE_HASH_SHIFT)) ^
1004 entry->e_hash = hash;
1008 ext2_extattr_rehash(struct ext2fs_extattr_header *header,
1009 struct ext2fs_extattr_entry *entry)
1011 struct ext2fs_extattr_entry *here;
1014 ext2_extattr_hash_entry(header, entry);
1016 here = EXT2_ENTRY(header+1);
1017 while (!EXT2_IS_LAST_ENTRY(here)) {
1018 if (!here->e_hash) {
1019 /* Block is not shared if an entry's hash value == 0 */
1024 hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^
1025 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^
1028 here = EXT2_EXTATTR_NEXT(here);
1031 header->h_hash = hash;
1035 ext2_extattr_block_set(struct inode *ip, int attrnamespace,
1036 const char *name, struct uio *uio)
1038 struct m_ext2fs *fs;
1040 struct ext2fs_extattr_header *header;
1041 struct ext2fs_extattr_entry *entry;
1042 const char *attr_name;
1050 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1051 fs->e2fs_bsize, NOCRED, &bp);
1057 /* Check attributes magic value */
1058 header = EXT2_HDR(bp);
1059 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1064 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp),
1065 bp->b_data + bp->b_bufsize);
1071 if (header->h_refcount > 1) {
1072 error = ext2_extattr_block_clone(ip, &bp);
1078 header = EXT2_HDR(bp);
1081 /* Find if entry exist */
1082 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
1083 entry = EXT2_EXTATTR_NEXT(entry)) {
1084 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
1088 name_len = entry->e_name_len;
1089 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
1090 entry->e_name, &name_len);
1096 if (strlen(name) == name_len &&
1097 0 == strncmp(attr_name, name, name_len))
1101 if (!EXT2_IS_LAST_ENTRY(entry)) {
1102 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), entry,
1103 sizeof(struct ext2fs_extattr_header),
1104 entry->e_name_len, uio->uio_resid);
1105 if (size > bp->b_bufsize) {
1110 ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1111 entry, bp->b_data + bp->b_bufsize, uio);
1113 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), NULL,
1114 sizeof(struct ext2fs_extattr_header),
1115 strlen(name), uio->uio_resid);
1116 if (size > bp->b_bufsize) {
1121 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1122 name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1124 /* Clean the same entry in the inode */
1125 error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1126 if (error && error != ENOATTR) {
1132 ext2_extattr_rehash(header, entry);
1134 return (bwrite(bp));
1137 size = ext2_extattr_get_size(NULL, NULL,
1138 sizeof(struct ext2fs_extattr_header),
1139 strlen(ext2_extattr_name_to_linux(attrnamespace, name)), uio->uio_resid);
1140 if (size > fs->e2fs_bsize)
1143 /* Allocate block, fill EA header and insert entry */
1144 ip->i_facl = ext2_alloc_meta(ip);
1145 if (0 == ip->i_facl)
1148 ip->i_blocks += btodb(fs->e2fs_bsize);
1149 ext2_update(ip->i_vnode, 1);
1151 bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0);
1153 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
1154 ip->i_blocks -= btodb(fs->e2fs_bsize);
1156 ext2_update(ip->i_vnode, 1);
1160 header = EXT2_HDR(bp);
1161 header->h_magic = EXTATTR_MAGIC;
1162 header->h_refcount = 1;
1163 header->h_blocks = 1;
1165 memset(header->h_reserved, 0, sizeof(header->h_reserved));
1166 memcpy(bp->b_data, header, sizeof(struct ext2fs_extattr_header));
1167 memset(EXT2_FIRST_ENTRY(bp), 0, sizeof(uint32_t));
1169 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1170 name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1172 /* Clean the same entry in the inode */
1173 error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1174 if (error && error != ENOATTR) {
1179 ext2_extattr_rehash(header, entry);
1181 return (bwrite(bp));
1184 int ext2_extattr_free(struct inode *ip)
1186 struct m_ext2fs *fs;
1188 struct ext2fs_extattr_header *header;
1196 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1197 fs->e2fs_bsize, NOCRED, &bp);
1203 /* Check attributes magic value */
1204 header = EXT2_HDR(bp);
1205 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1210 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize);
1216 if (header->h_refcount > 1) {
1217 header->h_refcount--;
1220 ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize);
1224 ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize);
1226 ext2_update(ip->i_vnode, 1);