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
50SDT_PROVIDER_DECLARE(ext2fs);
51/*
52 * ext2fs trace probe:
53 * arg0: verbosity. Higher numbers give more verbose messages
54 * arg1: Textual message
55 */
56SDT_PROBE_DEFINE2(ext2fs, , trace, extattr, "int", "char*");
57
58static int
59ext2_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
79static const char *
80ext2_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
105static int
106ext2_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
132static const char *
133ext2_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
143int
144ext2_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
158static int
159ext2_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
174static int
175ext2_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
190int
191ext2_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
264int
265ext2_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
331int
332ext2_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
406int
407ext2_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
474static uint16_t
475ext2_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
515static void
516ext2_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
537int
538ext2_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
627static int
628ext2_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
669int
670ext2_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
764static struct ext2fs_extattr_entry *
765allocate_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
789static void
790free_entry(struct ext2fs_extattr_entry *entry)
791{
792
793	free(entry, M_TEMP);
794}
795
796static int
797ext2_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
826static void
827ext2_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
847static struct ext2fs_extattr_entry *
848ext2_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
901int
902ext2_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
1007static void
1008ext2_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
1035static void
1036ext2_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
1062int
1063ext2_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
1213int 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