xref: /freebsd/sys/fs/ext2fs/ext2_extattr.c (revision fe8b3456)
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 #include <sys/sdt.h>
43 
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>
51 
52 SDT_PROVIDER_DECLARE(ext2fs);
53 /*
54  * ext2fs trace probe:
55  * arg0: verbosity. Higher numbers give more verbose messages
56  * arg1: Textual message
57  */
58 SDT_PROBE_DEFINE2(ext2fs, , trace, extattr, "int", "char*");
59 
60 static int
61 ext2_extattr_attrnamespace_to_bsd(int attrnamespace)
62 {
63 
64 	switch (attrnamespace) {
65 	case EXT4_XATTR_INDEX_SYSTEM:
66 		return (EXTATTR_NAMESPACE_SYSTEM);
67 
68 	case EXT4_XATTR_INDEX_USER:
69 		return (EXTATTR_NAMESPACE_USER);
70 
71 	case EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT:
72 		return (POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE);
73 
74 	case EXT4_XATTR_INDEX_POSIX_ACL_ACCESS:
75 		return (POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE);
76 	}
77 
78 	return (EXTATTR_NAMESPACE_EMPTY);
79 }
80 
81 static const char *
82 ext2_extattr_name_to_bsd(int attrnamespace, const char *name, int* name_len)
83 {
84 
85 	if (attrnamespace == EXT4_XATTR_INDEX_SYSTEM)
86 		return (name);
87 	else if (attrnamespace == EXT4_XATTR_INDEX_USER)
88 		return (name);
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);
95 	}
96 
97 	/*
98 	 * XXX: Not all linux namespaces are mapped to bsd for now,
99 	 * return NULL, which will be converted to ENOTSUP on upper layer.
100 	 */
101 	SDT_PROBE2(ext2fs, , trace, extattr, 1,
102 	    "can not convert ext2fs name to bsd namespace");
103 
104 	return (NULL);
105 }
106 
107 static int
108 ext2_extattr_attrnamespace_to_linux(int attrnamespace, const char *name)
109 {
110 
111 	if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE &&
112 	    !strcmp(name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME))
113 		return (EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT);
114 
115 	if (attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE &&
116 	    !strcmp(name, POSIX1E_ACL_ACCESS_EXTATTR_NAME))
117 		return (EXT4_XATTR_INDEX_POSIX_ACL_ACCESS);
118 
119 	switch (attrnamespace) {
120 	case EXTATTR_NAMESPACE_SYSTEM:
121 		return (EXT4_XATTR_INDEX_SYSTEM);
122 
123 	case EXTATTR_NAMESPACE_USER:
124 		return (EXT4_XATTR_INDEX_USER);
125 	}
126 
127 	/*
128 	 * In this case namespace conversion should be unique,
129 	 * so this point is unreachable.
130 	 */
131 	return (-1);
132 }
133 
134 static const char *
135 ext2_extattr_name_to_linux(int attrnamespace, const char *name)
136 {
137 
138 	if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE ||
139 	    attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE)
140 		return ("");
141 	else
142 		return (name);
143 }
144 
145 int
146 ext2_extattr_valid_attrname(int attrnamespace, const char *attrname)
147 {
148 	if (attrnamespace == EXTATTR_NAMESPACE_EMPTY)
149 		return (EINVAL);
150 
151 	if (strlen(attrname) == 0)
152 		return (EINVAL);
153 
154 	if (strlen(attrname) + 1 > EXT2_EXTATTR_NAMELEN_MAX)
155 		return (ENAMETOOLONG);
156 
157 	return (0);
158 }
159 
160 static int
161 ext2_extattr_check(struct ext2fs_extattr_entry *entry, char *end)
162 {
163 	struct ext2fs_extattr_entry *next;
164 
165 	while (!EXT2_IS_LAST_ENTRY(entry)) {
166 		next = EXT2_EXTATTR_NEXT(entry);
167 		if ((char *)next >= end)
168 			return (EIO);
169 
170 		entry = next;
171 	}
172 
173 	return (0);
174 }
175 
176 static int
177 ext2_extattr_block_check(struct inode *ip, struct buf *bp)
178 {
179 	struct ext2fs_extattr_header *header;
180 	int error;
181 
182 	header = (struct ext2fs_extattr_header *)bp->b_data;
183 
184 	error = ext2_extattr_check(EXT2_IFIRST(header),
185 	    bp->b_data + bp->b_bufsize);
186 	if (error)
187 		return (error);
188 
189 	return (ext2_extattr_blk_csum_verify(ip, bp));
190 }
191 
192 int
193 ext2_extattr_inode_list(struct inode *ip, int attrnamespace,
194     struct uio *uio, size_t *size)
195 {
196 	struct m_ext2fs *fs;
197 	struct buf *bp;
198 	struct ext2fs_extattr_dinode_header *header;
199 	struct ext2fs_extattr_entry *entry;
200 	const char *attr_name;
201 	int name_len;
202 	int error;
203 
204 	fs = ip->i_e2fs;
205 
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) {
209 		brelse(bp);
210 		return (error);
211 	}
212 
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));
216 
217 	/* Check attributes magic value */
218 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
219 	    E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
220 
221 	if (header->h_magic != EXTATTR_MAGIC) {
222 		brelse(bp);
223 		return (0);
224 	}
225 
226 	error = ext2_extattr_check(EXT2_IFIRST(header),
227 	    (char *)dinode + EXT2_INODE_SIZE(fs));
228 	if (error) {
229 		brelse(bp);
230 		return (error);
231 	}
232 
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) !=
236 		    attrnamespace)
237 			continue;
238 
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);
242 		if (!attr_name) {
243 			brelse(bp);
244 			return (ENOTSUP);
245 		}
246 
247 		if (size != NULL)
248 			*size += name_len + 1;
249 
250 		if (uio != NULL) {
251 			char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
252 			name[0] = name_len;
253 			memcpy(&name[1], attr_name, name_len);
254 			error = uiomove(name, name_len + 1, uio);
255 			free(name, M_TEMP);
256 			if (error)
257 				break;
258 		}
259 	}
260 
261 	brelse(bp);
262 
263 	return (error);
264 }
265 
266 int
267 ext2_extattr_block_list(struct inode *ip, int attrnamespace,
268     struct uio *uio, size_t *size)
269 {
270 	struct m_ext2fs *fs;
271 	struct buf *bp;
272 	struct ext2fs_extattr_header *header;
273 	struct ext2fs_extattr_entry *entry;
274 	const char *attr_name;
275 	int name_len;
276 	int error;
277 
278 	fs = ip->i_e2fs;
279 
280 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
281 	    fs->e2fs_bsize, NOCRED, &bp);
282 	if (error) {
283 		return (error);
284 	}
285 
286 	/* Check attributes magic value */
287 	header = EXT2_HDR(bp);
288 	if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
289 		brelse(bp);
290 		return (EINVAL);
291 	}
292 
293 	error = ext2_extattr_block_check(ip, bp);
294 	if (error) {
295 		brelse(bp);
296 		return (error);
297 	}
298 
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) !=
302 		    attrnamespace)
303 			continue;
304 
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);
308 		if (!attr_name) {
309 			brelse(bp);
310 			return (ENOTSUP);
311 		}
312 
313 		if (size != NULL)
314 			*size += name_len + 1;
315 
316 		if (uio != NULL) {
317 			char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
318 			name[0] = name_len;
319 			memcpy(&name[1], attr_name, name_len);
320 			error = uiomove(name, name_len + 1, uio);
321 			free(name, M_TEMP);
322 			if (error)
323 				break;
324 		}
325 	}
326 
327 	brelse(bp);
328 
329 	return (error);
330 }
331 
332 int
333 ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
334     const char *name, struct uio *uio, size_t *size)
335 {
336 	struct m_ext2fs *fs;
337 	struct buf *bp;
338 	struct ext2fs_extattr_dinode_header *header;
339 	struct ext2fs_extattr_entry *entry;
340 	const char *attr_name;
341 	int name_len;
342 	int error;
343 
344 	fs = ip->i_e2fs;
345 
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) {
349 		brelse(bp);
350 		return (error);
351 	}
352 
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));
356 
357 	/* Check attributes magic value */
358 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
359 	    E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
360 
361 	if (header->h_magic != EXTATTR_MAGIC) {
362 		brelse(bp);
363 		return (ENOATTR);
364 	}
365 
366 	error = ext2_extattr_check(EXT2_IFIRST(header),
367 	    (char *)dinode + EXT2_INODE_SIZE(fs));
368 	if (error) {
369 		brelse(bp);
370 		return (error);
371 	}
372 
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) !=
376 		    attrnamespace)
377 			continue;
378 
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);
382 		if (!attr_name) {
383 			brelse(bp);
384 			return (ENOTSUP);
385 		}
386 
387 		if (strlen(name) == name_len &&
388 		    0 == strncmp(attr_name, name, name_len)) {
389 			if (size != NULL)
390 				*size += entry->e_value_size;
391 
392 			if (uio != NULL)
393 				error = uiomove(((char *)EXT2_IFIRST(header)) +
394 				    entry->e_value_offs, entry->e_value_size, uio);
395 
396 			brelse(bp);
397 			return (error);
398 		}
399 	 }
400 
401 	brelse(bp);
402 
403 	return (ENOATTR);
404 }
405 
406 int
407 ext2_extattr_block_get(struct inode *ip, int attrnamespace,
408     const char *name, struct uio *uio, size_t *size)
409 {
410 	struct m_ext2fs *fs;
411 	struct buf *bp;
412 	struct ext2fs_extattr_header *header;
413 	struct ext2fs_extattr_entry *entry;
414 	const char *attr_name;
415 	int name_len;
416 	int error;
417 
418 	fs = ip->i_e2fs;
419 
420 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
421 	    fs->e2fs_bsize, NOCRED, &bp);
422 	if (error) {
423 		return (error);
424 	}
425 
426 	/* Check attributes magic value */
427 	header = EXT2_HDR(bp);
428 	if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
429 		brelse(bp);
430 		return (EINVAL);
431 	}
432 
433 	error = ext2_extattr_block_check(ip, bp);
434 	if (error) {
435 		brelse(bp);
436 		return (error);
437 	}
438 
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) !=
442 		    attrnamespace)
443 			continue;
444 
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);
448 		if (!attr_name) {
449 			brelse(bp);
450 			return (ENOTSUP);
451 		}
452 
453 		if (strlen(name) == name_len &&
454 		    0 == strncmp(attr_name, name, name_len)) {
455 			if (size != NULL)
456 				*size += entry->e_value_size;
457 
458 			if (uio != NULL)
459 				error = uiomove(bp->b_data + entry->e_value_offs,
460 				    entry->e_value_size, uio);
461 
462 			brelse(bp);
463 			return (error);
464 		}
465 	 }
466 
467 	brelse(bp);
468 
469 	return (ENOATTR);
470 }
471 
472 static uint16_t
473 ext2_extattr_delete_value(char *off,
474     struct ext2fs_extattr_entry *first_entry,
475     struct ext2fs_extattr_entry *entry, char *end)
476 {
477 	uint16_t min_offs;
478 	struct ext2fs_extattr_entry *next;
479 
480 	min_offs = end - off;
481 	next = first_entry;
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;
485 
486 		next = EXT2_EXTATTR_NEXT(next);
487 	}
488 
489 	if (entry->e_value_size == 0)
490 		return (min_offs);
491 
492 	memmove(off + min_offs + EXT2_EXTATTR_SIZE(entry->e_value_size),
493 	    off + min_offs, entry->e_value_offs - min_offs);
494 
495 	/* Adjust all value offsets */
496 	next = first_entry;
497 	while (!EXT2_IS_LAST_ENTRY(next))
498 	{
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);
503 
504 		next = EXT2_EXTATTR_NEXT(next);
505 	}
506 
507 	min_offs += EXT2_EXTATTR_SIZE(entry->e_value_size);
508 
509 	return (min_offs);
510 }
511 
512 static void
513 ext2_extattr_delete_entry(char *off,
514     struct ext2fs_extattr_entry *first_entry,
515     struct ext2fs_extattr_entry *entry, char *end)
516 {
517 	char *pad;
518 	struct ext2fs_extattr_entry *next;
519 
520 	/* Clean entry value */
521 	ext2_extattr_delete_value(off, first_entry, entry, end);
522 
523 	/* Clean the entry */
524 	next = first_entry;
525 	while (!EXT2_IS_LAST_ENTRY(next))
526 		next = EXT2_EXTATTR_NEXT(next);
527 
528 	pad = (char*)next + sizeof(uint32_t);
529 
530 	memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len),
531 	    pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len)));
532 }
533 
534 int
535 ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name)
536 {
537 	struct m_ext2fs *fs;
538 	struct buf *bp;
539 	struct ext2fs_extattr_dinode_header *header;
540 	struct ext2fs_extattr_entry *entry;
541 	const char *attr_name;
542 	int name_len;
543 	int error;
544 
545 	fs = ip->i_e2fs;
546 
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) {
550 		brelse(bp);
551 		return (error);
552 	}
553 
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));
557 
558 	/* Check attributes magic value */
559 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
560 	    E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
561 
562 	if (header->h_magic != EXTATTR_MAGIC) {
563 		brelse(bp);
564 		return (ENOATTR);
565 	}
566 
567 	error = ext2_extattr_check(EXT2_IFIRST(header),
568 	    (char *)dinode + EXT2_INODE_SIZE(fs));
569 	if (error) {
570 		brelse(bp);
571 		return (error);
572 	}
573 
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) ==
578 	    attrnamespace)) {
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 			memset(header, 0, sizeof(struct ext2fs_extattr_dinode_header));
591 
592 			return (bwrite(bp));
593 		}
594 	}
595 
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) !=
599 		    attrnamespace)
600 			continue;
601 
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);
605 		if (!attr_name) {
606 			brelse(bp);
607 			return (ENOTSUP);
608 		}
609 
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));
615 
616 			return (bwrite(bp));
617 		}
618 	}
619 
620 	brelse(bp);
621 
622 	return (ENOATTR);
623 }
624 
625 static int
626 ext2_extattr_block_clone(struct inode *ip, struct buf **bpp)
627 {
628 	struct m_ext2fs *fs;
629 	struct buf *sbp;
630 	struct buf *cbp;
631 	struct ext2fs_extattr_header *header;
632 	uint64_t facl;
633 
634 	fs = ip->i_e2fs;
635 	sbp = *bpp;
636 
637 	header = EXT2_HDR(sbp);
638 	if (header->h_magic != EXTATTR_MAGIC || header->h_refcount == 1)
639 		return (EINVAL);
640 
641 	facl = ext2_alloc_meta(ip);
642 	if (!facl)
643 		return (ENOSPC);
644 
645 	cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0);
646 	if (!cbp) {
647 		ext2_blkfree(ip, facl, fs->e2fs_bsize);
648 		return (EIO);
649 	}
650 
651 	memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize);
652 	header->h_refcount--;
653 	bwrite(sbp);
654 
655 	ip->i_facl = facl;
656 	ext2_update(ip->i_vnode, 1);
657 
658 	header = EXT2_HDR(cbp);
659 	header->h_refcount = 1;
660 
661 	*bpp = cbp;
662 
663 	return (0);
664 }
665 
666 int
667 ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name)
668 {
669 	struct m_ext2fs *fs;
670 	struct buf *bp;
671 	struct ext2fs_extattr_header *header;
672 	struct ext2fs_extattr_entry *entry;
673 	const char *attr_name;
674 	int name_len;
675 	int error;
676 
677 	fs = ip->i_e2fs;
678 
679 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
680 	    fs->e2fs_bsize, NOCRED, &bp);
681 	if (error) {
682 		return (error);
683 	}
684 
685 	/* Check attributes magic value */
686 	header = EXT2_HDR(bp);
687 	if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
688 		brelse(bp);
689 		return (EINVAL);
690 	}
691 
692 	error = ext2_extattr_block_check(ip, bp);
693 	if (error) {
694 		brelse(bp);
695 		return (error);
696 	}
697 
698 	if (header->h_refcount > 1) {
699 		error = ext2_extattr_block_clone(ip, &bp);
700 		if (error) {
701 			brelse(bp);
702 			return (error);
703 		}
704 	}
705 
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) ==
710 	    attrnamespace)) {
711 
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);
715 		if (!attr_name) {
716 			brelse(bp);
717 			return (ENOTSUP);
718 		}
719 
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);
724 			ip->i_facl = 0;
725 			error = ext2_update(ip->i_vnode, 1);
726 
727 			brelse(bp);
728 			return (error);
729 		}
730 	}
731 
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) !=
735 		    attrnamespace)
736 			continue;
737 
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);
741 		if (!attr_name) {
742 			brelse(bp);
743 			return (ENOTSUP);
744 		}
745 
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);
751 
752 			return (bwrite(bp));
753 		}
754 	}
755 
756 	brelse(bp);
757 
758 	return (ENOATTR);
759 }
760 
761 static struct ext2fs_extattr_entry *
762 allocate_entry(const char *name, int attrnamespace, uint16_t offs,
763     uint32_t size, uint32_t hash)
764 {
765 	const char *attr_name;
766 	int name_len;
767 	struct ext2fs_extattr_entry *entry;
768 
769 	attr_name = ext2_extattr_name_to_linux(attrnamespace, name);
770 	name_len = strlen(attr_name);
771 
772 	entry = malloc(sizeof(struct ext2fs_extattr_entry) + name_len,
773 	    M_TEMP, M_WAITOK);
774 
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);
782 
783 	return (entry);
784 }
785 
786 static void
787 free_entry(struct ext2fs_extattr_entry *entry)
788 {
789 
790 	free(entry, M_TEMP);
791 }
792 
793 static int
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)
797 {
798 	struct ext2fs_extattr_entry *entry;
799 	int size;
800 
801 	size = header_size;
802 	size += sizeof(uint32_t);
803 
804 	if (NULL == exist_entry) {
805 		size += EXT2_EXTATTR_LEN(name_len);
806 		size += EXT2_EXTATTR_SIZE(new_size);
807 	}
808 
809 	if (first_entry)
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);
815 			else
816 				size += EXT2_EXTATTR_LEN(entry->e_name_len) +
817 				    EXT2_EXTATTR_SIZE(new_size);
818 		}
819 
820 	return (size);
821 }
822 
823 static void
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)
828 {
829 	uint16_t min_offs;
830 
831 	min_offs = ext2_extattr_delete_value(off, first_entry, entry, end);
832 
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);
837 	else
838 		entry->e_value_offs = 0;
839 
840 	uiomove(off + entry->e_value_offs, entry->e_value_size, uio);
841 }
842 
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)
846 {
847 	int name_len;
848 	char *pad;
849 	uint16_t min_offs;
850 	struct ext2fs_extattr_entry *entry;
851 	struct ext2fs_extattr_entry *new_entry;
852 
853 	/* Find pad's */
854 	min_offs = end - off;
855 	entry = first_entry;
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;
859 
860 		entry = EXT2_EXTATTR_NEXT(entry);
861 	}
862 
863 	pad = (char*)entry + sizeof(uint32_t);
864 
865 	/* Find entry insert position */
866 	name_len = strlen(name);
867 	entry = first_entry;
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)
872 				break;
873 
874 		entry = EXT2_EXTATTR_NEXT(entry);
875 	}
876 
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,
880 	    pad - (char*)entry);
881 
882 	memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len));
883 	free_entry(new_entry);
884 
885 	new_entry = 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);
889 
890 	uiomove(off + new_entry->e_value_offs, new_entry->e_value_size, uio);
891 
892 	return (new_entry);
893 }
894 
895 int
896 ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
897     const char *name, struct uio *uio)
898 {
899 	struct m_ext2fs *fs;
900 	struct buf *bp;
901 	struct ext2fs_extattr_dinode_header *header;
902 	struct ext2fs_extattr_entry *entry;
903 	const char *attr_name;
904 	int name_len;
905 	size_t size = 0, max_size;
906 	int error;
907 
908 	fs = ip->i_e2fs;
909 
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) {
913 		brelse(bp);
914 		return (error);
915 	}
916 
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));
920 
921 	/* Check attributes magic value */
922 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
923 	    E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
924 
925 	if (header->h_magic != EXTATTR_MAGIC) {
926 		brelse(bp);
927 		return (ENOSPC);
928 	}
929 
930 	error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode +
931 	    EXT2_INODE_SIZE(fs));
932 	if (error) {
933 		brelse(bp);
934 		return (error);
935 	}
936 
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) !=
941 		    attrnamespace)
942 			continue;
943 
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);
947 		if (!attr_name) {
948 			brelse(bp);
949 			return (ENOTSUP);
950 		}
951 
952 		if (strlen(name) == name_len &&
953 		    0 == strncmp(attr_name, name, name_len))
954 			break;
955 	}
956 
957 	max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE -
958 	    dinode->e2di_extra_isize;
959 
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) {
965 			brelse(bp);
966 			return (ENOSPC);
967 		}
968 
969 		ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(header),
970 		    EXT2_IFIRST(header), entry, (char *)header + max_size, uio);
971 	} else {
972 		/* Ensure that the same entry does not exist in the block */
973 		if (ip->i_facl) {
974 			error = ext2_extattr_block_get(ip, attrnamespace, name,
975 			    NULL, &size);
976 			if (error != ENOATTR || size > 0) {
977 				brelse(bp);
978 				if (size > 0)
979 					error = ENOSPC;
980 
981 				return (error);
982 			}
983 		}
984 
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) {
989 			brelse(bp);
990 			return (ENOSPC);
991 		}
992 
993 		ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header),
994 		    EXT2_IFIRST(header), name, attrnamespace,
995 		    (char *)header + max_size, uio);
996 	}
997 
998 	return (bwrite(bp));
999 }
1000 
1001 static void
1002 ext2_extattr_hash_entry(struct ext2fs_extattr_header *header,
1003     struct ext2fs_extattr_entry *entry)
1004 {
1005 	uint32_t hash = 0;
1006 	char *name = entry->e_name;
1007 	int n;
1008 
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)) ^
1012 		    (*name++);
1013 	}
1014 
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)) ^
1021 			    (*value++);
1022 		}
1023 	}
1024 
1025 	entry->e_hash = hash;
1026 }
1027 
1028 static void
1029 ext2_extattr_rehash(struct ext2fs_extattr_header *header,
1030     struct ext2fs_extattr_entry *entry)
1031 {
1032 	struct ext2fs_extattr_entry *here;
1033 	uint32_t hash = 0;
1034 
1035 	ext2_extattr_hash_entry(header, entry);
1036 
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 */
1041 			hash = 0;
1042 			break;
1043 		}
1044 
1045 		hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^
1046 		    (hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^
1047 		    here->e_hash;
1048 
1049 		here = EXT2_EXTATTR_NEXT(here);
1050 	}
1051 
1052 	header->h_hash = hash;
1053 }
1054 
1055 int
1056 ext2_extattr_block_set(struct inode *ip, int attrnamespace,
1057     const char *name, struct uio *uio)
1058 {
1059 	struct m_ext2fs *fs;
1060 	struct buf *bp;
1061 	struct ext2fs_extattr_header *header;
1062 	struct ext2fs_extattr_entry *entry;
1063 	const char *attr_name;
1064 	int name_len;
1065 	size_t size;
1066 	int error;
1067 
1068 	fs = ip->i_e2fs;
1069 
1070 	if (ip->i_facl) {
1071 		error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1072 		    fs->e2fs_bsize, NOCRED, &bp);
1073 		if (error) {
1074 			return (error);
1075 		}
1076 
1077 		/* Check attributes magic value */
1078 		header = EXT2_HDR(bp);
1079 		if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1080 			brelse(bp);
1081 			return (EINVAL);
1082 		}
1083 
1084 		error = ext2_extattr_block_check(ip, bp);
1085 		if (error) {
1086 			brelse(bp);
1087 			return (error);
1088 		}
1089 
1090 		if (header->h_refcount > 1) {
1091 			error = ext2_extattr_block_clone(ip, &bp);
1092 			if (error) {
1093 				brelse(bp);
1094 				return (error);
1095 			}
1096 
1097 			header = EXT2_HDR(bp);
1098 		}
1099 
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) !=
1104 			    attrnamespace)
1105 				continue;
1106 
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);
1110 			if (!attr_name) {
1111 				brelse(bp);
1112 				return (ENOTSUP);
1113 			}
1114 
1115 			if (strlen(name) == name_len &&
1116 			    0 == strncmp(attr_name, name, name_len))
1117 				break;
1118 		}
1119 
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) {
1125 				brelse(bp);
1126 				return (ENOSPC);
1127 			}
1128 
1129 			ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1130 			    entry, bp->b_data + bp->b_bufsize, uio);
1131 		} else {
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) {
1136 				brelse(bp);
1137 				return (ENOSPC);
1138 			}
1139 
1140 			entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1141 			    name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1142 
1143 			/* Clean the same entry in the inode */
1144 			error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1145 			if (error && error != ENOATTR) {
1146 				brelse(bp);
1147 				return (error);
1148 			}
1149 		}
1150 
1151 		ext2_extattr_rehash(header, entry);
1152 		ext2_extattr_blk_csum_set(ip, bp);
1153 
1154 		return (bwrite(bp));
1155 	}
1156 
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)
1161 		return (ENOSPC);
1162 
1163 	/* Allocate block, fill EA header and insert entry */
1164 	ip->i_facl = ext2_alloc_meta(ip);
1165 	if (0 == ip->i_facl)
1166 		return (ENOSPC);
1167 
1168 	ip->i_blocks += btodb(fs->e2fs_bsize);
1169 	ext2_update(ip->i_vnode, 1);
1170 
1171 	bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0);
1172 	if (!bp) {
1173 		ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
1174 		ip->i_blocks -= btodb(fs->e2fs_bsize);
1175 		ip->i_facl = 0;
1176 		ext2_update(ip->i_vnode, 1);
1177 		return (EIO);
1178 	}
1179 
1180 	header = EXT2_HDR(bp);
1181 	header->h_magic = EXTATTR_MAGIC;
1182 	header->h_refcount = 1;
1183 	header->h_blocks = 1;
1184 	header->h_hash = 0;
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));
1188 
1189 	entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1190 	    name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1191 
1192 	/* Clean the same entry in the inode */
1193 	error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1194 	if (error && error != ENOATTR) {
1195 		brelse(bp);
1196 		return (error);
1197 	}
1198 
1199 	ext2_extattr_rehash(header, entry);
1200 	ext2_extattr_blk_csum_set(ip, bp);
1201 
1202 	return (bwrite(bp));
1203 }
1204 
1205 int ext2_extattr_free(struct inode *ip)
1206 {
1207 	struct m_ext2fs *fs;
1208 	struct buf *bp;
1209 	struct ext2fs_extattr_header *header;
1210 	int error;
1211 
1212 	fs = ip->i_e2fs;
1213 
1214 	if (!ip->i_facl)
1215 		return (0);
1216 
1217 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1218 	    fs->e2fs_bsize, NOCRED, &bp);
1219 	if (error) {
1220 		return (error);
1221 	}
1222 
1223 	/* Check attributes magic value */
1224 	header = EXT2_HDR(bp);
1225 	if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1226 		brelse(bp);
1227 		return (EINVAL);
1228 	}
1229 
1230 	error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp),
1231 	    bp->b_data + bp->b_bufsize);
1232 	if (error) {
1233 		brelse(bp);
1234 		return (error);
1235 	}
1236 
1237 	if (header->h_refcount > 1) {
1238 		header->h_refcount--;
1239 		bwrite(bp);
1240 	} else {
1241 		ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize);
1242 		brelse(bp);
1243 	}
1244 
1245 	ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize);
1246 	ip->i_facl = 0;
1247 	ext2_update(ip->i_vnode, 1);
1248 
1249 	return (0);
1250 }
1251