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