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>
44 #include <fs/ext2fs/fs.h>
45 #include <fs/ext2fs/ext2fs.h>
46 #include <fs/ext2fs/inode.h>
47 #include <fs/ext2fs/ext2_dinode.h>
48 #include <fs/ext2fs/ext2_mount.h>
49 #include <fs/ext2fs/ext2_extattr.h>
50 #include <fs/ext2fs/ext2_extern.h>
52 SDT_PROVIDER_DECLARE(ext2fs);
55 * arg0: verbosity. Higher numbers give more verbose messages
56 * arg1: Textual message
58 SDT_PROBE_DEFINE2(ext2fs, , trace, extattr, "int", "char*");
61 ext2_extattr_attrnamespace_to_bsd(int attrnamespace)
64 switch (attrnamespace) {
65 case EXT4_XATTR_INDEX_SYSTEM:
66 return (EXTATTR_NAMESPACE_SYSTEM);
68 case EXT4_XATTR_INDEX_USER:
69 return (EXTATTR_NAMESPACE_USER);
71 case EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT:
72 return (POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE);
74 case EXT4_XATTR_INDEX_POSIX_ACL_ACCESS:
75 return (POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE);
78 return (EXTATTR_NAMESPACE_EMPTY);
82 ext2_extattr_name_to_bsd(int attrnamespace, const char *name, int* name_len)
85 if (attrnamespace == EXT4_XATTR_INDEX_SYSTEM)
87 else if (attrnamespace == EXT4_XATTR_INDEX_USER)
89 else if (attrnamespace == EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT) {
90 *name_len = strlen(POSIX1E_ACL_DEFAULT_EXTATTR_NAME);
91 return (POSIX1E_ACL_DEFAULT_EXTATTR_NAME);
92 } else if (attrnamespace == EXT4_XATTR_INDEX_POSIX_ACL_ACCESS) {
93 *name_len = strlen(POSIX1E_ACL_ACCESS_EXTATTR_NAME);
94 return (POSIX1E_ACL_ACCESS_EXTATTR_NAME);
98 * XXX: Not all linux namespaces are mapped to bsd for now,
99 * return NULL, which will be converted to ENOTSUP on upper layer.
101 SDT_PROBE2(ext2fs, , trace, extattr, 1,
102 "can not convert ext2fs name to bsd namespace");
108 ext2_extattr_attrnamespace_to_linux(int attrnamespace, const char *name)
111 if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE &&
112 !strcmp(name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME))
113 return (EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT);
115 if (attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE &&
116 !strcmp(name, POSIX1E_ACL_ACCESS_EXTATTR_NAME))
117 return (EXT4_XATTR_INDEX_POSIX_ACL_ACCESS);
119 switch (attrnamespace) {
120 case EXTATTR_NAMESPACE_SYSTEM:
121 return (EXT4_XATTR_INDEX_SYSTEM);
123 case EXTATTR_NAMESPACE_USER:
124 return (EXT4_XATTR_INDEX_USER);
128 * In this case namespace conversion should be unique,
129 * so this point is unreachable.
135 ext2_extattr_name_to_linux(int attrnamespace, const char *name)
138 if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE ||
139 attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE)
146 ext2_extattr_valid_attrname(int attrnamespace, const char *attrname)
148 if (attrnamespace == EXTATTR_NAMESPACE_EMPTY)
151 if (strlen(attrname) == 0)
154 if (strlen(attrname) + 1 > EXT2_EXTATTR_NAMELEN_MAX)
155 return (ENAMETOOLONG);
161 ext2_extattr_check(struct ext2fs_extattr_entry *entry, char *end)
163 struct ext2fs_extattr_entry *next;
165 while (!EXT2_IS_LAST_ENTRY(entry)) {
166 next = EXT2_EXTATTR_NEXT(entry);
167 if ((char *)next >= end)
177 ext2_extattr_block_check(struct inode *ip, struct buf *bp)
179 struct ext2fs_extattr_header *header;
182 header = (struct ext2fs_extattr_header *)bp->b_data;
184 error = ext2_extattr_check(EXT2_IFIRST(header),
185 bp->b_data + bp->b_bufsize);
189 return (ext2_extattr_blk_csum_verify(ip, bp));
193 ext2_extattr_inode_list(struct inode *ip, int attrnamespace,
194 struct uio *uio, size_t *size)
198 struct ext2fs_extattr_dinode_header *header;
199 struct ext2fs_extattr_entry *entry;
200 const char *attr_name;
206 if ((error = bread(ip->i_devvp,
207 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
208 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
213 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
214 ((char *)bp->b_data +
215 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
217 /* Check attributes magic value */
218 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
219 E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
221 if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
226 error = ext2_extattr_check(EXT2_IFIRST(header),
227 (char *)dinode + EXT2_INODE_SIZE(fs));
233 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
234 entry = EXT2_EXTATTR_NEXT(entry)) {
235 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
239 name_len = entry->e_name_len;
240 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
241 entry->e_name, &name_len);
248 *size += name_len + 1;
251 char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
253 memcpy(&name[1], attr_name, name_len);
254 error = uiomove(name, name_len + 1, uio);
267 ext2_extattr_block_list(struct inode *ip, int attrnamespace,
268 struct uio *uio, size_t *size)
272 struct ext2fs_extattr_header *header;
273 struct ext2fs_extattr_entry *entry;
274 const char *attr_name;
280 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
281 fs->e2fs_bsize, NOCRED, &bp);
286 /* Check attributes magic value */
287 header = EXT2_HDR(bp);
288 if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
289 le32toh(header->h_blocks) != 1) {
294 error = ext2_extattr_block_check(ip, bp);
300 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
301 entry = EXT2_EXTATTR_NEXT(entry)) {
302 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
306 name_len = entry->e_name_len;
307 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
308 entry->e_name, &name_len);
315 *size += name_len + 1;
318 char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
320 memcpy(&name[1], attr_name, name_len);
321 error = uiomove(name, name_len + 1, uio);
334 ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
335 const char *name, struct uio *uio, size_t *size)
339 struct ext2fs_extattr_dinode_header *header;
340 struct ext2fs_extattr_entry *entry;
341 const char *attr_name;
347 if ((error = bread(ip->i_devvp,
348 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
349 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
354 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
355 ((char *)bp->b_data +
356 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
358 /* Check attributes magic value */
359 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
360 E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
362 if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
367 error = ext2_extattr_check(EXT2_IFIRST(header),
368 (char *)dinode + EXT2_INODE_SIZE(fs));
374 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
375 entry = EXT2_EXTATTR_NEXT(entry)) {
376 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
380 name_len = entry->e_name_len;
381 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
382 entry->e_name, &name_len);
388 if (strlen(name) == name_len &&
389 0 == strncmp(attr_name, name, name_len)) {
391 *size += le32toh(entry->e_value_size);
394 error = uiomove(((char *)EXT2_IFIRST(header)) +
395 le16toh(entry->e_value_offs),
396 le32toh(entry->e_value_size), uio);
409 ext2_extattr_block_get(struct inode *ip, int attrnamespace,
410 const char *name, struct uio *uio, size_t *size)
414 struct ext2fs_extattr_header *header;
415 struct ext2fs_extattr_entry *entry;
416 const char *attr_name;
422 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
423 fs->e2fs_bsize, NOCRED, &bp);
428 /* Check attributes magic value */
429 header = EXT2_HDR(bp);
430 if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
431 le32toh(header->h_blocks) != 1) {
436 error = ext2_extattr_block_check(ip, bp);
442 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
443 entry = EXT2_EXTATTR_NEXT(entry)) {
444 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
448 name_len = entry->e_name_len;
449 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
450 entry->e_name, &name_len);
456 if (strlen(name) == name_len &&
457 0 == strncmp(attr_name, name, name_len)) {
459 *size += le32toh(entry->e_value_size);
462 error = uiomove(bp->b_data +
463 le16toh(entry->e_value_offs),
464 le32toh(entry->e_value_size), uio);
477 ext2_extattr_delete_value(char *off,
478 struct ext2fs_extattr_entry *first_entry,
479 struct ext2fs_extattr_entry *entry, char *end)
482 struct ext2fs_extattr_entry *next;
484 min_offs = end - off;
486 while (!EXT2_IS_LAST_ENTRY(next)) {
487 if (min_offs > le16toh(next->e_value_offs) &&
488 le16toh(next->e_value_offs) > 0)
489 min_offs = le16toh(next->e_value_offs);
491 next = EXT2_EXTATTR_NEXT(next);
494 if (entry->e_value_size == 0)
497 memmove(off + min_offs + EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size)),
498 off + min_offs, le16toh(entry->e_value_offs) - min_offs);
500 /* Adjust all value offsets */
502 while (!EXT2_IS_LAST_ENTRY(next))
504 if (le16toh(next->e_value_offs) > 0 &&
505 le16toh(next->e_value_offs) < le16toh(entry->e_value_offs))
506 next->e_value_offs = htole16(le16toh(next->e_value_offs) +
507 EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size)));
509 next = EXT2_EXTATTR_NEXT(next);
512 min_offs += EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size));
518 ext2_extattr_delete_entry(char *off,
519 struct ext2fs_extattr_entry *first_entry,
520 struct ext2fs_extattr_entry *entry, char *end)
523 struct ext2fs_extattr_entry *next;
525 /* Clean entry value */
526 ext2_extattr_delete_value(off, first_entry, entry, end);
528 /* Clean the entry */
530 while (!EXT2_IS_LAST_ENTRY(next))
531 next = EXT2_EXTATTR_NEXT(next);
533 pad = (char*)next + sizeof(uint32_t);
535 memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len),
536 pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len)));
540 ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name)
544 struct ext2fs_extattr_dinode_header *header;
545 struct ext2fs_extattr_entry *entry;
546 const char *attr_name;
552 if ((error = bread(ip->i_devvp,
553 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
554 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
559 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
560 ((char *)bp->b_data +
561 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
563 /* Check attributes magic value */
564 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
565 E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
567 if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
572 error = ext2_extattr_check(EXT2_IFIRST(header),
573 (char *)dinode + EXT2_INODE_SIZE(fs));
579 /* If I am last entry, just make magic zero */
580 entry = EXT2_IFIRST(header);
581 if ((EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) &&
582 (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
585 name_len = entry->e_name_len;
586 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
587 entry->e_name, &name_len);
593 if (strlen(name) == name_len &&
594 0 == strncmp(attr_name, name, name_len)) {
595 memset(header, 0, sizeof(struct ext2fs_extattr_dinode_header));
601 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
602 entry = EXT2_EXTATTR_NEXT(entry)) {
603 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
607 name_len = entry->e_name_len;
608 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
609 entry->e_name, &name_len);
615 if (strlen(name) == name_len &&
616 0 == strncmp(attr_name, name, name_len)) {
617 ext2_extattr_delete_entry((char *)EXT2_IFIRST(header),
618 EXT2_IFIRST(header), entry,
619 (char *)dinode + EXT2_INODE_SIZE(fs));
631 ext2_extattr_block_clone(struct inode *ip, struct buf **bpp)
636 struct ext2fs_extattr_header *header;
642 header = EXT2_HDR(sbp);
643 if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
644 le32toh(header->h_refcount) == 1)
647 facl = ext2_alloc_meta(ip);
651 cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0);
653 ext2_blkfree(ip, facl, fs->e2fs_bsize);
657 memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize);
658 header->h_refcount = htole32(le32toh(header->h_refcount) - 1);
662 ext2_update(ip->i_vnode, 1);
664 header = EXT2_HDR(cbp);
665 header->h_refcount = htole32(1);
673 ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name)
677 struct ext2fs_extattr_header *header;
678 struct ext2fs_extattr_entry *entry;
679 const char *attr_name;
685 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
686 fs->e2fs_bsize, NOCRED, &bp);
691 /* Check attributes magic value */
692 header = EXT2_HDR(bp);
693 if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
694 le32toh(header->h_blocks) != 1) {
699 error = ext2_extattr_block_check(ip, bp);
705 if (le32toh(header->h_refcount) > 1) {
706 error = ext2_extattr_block_clone(ip, &bp);
713 /* If I am last entry, clean me and free the block */
714 entry = EXT2_FIRST_ENTRY(bp);
715 if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry)) &&
716 (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
719 name_len = entry->e_name_len;
720 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
721 entry->e_name, &name_len);
727 if (strlen(name) == name_len &&
728 0 == strncmp(attr_name, name, name_len)) {
729 ip->i_blocks -= btodb(fs->e2fs_bsize);
730 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
732 error = ext2_update(ip->i_vnode, 1);
739 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
740 entry = EXT2_EXTATTR_NEXT(entry)) {
741 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
745 name_len = entry->e_name_len;
746 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
747 entry->e_name, &name_len);
753 if (strlen(name) == name_len &&
754 0 == strncmp(attr_name, name, name_len)) {
755 ext2_extattr_delete_entry(bp->b_data,
756 EXT2_FIRST_ENTRY(bp), entry,
757 bp->b_data + bp->b_bufsize);
768 static struct ext2fs_extattr_entry *
769 allocate_entry(const char *name, int attrnamespace, uint16_t offs,
770 uint32_t size, uint32_t hash)
772 const char *attr_name;
774 struct ext2fs_extattr_entry *entry;
776 attr_name = ext2_extattr_name_to_linux(attrnamespace, name);
777 name_len = strlen(attr_name);
779 entry = malloc(sizeof(struct ext2fs_extattr_entry) + name_len,
782 entry->e_name_len = name_len;
783 entry->e_name_index = ext2_extattr_attrnamespace_to_linux(attrnamespace, name);
784 entry->e_value_offs = htole16(offs);
785 entry->e_value_block = 0;
786 entry->e_value_size = htole32(size);
787 entry->e_hash = htole32(hash);
788 memcpy(entry->e_name, name, name_len);
794 free_entry(struct ext2fs_extattr_entry *entry)
801 ext2_extattr_get_size(struct ext2fs_extattr_entry *first_entry,
802 struct ext2fs_extattr_entry *exist_entry, int header_size,
803 int name_len, int new_size)
805 struct ext2fs_extattr_entry *entry;
809 size += sizeof(uint32_t);
811 if (NULL == exist_entry) {
812 size += EXT2_EXTATTR_LEN(name_len);
813 size += EXT2_EXTATTR_SIZE(new_size);
817 for (entry = first_entry; !EXT2_IS_LAST_ENTRY(entry);
818 entry = EXT2_EXTATTR_NEXT(entry)) {
819 if (entry != exist_entry)
820 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
821 EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size));
823 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
824 EXT2_EXTATTR_SIZE(new_size);
831 ext2_extattr_set_exist_entry(char *off,
832 struct ext2fs_extattr_entry *first_entry,
833 struct ext2fs_extattr_entry *entry,
834 char *end, struct uio *uio)
838 min_offs = ext2_extattr_delete_value(off, first_entry, entry, end);
840 entry->e_value_size = htole32(uio->uio_resid);
841 if (le32toh(entry->e_value_size))
842 entry->e_value_offs = htole16(min_offs -
843 EXT2_EXTATTR_SIZE(uio->uio_resid));
845 entry->e_value_offs = 0;
847 uiomove(off + le16toh(entry->e_value_offs),
848 le32toh(entry->e_value_size), uio);
851 static struct ext2fs_extattr_entry *
852 ext2_extattr_set_new_entry(char *off, struct ext2fs_extattr_entry *first_entry,
853 const char *name, int attrnamespace, char *end, struct uio *uio)
858 struct ext2fs_extattr_entry *entry;
859 struct ext2fs_extattr_entry *new_entry;
862 min_offs = end - off;
864 while (!EXT2_IS_LAST_ENTRY(entry)) {
865 if (min_offs > le16toh(entry->e_value_offs) &&
866 le16toh(entry->e_value_offs) > 0)
867 min_offs = le16toh(entry->e_value_offs);
869 entry = EXT2_EXTATTR_NEXT(entry);
872 pad = (char*)entry + sizeof(uint32_t);
874 /* Find entry insert position */
875 name_len = strlen(name);
877 while (!EXT2_IS_LAST_ENTRY(entry)) {
878 if (!(attrnamespace - entry->e_name_index) &&
879 !(name_len - entry->e_name_len))
880 if (memcmp(name, entry->e_name, name_len) <= 0)
883 entry = EXT2_EXTATTR_NEXT(entry);
886 /* Create new entry and insert it */
887 new_entry = allocate_entry(name, attrnamespace, 0, uio->uio_resid, 0);
888 memmove((char *)entry + EXT2_EXTATTR_LEN(new_entry->e_name_len), entry,
891 memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len));
892 free_entry(new_entry);
895 if (le32toh(new_entry->e_value_size) > 0)
896 new_entry->e_value_offs = htole16(min_offs -
897 EXT2_EXTATTR_SIZE(le32toh(new_entry->e_value_size)));
899 uiomove(off + le16toh(new_entry->e_value_offs),
900 le32toh(new_entry->e_value_size), uio);
906 ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
907 const char *name, struct uio *uio)
911 struct ext2fs_extattr_dinode_header *header;
912 struct ext2fs_extattr_entry *entry;
913 const char *attr_name;
915 size_t size = 0, max_size;
920 if ((error = bread(ip->i_devvp,
921 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
922 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
927 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
928 ((char *)bp->b_data +
929 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
931 /* Check attributes magic value */
932 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
933 E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
935 if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
940 error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode +
941 EXT2_INODE_SIZE(fs));
947 /* Find if entry exist */
948 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
949 entry = EXT2_EXTATTR_NEXT(entry)) {
950 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
954 name_len = entry->e_name_len;
955 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
956 entry->e_name, &name_len);
962 if (strlen(name) == name_len &&
963 0 == strncmp(attr_name, name, name_len))
967 max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE -
968 le16toh(dinode->e2di_extra_isize);
970 if (!EXT2_IS_LAST_ENTRY(entry)) {
971 size = ext2_extattr_get_size(EXT2_IFIRST(header), entry,
972 sizeof(struct ext2fs_extattr_dinode_header),
973 entry->e_name_len, uio->uio_resid);
974 if (size > max_size) {
979 ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(header),
980 EXT2_IFIRST(header), entry, (char *)header + max_size, uio);
982 /* Ensure that the same entry does not exist in the block */
984 error = ext2_extattr_block_get(ip, attrnamespace, name,
986 if (error != ENOATTR || size > 0) {
995 size = ext2_extattr_get_size(EXT2_IFIRST(header), NULL,
996 sizeof(struct ext2fs_extattr_dinode_header),
997 entry->e_name_len, uio->uio_resid);
998 if (size > max_size) {
1003 ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header),
1004 EXT2_IFIRST(header), name, attrnamespace,
1005 (char *)header + max_size, uio);
1008 return (bwrite(bp));
1012 ext2_extattr_hash_entry(struct ext2fs_extattr_header *header,
1013 struct ext2fs_extattr_entry *entry)
1016 char *name = entry->e_name;
1019 for (n=0; n < entry->e_name_len; n++) {
1020 hash = (hash << EXT2_EXTATTR_NAME_HASH_SHIFT) ^
1021 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_NAME_HASH_SHIFT)) ^
1025 if (entry->e_value_block == 0 && entry->e_value_size != 0) {
1026 uint32_t *value = (uint32_t *)((char *)header +
1027 le16toh(entry->e_value_offs));
1028 for (n = (le32toh(entry->e_value_size) +
1029 EXT2_EXTATTR_ROUND) >> EXT2_EXTATTR_PAD_BITS; n; n--) {
1030 hash = (hash << EXT2_EXTATTR_VALUE_HASH_SHIFT) ^
1031 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_VALUE_HASH_SHIFT)) ^
1036 entry->e_hash = htole32(hash);
1040 ext2_extattr_rehash(struct ext2fs_extattr_header *header,
1041 struct ext2fs_extattr_entry *entry)
1043 struct ext2fs_extattr_entry *here;
1046 ext2_extattr_hash_entry(header, entry);
1048 here = EXT2_ENTRY(header+1);
1049 while (!EXT2_IS_LAST_ENTRY(here)) {
1050 if (here->e_hash == 0) {
1051 /* Block is not shared if an entry's hash value == 0 */
1056 hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^
1057 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^
1058 le32toh(here->e_hash);
1060 here = EXT2_EXTATTR_NEXT(here);
1063 header->h_hash = htole32(hash);
1067 ext2_extattr_block_set(struct inode *ip, int attrnamespace,
1068 const char *name, struct uio *uio)
1070 struct m_ext2fs *fs;
1072 struct ext2fs_extattr_header *header;
1073 struct ext2fs_extattr_entry *entry;
1074 const char *attr_name;
1082 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1083 fs->e2fs_bsize, NOCRED, &bp);
1088 /* Check attributes magic value */
1089 header = EXT2_HDR(bp);
1090 if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
1091 le32toh(header->h_blocks) != 1) {
1096 error = ext2_extattr_block_check(ip, bp);
1102 if (le32toh(header->h_refcount) > 1) {
1103 error = ext2_extattr_block_clone(ip, &bp);
1109 header = EXT2_HDR(bp);
1112 /* Find if entry exist */
1113 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
1114 entry = EXT2_EXTATTR_NEXT(entry)) {
1115 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
1119 name_len = entry->e_name_len;
1120 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
1121 entry->e_name, &name_len);
1127 if (strlen(name) == name_len &&
1128 0 == strncmp(attr_name, name, name_len))
1132 if (!EXT2_IS_LAST_ENTRY(entry)) {
1133 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), entry,
1134 sizeof(struct ext2fs_extattr_header),
1135 entry->e_name_len, uio->uio_resid);
1136 if (size > bp->b_bufsize) {
1141 ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1142 entry, bp->b_data + bp->b_bufsize, uio);
1144 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), NULL,
1145 sizeof(struct ext2fs_extattr_header),
1146 strlen(name), uio->uio_resid);
1147 if (size > bp->b_bufsize) {
1152 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1153 name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1155 /* Clean the same entry in the inode */
1156 error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1157 if (error && error != ENOATTR) {
1163 ext2_extattr_rehash(header, entry);
1164 ext2_extattr_blk_csum_set(ip, bp);
1166 return (bwrite(bp));
1169 size = ext2_extattr_get_size(NULL, NULL,
1170 sizeof(struct ext2fs_extattr_header),
1171 strlen(ext2_extattr_name_to_linux(attrnamespace, name)), uio->uio_resid);
1172 if (size > fs->e2fs_bsize)
1175 /* Allocate block, fill EA header and insert entry */
1176 ip->i_facl = ext2_alloc_meta(ip);
1177 if (0 == ip->i_facl)
1180 ip->i_blocks += btodb(fs->e2fs_bsize);
1181 ext2_update(ip->i_vnode, 1);
1183 bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0);
1185 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
1186 ip->i_blocks -= btodb(fs->e2fs_bsize);
1188 ext2_update(ip->i_vnode, 1);
1192 header = EXT2_HDR(bp);
1193 header->h_magic = htole32(EXTATTR_MAGIC);
1194 header->h_refcount = htole32(1);
1195 header->h_blocks = htole32(1);
1197 memset(header->h_reserved, 0, sizeof(header->h_reserved));
1198 memcpy(bp->b_data, header, sizeof(struct ext2fs_extattr_header));
1199 memset(EXT2_FIRST_ENTRY(bp), 0, sizeof(uint32_t));
1201 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1202 name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1204 /* Clean the same entry in the inode */
1205 error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1206 if (error && error != ENOATTR) {
1211 ext2_extattr_rehash(header, entry);
1212 ext2_extattr_blk_csum_set(ip, bp);
1214 return (bwrite(bp));
1217 int ext2_extattr_free(struct inode *ip)
1219 struct m_ext2fs *fs;
1221 struct ext2fs_extattr_header *header;
1229 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1230 fs->e2fs_bsize, NOCRED, &bp);
1235 /* Check attributes magic value */
1236 header = EXT2_HDR(bp);
1237 if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
1238 le32toh(header->h_blocks) != 1) {
1243 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp),
1244 bp->b_data + bp->b_bufsize);
1250 if (le32toh(header->h_refcount) > 1) {
1251 header->h_refcount = htole32(le32toh(header->h_refcount) - 1);
1254 ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize);
1258 ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize);
1260 ext2_update(ip->i_vnode, 1);