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