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 + dinode->e2di_extra_isize);
221 if (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 (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
293 error = ext2_extattr_block_check(ip, bp);
299 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
300 entry = EXT2_EXTATTR_NEXT(entry)) {
301 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
305 name_len = entry->e_name_len;
306 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
307 entry->e_name, &name_len);
314 *size += name_len + 1;
317 char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
319 memcpy(&name[1], attr_name, name_len);
320 error = uiomove(name, name_len + 1, uio);
333 ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
334 const char *name, struct uio *uio, size_t *size)
338 struct ext2fs_extattr_dinode_header *header;
339 struct ext2fs_extattr_entry *entry;
340 const char *attr_name;
346 if ((error = bread(ip->i_devvp,
347 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
348 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
353 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
354 ((char *)bp->b_data +
355 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
357 /* Check attributes magic value */
358 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
359 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
361 if (header->h_magic != EXTATTR_MAGIC) {
366 error = ext2_extattr_check(EXT2_IFIRST(header),
367 (char *)dinode + EXT2_INODE_SIZE(fs));
373 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
374 entry = EXT2_EXTATTR_NEXT(entry)) {
375 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
379 name_len = entry->e_name_len;
380 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
381 entry->e_name, &name_len);
387 if (strlen(name) == name_len &&
388 0 == strncmp(attr_name, name, name_len)) {
390 *size += entry->e_value_size;
393 error = uiomove(((char *)EXT2_IFIRST(header)) +
394 entry->e_value_offs, entry->e_value_size, uio);
407 ext2_extattr_block_get(struct inode *ip, int attrnamespace,
408 const char *name, struct uio *uio, size_t *size)
412 struct ext2fs_extattr_header *header;
413 struct ext2fs_extattr_entry *entry;
414 const char *attr_name;
420 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
421 fs->e2fs_bsize, NOCRED, &bp);
426 /* Check attributes magic value */
427 header = EXT2_HDR(bp);
428 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
433 error = ext2_extattr_block_check(ip, bp);
439 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
440 entry = EXT2_EXTATTR_NEXT(entry)) {
441 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
445 name_len = entry->e_name_len;
446 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
447 entry->e_name, &name_len);
453 if (strlen(name) == name_len &&
454 0 == strncmp(attr_name, name, name_len)) {
456 *size += entry->e_value_size;
459 error = uiomove(bp->b_data + entry->e_value_offs,
460 entry->e_value_size, uio);
473 ext2_extattr_delete_value(char *off,
474 struct ext2fs_extattr_entry *first_entry,
475 struct ext2fs_extattr_entry *entry, char *end)
478 struct ext2fs_extattr_entry *next;
480 min_offs = end - off;
482 while (!EXT2_IS_LAST_ENTRY(next)) {
483 if (min_offs > next->e_value_offs && next->e_value_offs > 0)
484 min_offs = next->e_value_offs;
486 next = EXT2_EXTATTR_NEXT(next);
489 if (entry->e_value_size == 0)
492 memmove(off + min_offs + EXT2_EXTATTR_SIZE(entry->e_value_size),
493 off + min_offs, entry->e_value_offs - min_offs);
495 /* Adjust all value offsets */
497 while (!EXT2_IS_LAST_ENTRY(next))
499 if (next->e_value_offs > 0 &&
500 next->e_value_offs < entry->e_value_offs)
501 next->e_value_offs +=
502 EXT2_EXTATTR_SIZE(entry->e_value_size);
504 next = EXT2_EXTATTR_NEXT(next);
507 min_offs += EXT2_EXTATTR_SIZE(entry->e_value_size);
513 ext2_extattr_delete_entry(char *off,
514 struct ext2fs_extattr_entry *first_entry,
515 struct ext2fs_extattr_entry *entry, char *end)
518 struct ext2fs_extattr_entry *next;
520 /* Clean entry value */
521 ext2_extattr_delete_value(off, first_entry, entry, end);
523 /* Clean the entry */
525 while (!EXT2_IS_LAST_ENTRY(next))
526 next = EXT2_EXTATTR_NEXT(next);
528 pad = (char*)next + sizeof(uint32_t);
530 memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len),
531 pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len)));
535 ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name)
539 struct ext2fs_extattr_dinode_header *header;
540 struct ext2fs_extattr_entry *entry;
541 const char *attr_name;
547 if ((error = bread(ip->i_devvp,
548 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
549 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
554 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
555 ((char *)bp->b_data +
556 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
558 /* Check attributes magic value */
559 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
560 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
562 if (header->h_magic != EXTATTR_MAGIC) {
567 error = ext2_extattr_check(EXT2_IFIRST(header),
568 (char *)dinode + EXT2_INODE_SIZE(fs));
574 /* If I am last entry, just make magic zero */
575 entry = EXT2_IFIRST(header);
576 if ((EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) &&
577 (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 memset(header, 0, sizeof(struct ext2fs_extattr_dinode_header));
596 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
597 entry = EXT2_EXTATTR_NEXT(entry)) {
598 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
602 name_len = entry->e_name_len;
603 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
604 entry->e_name, &name_len);
610 if (strlen(name) == name_len &&
611 0 == strncmp(attr_name, name, name_len)) {
612 ext2_extattr_delete_entry((char *)EXT2_IFIRST(header),
613 EXT2_IFIRST(header), entry,
614 (char *)dinode + EXT2_INODE_SIZE(fs));
626 ext2_extattr_block_clone(struct inode *ip, struct buf **bpp)
631 struct ext2fs_extattr_header *header;
637 header = EXT2_HDR(sbp);
638 if (header->h_magic != EXTATTR_MAGIC || header->h_refcount == 1)
641 facl = ext2_alloc_meta(ip);
645 cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0);
647 ext2_blkfree(ip, facl, fs->e2fs_bsize);
651 memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize);
652 header->h_refcount--;
656 ext2_update(ip->i_vnode, 1);
658 header = EXT2_HDR(cbp);
659 header->h_refcount = 1;
667 ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name)
671 struct ext2fs_extattr_header *header;
672 struct ext2fs_extattr_entry *entry;
673 const char *attr_name;
679 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
680 fs->e2fs_bsize, NOCRED, &bp);
685 /* Check attributes magic value */
686 header = EXT2_HDR(bp);
687 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
692 error = ext2_extattr_block_check(ip, bp);
698 if (header->h_refcount > 1) {
699 error = ext2_extattr_block_clone(ip, &bp);
706 /* If I am last entry, clean me and free the block */
707 entry = EXT2_FIRST_ENTRY(bp);
708 if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry)) &&
709 (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
712 name_len = entry->e_name_len;
713 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
714 entry->e_name, &name_len);
720 if (strlen(name) == name_len &&
721 0 == strncmp(attr_name, name, name_len)) {
722 ip->i_blocks -= btodb(fs->e2fs_bsize);
723 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
725 error = ext2_update(ip->i_vnode, 1);
732 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
733 entry = EXT2_EXTATTR_NEXT(entry)) {
734 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
738 name_len = entry->e_name_len;
739 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
740 entry->e_name, &name_len);
746 if (strlen(name) == name_len &&
747 0 == strncmp(attr_name, name, name_len)) {
748 ext2_extattr_delete_entry(bp->b_data,
749 EXT2_FIRST_ENTRY(bp), entry,
750 bp->b_data + bp->b_bufsize);
761 static struct ext2fs_extattr_entry *
762 allocate_entry(const char *name, int attrnamespace, uint16_t offs,
763 uint32_t size, uint32_t hash)
765 const char *attr_name;
767 struct ext2fs_extattr_entry *entry;
769 attr_name = ext2_extattr_name_to_linux(attrnamespace, name);
770 name_len = strlen(attr_name);
772 entry = malloc(sizeof(struct ext2fs_extattr_entry) + name_len,
775 entry->e_name_len = name_len;
776 entry->e_name_index = ext2_extattr_attrnamespace_to_linux(attrnamespace, name);
777 entry->e_value_offs = offs;
778 entry->e_value_block = 0;
779 entry->e_value_size = size;
780 entry->e_hash = hash;
781 memcpy(entry->e_name, name, name_len);
787 free_entry(struct ext2fs_extattr_entry *entry)
794 ext2_extattr_get_size(struct ext2fs_extattr_entry *first_entry,
795 struct ext2fs_extattr_entry *exist_entry, int header_size,
796 int name_len, int new_size)
798 struct ext2fs_extattr_entry *entry;
802 size += sizeof(uint32_t);
804 if (NULL == exist_entry) {
805 size += EXT2_EXTATTR_LEN(name_len);
806 size += EXT2_EXTATTR_SIZE(new_size);
810 for (entry = first_entry; !EXT2_IS_LAST_ENTRY(entry);
811 entry = EXT2_EXTATTR_NEXT(entry)) {
812 if (entry != exist_entry)
813 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
814 EXT2_EXTATTR_SIZE(entry->e_value_size);
816 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
817 EXT2_EXTATTR_SIZE(new_size);
824 ext2_extattr_set_exist_entry(char *off,
825 struct ext2fs_extattr_entry *first_entry,
826 struct ext2fs_extattr_entry *entry,
827 char *end, struct uio *uio)
831 min_offs = ext2_extattr_delete_value(off, first_entry, entry, end);
833 entry->e_value_size = uio->uio_resid;
834 if (entry->e_value_size)
835 entry->e_value_offs = min_offs -
836 EXT2_EXTATTR_SIZE(uio->uio_resid);
838 entry->e_value_offs = 0;
840 uiomove(off + entry->e_value_offs, entry->e_value_size, uio);
843 static struct ext2fs_extattr_entry *
844 ext2_extattr_set_new_entry(char *off, struct ext2fs_extattr_entry *first_entry,
845 const char *name, int attrnamespace, char *end, struct uio *uio)
850 struct ext2fs_extattr_entry *entry;
851 struct ext2fs_extattr_entry *new_entry;
854 min_offs = end - off;
856 while (!EXT2_IS_LAST_ENTRY(entry)) {
857 if (min_offs > entry->e_value_offs && entry->e_value_offs > 0)
858 min_offs = entry->e_value_offs;
860 entry = EXT2_EXTATTR_NEXT(entry);
863 pad = (char*)entry + sizeof(uint32_t);
865 /* Find entry insert position */
866 name_len = strlen(name);
868 while (!EXT2_IS_LAST_ENTRY(entry)) {
869 if (!(attrnamespace - entry->e_name_index) &&
870 !(name_len - entry->e_name_len))
871 if (memcmp(name, entry->e_name, name_len) <= 0)
874 entry = EXT2_EXTATTR_NEXT(entry);
877 /* Create new entry and insert it */
878 new_entry = allocate_entry(name, attrnamespace, 0, uio->uio_resid, 0);
879 memmove((char *)entry + EXT2_EXTATTR_LEN(new_entry->e_name_len), entry,
882 memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len));
883 free_entry(new_entry);
886 if (new_entry->e_value_size > 0)
887 new_entry->e_value_offs = min_offs -
888 EXT2_EXTATTR_SIZE(new_entry->e_value_size);
890 uiomove(off + new_entry->e_value_offs, new_entry->e_value_size, uio);
896 ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
897 const char *name, struct uio *uio)
901 struct ext2fs_extattr_dinode_header *header;
902 struct ext2fs_extattr_entry *entry;
903 const char *attr_name;
905 size_t size = 0, max_size;
910 if ((error = bread(ip->i_devvp,
911 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
912 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
917 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
918 ((char *)bp->b_data +
919 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
921 /* Check attributes magic value */
922 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
923 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
925 if (header->h_magic != EXTATTR_MAGIC) {
930 error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode +
931 EXT2_INODE_SIZE(fs));
937 /* Find if entry exist */
938 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
939 entry = EXT2_EXTATTR_NEXT(entry)) {
940 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
944 name_len = entry->e_name_len;
945 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
946 entry->e_name, &name_len);
952 if (strlen(name) == name_len &&
953 0 == strncmp(attr_name, name, name_len))
957 max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE -
958 dinode->e2di_extra_isize;
960 if (!EXT2_IS_LAST_ENTRY(entry)) {
961 size = ext2_extattr_get_size(EXT2_IFIRST(header), entry,
962 sizeof(struct ext2fs_extattr_dinode_header),
963 entry->e_name_len, uio->uio_resid);
964 if (size > max_size) {
969 ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(header),
970 EXT2_IFIRST(header), entry, (char *)header + max_size, uio);
972 /* Ensure that the same entry does not exist in the block */
974 error = ext2_extattr_block_get(ip, attrnamespace, name,
976 if (error != ENOATTR || size > 0) {
985 size = ext2_extattr_get_size(EXT2_IFIRST(header), NULL,
986 sizeof(struct ext2fs_extattr_dinode_header),
987 entry->e_name_len, uio->uio_resid);
988 if (size > max_size) {
993 ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header),
994 EXT2_IFIRST(header), name, attrnamespace,
995 (char *)header + max_size, uio);
1002 ext2_extattr_hash_entry(struct ext2fs_extattr_header *header,
1003 struct ext2fs_extattr_entry *entry)
1006 char *name = entry->e_name;
1009 for (n=0; n < entry->e_name_len; n++) {
1010 hash = (hash << EXT2_EXTATTR_NAME_HASH_SHIFT) ^
1011 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_NAME_HASH_SHIFT)) ^
1015 if (entry->e_value_block == 0 && entry->e_value_size != 0) {
1016 uint32_t *value = (uint32_t *)((char *)header + entry->e_value_offs);
1017 for (n = (entry->e_value_size +
1018 EXT2_EXTATTR_ROUND) >> EXT2_EXTATTR_PAD_BITS; n; n--) {
1019 hash = (hash << EXT2_EXTATTR_VALUE_HASH_SHIFT) ^
1020 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_VALUE_HASH_SHIFT)) ^
1025 entry->e_hash = hash;
1029 ext2_extattr_rehash(struct ext2fs_extattr_header *header,
1030 struct ext2fs_extattr_entry *entry)
1032 struct ext2fs_extattr_entry *here;
1035 ext2_extattr_hash_entry(header, entry);
1037 here = EXT2_ENTRY(header+1);
1038 while (!EXT2_IS_LAST_ENTRY(here)) {
1039 if (!here->e_hash) {
1040 /* Block is not shared if an entry's hash value == 0 */
1045 hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^
1046 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^
1049 here = EXT2_EXTATTR_NEXT(here);
1052 header->h_hash = hash;
1056 ext2_extattr_block_set(struct inode *ip, int attrnamespace,
1057 const char *name, struct uio *uio)
1059 struct m_ext2fs *fs;
1061 struct ext2fs_extattr_header *header;
1062 struct ext2fs_extattr_entry *entry;
1063 const char *attr_name;
1071 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1072 fs->e2fs_bsize, NOCRED, &bp);
1077 /* Check attributes magic value */
1078 header = EXT2_HDR(bp);
1079 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1084 error = ext2_extattr_block_check(ip, bp);
1090 if (header->h_refcount > 1) {
1091 error = ext2_extattr_block_clone(ip, &bp);
1097 header = EXT2_HDR(bp);
1100 /* Find if entry exist */
1101 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
1102 entry = EXT2_EXTATTR_NEXT(entry)) {
1103 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
1107 name_len = entry->e_name_len;
1108 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
1109 entry->e_name, &name_len);
1115 if (strlen(name) == name_len &&
1116 0 == strncmp(attr_name, name, name_len))
1120 if (!EXT2_IS_LAST_ENTRY(entry)) {
1121 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), entry,
1122 sizeof(struct ext2fs_extattr_header),
1123 entry->e_name_len, uio->uio_resid);
1124 if (size > bp->b_bufsize) {
1129 ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1130 entry, bp->b_data + bp->b_bufsize, uio);
1132 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), NULL,
1133 sizeof(struct ext2fs_extattr_header),
1134 strlen(name), uio->uio_resid);
1135 if (size > bp->b_bufsize) {
1140 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1141 name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1143 /* Clean the same entry in the inode */
1144 error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1145 if (error && error != ENOATTR) {
1151 ext2_extattr_rehash(header, entry);
1152 ext2_extattr_blk_csum_set(ip, bp);
1154 return (bwrite(bp));
1157 size = ext2_extattr_get_size(NULL, NULL,
1158 sizeof(struct ext2fs_extattr_header),
1159 strlen(ext2_extattr_name_to_linux(attrnamespace, name)), uio->uio_resid);
1160 if (size > fs->e2fs_bsize)
1163 /* Allocate block, fill EA header and insert entry */
1164 ip->i_facl = ext2_alloc_meta(ip);
1165 if (0 == ip->i_facl)
1168 ip->i_blocks += btodb(fs->e2fs_bsize);
1169 ext2_update(ip->i_vnode, 1);
1171 bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0);
1173 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
1174 ip->i_blocks -= btodb(fs->e2fs_bsize);
1176 ext2_update(ip->i_vnode, 1);
1180 header = EXT2_HDR(bp);
1181 header->h_magic = EXTATTR_MAGIC;
1182 header->h_refcount = 1;
1183 header->h_blocks = 1;
1185 memset(header->h_reserved, 0, sizeof(header->h_reserved));
1186 memcpy(bp->b_data, header, sizeof(struct ext2fs_extattr_header));
1187 memset(EXT2_FIRST_ENTRY(bp), 0, sizeof(uint32_t));
1189 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1190 name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1192 /* Clean the same entry in the inode */
1193 error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1194 if (error && error != ENOATTR) {
1199 ext2_extattr_rehash(header, entry);
1200 ext2_extattr_blk_csum_set(ip, bp);
1202 return (bwrite(bp));
1205 int ext2_extattr_free(struct inode *ip)
1207 struct m_ext2fs *fs;
1209 struct ext2fs_extattr_header *header;
1217 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1218 fs->e2fs_bsize, NOCRED, &bp);
1223 /* Check attributes magic value */
1224 header = EXT2_HDR(bp);
1225 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1230 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp),
1231 bp->b_data + bp->b_bufsize);
1237 if (header->h_refcount > 1) {
1238 header->h_refcount--;
1241 ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize);
1245 ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize);
1247 ext2_update(ip->i_vnode, 1);