2 * Copyright (c) 2017, Fedor Uporov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/types.h>
32 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/vnode.h>
37 #include <sys/endian.h>
39 #include <sys/extattr.h>
41 #include <fs/ext2fs/fs.h>
42 #include <fs/ext2fs/ext2fs.h>
43 #include <fs/ext2fs/inode.h>
44 #include <fs/ext2fs/ext2_dinode.h>
45 #include <fs/ext2fs/ext2_mount.h>
46 #include <fs/ext2fs/ext2_extattr.h>
47 #include <fs/ext2fs/ext2_extern.h>
51 ext2_extattr_index_to_bsd(int index)
54 case EXT4_XATTR_INDEX_SYSTEM:
55 return (EXTATTR_NAMESPACE_SYSTEM);
57 case EXT4_XATTR_INDEX_USER:
58 return (EXTATTR_NAMESPACE_USER);
61 return (EXTATTR_NAMESPACE_EMPTY);
65 ext2_extattr_index_to_linux(int index)
68 case EXTATTR_NAMESPACE_SYSTEM:
69 return (EXT4_XATTR_INDEX_SYSTEM);
71 case EXTATTR_NAMESPACE_USER:
72 return (EXT4_XATTR_INDEX_USER);
79 ext2_extattr_valid_attrname(int attrnamespace, const char *attrname)
81 if (attrnamespace == EXTATTR_NAMESPACE_EMPTY)
84 if (strlen(attrname) == 0)
87 if (strlen(attrname) + 1 > EXT2_EXTATTR_NAMELEN_MAX)
88 return (ENAMETOOLONG);
94 ext2_extattr_check(struct ext2fs_extattr_entry *entry, char *end)
96 struct ext2fs_extattr_entry *next;
98 while (!EXT2_IS_LAST_ENTRY(entry)) {
99 next = EXT2_EXTATTR_NEXT(entry);
100 if ((char *)next >= end)
110 ext2_extattr_inode_list(struct inode *ip, int attrnamespace,
111 struct uio *uio, size_t *size)
115 struct ext2fs_extattr_dinode_header *header;
116 struct ext2fs_extattr_entry *entry;
121 if ((error = bread(ip->i_devvp,
122 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
123 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
128 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
129 ((char *)bp->b_data +
130 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
132 /* Check attributes magic value */
133 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
134 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
136 if (header->h_magic != EXTATTR_MAGIC) {
141 error = ext2_extattr_check(EXT2_IFIRST(header),
142 (char *)dinode + EXT2_INODE_SIZE(fs));
148 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
149 entry = EXT2_EXTATTR_NEXT(entry)) {
150 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
154 *size += entry->e_name_len + 1;
156 char *attr_name = malloc(entry->e_name_len + 1, M_TEMP, M_WAITOK);
157 attr_name[0] = entry->e_name_len;
158 memcpy(&attr_name[1], entry->e_name, entry->e_name_len);
159 error = uiomove(attr_name, entry->e_name_len + 1, uio);
160 free(attr_name, M_TEMP);
172 ext2_extattr_block_list(struct inode *ip, int attrnamespace,
173 struct uio *uio, size_t *size)
177 struct ext2fs_extattr_header *header;
178 struct ext2fs_extattr_entry *entry;
183 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
184 fs->e2fs_bsize, NOCRED, &bp);
190 /* Check attributes magic value */
191 header = EXT2_HDR(bp);
192 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
197 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize);
203 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
204 entry = EXT2_EXTATTR_NEXT(entry)) {
205 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
209 *size += entry->e_name_len + 1;
211 char *attr_name = malloc(entry->e_name_len + 1, M_TEMP, M_WAITOK);
212 attr_name[0] = entry->e_name_len;
213 memcpy(&attr_name[1], entry->e_name, entry->e_name_len);
214 error = uiomove(attr_name, entry->e_name_len + 1, uio);
215 free(attr_name, M_TEMP);
227 ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
228 const char *name, struct uio *uio, size_t *size)
232 struct ext2fs_extattr_dinode_header *header;
233 struct ext2fs_extattr_entry *entry;
238 if ((error = bread(ip->i_devvp,
239 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
240 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
245 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
246 ((char *)bp->b_data +
247 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
249 /* Check attributes magic value */
250 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
251 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
253 if (header->h_magic != EXTATTR_MAGIC) {
258 error = ext2_extattr_check(EXT2_IFIRST(header),
259 (char *)dinode + EXT2_INODE_SIZE(fs));
265 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
266 entry = EXT2_EXTATTR_NEXT(entry)) {
267 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
270 if (strlen(name) == entry->e_name_len &&
271 0 == strncmp(entry->e_name, name, entry->e_name_len)) {
273 *size += entry->e_value_size;
275 error = uiomove(((char *)EXT2_IFIRST(header)) +
276 entry->e_value_offs, entry->e_value_size, uio);
290 ext2_extattr_block_get(struct inode *ip, int attrnamespace,
291 const char *name, struct uio *uio, size_t *size)
295 struct ext2fs_extattr_header *header;
296 struct ext2fs_extattr_entry *entry;
301 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
302 fs->e2fs_bsize, NOCRED, &bp);
308 /* Check attributes magic value */
309 header = EXT2_HDR(bp);
310 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
315 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize);
321 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
322 entry = EXT2_EXTATTR_NEXT(entry)) {
323 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
326 if (strlen(name) == entry->e_name_len &&
327 0 == strncmp(entry->e_name, name, entry->e_name_len)) {
329 *size += entry->e_value_size;
331 error = uiomove(bp->b_data + entry->e_value_offs,
332 entry->e_value_size, uio);
346 ext2_extattr_delete_value(char *off,
347 struct ext2fs_extattr_entry *first_entry,
348 struct ext2fs_extattr_entry *entry, char *end)
351 struct ext2fs_extattr_entry *next;
353 min_offs = end - off;
355 while (!EXT2_IS_LAST_ENTRY(next)) {
356 if (min_offs > next->e_value_offs && next->e_value_offs > 0)
357 min_offs = next->e_value_offs;
359 next = EXT2_EXTATTR_NEXT(next);
362 if (entry->e_value_size == 0)
365 memmove(off + min_offs + EXT2_EXTATTR_SIZE(entry->e_value_size),
366 off + min_offs, entry->e_value_offs - min_offs);
368 /* Adjust all value offsets */
370 while (!EXT2_IS_LAST_ENTRY(next))
372 if (next->e_value_offs > 0 &&
373 next->e_value_offs < entry->e_value_offs)
374 next->e_value_offs +=
375 EXT2_EXTATTR_SIZE(entry->e_value_size);
377 next = EXT2_EXTATTR_NEXT(next);
380 min_offs += EXT2_EXTATTR_SIZE(entry->e_value_size);
386 ext2_extattr_delete_entry(char *off,
387 struct ext2fs_extattr_entry *first_entry,
388 struct ext2fs_extattr_entry *entry, char *end)
391 struct ext2fs_extattr_entry *next;
393 /* Clean entry value */
394 ext2_extattr_delete_value(off, first_entry, entry, end);
396 /* Clean the entry */
398 while (!EXT2_IS_LAST_ENTRY(next))
399 next = EXT2_EXTATTR_NEXT(next);
401 pad = (char*)next + sizeof(uint32_t);
403 memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len),
404 pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len)));
408 ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name)
412 struct ext2fs_extattr_dinode_header *header;
413 struct ext2fs_extattr_entry *entry;
418 if ((error = bread(ip->i_devvp,
419 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
420 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
425 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
426 ((char *)bp->b_data +
427 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
429 /* Check attributes magic value */
430 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
431 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
433 if (header->h_magic != EXTATTR_MAGIC) {
438 error = ext2_extattr_check(EXT2_IFIRST(header),
439 (char *)dinode + EXT2_INODE_SIZE(fs));
445 /* If I am last entry, just make magic zero */
446 entry = EXT2_IFIRST(header);
447 if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) {
448 if (strlen(name) == entry->e_name_len &&
449 0 == strncmp(entry->e_name, name, entry->e_name_len)) {
450 memset(header, 0, sizeof(struct ext2fs_extattr_dinode_header));
456 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
457 entry = EXT2_EXTATTR_NEXT(entry)) {
458 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
461 if (strlen(name) == entry->e_name_len &&
462 0 == strncmp(entry->e_name, name, entry->e_name_len)) {
463 ext2_extattr_delete_entry((char *)EXT2_IFIRST(header),
464 EXT2_IFIRST(header), entry,
465 (char *)dinode + EXT2_INODE_SIZE(fs));
477 ext2_extattr_block_clone(struct inode *ip, struct buf **bpp)
482 struct ext2fs_extattr_header *header;
488 header = EXT2_HDR(sbp);
489 if (header->h_magic != EXTATTR_MAGIC || header->h_refcount == 1)
492 facl = ext2_allocfacl(ip);
496 cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0);
498 ext2_blkfree(ip, facl, fs->e2fs_bsize);
502 memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize);
503 header->h_refcount--;
507 ext2_update(ip->i_vnode, 1);
509 header = EXT2_HDR(cbp);
510 header->h_refcount = 1;
518 ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name)
522 struct ext2fs_extattr_header *header;
523 struct ext2fs_extattr_entry *entry;
528 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
529 fs->e2fs_bsize, NOCRED, &bp);
535 /* Check attributes magic value */
536 header = EXT2_HDR(bp);
537 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
542 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize);
548 if (header->h_refcount > 1) {
549 error = ext2_extattr_block_clone(ip, &bp);
556 /* If I am last entry, clean me and free the block */
557 entry = EXT2_FIRST_ENTRY(bp);
558 if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) {
559 if (strlen(name) == entry->e_name_len &&
560 0 == strncmp(entry->e_name, name, entry->e_name_len)) {
561 ip->i_blocks -= btodb(fs->e2fs_bsize);
562 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
564 error = ext2_update(ip->i_vnode, 1);
571 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
572 entry = EXT2_EXTATTR_NEXT(entry)) {
573 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
576 if (strlen(name) == entry->e_name_len &&
577 0 == strncmp(entry->e_name, name, entry->e_name_len)) {
578 ext2_extattr_delete_entry(bp->b_data,
579 EXT2_FIRST_ENTRY(bp), entry,
580 bp->b_data + bp->b_bufsize);
591 static struct ext2fs_extattr_entry *
592 allocate_entry(const char *name, int attrnamespace, uint16_t offs,
593 uint32_t size, uint32_t hash)
596 struct ext2fs_extattr_entry *entry;
598 name_len = strlen(name);
599 entry = malloc(sizeof(struct ext2fs_extattr_entry) + name_len,
602 entry->e_name_len = name_len;
603 entry->e_name_index = ext2_extattr_index_to_linux(attrnamespace);
604 entry->e_value_offs = offs;
605 entry->e_value_block = 0;
606 entry->e_value_size = size;
607 entry->e_hash = hash;
608 memcpy(entry->e_name, name, name_len);
614 free_entry(struct ext2fs_extattr_entry *entry)
621 ext2_extattr_get_size(struct ext2fs_extattr_entry *first_entry,
622 struct ext2fs_extattr_entry *exist_entry, int header_size,
623 int name_len, int new_size)
625 struct ext2fs_extattr_entry *entry;
629 size += sizeof(uint32_t);
631 if (NULL == exist_entry) {
632 size += EXT2_EXTATTR_LEN(name_len);
633 size += EXT2_EXTATTR_SIZE(new_size);
637 for (entry = first_entry; !EXT2_IS_LAST_ENTRY(entry);
638 entry = EXT2_EXTATTR_NEXT(entry)) {
639 if (entry != exist_entry)
640 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
641 EXT2_EXTATTR_SIZE(entry->e_value_size);
643 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
644 EXT2_EXTATTR_SIZE(new_size);
651 ext2_extattr_set_exist_entry(char *off,
652 struct ext2fs_extattr_entry *first_entry,
653 struct ext2fs_extattr_entry *entry,
654 char *end, struct uio *uio)
658 min_offs = ext2_extattr_delete_value(off, first_entry, entry, end);
660 entry->e_value_size = uio->uio_resid;
661 if (entry->e_value_size)
662 entry->e_value_offs = min_offs -
663 EXT2_EXTATTR_SIZE(uio->uio_resid);
665 entry->e_value_offs = 0;
667 uiomove(off + entry->e_value_offs, entry->e_value_size, uio);
670 static struct ext2fs_extattr_entry *
671 ext2_extattr_set_new_entry(char *off, struct ext2fs_extattr_entry *first_entry,
672 const char *name, int attrnamespace, char *end, struct uio *uio)
677 struct ext2fs_extattr_entry *entry;
678 struct ext2fs_extattr_entry *new_entry;
681 min_offs = end - off;
683 while (!EXT2_IS_LAST_ENTRY(entry)) {
684 if (min_offs > entry->e_value_offs && entry->e_value_offs > 0)
685 min_offs = entry->e_value_offs;
687 entry = EXT2_EXTATTR_NEXT(entry);
690 pad = (char*)entry + sizeof(uint32_t);
692 /* Find entry insert position */
693 name_len = strlen(name);
695 while (!EXT2_IS_LAST_ENTRY(entry)) {
696 if (!(attrnamespace - entry->e_name_index) &&
697 !(name_len - entry->e_name_len))
698 if (memcmp(name, entry->e_name, name_len) <= 0)
701 entry = EXT2_EXTATTR_NEXT(entry);
704 /* Create new entry and insert it */
705 new_entry = allocate_entry(name, attrnamespace, 0, uio->uio_resid, 0);
706 memmove((char *)entry + EXT2_EXTATTR_LEN(new_entry->e_name_len), entry,
709 memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len));
710 free_entry(new_entry);
713 if (new_entry->e_value_size > 0)
714 new_entry->e_value_offs = min_offs -
715 EXT2_EXTATTR_SIZE(new_entry->e_value_size);
717 uiomove(off + new_entry->e_value_offs, new_entry->e_value_size, uio);
723 ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
724 const char *name, struct uio *uio)
728 struct ext2fs_extattr_dinode_header *header;
729 struct ext2fs_extattr_entry *entry;
730 size_t size = 0, max_size;
735 if ((error = bread(ip->i_devvp,
736 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
737 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
742 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
743 ((char *)bp->b_data +
744 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
746 /* Check attributes magic value */
747 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
748 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
750 if (header->h_magic != EXTATTR_MAGIC) {
755 error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode +
756 EXT2_INODE_SIZE(fs));
762 /* Find if entry exist */
763 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
764 entry = EXT2_EXTATTR_NEXT(entry)) {
765 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
768 if (strlen(name) == entry->e_name_len &&
769 0 == strncmp(entry->e_name, name, entry->e_name_len))
773 max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE -
774 dinode->e2di_extra_isize;
776 if (!EXT2_IS_LAST_ENTRY(entry)) {
777 size = ext2_extattr_get_size(EXT2_IFIRST(header), entry,
778 sizeof(struct ext2fs_extattr_dinode_header),
779 entry->e_name_len, uio->uio_resid);
780 if (size > max_size) {
785 ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(header),
786 EXT2_IFIRST(header), entry, (char *)header + max_size, uio);
788 /* Ensure that the same entry does not exist in the block */
790 error = ext2_extattr_block_get(ip, attrnamespace, name,
792 if (error != ENOATTR || size > 0) {
801 size = ext2_extattr_get_size(EXT2_IFIRST(header), NULL,
802 sizeof(struct ext2fs_extattr_dinode_header),
803 entry->e_name_len, uio->uio_resid);
804 if (size > max_size) {
809 ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header),
810 EXT2_IFIRST(header), name, attrnamespace,
811 (char *)header + max_size, uio);
818 ext2_extattr_hash_entry(struct ext2fs_extattr_header *header,
819 struct ext2fs_extattr_entry *entry)
822 char *name = entry->e_name;
825 for (n=0; n < entry->e_name_len; n++) {
826 hash = (hash << EXT2_EXTATTR_NAME_HASH_SHIFT) ^
827 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_NAME_HASH_SHIFT)) ^
831 if (entry->e_value_block == 0 && entry->e_value_size != 0) {
832 uint32_t *value = (uint32_t *)((char *)header + entry->e_value_offs);
833 for (n = (entry->e_value_size +
834 EXT2_EXTATTR_ROUND) >> EXT2_EXTATTR_PAD_BITS; n; n--) {
835 hash = (hash << EXT2_EXTATTR_VALUE_HASH_SHIFT) ^
836 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_VALUE_HASH_SHIFT)) ^
841 entry->e_hash = hash;
845 ext2_extattr_rehash(struct ext2fs_extattr_header *header,
846 struct ext2fs_extattr_entry *entry)
848 struct ext2fs_extattr_entry *here;
851 ext2_extattr_hash_entry(header, entry);
853 here = EXT2_ENTRY(header+1);
854 while (!EXT2_IS_LAST_ENTRY(here)) {
856 /* Block is not shared if an entry's hash value == 0 */
861 hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^
862 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^
865 here = EXT2_EXTATTR_NEXT(here);
868 header->h_hash = hash;
872 ext2_extattr_block_set(struct inode *ip, int attrnamespace,
873 const char *name, struct uio *uio)
877 struct ext2fs_extattr_header *header;
878 struct ext2fs_extattr_entry *entry;
885 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
886 fs->e2fs_bsize, NOCRED, &bp);
892 /* Check attributes magic value */
893 header = EXT2_HDR(bp);
894 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
899 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp),
900 bp->b_data + bp->b_bufsize);
906 if (header->h_refcount > 1) {
907 error = ext2_extattr_block_clone(ip, &bp);
913 header = EXT2_HDR(bp);
916 /* Find if entry exist */
917 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
918 entry = EXT2_EXTATTR_NEXT(entry)) {
919 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
922 if (strlen(name) == entry->e_name_len &&
923 0 == strncmp(entry->e_name, name, entry->e_name_len))
927 if (!EXT2_IS_LAST_ENTRY(entry)) {
928 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), entry,
929 sizeof(struct ext2fs_extattr_header),
930 entry->e_name_len, uio->uio_resid);
931 if (size > bp->b_bufsize) {
936 ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
937 entry, bp->b_data + bp->b_bufsize, uio);
939 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), NULL,
940 sizeof(struct ext2fs_extattr_header),
941 strlen(name), uio->uio_resid);
942 if (size > bp->b_bufsize) {
947 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
948 name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
950 /* Clean the same entry in the inode */
951 error = ext2_extattr_inode_delete(ip, attrnamespace, name);
952 if (error && error != ENOATTR) {
958 ext2_extattr_rehash(header, entry);
963 size = ext2_extattr_get_size(NULL, NULL,
964 sizeof(struct ext2fs_extattr_header), strlen(name), uio->uio_resid);
965 if (size > fs->e2fs_bsize)
968 /* Allocate block, fill EA header and insert entry */
969 ip->i_facl = ext2_allocfacl(ip);
973 ip->i_blocks += btodb(fs->e2fs_bsize);
974 ext2_update(ip->i_vnode, 1);
976 bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0);
978 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
979 ip->i_blocks -= btodb(fs->e2fs_bsize);
981 ext2_update(ip->i_vnode, 1);
985 header = EXT2_HDR(bp);
986 header->h_magic = EXTATTR_MAGIC;
987 header->h_refcount = 1;
988 header->h_blocks = 1;
990 memset(header->h_reserved, 0, sizeof(header->h_reserved));
991 memcpy(bp->b_data, header, sizeof(struct ext2fs_extattr_header));
992 memset(EXT2_FIRST_ENTRY(bp), 0, sizeof(uint32_t));
994 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
995 name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
997 /* Clean the same entry in the inode */
998 error = ext2_extattr_inode_delete(ip, attrnamespace, name);
999 if (error && error != ENOATTR) {
1004 ext2_extattr_rehash(header, entry);
1006 return (bwrite(bp));
1009 int ext2_extattr_free(struct inode *ip)
1011 struct m_ext2fs *fs;
1013 struct ext2fs_extattr_header *header;
1021 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1022 fs->e2fs_bsize, NOCRED, &bp);
1028 /* Check attributes magic value */
1029 header = EXT2_HDR(bp);
1030 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1035 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize);
1041 if (header->h_refcount > 1) {
1042 header->h_refcount--;
1045 ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize);
1049 ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize);
1051 ext2_update(ip->i_vnode, 1);