xref: /freebsd/sys/fs/ext2fs/ext2_extattr.c (revision 54e9e4e72d711fb41f88f793f6c64df1126112f9)
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 		brelse(bp);
284 		return (error);
285 	}
286 
287 	/* Check attributes magic value */
288 	header = EXT2_HDR(bp);
289 	if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
290 		brelse(bp);
291 		return (EINVAL);
292 	}
293 
294 	error = ext2_extattr_block_check(ip, bp);
295 	if (error) {
296 		brelse(bp);
297 		return (error);
298 	}
299 
300 	for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
301 	    entry = EXT2_EXTATTR_NEXT(entry)) {
302 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
303 		    attrnamespace)
304 			continue;
305 
306 		name_len = entry->e_name_len;
307 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
308 		    entry->e_name, &name_len);
309 		if (!attr_name) {
310 			brelse(bp);
311 			return (ENOTSUP);
312 		}
313 
314 		if (size != NULL)
315 			*size += name_len + 1;
316 
317 		if (uio != NULL) {
318 			char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
319 			name[0] = name_len;
320 			memcpy(&name[1], attr_name, name_len);
321 			error = uiomove(name, name_len + 1, uio);
322 			free(name, M_TEMP);
323 			if (error)
324 				break;
325 		}
326 	}
327 
328 	brelse(bp);
329 
330 	return (error);
331 }
332 
333 int
334 ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
335     const char *name, struct uio *uio, size_t *size)
336 {
337 	struct m_ext2fs *fs;
338 	struct buf *bp;
339 	struct ext2fs_extattr_dinode_header *header;
340 	struct ext2fs_extattr_entry *entry;
341 	const char *attr_name;
342 	int name_len;
343 	int error;
344 
345 	fs = ip->i_e2fs;
346 
347 	if ((error = bread(ip->i_devvp,
348 	    fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
349 	    (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
350 		brelse(bp);
351 		return (error);
352 	}
353 
354 	struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
355 	    ((char *)bp->b_data +
356 	    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
357 
358 	/* Check attributes magic value */
359 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
360 	    E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
361 
362 	if (header->h_magic != EXTATTR_MAGIC) {
363 		brelse(bp);
364 		return (ENOATTR);
365 	}
366 
367 	error = ext2_extattr_check(EXT2_IFIRST(header),
368 	    (char *)dinode + EXT2_INODE_SIZE(fs));
369 	if (error) {
370 		brelse(bp);
371 		return (error);
372 	}
373 
374 	for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
375 	    entry = EXT2_EXTATTR_NEXT(entry)) {
376 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
377 		    attrnamespace)
378 			continue;
379 
380 		name_len = entry->e_name_len;
381 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
382 		    entry->e_name, &name_len);
383 		if (!attr_name) {
384 			brelse(bp);
385 			return (ENOTSUP);
386 		}
387 
388 		if (strlen(name) == name_len &&
389 		    0 == strncmp(attr_name, name, name_len)) {
390 			if (size != NULL)
391 				*size += entry->e_value_size;
392 
393 			if (uio != NULL)
394 				error = uiomove(((char *)EXT2_IFIRST(header)) +
395 				    entry->e_value_offs, entry->e_value_size, uio);
396 
397 			brelse(bp);
398 			return (error);
399 		}
400 	 }
401 
402 	brelse(bp);
403 
404 	return (ENOATTR);
405 }
406 
407 int
408 ext2_extattr_block_get(struct inode *ip, int attrnamespace,
409     const char *name, struct uio *uio, size_t *size)
410 {
411 	struct m_ext2fs *fs;
412 	struct buf *bp;
413 	struct ext2fs_extattr_header *header;
414 	struct ext2fs_extattr_entry *entry;
415 	const char *attr_name;
416 	int name_len;
417 	int error;
418 
419 	fs = ip->i_e2fs;
420 
421 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
422 	    fs->e2fs_bsize, NOCRED, &bp);
423 	if (error) {
424 		brelse(bp);
425 		return (error);
426 	}
427 
428 	/* Check attributes magic value */
429 	header = EXT2_HDR(bp);
430 	if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
431 		brelse(bp);
432 		return (EINVAL);
433 	}
434 
435 	error = ext2_extattr_block_check(ip, bp);
436 	if (error) {
437 		brelse(bp);
438 		return (error);
439 	}
440 
441 	for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
442 	    entry = EXT2_EXTATTR_NEXT(entry)) {
443 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
444 		    attrnamespace)
445 			continue;
446 
447 		name_len = entry->e_name_len;
448 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
449 		    entry->e_name, &name_len);
450 		if (!attr_name) {
451 			brelse(bp);
452 			return (ENOTSUP);
453 		}
454 
455 		if (strlen(name) == name_len &&
456 		    0 == strncmp(attr_name, name, name_len)) {
457 			if (size != NULL)
458 				*size += entry->e_value_size;
459 
460 			if (uio != NULL)
461 				error = uiomove(bp->b_data + entry->e_value_offs,
462 				    entry->e_value_size, uio);
463 
464 			brelse(bp);
465 			return (error);
466 		}
467 	 }
468 
469 	brelse(bp);
470 
471 	return (ENOATTR);
472 }
473 
474 static uint16_t
475 ext2_extattr_delete_value(char *off,
476     struct ext2fs_extattr_entry *first_entry,
477     struct ext2fs_extattr_entry *entry, char *end)
478 {
479 	uint16_t min_offs;
480 	struct ext2fs_extattr_entry *next;
481 
482 	min_offs = end - off;
483 	next = first_entry;
484 	while (!EXT2_IS_LAST_ENTRY(next)) {
485 		if (min_offs > next->e_value_offs && next->e_value_offs > 0)
486 			min_offs = next->e_value_offs;
487 
488 		next = EXT2_EXTATTR_NEXT(next);
489 	}
490 
491 	if (entry->e_value_size == 0)
492 		return (min_offs);
493 
494 	memmove(off + min_offs + EXT2_EXTATTR_SIZE(entry->e_value_size),
495 	    off + min_offs, entry->e_value_offs - min_offs);
496 
497 	/* Adjust all value offsets */
498 	next = first_entry;
499 	while (!EXT2_IS_LAST_ENTRY(next))
500 	{
501 		if (next->e_value_offs > 0 &&
502 		    next->e_value_offs < entry->e_value_offs)
503 			next->e_value_offs +=
504 			    EXT2_EXTATTR_SIZE(entry->e_value_size);
505 
506 		next = EXT2_EXTATTR_NEXT(next);
507 	}
508 
509 	min_offs += EXT2_EXTATTR_SIZE(entry->e_value_size);
510 
511 	return (min_offs);
512 }
513 
514 static void
515 ext2_extattr_delete_entry(char *off,
516     struct ext2fs_extattr_entry *first_entry,
517     struct ext2fs_extattr_entry *entry, char *end)
518 {
519 	char *pad;
520 	struct ext2fs_extattr_entry *next;
521 
522 	/* Clean entry value */
523 	ext2_extattr_delete_value(off, first_entry, entry, end);
524 
525 	/* Clean the entry */
526 	next = first_entry;
527 	while (!EXT2_IS_LAST_ENTRY(next))
528 		next = EXT2_EXTATTR_NEXT(next);
529 
530 	pad = (char*)next + sizeof(uint32_t);
531 
532 	memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len),
533 	    pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len)));
534 }
535 
536 int
537 ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name)
538 {
539 	struct m_ext2fs *fs;
540 	struct buf *bp;
541 	struct ext2fs_extattr_dinode_header *header;
542 	struct ext2fs_extattr_entry *entry;
543 	const char *attr_name;
544 	int name_len;
545 	int error;
546 
547 	fs = ip->i_e2fs;
548 
549 	if ((error = bread(ip->i_devvp,
550 	    fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
551 	    (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
552 		brelse(bp);
553 		return (error);
554 	}
555 
556 	struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
557 	    ((char *)bp->b_data +
558 	    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
559 
560 	/* Check attributes magic value */
561 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
562 	    E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
563 
564 	if (header->h_magic != EXTATTR_MAGIC) {
565 		brelse(bp);
566 		return (ENOATTR);
567 	}
568 
569 	error = ext2_extattr_check(EXT2_IFIRST(header),
570 	    (char *)dinode + EXT2_INODE_SIZE(fs));
571 	if (error) {
572 		brelse(bp);
573 		return (error);
574 	}
575 
576 	/* If I am last entry, just make magic zero */
577 	entry = EXT2_IFIRST(header);
578 	if ((EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) &&
579 	    (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
580 	    attrnamespace)) {
581 
582 		name_len = entry->e_name_len;
583 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
584 		    entry->e_name, &name_len);
585 		if (!attr_name) {
586 			brelse(bp);
587 			return (ENOTSUP);
588 		}
589 
590 		if (strlen(name) == name_len &&
591 		    0 == strncmp(attr_name, name, name_len)) {
592 			memset(header, 0, sizeof(struct ext2fs_extattr_dinode_header));
593 
594 			return (bwrite(bp));
595 		}
596 	}
597 
598 	for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
599 	    entry = EXT2_EXTATTR_NEXT(entry)) {
600 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
601 		    attrnamespace)
602 			continue;
603 
604 		name_len = entry->e_name_len;
605 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
606 		    entry->e_name, &name_len);
607 		if (!attr_name) {
608 			brelse(bp);
609 			return (ENOTSUP);
610 		}
611 
612 		if (strlen(name) == name_len &&
613 		    0 == strncmp(attr_name, name, name_len)) {
614 			ext2_extattr_delete_entry((char *)EXT2_IFIRST(header),
615 			    EXT2_IFIRST(header), entry,
616 			    (char *)dinode + EXT2_INODE_SIZE(fs));
617 
618 			return (bwrite(bp));
619 		}
620 	}
621 
622 	brelse(bp);
623 
624 	return (ENOATTR);
625 }
626 
627 static int
628 ext2_extattr_block_clone(struct inode *ip, struct buf **bpp)
629 {
630 	struct m_ext2fs *fs;
631 	struct buf *sbp;
632 	struct buf *cbp;
633 	struct ext2fs_extattr_header *header;
634 	uint64_t facl;
635 
636 	fs = ip->i_e2fs;
637 	sbp = *bpp;
638 
639 	header = EXT2_HDR(sbp);
640 	if (header->h_magic != EXTATTR_MAGIC || header->h_refcount == 1)
641 		return (EINVAL);
642 
643 	facl = ext2_alloc_meta(ip);
644 	if (!facl)
645 		return (ENOSPC);
646 
647 	cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0);
648 	if (!cbp) {
649 		ext2_blkfree(ip, facl, fs->e2fs_bsize);
650 		return (EIO);
651 	}
652 
653 	memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize);
654 	header->h_refcount--;
655 	bwrite(sbp);
656 
657 	ip->i_facl = facl;
658 	ext2_update(ip->i_vnode, 1);
659 
660 	header = EXT2_HDR(cbp);
661 	header->h_refcount = 1;
662 
663 	*bpp = cbp;
664 
665 	return (0);
666 }
667 
668 int
669 ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name)
670 {
671 	struct m_ext2fs *fs;
672 	struct buf *bp;
673 	struct ext2fs_extattr_header *header;
674 	struct ext2fs_extattr_entry *entry;
675 	const char *attr_name;
676 	int name_len;
677 	int error;
678 
679 	fs = ip->i_e2fs;
680 
681 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
682 	    fs->e2fs_bsize, NOCRED, &bp);
683 	if (error) {
684 		brelse(bp);
685 		return (error);
686 	}
687 
688 	/* Check attributes magic value */
689 	header = EXT2_HDR(bp);
690 	if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
691 		brelse(bp);
692 		return (EINVAL);
693 	}
694 
695 	error = ext2_extattr_block_check(ip, bp);
696 	if (error) {
697 		brelse(bp);
698 		return (error);
699 	}
700 
701 	if (header->h_refcount > 1) {
702 		error = ext2_extattr_block_clone(ip, &bp);
703 		if (error) {
704 			brelse(bp);
705 			return (error);
706 		}
707 	}
708 
709 	/* If I am last entry, clean me and free the block */
710 	entry = EXT2_FIRST_ENTRY(bp);
711 	if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry)) &&
712 	    (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
713 	    attrnamespace)) {
714 
715 		name_len = entry->e_name_len;
716 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
717 		    entry->e_name, &name_len);
718 		if (!attr_name) {
719 			brelse(bp);
720 			return (ENOTSUP);
721 		}
722 
723 		if (strlen(name) == name_len &&
724 		    0 == strncmp(attr_name, name, name_len)) {
725 			ip->i_blocks -= btodb(fs->e2fs_bsize);
726 			ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
727 			ip->i_facl = 0;
728 			error = ext2_update(ip->i_vnode, 1);
729 
730 			brelse(bp);
731 			return (error);
732 		}
733 	}
734 
735 	for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
736 	    entry = EXT2_EXTATTR_NEXT(entry)) {
737 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
738 		    attrnamespace)
739 			continue;
740 
741 		name_len = entry->e_name_len;
742 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
743 		    entry->e_name, &name_len);
744 		if (!attr_name) {
745 			brelse(bp);
746 			return (ENOTSUP);
747 		}
748 
749 		if (strlen(name) == name_len &&
750 		    0 == strncmp(attr_name, name, name_len)) {
751 			ext2_extattr_delete_entry(bp->b_data,
752 			    EXT2_FIRST_ENTRY(bp), entry,
753 			    bp->b_data + bp->b_bufsize);
754 
755 			return (bwrite(bp));
756 		}
757 	}
758 
759 	brelse(bp);
760 
761 	return (ENOATTR);
762 }
763 
764 static struct ext2fs_extattr_entry *
765 allocate_entry(const char *name, int attrnamespace, uint16_t offs,
766     uint32_t size, uint32_t hash)
767 {
768 	const char *attr_name;
769 	int name_len;
770 	struct ext2fs_extattr_entry *entry;
771 
772 	attr_name = ext2_extattr_name_to_linux(attrnamespace, name);
773 	name_len = strlen(attr_name);
774 
775 	entry = malloc(sizeof(struct ext2fs_extattr_entry) + name_len,
776 	    M_TEMP, M_WAITOK);
777 
778 	entry->e_name_len = name_len;
779 	entry->e_name_index = ext2_extattr_attrnamespace_to_linux(attrnamespace, name);
780 	entry->e_value_offs = offs;
781 	entry->e_value_block = 0;
782 	entry->e_value_size = size;
783 	entry->e_hash = hash;
784 	memcpy(entry->e_name, name, name_len);
785 
786 	return (entry);
787 }
788 
789 static void
790 free_entry(struct ext2fs_extattr_entry *entry)
791 {
792 
793 	free(entry, M_TEMP);
794 }
795 
796 static int
797 ext2_extattr_get_size(struct ext2fs_extattr_entry *first_entry,
798     struct ext2fs_extattr_entry *exist_entry, int header_size,
799     int name_len, int new_size)
800 {
801 	struct ext2fs_extattr_entry *entry;
802 	int size;
803 
804 	size = header_size;
805 	size += sizeof(uint32_t);
806 
807 	if (NULL == exist_entry) {
808 		size += EXT2_EXTATTR_LEN(name_len);
809 		size += EXT2_EXTATTR_SIZE(new_size);
810 	}
811 
812 	if (first_entry)
813 		for (entry = first_entry; !EXT2_IS_LAST_ENTRY(entry);
814 		    entry = EXT2_EXTATTR_NEXT(entry)) {
815 			if (entry != exist_entry)
816 				size += EXT2_EXTATTR_LEN(entry->e_name_len) +
817 				    EXT2_EXTATTR_SIZE(entry->e_value_size);
818 			else
819 				size += EXT2_EXTATTR_LEN(entry->e_name_len) +
820 				    EXT2_EXTATTR_SIZE(new_size);
821 		}
822 
823 	return (size);
824 }
825 
826 static void
827 ext2_extattr_set_exist_entry(char *off,
828     struct ext2fs_extattr_entry *first_entry,
829     struct ext2fs_extattr_entry *entry,
830     char *end, struct uio *uio)
831 {
832 	uint16_t min_offs;
833 
834 	min_offs = ext2_extattr_delete_value(off, first_entry, entry, end);
835 
836 	entry->e_value_size = uio->uio_resid;
837 	if (entry->e_value_size)
838 		entry->e_value_offs = min_offs -
839 		    EXT2_EXTATTR_SIZE(uio->uio_resid);
840 	else
841 		entry->e_value_offs = 0;
842 
843 	uiomove(off + entry->e_value_offs, entry->e_value_size, uio);
844 }
845 
846 static struct ext2fs_extattr_entry *
847 ext2_extattr_set_new_entry(char *off, struct ext2fs_extattr_entry *first_entry,
848     const char *name, int attrnamespace, char *end, struct uio *uio)
849 {
850 	int name_len;
851 	char *pad;
852 	uint16_t min_offs;
853 	struct ext2fs_extattr_entry *entry;
854 	struct ext2fs_extattr_entry *new_entry;
855 
856 	/* Find pad's */
857 	min_offs = end - off;
858 	entry = first_entry;
859 	while (!EXT2_IS_LAST_ENTRY(entry)) {
860 		if (min_offs > entry->e_value_offs && entry->e_value_offs > 0)
861 			min_offs = entry->e_value_offs;
862 
863 		entry = EXT2_EXTATTR_NEXT(entry);
864 	}
865 
866 	pad = (char*)entry + sizeof(uint32_t);
867 
868 	/* Find entry insert position */
869 	name_len = strlen(name);
870 	entry = first_entry;
871 	while (!EXT2_IS_LAST_ENTRY(entry)) {
872 		if (!(attrnamespace - entry->e_name_index) &&
873 		    !(name_len - entry->e_name_len))
874 			if (memcmp(name, entry->e_name, name_len) <= 0)
875 				break;
876 
877 		entry = EXT2_EXTATTR_NEXT(entry);
878 	}
879 
880 	/* Create new entry and insert it */
881 	new_entry = allocate_entry(name, attrnamespace, 0, uio->uio_resid, 0);
882 	memmove((char *)entry + EXT2_EXTATTR_LEN(new_entry->e_name_len), entry,
883 	    pad - (char*)entry);
884 
885 	memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len));
886 	free_entry(new_entry);
887 
888 	new_entry = entry;
889 	if (new_entry->e_value_size > 0)
890 		new_entry->e_value_offs = min_offs -
891 		    EXT2_EXTATTR_SIZE(new_entry->e_value_size);
892 
893 	uiomove(off + new_entry->e_value_offs, new_entry->e_value_size, uio);
894 
895 	return (new_entry);
896 }
897 
898 int
899 ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
900     const char *name, struct uio *uio)
901 {
902 	struct m_ext2fs *fs;
903 	struct buf *bp;
904 	struct ext2fs_extattr_dinode_header *header;
905 	struct ext2fs_extattr_entry *entry;
906 	const char *attr_name;
907 	int name_len;
908 	size_t size = 0, max_size;
909 	int error;
910 
911 	fs = ip->i_e2fs;
912 
913 	if ((error = bread(ip->i_devvp,
914 	    fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
915 	    (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
916 		brelse(bp);
917 		return (error);
918 	}
919 
920 	struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
921 	    ((char *)bp->b_data +
922 	    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
923 
924 	/* Check attributes magic value */
925 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
926 	    E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
927 
928 	if (header->h_magic != EXTATTR_MAGIC) {
929 		brelse(bp);
930 		return (ENOSPC);
931 	}
932 
933 	error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode +
934 	    EXT2_INODE_SIZE(fs));
935 	if (error) {
936 		brelse(bp);
937 		return (error);
938 	}
939 
940 	/* Find if entry exist */
941 	for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
942 	    entry = EXT2_EXTATTR_NEXT(entry)) {
943 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
944 		    attrnamespace)
945 			continue;
946 
947 		name_len = entry->e_name_len;
948 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
949 		    entry->e_name, &name_len);
950 		if (!attr_name) {
951 			brelse(bp);
952 			return (ENOTSUP);
953 		}
954 
955 		if (strlen(name) == name_len &&
956 		    0 == strncmp(attr_name, name, name_len))
957 			break;
958 	}
959 
960 	max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE -
961 	    dinode->e2di_extra_isize;
962 
963 	if (!EXT2_IS_LAST_ENTRY(entry)) {
964 		size = ext2_extattr_get_size(EXT2_IFIRST(header), entry,
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_exist_entry((char *)EXT2_IFIRST(header),
973 		    EXT2_IFIRST(header), entry, (char *)header + max_size, uio);
974 	} else {
975 		/* Ensure that the same entry does not exist in the block */
976 		if (ip->i_facl) {
977 			error = ext2_extattr_block_get(ip, attrnamespace, name,
978 			    NULL, &size);
979 			if (error != ENOATTR || size > 0) {
980 				brelse(bp);
981 				if (size > 0)
982 					error = ENOSPC;
983 
984 				return (error);
985 			}
986 		}
987 
988 		size = ext2_extattr_get_size(EXT2_IFIRST(header), NULL,
989 		    sizeof(struct ext2fs_extattr_dinode_header),
990 		    entry->e_name_len, uio->uio_resid);
991 		if (size > max_size) {
992 			brelse(bp);
993 			return (ENOSPC);
994 		}
995 
996 		ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header),
997 		    EXT2_IFIRST(header), name, attrnamespace,
998 		    (char *)header + max_size, uio);
999 	}
1000 
1001 	return (bwrite(bp));
1002 }
1003 
1004 static void
1005 ext2_extattr_hash_entry(struct ext2fs_extattr_header *header,
1006     struct ext2fs_extattr_entry *entry)
1007 {
1008 	uint32_t hash = 0;
1009 	char *name = entry->e_name;
1010 	int n;
1011 
1012 	for (n=0; n < entry->e_name_len; n++) {
1013 		hash = (hash << EXT2_EXTATTR_NAME_HASH_SHIFT) ^
1014 		    (hash >> (8*sizeof(hash) - EXT2_EXTATTR_NAME_HASH_SHIFT)) ^
1015 		    (*name++);
1016 	}
1017 
1018 	if (entry->e_value_block == 0 && entry->e_value_size != 0) {
1019 		uint32_t *value = (uint32_t *)((char *)header + entry->e_value_offs);
1020 		for (n = (entry->e_value_size +
1021 		    EXT2_EXTATTR_ROUND) >> EXT2_EXTATTR_PAD_BITS; n; n--) {
1022 			hash = (hash << EXT2_EXTATTR_VALUE_HASH_SHIFT) ^
1023 			    (hash >> (8*sizeof(hash) - EXT2_EXTATTR_VALUE_HASH_SHIFT)) ^
1024 			    (*value++);
1025 		}
1026 	}
1027 
1028 	entry->e_hash = hash;
1029 }
1030 
1031 static void
1032 ext2_extattr_rehash(struct ext2fs_extattr_header *header,
1033     struct ext2fs_extattr_entry *entry)
1034 {
1035 	struct ext2fs_extattr_entry *here;
1036 	uint32_t hash = 0;
1037 
1038 	ext2_extattr_hash_entry(header, entry);
1039 
1040 	here = EXT2_ENTRY(header+1);
1041 	while (!EXT2_IS_LAST_ENTRY(here)) {
1042 		if (!here->e_hash) {
1043 			/* Block is not shared if an entry's hash value == 0 */
1044 			hash = 0;
1045 			break;
1046 		}
1047 
1048 		hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^
1049 		    (hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^
1050 		    here->e_hash;
1051 
1052 		here = EXT2_EXTATTR_NEXT(here);
1053 	}
1054 
1055 	header->h_hash = hash;
1056 }
1057 
1058 int
1059 ext2_extattr_block_set(struct inode *ip, int attrnamespace,
1060     const char *name, struct uio *uio)
1061 {
1062 	struct m_ext2fs *fs;
1063 	struct buf *bp;
1064 	struct ext2fs_extattr_header *header;
1065 	struct ext2fs_extattr_entry *entry;
1066 	const char *attr_name;
1067 	int name_len;
1068 	size_t size;
1069 	int error;
1070 
1071 	fs = ip->i_e2fs;
1072 
1073 	if (ip->i_facl) {
1074 		error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1075 		    fs->e2fs_bsize, NOCRED, &bp);
1076 		if (error) {
1077 			brelse(bp);
1078 			return (error);
1079 		}
1080 
1081 		/* Check attributes magic value */
1082 		header = EXT2_HDR(bp);
1083 		if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1084 			brelse(bp);
1085 			return (EINVAL);
1086 		}
1087 
1088 		error = ext2_extattr_block_check(ip, bp);
1089 		if (error) {
1090 			brelse(bp);
1091 			return (error);
1092 		}
1093 
1094 		if (header->h_refcount > 1) {
1095 			error = ext2_extattr_block_clone(ip, &bp);
1096 			if (error) {
1097 				brelse(bp);
1098 				return (error);
1099 			}
1100 
1101 			header = EXT2_HDR(bp);
1102 		}
1103 
1104 		/* Find if entry exist */
1105 		for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
1106 		    entry = EXT2_EXTATTR_NEXT(entry)) {
1107 			if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
1108 			    attrnamespace)
1109 				continue;
1110 
1111 			name_len = entry->e_name_len;
1112 			attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
1113 			    entry->e_name, &name_len);
1114 			if (!attr_name) {
1115 				brelse(bp);
1116 				return (ENOTSUP);
1117 			}
1118 
1119 			if (strlen(name) == name_len &&
1120 			    0 == strncmp(attr_name, name, name_len))
1121 				break;
1122 		}
1123 
1124 		if (!EXT2_IS_LAST_ENTRY(entry)) {
1125 			size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), entry,
1126 			    sizeof(struct ext2fs_extattr_header),
1127 			    entry->e_name_len, uio->uio_resid);
1128 			if (size > bp->b_bufsize) {
1129 				brelse(bp);
1130 				return (ENOSPC);
1131 			}
1132 
1133 			ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1134 			    entry, bp->b_data + bp->b_bufsize, uio);
1135 		} else {
1136 			size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), NULL,
1137 			    sizeof(struct ext2fs_extattr_header),
1138 			    strlen(name), uio->uio_resid);
1139 			if (size > bp->b_bufsize) {
1140 				brelse(bp);
1141 				return (ENOSPC);
1142 			}
1143 
1144 			entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1145 			    name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1146 
1147 			/* Clean the same entry in the inode */
1148 			error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1149 			if (error && error != ENOATTR) {
1150 				brelse(bp);
1151 				return (error);
1152 			}
1153 		}
1154 
1155 		ext2_extattr_rehash(header, entry);
1156 		ext2_extattr_blk_csum_set(ip, bp);
1157 
1158 		return (bwrite(bp));
1159 	}
1160 
1161 	size = ext2_extattr_get_size(NULL, NULL,
1162 	    sizeof(struct ext2fs_extattr_header),
1163 	    strlen(ext2_extattr_name_to_linux(attrnamespace, name)), uio->uio_resid);
1164 	if (size > fs->e2fs_bsize)
1165 		return (ENOSPC);
1166 
1167 	/* Allocate block, fill EA header and insert entry */
1168 	ip->i_facl = ext2_alloc_meta(ip);
1169 	if (0 == ip->i_facl)
1170 		return (ENOSPC);
1171 
1172 	ip->i_blocks += btodb(fs->e2fs_bsize);
1173 	ext2_update(ip->i_vnode, 1);
1174 
1175 	bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0);
1176 	if (!bp) {
1177 		ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
1178 		ip->i_blocks -= btodb(fs->e2fs_bsize);
1179 		ip->i_facl = 0;
1180 		ext2_update(ip->i_vnode, 1);
1181 		return (EIO);
1182 	}
1183 
1184 	header = EXT2_HDR(bp);
1185 	header->h_magic = EXTATTR_MAGIC;
1186 	header->h_refcount = 1;
1187 	header->h_blocks = 1;
1188 	header->h_hash = 0;
1189 	memset(header->h_reserved, 0, sizeof(header->h_reserved));
1190 	memcpy(bp->b_data, header, sizeof(struct ext2fs_extattr_header));
1191 	memset(EXT2_FIRST_ENTRY(bp), 0, sizeof(uint32_t));
1192 
1193 	entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1194 	    name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1195 
1196 	/* Clean the same entry in the inode */
1197 	error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1198 	if (error && error != ENOATTR) {
1199 		brelse(bp);
1200 		return (error);
1201 	}
1202 
1203 	ext2_extattr_rehash(header, entry);
1204 	ext2_extattr_blk_csum_set(ip, bp);
1205 
1206 	return (bwrite(bp));
1207 }
1208 
1209 int ext2_extattr_free(struct inode *ip)
1210 {
1211 	struct m_ext2fs *fs;
1212 	struct buf *bp;
1213 	struct ext2fs_extattr_header *header;
1214 	int error;
1215 
1216 	fs = ip->i_e2fs;
1217 
1218 	if (!ip->i_facl)
1219 		return (0);
1220 
1221 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1222 	    fs->e2fs_bsize, NOCRED, &bp);
1223 	if (error) {
1224 		brelse(bp);
1225 		return (error);
1226 	}
1227 
1228 	/* Check attributes magic value */
1229 	header = EXT2_HDR(bp);
1230 	if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1231 		brelse(bp);
1232 		return (EINVAL);
1233 	}
1234 
1235 	error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp),
1236 	    bp->b_data + bp->b_bufsize);
1237 	if (error) {
1238 		brelse(bp);
1239 		return (error);
1240 	}
1241 
1242 	if (header->h_refcount > 1) {
1243 		header->h_refcount--;
1244 		bwrite(bp);
1245 	} else {
1246 		ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize);
1247 		brelse(bp);
1248 	}
1249 
1250 	ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize);
1251 	ip->i_facl = 0;
1252 	ext2_update(ip->i_vnode, 1);
1253 
1254 	return (0);
1255 }
1256