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