xref: /freebsd/sys/fs/ext2fs/ext2_extattr.c (revision 95ee2897e98f5d444f26ed2334cc7c439f9c16c6)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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 
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 #include <sys/sdt.h>
41 
42 #include <fs/ext2fs/fs.h>
43 #include <fs/ext2fs/ext2fs.h>
44 #include <fs/ext2fs/inode.h>
45 #include <fs/ext2fs/ext2_dinode.h>
46 #include <fs/ext2fs/ext2_mount.h>
47 #include <fs/ext2fs/ext2_extattr.h>
48 #include <fs/ext2fs/ext2_extern.h>
49 
50 SDT_PROVIDER_DECLARE(ext2fs);
51 /*
52  * ext2fs trace probe:
53  * arg0: verbosity. Higher numbers give more verbose messages
54  * arg1: Textual message
55  */
56 SDT_PROBE_DEFINE2(ext2fs, , trace, extattr, "int", "char*");
57 
58 static int
ext2_extattr_attrnamespace_to_bsd(int attrnamespace)59 ext2_extattr_attrnamespace_to_bsd(int attrnamespace)
60 {
61 
62 	switch (attrnamespace) {
63 	case EXT4_XATTR_INDEX_SYSTEM:
64 		return (EXTATTR_NAMESPACE_SYSTEM);
65 
66 	case EXT4_XATTR_INDEX_USER:
67 		return (EXTATTR_NAMESPACE_USER);
68 
69 	case EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT:
70 		return (POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE);
71 
72 	case EXT4_XATTR_INDEX_POSIX_ACL_ACCESS:
73 		return (POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE);
74 	}
75 
76 	return (EXTATTR_NAMESPACE_EMPTY);
77 }
78 
79 static const char *
ext2_extattr_name_to_bsd(int attrnamespace,const char * name,int * name_len)80 ext2_extattr_name_to_bsd(int attrnamespace, const char *name, int* name_len)
81 {
82 
83 	if (attrnamespace == EXT4_XATTR_INDEX_SYSTEM)
84 		return (name);
85 	else if (attrnamespace == EXT4_XATTR_INDEX_USER)
86 		return (name);
87 	else if (attrnamespace == EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT) {
88 		*name_len = strlen(POSIX1E_ACL_DEFAULT_EXTATTR_NAME);
89 		return (POSIX1E_ACL_DEFAULT_EXTATTR_NAME);
90 	} else if (attrnamespace == EXT4_XATTR_INDEX_POSIX_ACL_ACCESS) {
91 		*name_len = strlen(POSIX1E_ACL_ACCESS_EXTATTR_NAME);
92 		return (POSIX1E_ACL_ACCESS_EXTATTR_NAME);
93 	}
94 
95 	/*
96 	 * XXX: Not all linux namespaces are mapped to bsd for now,
97 	 * return NULL, which will be converted to ENOTSUP on upper layer.
98 	 */
99 	SDT_PROBE2(ext2fs, , trace, extattr, 1,
100 	    "can not convert ext2fs name to bsd namespace");
101 
102 	return (NULL);
103 }
104 
105 static int
ext2_extattr_attrnamespace_to_linux(int attrnamespace,const char * name)106 ext2_extattr_attrnamespace_to_linux(int attrnamespace, const char *name)
107 {
108 
109 	if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE &&
110 	    !strcmp(name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME))
111 		return (EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT);
112 
113 	if (attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE &&
114 	    !strcmp(name, POSIX1E_ACL_ACCESS_EXTATTR_NAME))
115 		return (EXT4_XATTR_INDEX_POSIX_ACL_ACCESS);
116 
117 	switch (attrnamespace) {
118 	case EXTATTR_NAMESPACE_SYSTEM:
119 		return (EXT4_XATTR_INDEX_SYSTEM);
120 
121 	case EXTATTR_NAMESPACE_USER:
122 		return (EXT4_XATTR_INDEX_USER);
123 	}
124 
125 	/*
126 	 * In this case namespace conversion should be unique,
127 	 * so this point is unreachable.
128 	 */
129 	return (-1);
130 }
131 
132 static const char *
ext2_extattr_name_to_linux(int attrnamespace,const char * name)133 ext2_extattr_name_to_linux(int attrnamespace, const char *name)
134 {
135 
136 	if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE ||
137 	    attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE)
138 		return ("");
139 	else
140 		return (name);
141 }
142 
143 int
ext2_extattr_valid_attrname(int attrnamespace,const char * attrname)144 ext2_extattr_valid_attrname(int attrnamespace, const char *attrname)
145 {
146 	if (attrnamespace == EXTATTR_NAMESPACE_EMPTY)
147 		return (EINVAL);
148 
149 	if (strlen(attrname) == 0)
150 		return (EINVAL);
151 
152 	if (strlen(attrname) + 1 > EXT2_EXTATTR_NAMELEN_MAX)
153 		return (ENAMETOOLONG);
154 
155 	return (0);
156 }
157 
158 static int
ext2_extattr_check(struct ext2fs_extattr_entry * entry,char * end)159 ext2_extattr_check(struct ext2fs_extattr_entry *entry, char *end)
160 {
161 	struct ext2fs_extattr_entry *next;
162 
163 	while (!EXT2_IS_LAST_ENTRY(entry)) {
164 		next = EXT2_EXTATTR_NEXT(entry);
165 		if ((char *)next >= end)
166 			return (EIO);
167 
168 		entry = next;
169 	}
170 
171 	return (0);
172 }
173 
174 static int
ext2_extattr_block_check(struct inode * ip,struct buf * bp)175 ext2_extattr_block_check(struct inode *ip, struct buf *bp)
176 {
177 	struct ext2fs_extattr_header *header;
178 	int error;
179 
180 	header = (struct ext2fs_extattr_header *)bp->b_data;
181 
182 	error = ext2_extattr_check(EXT2_IFIRST(header),
183 	    bp->b_data + bp->b_bufsize);
184 	if (error)
185 		return (error);
186 
187 	return (ext2_extattr_blk_csum_verify(ip, bp));
188 }
189 
190 int
ext2_extattr_inode_list(struct inode * ip,int attrnamespace,struct uio * uio,size_t * size)191 ext2_extattr_inode_list(struct inode *ip, int attrnamespace,
192     struct uio *uio, size_t *size)
193 {
194 	struct m_ext2fs *fs;
195 	struct buf *bp;
196 	struct ext2fs_extattr_dinode_header *header;
197 	struct ext2fs_extattr_entry *entry;
198 	const char *attr_name;
199 	int name_len;
200 	int error;
201 
202 	fs = ip->i_e2fs;
203 
204 	if ((error = bread(ip->i_devvp,
205 	    fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
206 	    (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
207 		brelse(bp);
208 		return (error);
209 	}
210 
211 	struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
212 	    ((char *)bp->b_data +
213 	    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
214 
215 	/* Check attributes magic value */
216 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
217 	    E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
218 
219 	if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
220 		brelse(bp);
221 		return (0);
222 	}
223 
224 	error = ext2_extattr_check(EXT2_IFIRST(header),
225 	    (char *)dinode + EXT2_INODE_SIZE(fs));
226 	if (error) {
227 		brelse(bp);
228 		return (error);
229 	}
230 
231 	for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
232 	    entry = EXT2_EXTATTR_NEXT(entry)) {
233 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
234 		    attrnamespace)
235 			continue;
236 
237 		name_len = entry->e_name_len;
238 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
239 		    entry->e_name, &name_len);
240 		if (!attr_name) {
241 			brelse(bp);
242 			return (ENOTSUP);
243 		}
244 
245 		if (size != NULL)
246 			*size += name_len + 1;
247 
248 		if (uio != NULL) {
249 			char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
250 			name[0] = name_len;
251 			memcpy(&name[1], attr_name, name_len);
252 			error = uiomove(name, name_len + 1, uio);
253 			free(name, M_TEMP);
254 			if (error)
255 				break;
256 		}
257 	}
258 
259 	brelse(bp);
260 
261 	return (error);
262 }
263 
264 int
ext2_extattr_block_list(struct inode * ip,int attrnamespace,struct uio * uio,size_t * size)265 ext2_extattr_block_list(struct inode *ip, int attrnamespace,
266     struct uio *uio, size_t *size)
267 {
268 	struct m_ext2fs *fs;
269 	struct buf *bp;
270 	struct ext2fs_extattr_header *header;
271 	struct ext2fs_extattr_entry *entry;
272 	const char *attr_name;
273 	int name_len;
274 	int error;
275 
276 	fs = ip->i_e2fs;
277 
278 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
279 	    fs->e2fs_bsize, NOCRED, &bp);
280 	if (error) {
281 		return (error);
282 	}
283 
284 	/* Check attributes magic value */
285 	header = EXT2_HDR(bp);
286 	if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
287 	    le32toh(header->h_blocks) != 1) {
288 		brelse(bp);
289 		return (EINVAL);
290 	}
291 
292 	error = ext2_extattr_block_check(ip, bp);
293 	if (error) {
294 		brelse(bp);
295 		return (error);
296 	}
297 
298 	for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
299 	    entry = EXT2_EXTATTR_NEXT(entry)) {
300 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
301 		    attrnamespace)
302 			continue;
303 
304 		name_len = entry->e_name_len;
305 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
306 		    entry->e_name, &name_len);
307 		if (!attr_name) {
308 			brelse(bp);
309 			return (ENOTSUP);
310 		}
311 
312 		if (size != NULL)
313 			*size += name_len + 1;
314 
315 		if (uio != NULL) {
316 			char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
317 			name[0] = name_len;
318 			memcpy(&name[1], attr_name, name_len);
319 			error = uiomove(name, name_len + 1, uio);
320 			free(name, M_TEMP);
321 			if (error)
322 				break;
323 		}
324 	}
325 
326 	brelse(bp);
327 
328 	return (error);
329 }
330 
331 int
ext2_extattr_inode_get(struct inode * ip,int attrnamespace,const char * name,struct uio * uio,size_t * size)332 ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
333     const char *name, struct uio *uio, size_t *size)
334 {
335 	struct m_ext2fs *fs;
336 	struct buf *bp;
337 	struct ext2fs_extattr_dinode_header *header;
338 	struct ext2fs_extattr_entry *entry;
339 	const char *attr_name;
340 	int name_len;
341 	int error;
342 
343 	fs = ip->i_e2fs;
344 
345 	if ((error = bread(ip->i_devvp,
346 	    fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
347 	    (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
348 		brelse(bp);
349 		return (error);
350 	}
351 
352 	struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
353 	    ((char *)bp->b_data +
354 	    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
355 
356 	/* Check attributes magic value */
357 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
358 	    E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
359 
360 	if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
361 		brelse(bp);
362 		return (ENOATTR);
363 	}
364 
365 	error = ext2_extattr_check(EXT2_IFIRST(header),
366 	    (char *)dinode + EXT2_INODE_SIZE(fs));
367 	if (error) {
368 		brelse(bp);
369 		return (error);
370 	}
371 
372 	for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
373 	    entry = EXT2_EXTATTR_NEXT(entry)) {
374 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
375 		    attrnamespace)
376 			continue;
377 
378 		name_len = entry->e_name_len;
379 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
380 		    entry->e_name, &name_len);
381 		if (!attr_name) {
382 			brelse(bp);
383 			return (ENOTSUP);
384 		}
385 
386 		if (strlen(name) == name_len &&
387 		    0 == strncmp(attr_name, name, name_len)) {
388 			if (size != NULL)
389 				*size += le32toh(entry->e_value_size);
390 
391 			if (uio != NULL)
392 				error = uiomove(((char *)EXT2_IFIRST(header)) +
393 				    le16toh(entry->e_value_offs),
394 				    le32toh(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
ext2_extattr_block_get(struct inode * ip,int attrnamespace,const char * name,struct uio * uio,size_t * size)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 (le32toh(header->h_magic) != EXTATTR_MAGIC ||
429 	    le32toh(header->h_blocks) != 1) {
430 		brelse(bp);
431 		return (EINVAL);
432 	}
433 
434 	error = ext2_extattr_block_check(ip, bp);
435 	if (error) {
436 		brelse(bp);
437 		return (error);
438 	}
439 
440 	for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
441 	    entry = EXT2_EXTATTR_NEXT(entry)) {
442 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
443 		    attrnamespace)
444 			continue;
445 
446 		name_len = entry->e_name_len;
447 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
448 		    entry->e_name, &name_len);
449 		if (!attr_name) {
450 			brelse(bp);
451 			return (ENOTSUP);
452 		}
453 
454 		if (strlen(name) == name_len &&
455 		    0 == strncmp(attr_name, name, name_len)) {
456 			if (size != NULL)
457 				*size += le32toh(entry->e_value_size);
458 
459 			if (uio != NULL)
460 				error = uiomove(bp->b_data +
461 				    le16toh(entry->e_value_offs),
462 				    le32toh(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
ext2_extattr_delete_value(char * off,struct ext2fs_extattr_entry * first_entry,struct ext2fs_extattr_entry * entry,char * end)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 > le16toh(next->e_value_offs) &&
486 		    le16toh(next->e_value_offs) > 0)
487 			min_offs = le16toh(next->e_value_offs);
488 
489 		next = EXT2_EXTATTR_NEXT(next);
490 	}
491 
492 	if (entry->e_value_size == 0)
493 		return (min_offs);
494 
495 	memmove(off + min_offs + EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size)),
496 	    off + min_offs, le16toh(entry->e_value_offs) - min_offs);
497 
498 	/* Adjust all value offsets */
499 	next = first_entry;
500 	while (!EXT2_IS_LAST_ENTRY(next))
501 	{
502 		if (le16toh(next->e_value_offs) > 0 &&
503 		    le16toh(next->e_value_offs) < le16toh(entry->e_value_offs))
504 			next->e_value_offs = htole16(le16toh(next->e_value_offs) +
505 			    EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size)));
506 
507 		next = EXT2_EXTATTR_NEXT(next);
508 	}
509 
510 	min_offs += EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size));
511 
512 	return (min_offs);
513 }
514 
515 static void
ext2_extattr_delete_entry(char * off,struct ext2fs_extattr_entry * first_entry,struct ext2fs_extattr_entry * entry,char * end)516 ext2_extattr_delete_entry(char *off,
517     struct ext2fs_extattr_entry *first_entry,
518     struct ext2fs_extattr_entry *entry, char *end)
519 {
520 	char *pad;
521 	struct ext2fs_extattr_entry *next;
522 
523 	/* Clean entry value */
524 	ext2_extattr_delete_value(off, first_entry, entry, end);
525 
526 	/* Clean the entry */
527 	next = first_entry;
528 	while (!EXT2_IS_LAST_ENTRY(next))
529 		next = EXT2_EXTATTR_NEXT(next);
530 
531 	pad = (char*)next + sizeof(uint32_t);
532 
533 	memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len),
534 	    pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len)));
535 }
536 
537 int
ext2_extattr_inode_delete(struct inode * ip,int attrnamespace,const char * name)538 ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name)
539 {
540 	struct m_ext2fs *fs;
541 	struct buf *bp;
542 	struct ext2fs_extattr_dinode_header *header;
543 	struct ext2fs_extattr_entry *entry;
544 	const char *attr_name;
545 	int name_len;
546 	int error;
547 
548 	fs = ip->i_e2fs;
549 
550 	if ((error = bread(ip->i_devvp,
551 	    fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
552 	    (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
553 		brelse(bp);
554 		return (error);
555 	}
556 
557 	struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
558 	    ((char *)bp->b_data +
559 	    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
560 
561 	/* Check attributes magic value */
562 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
563 	    E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
564 
565 	if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
566 		brelse(bp);
567 		return (ENOATTR);
568 	}
569 
570 	error = ext2_extattr_check(EXT2_IFIRST(header),
571 	    (char *)dinode + EXT2_INODE_SIZE(fs));
572 	if (error) {
573 		brelse(bp);
574 		return (error);
575 	}
576 
577 	/* If I am last entry, just make magic zero */
578 	entry = EXT2_IFIRST(header);
579 	if ((EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) &&
580 	    (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
581 	    attrnamespace)) {
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
ext2_extattr_block_clone(struct inode * ip,struct buf ** bpp)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 (le32toh(header->h_magic) != EXTATTR_MAGIC ||
641 	    le32toh(header->h_refcount) == 1)
642 		return (EINVAL);
643 
644 	facl = ext2_alloc_meta(ip);
645 	if (!facl)
646 		return (ENOSPC);
647 
648 	cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0);
649 	if (!cbp) {
650 		ext2_blkfree(ip, facl, fs->e2fs_bsize);
651 		return (EIO);
652 	}
653 
654 	memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize);
655 	header->h_refcount = htole32(le32toh(header->h_refcount) - 1);
656 	bwrite(sbp);
657 
658 	ip->i_facl = facl;
659 	ext2_update(ip->i_vnode, 1);
660 
661 	header = EXT2_HDR(cbp);
662 	header->h_refcount = htole32(1);
663 
664 	*bpp = cbp;
665 
666 	return (0);
667 }
668 
669 int
ext2_extattr_block_delete(struct inode * ip,int attrnamespace,const char * name)670 ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name)
671 {
672 	struct m_ext2fs *fs;
673 	struct buf *bp;
674 	struct ext2fs_extattr_header *header;
675 	struct ext2fs_extattr_entry *entry;
676 	const char *attr_name;
677 	int name_len;
678 	int error;
679 
680 	fs = ip->i_e2fs;
681 
682 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
683 	    fs->e2fs_bsize, NOCRED, &bp);
684 	if (error) {
685 		return (error);
686 	}
687 
688 	/* Check attributes magic value */
689 	header = EXT2_HDR(bp);
690 	if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
691 	    le32toh(header->h_blocks) != 1) {
692 		brelse(bp);
693 		return (EINVAL);
694 	}
695 
696 	error = ext2_extattr_block_check(ip, bp);
697 	if (error) {
698 		brelse(bp);
699 		return (error);
700 	}
701 
702 	if (le32toh(header->h_refcount) > 1) {
703 		error = ext2_extattr_block_clone(ip, &bp);
704 		if (error) {
705 			brelse(bp);
706 			return (error);
707 		}
708 	}
709 
710 	/* If I am last entry, clean me and free the block */
711 	entry = EXT2_FIRST_ENTRY(bp);
712 	if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry)) &&
713 	    (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
714 	    attrnamespace)) {
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 *
allocate_entry(const char * name,int attrnamespace,uint16_t offs,uint32_t size,uint32_t hash)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 = htole16(offs);
781 	entry->e_value_block = 0;
782 	entry->e_value_size = htole32(size);
783 	entry->e_hash = htole32(hash);
784 	memcpy(entry->e_name, name, name_len);
785 
786 	return (entry);
787 }
788 
789 static void
free_entry(struct ext2fs_extattr_entry * entry)790 free_entry(struct ext2fs_extattr_entry *entry)
791 {
792 
793 	free(entry, M_TEMP);
794 }
795 
796 static int
ext2_extattr_get_size(struct ext2fs_extattr_entry * first_entry,struct ext2fs_extattr_entry * exist_entry,int header_size,int name_len,int new_size)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(le32toh(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
ext2_extattr_set_exist_entry(char * off,struct ext2fs_extattr_entry * first_entry,struct ext2fs_extattr_entry * entry,char * end,struct uio * uio)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 = htole32(uio->uio_resid);
837 	if (le32toh(entry->e_value_size))
838 		entry->e_value_offs = htole16(min_offs -
839 		    EXT2_EXTATTR_SIZE(uio->uio_resid));
840 	else
841 		entry->e_value_offs = 0;
842 
843 	uiomove(off + le16toh(entry->e_value_offs),
844 	    le32toh(entry->e_value_size), uio);
845 }
846 
847 static struct ext2fs_extattr_entry *
ext2_extattr_set_new_entry(char * off,struct ext2fs_extattr_entry * first_entry,const char * name,int attrnamespace,char * end,struct uio * uio)848 ext2_extattr_set_new_entry(char *off, struct ext2fs_extattr_entry *first_entry,
849     const char *name, int attrnamespace, char *end, struct uio *uio)
850 {
851 	int name_len;
852 	char *pad;
853 	uint16_t min_offs;
854 	struct ext2fs_extattr_entry *entry;
855 	struct ext2fs_extattr_entry *new_entry;
856 
857 	/* Find pad's */
858 	min_offs = end - off;
859 	entry = first_entry;
860 	while (!EXT2_IS_LAST_ENTRY(entry)) {
861 		if (min_offs > le16toh(entry->e_value_offs) &&
862 		    le16toh(entry->e_value_offs) > 0)
863 			min_offs = le16toh(entry->e_value_offs);
864 
865 		entry = EXT2_EXTATTR_NEXT(entry);
866 	}
867 
868 	pad = (char*)entry + sizeof(uint32_t);
869 
870 	/* Find entry insert position */
871 	name_len = strlen(name);
872 	entry = first_entry;
873 	while (!EXT2_IS_LAST_ENTRY(entry)) {
874 		if (!(attrnamespace - entry->e_name_index) &&
875 		    !(name_len - entry->e_name_len))
876 			if (memcmp(name, entry->e_name, name_len) <= 0)
877 				break;
878 
879 		entry = EXT2_EXTATTR_NEXT(entry);
880 	}
881 
882 	/* Create new entry and insert it */
883 	new_entry = allocate_entry(name, attrnamespace, 0, uio->uio_resid, 0);
884 	memmove((char *)entry + EXT2_EXTATTR_LEN(new_entry->e_name_len), entry,
885 	    pad - (char*)entry);
886 
887 	memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len));
888 	free_entry(new_entry);
889 
890 	new_entry = entry;
891 	if (le32toh(new_entry->e_value_size) > 0)
892 		new_entry->e_value_offs = htole16(min_offs -
893 		    EXT2_EXTATTR_SIZE(le32toh(new_entry->e_value_size)));
894 
895 	uiomove(off + le16toh(new_entry->e_value_offs),
896 	    le32toh(new_entry->e_value_size), uio);
897 
898 	return (new_entry);
899 }
900 
901 int
ext2_extattr_inode_set(struct inode * ip,int attrnamespace,const char * name,struct uio * uio)902 ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
903     const char *name, struct uio *uio)
904 {
905 	struct m_ext2fs *fs;
906 	struct buf *bp;
907 	struct ext2fs_extattr_dinode_header *header;
908 	struct ext2fs_extattr_entry *entry;
909 	const char *attr_name;
910 	int name_len;
911 	size_t size = 0, max_size;
912 	int error;
913 
914 	fs = ip->i_e2fs;
915 
916 	if ((error = bread(ip->i_devvp,
917 	    fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
918 	    (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
919 		brelse(bp);
920 		return (error);
921 	}
922 
923 	struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
924 	    ((char *)bp->b_data +
925 	    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
926 
927 	/* Check attributes magic value */
928 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
929 	    E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
930 
931 	if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
932 		brelse(bp);
933 		return (ENOSPC);
934 	}
935 
936 	error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode +
937 	    EXT2_INODE_SIZE(fs));
938 	if (error) {
939 		brelse(bp);
940 		return (error);
941 	}
942 
943 	/* Find if entry exist */
944 	for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
945 	    entry = EXT2_EXTATTR_NEXT(entry)) {
946 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
947 		    attrnamespace)
948 			continue;
949 
950 		name_len = entry->e_name_len;
951 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
952 		    entry->e_name, &name_len);
953 		if (!attr_name) {
954 			brelse(bp);
955 			return (ENOTSUP);
956 		}
957 
958 		if (strlen(name) == name_len &&
959 		    0 == strncmp(attr_name, name, name_len))
960 			break;
961 	}
962 
963 	max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE -
964 	    le16toh(dinode->e2di_extra_isize);
965 
966 	if (!EXT2_IS_LAST_ENTRY(entry)) {
967 		size = ext2_extattr_get_size(EXT2_IFIRST(header), entry,
968 		    sizeof(struct ext2fs_extattr_dinode_header),
969 		    entry->e_name_len, uio->uio_resid);
970 		if (size > max_size) {
971 			brelse(bp);
972 			return (ENOSPC);
973 		}
974 
975 		ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(header),
976 		    EXT2_IFIRST(header), entry, (char *)header + max_size, uio);
977 	} else {
978 		/* Ensure that the same entry does not exist in the block */
979 		if (ip->i_facl) {
980 			error = ext2_extattr_block_get(ip, attrnamespace, name,
981 			    NULL, &size);
982 			if (error != ENOATTR || size > 0) {
983 				brelse(bp);
984 				if (size > 0)
985 					error = ENOSPC;
986 
987 				return (error);
988 			}
989 		}
990 
991 		size = ext2_extattr_get_size(EXT2_IFIRST(header), NULL,
992 		    sizeof(struct ext2fs_extattr_dinode_header),
993 		    entry->e_name_len, uio->uio_resid);
994 		if (size > max_size) {
995 			brelse(bp);
996 			return (ENOSPC);
997 		}
998 
999 		ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header),
1000 		    EXT2_IFIRST(header), name, attrnamespace,
1001 		    (char *)header + max_size, uio);
1002 	}
1003 
1004 	return (bwrite(bp));
1005 }
1006 
1007 static void
ext2_extattr_hash_entry(struct ext2fs_extattr_header * header,struct ext2fs_extattr_entry * entry)1008 ext2_extattr_hash_entry(struct ext2fs_extattr_header *header,
1009     struct ext2fs_extattr_entry *entry)
1010 {
1011 	uint32_t hash = 0;
1012 	char *name = entry->e_name;
1013 	int n;
1014 
1015 	for (n=0; n < entry->e_name_len; n++) {
1016 		hash = (hash << EXT2_EXTATTR_NAME_HASH_SHIFT) ^
1017 		    (hash >> (8*sizeof(hash) - EXT2_EXTATTR_NAME_HASH_SHIFT)) ^
1018 		    (*name++);
1019 	}
1020 
1021 	if (entry->e_value_block == 0 && entry->e_value_size != 0) {
1022 		uint32_t *value = (uint32_t *)((char *)header +
1023 		    le16toh(entry->e_value_offs));
1024 		for (n = (le32toh(entry->e_value_size) +
1025 		    EXT2_EXTATTR_ROUND) >> EXT2_EXTATTR_PAD_BITS; n; n--) {
1026 			hash = (hash << EXT2_EXTATTR_VALUE_HASH_SHIFT) ^
1027 			    (hash >> (8*sizeof(hash) - EXT2_EXTATTR_VALUE_HASH_SHIFT)) ^
1028 			    le32toh(*value++);
1029 		}
1030 	}
1031 
1032 	entry->e_hash = htole32(hash);
1033 }
1034 
1035 static void
ext2_extattr_rehash(struct ext2fs_extattr_header * header,struct ext2fs_extattr_entry * entry)1036 ext2_extattr_rehash(struct ext2fs_extattr_header *header,
1037     struct ext2fs_extattr_entry *entry)
1038 {
1039 	struct ext2fs_extattr_entry *here;
1040 	uint32_t hash = 0;
1041 
1042 	ext2_extattr_hash_entry(header, entry);
1043 
1044 	here = EXT2_ENTRY(header+1);
1045 	while (!EXT2_IS_LAST_ENTRY(here)) {
1046 		if (here->e_hash == 0) {
1047 			/* Block is not shared if an entry's hash value == 0 */
1048 			hash = 0;
1049 			break;
1050 		}
1051 
1052 		hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^
1053 		    (hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^
1054 		    le32toh(here->e_hash);
1055 
1056 		here = EXT2_EXTATTR_NEXT(here);
1057 	}
1058 
1059 	header->h_hash = htole32(hash);
1060 }
1061 
1062 int
ext2_extattr_block_set(struct inode * ip,int attrnamespace,const char * name,struct uio * uio)1063 ext2_extattr_block_set(struct inode *ip, int attrnamespace,
1064     const char *name, struct uio *uio)
1065 {
1066 	struct m_ext2fs *fs;
1067 	struct buf *bp;
1068 	struct ext2fs_extattr_header *header;
1069 	struct ext2fs_extattr_entry *entry;
1070 	const char *attr_name;
1071 	int name_len;
1072 	size_t size;
1073 	int error;
1074 
1075 	fs = ip->i_e2fs;
1076 
1077 	if (ip->i_facl) {
1078 		error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1079 		    fs->e2fs_bsize, NOCRED, &bp);
1080 		if (error) {
1081 			return (error);
1082 		}
1083 
1084 		/* Check attributes magic value */
1085 		header = EXT2_HDR(bp);
1086 		if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
1087 		    le32toh(header->h_blocks) != 1) {
1088 			brelse(bp);
1089 			return (EINVAL);
1090 		}
1091 
1092 		error = ext2_extattr_block_check(ip, bp);
1093 		if (error) {
1094 			brelse(bp);
1095 			return (error);
1096 		}
1097 
1098 		if (le32toh(header->h_refcount) > 1) {
1099 			error = ext2_extattr_block_clone(ip, &bp);
1100 			if (error) {
1101 				brelse(bp);
1102 				return (error);
1103 			}
1104 
1105 			header = EXT2_HDR(bp);
1106 		}
1107 
1108 		/* Find if entry exist */
1109 		for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
1110 		    entry = EXT2_EXTATTR_NEXT(entry)) {
1111 			if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
1112 			    attrnamespace)
1113 				continue;
1114 
1115 			name_len = entry->e_name_len;
1116 			attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
1117 			    entry->e_name, &name_len);
1118 			if (!attr_name) {
1119 				brelse(bp);
1120 				return (ENOTSUP);
1121 			}
1122 
1123 			if (strlen(name) == name_len &&
1124 			    0 == strncmp(attr_name, name, name_len))
1125 				break;
1126 		}
1127 
1128 		if (!EXT2_IS_LAST_ENTRY(entry)) {
1129 			size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), entry,
1130 			    sizeof(struct ext2fs_extattr_header),
1131 			    entry->e_name_len, uio->uio_resid);
1132 			if (size > bp->b_bufsize) {
1133 				brelse(bp);
1134 				return (ENOSPC);
1135 			}
1136 
1137 			ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1138 			    entry, bp->b_data + bp->b_bufsize, uio);
1139 		} else {
1140 			size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), NULL,
1141 			    sizeof(struct ext2fs_extattr_header),
1142 			    strlen(name), uio->uio_resid);
1143 			if (size > bp->b_bufsize) {
1144 				brelse(bp);
1145 				return (ENOSPC);
1146 			}
1147 
1148 			entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1149 			    name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1150 
1151 			/* Clean the same entry in the inode */
1152 			error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1153 			if (error && error != ENOATTR) {
1154 				brelse(bp);
1155 				return (error);
1156 			}
1157 		}
1158 
1159 		ext2_extattr_rehash(header, entry);
1160 		ext2_extattr_blk_csum_set(ip, bp);
1161 
1162 		return (bwrite(bp));
1163 	}
1164 
1165 	size = ext2_extattr_get_size(NULL, NULL,
1166 	    sizeof(struct ext2fs_extattr_header),
1167 	    strlen(ext2_extattr_name_to_linux(attrnamespace, name)), uio->uio_resid);
1168 	if (size > fs->e2fs_bsize)
1169 		return (ENOSPC);
1170 
1171 	/* Allocate block, fill EA header and insert entry */
1172 	ip->i_facl = ext2_alloc_meta(ip);
1173 	if (0 == ip->i_facl)
1174 		return (ENOSPC);
1175 
1176 	ip->i_blocks += btodb(fs->e2fs_bsize);
1177 	ext2_update(ip->i_vnode, 1);
1178 
1179 	bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0);
1180 	if (!bp) {
1181 		ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
1182 		ip->i_blocks -= btodb(fs->e2fs_bsize);
1183 		ip->i_facl = 0;
1184 		ext2_update(ip->i_vnode, 1);
1185 		return (EIO);
1186 	}
1187 
1188 	header = EXT2_HDR(bp);
1189 	header->h_magic = htole32(EXTATTR_MAGIC);
1190 	header->h_refcount = htole32(1);
1191 	header->h_blocks = htole32(1);
1192 	header->h_hash = 0;
1193 	memset(header->h_reserved, 0, sizeof(header->h_reserved));
1194 	memcpy(bp->b_data, header, sizeof(struct ext2fs_extattr_header));
1195 	memset(EXT2_FIRST_ENTRY(bp), 0, sizeof(uint32_t));
1196 
1197 	entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1198 	    name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1199 
1200 	/* Clean the same entry in the inode */
1201 	error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1202 	if (error && error != ENOATTR) {
1203 		brelse(bp);
1204 		return (error);
1205 	}
1206 
1207 	ext2_extattr_rehash(header, entry);
1208 	ext2_extattr_blk_csum_set(ip, bp);
1209 
1210 	return (bwrite(bp));
1211 }
1212 
ext2_extattr_free(struct inode * ip)1213 int ext2_extattr_free(struct inode *ip)
1214 {
1215 	struct m_ext2fs *fs;
1216 	struct buf *bp;
1217 	struct ext2fs_extattr_header *header;
1218 	int error;
1219 
1220 	fs = ip->i_e2fs;
1221 
1222 	if (!ip->i_facl)
1223 		return (0);
1224 
1225 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1226 	    fs->e2fs_bsize, NOCRED, &bp);
1227 	if (error) {
1228 		return (error);
1229 	}
1230 
1231 	/* Check attributes magic value */
1232 	header = EXT2_HDR(bp);
1233 	if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
1234 	    le32toh(header->h_blocks) != 1) {
1235 		brelse(bp);
1236 		return (EINVAL);
1237 	}
1238 
1239 	error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp),
1240 	    bp->b_data + bp->b_bufsize);
1241 	if (error) {
1242 		brelse(bp);
1243 		return (error);
1244 	}
1245 
1246 	if (le32toh(header->h_refcount) > 1) {
1247 		header->h_refcount = htole32(le32toh(header->h_refcount) - 1);
1248 		bwrite(bp);
1249 	} else {
1250 		ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize);
1251 		brelse(bp);
1252 	}
1253 
1254 	ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize);
1255 	ip->i_facl = 0;
1256 	ext2_update(ip->i_vnode, 1);
1257 
1258 	return (0);
1259 }
1260