xref: /freebsd/sys/fs/ext2fs/ext2_extattr.c (revision 95ee2897e98f5d444f26ed2334cc7c439f9c16c6)
1ac506a8fSPedro F. Giffuni /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
37abc09cdSPedro F. Giffuni  *
4ac506a8fSPedro F. Giffuni  * Copyright (c) 2017, Fedor Uporov
5ac506a8fSPedro F. Giffuni  * All rights reserved.
6ac506a8fSPedro F. Giffuni  *
7ac506a8fSPedro F. Giffuni  * Redistribution and use in source and binary forms, with or without
8ac506a8fSPedro F. Giffuni  * modification, are permitted provided that the following conditions
9ac506a8fSPedro F. Giffuni  * are met:
10ac506a8fSPedro F. Giffuni  * 1. Redistributions of source code must retain the above copyright
11ac506a8fSPedro F. Giffuni  *    notice, this list of conditions and the following disclaimer.
12ac506a8fSPedro F. Giffuni  * 2. Redistributions in binary form must reproduce the above copyright
13ac506a8fSPedro F. Giffuni  *    notice, this list of conditions and the following disclaimer in the
14ac506a8fSPedro F. Giffuni  *    documentation and/or other materials provided with the distribution.
15ac506a8fSPedro F. Giffuni  *
16ac506a8fSPedro F. Giffuni  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17ac506a8fSPedro F. Giffuni  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18ac506a8fSPedro F. Giffuni  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19ac506a8fSPedro F. Giffuni  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20ac506a8fSPedro F. Giffuni  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21ac506a8fSPedro F. Giffuni  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22ac506a8fSPedro F. Giffuni  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23ac506a8fSPedro F. Giffuni  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24ac506a8fSPedro F. Giffuni  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25ac506a8fSPedro F. Giffuni  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26ac506a8fSPedro F. Giffuni  * SUCH DAMAGE.
27ac506a8fSPedro F. Giffuni  */
28ac506a8fSPedro F. Giffuni 
29ac506a8fSPedro F. Giffuni #include <sys/param.h>
30ac506a8fSPedro F. Giffuni #include <sys/systm.h>
31ac506a8fSPedro F. Giffuni #include <sys/types.h>
32ac506a8fSPedro F. Giffuni #include <sys/kernel.h>
33ac506a8fSPedro F. Giffuni #include <sys/malloc.h>
34ac506a8fSPedro F. Giffuni #include <sys/vnode.h>
35ac506a8fSPedro F. Giffuni #include <sys/bio.h>
36ac506a8fSPedro F. Giffuni #include <sys/buf.h>
37ac506a8fSPedro F. Giffuni #include <sys/endian.h>
38ac506a8fSPedro F. Giffuni #include <sys/conf.h>
39ac506a8fSPedro F. Giffuni #include <sys/extattr.h>
40ebc94b66SFedor Uporov #include <sys/sdt.h>
41ac506a8fSPedro F. Giffuni 
42ac506a8fSPedro F. Giffuni #include <fs/ext2fs/fs.h>
43ac506a8fSPedro F. Giffuni #include <fs/ext2fs/ext2fs.h>
44ac506a8fSPedro F. Giffuni #include <fs/ext2fs/inode.h>
45ac506a8fSPedro F. Giffuni #include <fs/ext2fs/ext2_dinode.h>
46ac506a8fSPedro F. Giffuni #include <fs/ext2fs/ext2_mount.h>
47ac506a8fSPedro F. Giffuni #include <fs/ext2fs/ext2_extattr.h>
4834f43888SPedro F. Giffuni #include <fs/ext2fs/ext2_extern.h>
49ac506a8fSPedro F. Giffuni 
50ebc94b66SFedor Uporov SDT_PROVIDER_DECLARE(ext2fs);
51ebc94b66SFedor Uporov /*
52ebc94b66SFedor Uporov  * ext2fs trace probe:
53ebc94b66SFedor Uporov  * arg0: verbosity. Higher numbers give more verbose messages
54ebc94b66SFedor Uporov  * arg1: Textual message
55ebc94b66SFedor Uporov  */
56ebc94b66SFedor Uporov SDT_PROBE_DEFINE2(ext2fs, , trace, extattr, "int", "char*");
57ebc94b66SFedor Uporov 
58ac506a8fSPedro F. Giffuni static int
ext2_extattr_attrnamespace_to_bsd(int attrnamespace)5939999a69SPedro F. Giffuni ext2_extattr_attrnamespace_to_bsd(int attrnamespace)
60ac506a8fSPedro F. Giffuni {
6139999a69SPedro F. Giffuni 
6239999a69SPedro F. Giffuni 	switch (attrnamespace) {
63ac506a8fSPedro F. Giffuni 	case EXT4_XATTR_INDEX_SYSTEM:
6434f43888SPedro F. Giffuni 		return (EXTATTR_NAMESPACE_SYSTEM);
65ac506a8fSPedro F. Giffuni 
6634f43888SPedro F. Giffuni 	case EXT4_XATTR_INDEX_USER:
6734f43888SPedro F. Giffuni 		return (EXTATTR_NAMESPACE_USER);
6839999a69SPedro F. Giffuni 
6939999a69SPedro F. Giffuni 	case EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT:
7039999a69SPedro F. Giffuni 		return (POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE);
7139999a69SPedro F. Giffuni 
7239999a69SPedro F. Giffuni 	case EXT4_XATTR_INDEX_POSIX_ACL_ACCESS:
7339999a69SPedro F. Giffuni 		return (POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE);
74ac506a8fSPedro F. Giffuni 	}
7534f43888SPedro F. Giffuni 
7634f43888SPedro F. Giffuni 	return (EXTATTR_NAMESPACE_EMPTY);
7734f43888SPedro F. Giffuni }
7834f43888SPedro F. Giffuni 
7939999a69SPedro F. Giffuni static const char *
ext2_extattr_name_to_bsd(int attrnamespace,const char * name,int * name_len)8039999a69SPedro F. Giffuni ext2_extattr_name_to_bsd(int attrnamespace, const char *name, int* name_len)
8134f43888SPedro F. Giffuni {
8239999a69SPedro F. Giffuni 
8339999a69SPedro F. Giffuni 	if (attrnamespace == EXT4_XATTR_INDEX_SYSTEM)
8439999a69SPedro F. Giffuni 		return (name);
8539999a69SPedro F. Giffuni 	else if (attrnamespace == EXT4_XATTR_INDEX_USER)
8639999a69SPedro F. Giffuni 		return (name);
8739999a69SPedro F. Giffuni 	else if (attrnamespace == EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT) {
8839999a69SPedro F. Giffuni 		*name_len = strlen(POSIX1E_ACL_DEFAULT_EXTATTR_NAME);
8939999a69SPedro F. Giffuni 		return (POSIX1E_ACL_DEFAULT_EXTATTR_NAME);
9039999a69SPedro F. Giffuni 	} else if (attrnamespace == EXT4_XATTR_INDEX_POSIX_ACL_ACCESS) {
9139999a69SPedro F. Giffuni 		*name_len = strlen(POSIX1E_ACL_ACCESS_EXTATTR_NAME);
9239999a69SPedro F. Giffuni 		return (POSIX1E_ACL_ACCESS_EXTATTR_NAME);
9339999a69SPedro F. Giffuni 	}
9439999a69SPedro F. Giffuni 
9539999a69SPedro F. Giffuni 	/*
9639999a69SPedro F. Giffuni 	 * XXX: Not all linux namespaces are mapped to bsd for now,
9739999a69SPedro F. Giffuni 	 * return NULL, which will be converted to ENOTSUP on upper layer.
9839999a69SPedro F. Giffuni 	 */
99ebc94b66SFedor Uporov 	SDT_PROBE2(ext2fs, , trace, extattr, 1,
100ebc94b66SFedor Uporov 	    "can not convert ext2fs name to bsd namespace");
10139999a69SPedro F. Giffuni 
10239999a69SPedro F. Giffuni 	return (NULL);
10339999a69SPedro F. Giffuni }
10439999a69SPedro F. Giffuni 
10539999a69SPedro F. Giffuni static int
ext2_extattr_attrnamespace_to_linux(int attrnamespace,const char * name)10639999a69SPedro F. Giffuni ext2_extattr_attrnamespace_to_linux(int attrnamespace, const char *name)
10739999a69SPedro F. Giffuni {
10839999a69SPedro F. Giffuni 
10939999a69SPedro F. Giffuni 	if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE &&
11039999a69SPedro F. Giffuni 	    !strcmp(name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME))
11139999a69SPedro F. Giffuni 		return (EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT);
11239999a69SPedro F. Giffuni 
11339999a69SPedro F. Giffuni 	if (attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE &&
11439999a69SPedro F. Giffuni 	    !strcmp(name, POSIX1E_ACL_ACCESS_EXTATTR_NAME))
11539999a69SPedro F. Giffuni 		return (EXT4_XATTR_INDEX_POSIX_ACL_ACCESS);
11639999a69SPedro F. Giffuni 
11739999a69SPedro F. Giffuni 	switch (attrnamespace) {
11834f43888SPedro F. Giffuni 	case EXTATTR_NAMESPACE_SYSTEM:
11934f43888SPedro F. Giffuni 		return (EXT4_XATTR_INDEX_SYSTEM);
12034f43888SPedro F. Giffuni 
12134f43888SPedro F. Giffuni 	case EXTATTR_NAMESPACE_USER:
12234f43888SPedro F. Giffuni 		return (EXT4_XATTR_INDEX_USER);
12334f43888SPedro F. Giffuni 	}
12434f43888SPedro F. Giffuni 
12539999a69SPedro F. Giffuni 	/*
12639999a69SPedro F. Giffuni 	 * In this case namespace conversion should be unique,
12739999a69SPedro F. Giffuni 	 * so this point is unreachable.
12839999a69SPedro F. Giffuni 	 */
12934f43888SPedro F. Giffuni 	return (-1);
13034f43888SPedro F. Giffuni }
13134f43888SPedro F. Giffuni 
13239999a69SPedro F. Giffuni static const char *
ext2_extattr_name_to_linux(int attrnamespace,const char * name)13339999a69SPedro F. Giffuni ext2_extattr_name_to_linux(int attrnamespace, const char *name)
13439999a69SPedro F. Giffuni {
13539999a69SPedro F. Giffuni 
13639999a69SPedro F. Giffuni 	if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE ||
13739999a69SPedro F. Giffuni 	    attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE)
13839999a69SPedro F. Giffuni 		return ("");
13939999a69SPedro F. Giffuni 	else
14039999a69SPedro F. Giffuni 		return (name);
14139999a69SPedro F. Giffuni }
14239999a69SPedro F. Giffuni 
14334f43888SPedro F. Giffuni int
ext2_extattr_valid_attrname(int attrnamespace,const char * attrname)14434f43888SPedro F. Giffuni ext2_extattr_valid_attrname(int attrnamespace, const char *attrname)
14534f43888SPedro F. Giffuni {
14634f43888SPedro F. Giffuni 	if (attrnamespace == EXTATTR_NAMESPACE_EMPTY)
14734f43888SPedro F. Giffuni 		return (EINVAL);
14834f43888SPedro F. Giffuni 
14934f43888SPedro F. Giffuni 	if (strlen(attrname) == 0)
15034f43888SPedro F. Giffuni 		return (EINVAL);
15134f43888SPedro F. Giffuni 
15234f43888SPedro F. Giffuni 	if (strlen(attrname) + 1 > EXT2_EXTATTR_NAMELEN_MAX)
15334f43888SPedro F. Giffuni 		return (ENAMETOOLONG);
15434f43888SPedro F. Giffuni 
15534f43888SPedro F. Giffuni 	return (0);
15634f43888SPedro F. Giffuni }
15734f43888SPedro F. Giffuni 
15834f43888SPedro F. Giffuni static int
ext2_extattr_check(struct ext2fs_extattr_entry * entry,char * end)15934f43888SPedro F. Giffuni ext2_extattr_check(struct ext2fs_extattr_entry *entry, char *end)
16034f43888SPedro F. Giffuni {
16134f43888SPedro F. Giffuni 	struct ext2fs_extattr_entry *next;
16234f43888SPedro F. Giffuni 
16334f43888SPedro F. Giffuni 	while (!EXT2_IS_LAST_ENTRY(entry)) {
16434f43888SPedro F. Giffuni 		next = EXT2_EXTATTR_NEXT(entry);
16534f43888SPedro F. Giffuni 		if ((char *)next >= end)
16634f43888SPedro F. Giffuni 			return (EIO);
16734f43888SPedro F. Giffuni 
16834f43888SPedro F. Giffuni 		entry = next;
16934f43888SPedro F. Giffuni 	}
17034f43888SPedro F. Giffuni 
17134f43888SPedro F. Giffuni 	return (0);
172ac506a8fSPedro F. Giffuni }
173ac506a8fSPedro F. Giffuni 
174512f29d1SFedor Uporov static int
ext2_extattr_block_check(struct inode * ip,struct buf * bp)175512f29d1SFedor Uporov ext2_extattr_block_check(struct inode *ip, struct buf *bp)
176512f29d1SFedor Uporov {
177512f29d1SFedor Uporov 	struct ext2fs_extattr_header *header;
178512f29d1SFedor Uporov 	int error;
179512f29d1SFedor Uporov 
180512f29d1SFedor Uporov 	header = (struct ext2fs_extattr_header *)bp->b_data;
181512f29d1SFedor Uporov 
182512f29d1SFedor Uporov 	error = ext2_extattr_check(EXT2_IFIRST(header),
183512f29d1SFedor Uporov 	    bp->b_data + bp->b_bufsize);
184512f29d1SFedor Uporov 	if (error)
185512f29d1SFedor Uporov 		return (error);
186512f29d1SFedor Uporov 
187512f29d1SFedor Uporov 	return (ext2_extattr_blk_csum_verify(ip, bp));
188512f29d1SFedor Uporov }
189512f29d1SFedor Uporov 
190ac506a8fSPedro F. Giffuni int
ext2_extattr_inode_list(struct inode * ip,int attrnamespace,struct uio * uio,size_t * size)191ac506a8fSPedro F. Giffuni ext2_extattr_inode_list(struct inode *ip, int attrnamespace,
192ac506a8fSPedro F. Giffuni     struct uio *uio, size_t *size)
193ac506a8fSPedro F. Giffuni {
194ac506a8fSPedro F. Giffuni 	struct m_ext2fs *fs;
195ac506a8fSPedro F. Giffuni 	struct buf *bp;
196ac506a8fSPedro F. Giffuni 	struct ext2fs_extattr_dinode_header *header;
197ac506a8fSPedro F. Giffuni 	struct ext2fs_extattr_entry *entry;
19839999a69SPedro F. Giffuni 	const char *attr_name;
19939999a69SPedro F. Giffuni 	int name_len;
200ac506a8fSPedro F. Giffuni 	int error;
201ac506a8fSPedro F. Giffuni 
202ac506a8fSPedro F. Giffuni 	fs = ip->i_e2fs;
203ac506a8fSPedro F. Giffuni 
204ac506a8fSPedro F. Giffuni 	if ((error = bread(ip->i_devvp,
205ac506a8fSPedro F. Giffuni 	    fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
206ac506a8fSPedro F. Giffuni 	    (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
207ac506a8fSPedro F. Giffuni 		brelse(bp);
208ac506a8fSPedro F. Giffuni 		return (error);
209ac506a8fSPedro F. Giffuni 	}
210ac506a8fSPedro F. Giffuni 
211ac506a8fSPedro F. Giffuni 	struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
212ac506a8fSPedro F. Giffuni 	    ((char *)bp->b_data +
213ac506a8fSPedro F. Giffuni 	    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
214ac506a8fSPedro F. Giffuni 
215ac506a8fSPedro F. Giffuni 	/* Check attributes magic value */
216ac506a8fSPedro F. Giffuni 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
217cd3acfe7SFedor Uporov 	    E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
218ac506a8fSPedro F. Giffuni 
219cd3acfe7SFedor Uporov 	if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
220ac506a8fSPedro F. Giffuni 		brelse(bp);
221ac506a8fSPedro F. Giffuni 		return (0);
222ac506a8fSPedro F. Giffuni 	}
223ac506a8fSPedro F. Giffuni 
22434f43888SPedro F. Giffuni 	error = ext2_extattr_check(EXT2_IFIRST(header),
22534f43888SPedro F. Giffuni 	    (char *)dinode + EXT2_INODE_SIZE(fs));
22634f43888SPedro F. Giffuni 	if (error) {
227ac506a8fSPedro F. Giffuni 		brelse(bp);
22834f43888SPedro F. Giffuni 		return (error);
229ac506a8fSPedro F. Giffuni 	}
230ac506a8fSPedro F. Giffuni 
231ac506a8fSPedro F. Giffuni 	for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
232ac506a8fSPedro F. Giffuni 	    entry = EXT2_EXTATTR_NEXT(entry)) {
23339999a69SPedro F. Giffuni 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
23439999a69SPedro F. Giffuni 		    attrnamespace)
235ac506a8fSPedro F. Giffuni 			continue;
236ac506a8fSPedro F. Giffuni 
23739999a69SPedro F. Giffuni 		name_len = entry->e_name_len;
23839999a69SPedro F. Giffuni 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
23939999a69SPedro F. Giffuni 		    entry->e_name, &name_len);
24039999a69SPedro F. Giffuni 		if (!attr_name) {
24139999a69SPedro F. Giffuni 			brelse(bp);
24239999a69SPedro F. Giffuni 			return (ENOTSUP);
24339999a69SPedro F. Giffuni 		}
24439999a69SPedro F. Giffuni 
2451806c9abSFedor Uporov 		if (size != NULL)
24639999a69SPedro F. Giffuni 			*size += name_len + 1;
2471806c9abSFedor Uporov 
2481806c9abSFedor Uporov 		if (uio != NULL) {
24939999a69SPedro F. Giffuni 			char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
25039999a69SPedro F. Giffuni 			name[0] = name_len;
25139999a69SPedro F. Giffuni 			memcpy(&name[1], attr_name, name_len);
25239999a69SPedro F. Giffuni 			error = uiomove(name, name_len + 1, uio);
25339999a69SPedro F. Giffuni 			free(name, M_TEMP);
25434f43888SPedro F. Giffuni 			if (error)
25534f43888SPedro F. Giffuni 				break;
256ac506a8fSPedro F. Giffuni 		}
257ac506a8fSPedro F. Giffuni 	}
258ac506a8fSPedro F. Giffuni 
259ac506a8fSPedro F. Giffuni 	brelse(bp);
260ac506a8fSPedro F. Giffuni 
26134f43888SPedro F. Giffuni 	return (error);
262ac506a8fSPedro F. Giffuni }
263ac506a8fSPedro F. Giffuni 
264ac506a8fSPedro F. Giffuni int
ext2_extattr_block_list(struct inode * ip,int attrnamespace,struct uio * uio,size_t * size)265ac506a8fSPedro F. Giffuni ext2_extattr_block_list(struct inode *ip, int attrnamespace,
266ac506a8fSPedro F. Giffuni     struct uio *uio, size_t *size)
267ac506a8fSPedro F. Giffuni {
268ac506a8fSPedro F. Giffuni 	struct m_ext2fs *fs;
269ac506a8fSPedro F. Giffuni 	struct buf *bp;
270ac506a8fSPedro F. Giffuni 	struct ext2fs_extattr_header *header;
271ac506a8fSPedro F. Giffuni 	struct ext2fs_extattr_entry *entry;
27239999a69SPedro F. Giffuni 	const char *attr_name;
27339999a69SPedro F. Giffuni 	int name_len;
274ac506a8fSPedro F. Giffuni 	int error;
275ac506a8fSPedro F. Giffuni 
276ac506a8fSPedro F. Giffuni 	fs = ip->i_e2fs;
277ac506a8fSPedro F. Giffuni 
278ac506a8fSPedro F. Giffuni 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
279ac506a8fSPedro F. Giffuni 	    fs->e2fs_bsize, NOCRED, &bp);
280ac506a8fSPedro F. Giffuni 	if (error) {
281ac506a8fSPedro F. Giffuni 		return (error);
282ac506a8fSPedro F. Giffuni 	}
283ac506a8fSPedro F. Giffuni 
284ac506a8fSPedro F. Giffuni 	/* Check attributes magic value */
285ac506a8fSPedro F. Giffuni 	header = EXT2_HDR(bp);
286cd3acfe7SFedor Uporov 	if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
287cd3acfe7SFedor Uporov 	    le32toh(header->h_blocks) != 1) {
288ac506a8fSPedro F. Giffuni 		brelse(bp);
289ac506a8fSPedro F. Giffuni 		return (EINVAL);
290ac506a8fSPedro F. Giffuni 	}
291ac506a8fSPedro F. Giffuni 
292512f29d1SFedor Uporov 	error = ext2_extattr_block_check(ip, bp);
29334f43888SPedro F. Giffuni 	if (error) {
294ac506a8fSPedro F. Giffuni 		brelse(bp);
29534f43888SPedro F. Giffuni 		return (error);
296ac506a8fSPedro F. Giffuni 	}
297ac506a8fSPedro F. Giffuni 
298ac506a8fSPedro F. Giffuni 	for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
299ac506a8fSPedro F. Giffuni 	    entry = EXT2_EXTATTR_NEXT(entry)) {
30039999a69SPedro F. Giffuni 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
30139999a69SPedro F. Giffuni 		    attrnamespace)
302ac506a8fSPedro F. Giffuni 			continue;
303ac506a8fSPedro F. Giffuni 
30439999a69SPedro F. Giffuni 		name_len = entry->e_name_len;
30539999a69SPedro F. Giffuni 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
30639999a69SPedro F. Giffuni 		    entry->e_name, &name_len);
30739999a69SPedro F. Giffuni 		if (!attr_name) {
30839999a69SPedro F. Giffuni 			brelse(bp);
30939999a69SPedro F. Giffuni 			return (ENOTSUP);
31039999a69SPedro F. Giffuni 		}
31139999a69SPedro F. Giffuni 
3121806c9abSFedor Uporov 		if (size != NULL)
31339999a69SPedro F. Giffuni 			*size += name_len + 1;
3141806c9abSFedor Uporov 
3151806c9abSFedor Uporov 		if (uio != NULL) {
31639999a69SPedro F. Giffuni 			char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
31739999a69SPedro F. Giffuni 			name[0] = name_len;
31839999a69SPedro F. Giffuni 			memcpy(&name[1], attr_name, name_len);
31939999a69SPedro F. Giffuni 			error = uiomove(name, name_len + 1, uio);
32039999a69SPedro F. Giffuni 			free(name, M_TEMP);
32134f43888SPedro F. Giffuni 			if (error)
32234f43888SPedro F. Giffuni 				break;
323ac506a8fSPedro F. Giffuni 		}
324ac506a8fSPedro F. Giffuni 	}
325ac506a8fSPedro F. Giffuni 
326ac506a8fSPedro F. Giffuni 	brelse(bp);
327ac506a8fSPedro F. Giffuni 
32834f43888SPedro F. Giffuni 	return (error);
329ac506a8fSPedro F. Giffuni }
330ac506a8fSPedro F. Giffuni 
331ac506a8fSPedro F. Giffuni int
ext2_extattr_inode_get(struct inode * ip,int attrnamespace,const char * name,struct uio * uio,size_t * size)332ac506a8fSPedro F. Giffuni ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
333ac506a8fSPedro F. Giffuni     const char *name, struct uio *uio, size_t *size)
334ac506a8fSPedro F. Giffuni {
335ac506a8fSPedro F. Giffuni 	struct m_ext2fs *fs;
336ac506a8fSPedro F. Giffuni 	struct buf *bp;
337ac506a8fSPedro F. Giffuni 	struct ext2fs_extattr_dinode_header *header;
338ac506a8fSPedro F. Giffuni 	struct ext2fs_extattr_entry *entry;
33939999a69SPedro F. Giffuni 	const char *attr_name;
34039999a69SPedro F. Giffuni 	int name_len;
341ac506a8fSPedro F. Giffuni 	int error;
342ac506a8fSPedro F. Giffuni 
343ac506a8fSPedro F. Giffuni 	fs = ip->i_e2fs;
344ac506a8fSPedro F. Giffuni 
345ac506a8fSPedro F. Giffuni 	if ((error = bread(ip->i_devvp,
346ac506a8fSPedro F. Giffuni 	    fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
347ac506a8fSPedro F. Giffuni 	    (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
348ac506a8fSPedro F. Giffuni 		brelse(bp);
349ac506a8fSPedro F. Giffuni 		return (error);
350ac506a8fSPedro F. Giffuni 	}
351ac506a8fSPedro F. Giffuni 
352ac506a8fSPedro F. Giffuni 	struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
353ac506a8fSPedro F. Giffuni 	    ((char *)bp->b_data +
354ac506a8fSPedro F. Giffuni 	    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
355ac506a8fSPedro F. Giffuni 
356ac506a8fSPedro F. Giffuni 	/* Check attributes magic value */
357ac506a8fSPedro F. Giffuni 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
358cd3acfe7SFedor Uporov 	    E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
359ac506a8fSPedro F. Giffuni 
360cd3acfe7SFedor Uporov 	if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
361ac506a8fSPedro F. Giffuni 		brelse(bp);
36234f43888SPedro F. Giffuni 		return (ENOATTR);
363ac506a8fSPedro F. Giffuni 	}
364ac506a8fSPedro F. Giffuni 
36534f43888SPedro F. Giffuni 	error = ext2_extattr_check(EXT2_IFIRST(header),
36634f43888SPedro F. Giffuni 	    (char *)dinode + EXT2_INODE_SIZE(fs));
36734f43888SPedro F. Giffuni 	if (error) {
368ac506a8fSPedro F. Giffuni 		brelse(bp);
36934f43888SPedro F. Giffuni 		return (error);
370ac506a8fSPedro F. Giffuni 	}
371ac506a8fSPedro F. Giffuni 
372ac506a8fSPedro F. Giffuni 	for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
373ac506a8fSPedro F. Giffuni 	    entry = EXT2_EXTATTR_NEXT(entry)) {
37439999a69SPedro F. Giffuni 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
37539999a69SPedro F. Giffuni 		    attrnamespace)
376ac506a8fSPedro F. Giffuni 			continue;
377ac506a8fSPedro F. Giffuni 
37839999a69SPedro F. Giffuni 		name_len = entry->e_name_len;
37939999a69SPedro F. Giffuni 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
38039999a69SPedro F. Giffuni 		    entry->e_name, &name_len);
38139999a69SPedro F. Giffuni 		if (!attr_name) {
38239999a69SPedro F. Giffuni 			brelse(bp);
38339999a69SPedro F. Giffuni 			return (ENOTSUP);
38439999a69SPedro F. Giffuni 		}
38539999a69SPedro F. Giffuni 
38639999a69SPedro F. Giffuni 		if (strlen(name) == name_len &&
38739999a69SPedro F. Giffuni 		    0 == strncmp(attr_name, name, name_len)) {
3881806c9abSFedor Uporov 			if (size != NULL)
389cd3acfe7SFedor Uporov 				*size += le32toh(entry->e_value_size);
3901806c9abSFedor Uporov 
3911806c9abSFedor Uporov 			if (uio != NULL)
39234f43888SPedro F. Giffuni 				error = uiomove(((char *)EXT2_IFIRST(header)) +
393cd3acfe7SFedor Uporov 				    le16toh(entry->e_value_offs),
394cd3acfe7SFedor Uporov 				    le32toh(entry->e_value_size), uio);
39534f43888SPedro F. Giffuni 
396ac506a8fSPedro F. Giffuni 			brelse(bp);
397ac506a8fSPedro F. Giffuni 			return (error);
398ac506a8fSPedro F. Giffuni 		}
399ac506a8fSPedro F. Giffuni 	 }
400ac506a8fSPedro F. Giffuni 
401ac506a8fSPedro F. Giffuni 	brelse(bp);
402ac506a8fSPedro F. Giffuni 
40334f43888SPedro F. Giffuni 	return (ENOATTR);
404ac506a8fSPedro F. Giffuni }
405ac506a8fSPedro F. Giffuni 
406ac506a8fSPedro F. Giffuni int
ext2_extattr_block_get(struct inode * ip,int attrnamespace,const char * name,struct uio * uio,size_t * size)407ac506a8fSPedro F. Giffuni ext2_extattr_block_get(struct inode *ip, int attrnamespace,
408ac506a8fSPedro F. Giffuni     const char *name, struct uio *uio, size_t *size)
409ac506a8fSPedro F. Giffuni {
410ac506a8fSPedro F. Giffuni 	struct m_ext2fs *fs;
411ac506a8fSPedro F. Giffuni 	struct buf *bp;
412ac506a8fSPedro F. Giffuni 	struct ext2fs_extattr_header *header;
413ac506a8fSPedro F. Giffuni 	struct ext2fs_extattr_entry *entry;
41439999a69SPedro F. Giffuni 	const char *attr_name;
41539999a69SPedro F. Giffuni 	int name_len;
416ac506a8fSPedro F. Giffuni 	int error;
417ac506a8fSPedro F. Giffuni 
418ac506a8fSPedro F. Giffuni 	fs = ip->i_e2fs;
419ac506a8fSPedro F. Giffuni 
420ac506a8fSPedro F. Giffuni 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
421ac506a8fSPedro F. Giffuni 	    fs->e2fs_bsize, NOCRED, &bp);
422ac506a8fSPedro F. Giffuni 	if (error) {
423ac506a8fSPedro F. Giffuni 		return (error);
424ac506a8fSPedro F. Giffuni 	}
425ac506a8fSPedro F. Giffuni 
426ac506a8fSPedro F. Giffuni 	/* Check attributes magic value */
427ac506a8fSPedro F. Giffuni 	header = EXT2_HDR(bp);
428cd3acfe7SFedor Uporov 	if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
429cd3acfe7SFedor Uporov 	    le32toh(header->h_blocks) != 1) {
430ac506a8fSPedro F. Giffuni 		brelse(bp);
431ac506a8fSPedro F. Giffuni 		return (EINVAL);
432ac506a8fSPedro F. Giffuni 	}
433ac506a8fSPedro F. Giffuni 
434512f29d1SFedor Uporov 	error = ext2_extattr_block_check(ip, bp);
43534f43888SPedro F. Giffuni 	if (error) {
436ac506a8fSPedro F. Giffuni 		brelse(bp);
43734f43888SPedro F. Giffuni 		return (error);
438ac506a8fSPedro F. Giffuni 	}
439ac506a8fSPedro F. Giffuni 
440ac506a8fSPedro F. Giffuni 	for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
441ac506a8fSPedro F. Giffuni 	    entry = EXT2_EXTATTR_NEXT(entry)) {
44239999a69SPedro F. Giffuni 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
44339999a69SPedro F. Giffuni 		    attrnamespace)
444ac506a8fSPedro F. Giffuni 			continue;
445ac506a8fSPedro F. Giffuni 
44639999a69SPedro F. Giffuni 		name_len = entry->e_name_len;
44739999a69SPedro F. Giffuni 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
44839999a69SPedro F. Giffuni 		    entry->e_name, &name_len);
44939999a69SPedro F. Giffuni 		if (!attr_name) {
45039999a69SPedro F. Giffuni 			brelse(bp);
45139999a69SPedro F. Giffuni 			return (ENOTSUP);
45239999a69SPedro F. Giffuni 		}
45339999a69SPedro F. Giffuni 
45439999a69SPedro F. Giffuni 		if (strlen(name) == name_len &&
45539999a69SPedro F. Giffuni 		    0 == strncmp(attr_name, name, name_len)) {
4561806c9abSFedor Uporov 			if (size != NULL)
457cd3acfe7SFedor Uporov 				*size += le32toh(entry->e_value_size);
4581806c9abSFedor Uporov 
4591806c9abSFedor Uporov 			if (uio != NULL)
460cd3acfe7SFedor Uporov 				error = uiomove(bp->b_data +
461cd3acfe7SFedor Uporov 				    le16toh(entry->e_value_offs),
462cd3acfe7SFedor Uporov 				    le32toh(entry->e_value_size), uio);
46334f43888SPedro F. Giffuni 
46434f43888SPedro F. Giffuni 			brelse(bp);
46534f43888SPedro F. Giffuni 			return (error);
46634f43888SPedro F. Giffuni 		}
46734f43888SPedro F. Giffuni 	 }
46834f43888SPedro F. Giffuni 
46934f43888SPedro F. Giffuni 	brelse(bp);
47034f43888SPedro F. Giffuni 
47134f43888SPedro F. Giffuni 	return (ENOATTR);
47234f43888SPedro F. Giffuni }
47334f43888SPedro F. Giffuni 
47434f43888SPedro F. Giffuni static uint16_t
ext2_extattr_delete_value(char * off,struct ext2fs_extattr_entry * first_entry,struct ext2fs_extattr_entry * entry,char * end)47534f43888SPedro F. Giffuni ext2_extattr_delete_value(char *off,
47634f43888SPedro F. Giffuni     struct ext2fs_extattr_entry *first_entry,
47734f43888SPedro F. Giffuni     struct ext2fs_extattr_entry *entry, char *end)
47834f43888SPedro F. Giffuni {
47934f43888SPedro F. Giffuni 	uint16_t min_offs;
48034f43888SPedro F. Giffuni 	struct ext2fs_extattr_entry *next;
48134f43888SPedro F. Giffuni 
48234f43888SPedro F. Giffuni 	min_offs = end - off;
48334f43888SPedro F. Giffuni 	next = first_entry;
48434f43888SPedro F. Giffuni 	while (!EXT2_IS_LAST_ENTRY(next)) {
485cd3acfe7SFedor Uporov 		if (min_offs > le16toh(next->e_value_offs) &&
486cd3acfe7SFedor Uporov 		    le16toh(next->e_value_offs) > 0)
487cd3acfe7SFedor Uporov 			min_offs = le16toh(next->e_value_offs);
48834f43888SPedro F. Giffuni 
48934f43888SPedro F. Giffuni 		next = EXT2_EXTATTR_NEXT(next);
49034f43888SPedro F. Giffuni 	}
49134f43888SPedro F. Giffuni 
49234f43888SPedro F. Giffuni 	if (entry->e_value_size == 0)
49334f43888SPedro F. Giffuni 		return (min_offs);
49434f43888SPedro F. Giffuni 
495cd3acfe7SFedor Uporov 	memmove(off + min_offs + EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size)),
496cd3acfe7SFedor Uporov 	    off + min_offs, le16toh(entry->e_value_offs) - min_offs);
49734f43888SPedro F. Giffuni 
49834f43888SPedro F. Giffuni 	/* Adjust all value offsets */
49934f43888SPedro F. Giffuni 	next = first_entry;
50034f43888SPedro F. Giffuni 	while (!EXT2_IS_LAST_ENTRY(next))
50134f43888SPedro F. Giffuni 	{
502cd3acfe7SFedor Uporov 		if (le16toh(next->e_value_offs) > 0 &&
503cd3acfe7SFedor Uporov 		    le16toh(next->e_value_offs) < le16toh(entry->e_value_offs))
504cd3acfe7SFedor Uporov 			next->e_value_offs = htole16(le16toh(next->e_value_offs) +
505cd3acfe7SFedor Uporov 			    EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size)));
50634f43888SPedro F. Giffuni 
50734f43888SPedro F. Giffuni 		next = EXT2_EXTATTR_NEXT(next);
50834f43888SPedro F. Giffuni 	}
50934f43888SPedro F. Giffuni 
510cd3acfe7SFedor Uporov 	min_offs += EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size));
51134f43888SPedro F. Giffuni 
51234f43888SPedro F. Giffuni 	return (min_offs);
51334f43888SPedro F. Giffuni }
51434f43888SPedro F. Giffuni 
51534f43888SPedro F. Giffuni static void
ext2_extattr_delete_entry(char * off,struct ext2fs_extattr_entry * first_entry,struct ext2fs_extattr_entry * entry,char * end)51634f43888SPedro F. Giffuni ext2_extattr_delete_entry(char *off,
51734f43888SPedro F. Giffuni     struct ext2fs_extattr_entry *first_entry,
51834f43888SPedro F. Giffuni     struct ext2fs_extattr_entry *entry, char *end)
51934f43888SPedro F. Giffuni {
52034f43888SPedro F. Giffuni 	char *pad;
52134f43888SPedro F. Giffuni 	struct ext2fs_extattr_entry *next;
52234f43888SPedro F. Giffuni 
52334f43888SPedro F. Giffuni 	/* Clean entry value */
52434f43888SPedro F. Giffuni 	ext2_extattr_delete_value(off, first_entry, entry, end);
52534f43888SPedro F. Giffuni 
52634f43888SPedro F. Giffuni 	/* Clean the entry */
52734f43888SPedro F. Giffuni 	next = first_entry;
52834f43888SPedro F. Giffuni 	while (!EXT2_IS_LAST_ENTRY(next))
52934f43888SPedro F. Giffuni 		next = EXT2_EXTATTR_NEXT(next);
53034f43888SPedro F. Giffuni 
53134f43888SPedro F. Giffuni 	pad = (char*)next + sizeof(uint32_t);
53234f43888SPedro F. Giffuni 
53334f43888SPedro F. Giffuni 	memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len),
53434f43888SPedro F. Giffuni 	    pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len)));
53534f43888SPedro F. Giffuni }
53634f43888SPedro F. Giffuni 
53734f43888SPedro F. Giffuni int
ext2_extattr_inode_delete(struct inode * ip,int attrnamespace,const char * name)53834f43888SPedro F. Giffuni ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name)
53934f43888SPedro F. Giffuni {
54034f43888SPedro F. Giffuni 	struct m_ext2fs *fs;
54134f43888SPedro F. Giffuni 	struct buf *bp;
54234f43888SPedro F. Giffuni 	struct ext2fs_extattr_dinode_header *header;
54334f43888SPedro F. Giffuni 	struct ext2fs_extattr_entry *entry;
54439999a69SPedro F. Giffuni 	const char *attr_name;
54539999a69SPedro F. Giffuni 	int name_len;
54634f43888SPedro F. Giffuni 	int error;
54734f43888SPedro F. Giffuni 
54834f43888SPedro F. Giffuni 	fs = ip->i_e2fs;
54934f43888SPedro F. Giffuni 
55034f43888SPedro F. Giffuni 	if ((error = bread(ip->i_devvp,
55134f43888SPedro F. Giffuni 	    fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
55234f43888SPedro F. Giffuni 	    (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
55334f43888SPedro F. Giffuni 		brelse(bp);
55434f43888SPedro F. Giffuni 		return (error);
55534f43888SPedro F. Giffuni 	}
55634f43888SPedro F. Giffuni 
55734f43888SPedro F. Giffuni 	struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
55834f43888SPedro F. Giffuni 	    ((char *)bp->b_data +
55934f43888SPedro F. Giffuni 	    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
56034f43888SPedro F. Giffuni 
56134f43888SPedro F. Giffuni 	/* Check attributes magic value */
56234f43888SPedro F. Giffuni 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
563cd3acfe7SFedor Uporov 	    E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
56434f43888SPedro F. Giffuni 
565cd3acfe7SFedor Uporov 	if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
56634f43888SPedro F. Giffuni 		brelse(bp);
56734f43888SPedro F. Giffuni 		return (ENOATTR);
56834f43888SPedro F. Giffuni 	}
56934f43888SPedro F. Giffuni 
57034f43888SPedro F. Giffuni 	error = ext2_extattr_check(EXT2_IFIRST(header),
57134f43888SPedro F. Giffuni 	    (char *)dinode + EXT2_INODE_SIZE(fs));
57234f43888SPedro F. Giffuni 	if (error) {
57334f43888SPedro F. Giffuni 		brelse(bp);
57434f43888SPedro F. Giffuni 		return (error);
57534f43888SPedro F. Giffuni 	}
57634f43888SPedro F. Giffuni 
57734f43888SPedro F. Giffuni 	/* If I am last entry, just make magic zero */
57834f43888SPedro F. Giffuni 	entry = EXT2_IFIRST(header);
57939999a69SPedro F. Giffuni 	if ((EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) &&
58039999a69SPedro F. Giffuni 	    (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
58139999a69SPedro F. Giffuni 	    attrnamespace)) {
58239999a69SPedro F. Giffuni 		name_len = entry->e_name_len;
58339999a69SPedro F. Giffuni 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
58439999a69SPedro F. Giffuni 		    entry->e_name, &name_len);
58539999a69SPedro F. Giffuni 		if (!attr_name) {
58639999a69SPedro F. Giffuni 			brelse(bp);
58739999a69SPedro F. Giffuni 			return (ENOTSUP);
58839999a69SPedro F. Giffuni 		}
58939999a69SPedro F. Giffuni 
59039999a69SPedro F. Giffuni 		if (strlen(name) == name_len &&
59139999a69SPedro F. Giffuni 		    0 == strncmp(attr_name, name, name_len)) {
59234f43888SPedro F. Giffuni 			memset(header, 0, sizeof(struct ext2fs_extattr_dinode_header));
59334f43888SPedro F. Giffuni 
59434f43888SPedro F. Giffuni 			return (bwrite(bp));
59534f43888SPedro F. Giffuni 		}
59634f43888SPedro F. Giffuni 	}
59734f43888SPedro F. Giffuni 
59834f43888SPedro F. Giffuni 	for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
59934f43888SPedro F. Giffuni 	    entry = EXT2_EXTATTR_NEXT(entry)) {
60039999a69SPedro F. Giffuni 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
60139999a69SPedro F. Giffuni 		    attrnamespace)
60234f43888SPedro F. Giffuni 			continue;
60334f43888SPedro F. Giffuni 
60439999a69SPedro F. Giffuni 		name_len = entry->e_name_len;
60539999a69SPedro F. Giffuni 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
60639999a69SPedro F. Giffuni 		    entry->e_name, &name_len);
60739999a69SPedro F. Giffuni 		if (!attr_name) {
60839999a69SPedro F. Giffuni 			brelse(bp);
60939999a69SPedro F. Giffuni 			return (ENOTSUP);
61039999a69SPedro F. Giffuni 		}
61139999a69SPedro F. Giffuni 
61239999a69SPedro F. Giffuni 		if (strlen(name) == name_len &&
61339999a69SPedro F. Giffuni 		    0 == strncmp(attr_name, name, name_len)) {
61434f43888SPedro F. Giffuni 			ext2_extattr_delete_entry((char *)EXT2_IFIRST(header),
61534f43888SPedro F. Giffuni 			    EXT2_IFIRST(header), entry,
61634f43888SPedro F. Giffuni 			    (char *)dinode + EXT2_INODE_SIZE(fs));
61734f43888SPedro F. Giffuni 
61834f43888SPedro F. Giffuni 			return (bwrite(bp));
61934f43888SPedro F. Giffuni 		}
62034f43888SPedro F. Giffuni 	}
62134f43888SPedro F. Giffuni 
62234f43888SPedro F. Giffuni 	brelse(bp);
62334f43888SPedro F. Giffuni 
62434f43888SPedro F. Giffuni 	return (ENOATTR);
62534f43888SPedro F. Giffuni }
62634f43888SPedro F. Giffuni 
62734f43888SPedro F. Giffuni static int
ext2_extattr_block_clone(struct inode * ip,struct buf ** bpp)62834f43888SPedro F. Giffuni ext2_extattr_block_clone(struct inode *ip, struct buf **bpp)
62934f43888SPedro F. Giffuni {
63034f43888SPedro F. Giffuni 	struct m_ext2fs *fs;
63134f43888SPedro F. Giffuni 	struct buf *sbp;
63234f43888SPedro F. Giffuni 	struct buf *cbp;
63334f43888SPedro F. Giffuni 	struct ext2fs_extattr_header *header;
63434f43888SPedro F. Giffuni 	uint64_t facl;
63534f43888SPedro F. Giffuni 
63634f43888SPedro F. Giffuni 	fs = ip->i_e2fs;
63734f43888SPedro F. Giffuni 	sbp = *bpp;
63834f43888SPedro F. Giffuni 
63934f43888SPedro F. Giffuni 	header = EXT2_HDR(sbp);
640cd3acfe7SFedor Uporov 	if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
641cd3acfe7SFedor Uporov 	    le32toh(header->h_refcount) == 1)
64234f43888SPedro F. Giffuni 		return (EINVAL);
64334f43888SPedro F. Giffuni 
644b394cd1eSFedor Uporov 	facl = ext2_alloc_meta(ip);
64534f43888SPedro F. Giffuni 	if (!facl)
64634f43888SPedro F. Giffuni 		return (ENOSPC);
64734f43888SPedro F. Giffuni 
64834f43888SPedro F. Giffuni 	cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0);
64934f43888SPedro F. Giffuni 	if (!cbp) {
65034f43888SPedro F. Giffuni 		ext2_blkfree(ip, facl, fs->e2fs_bsize);
65134f43888SPedro F. Giffuni 		return (EIO);
65234f43888SPedro F. Giffuni 	}
65334f43888SPedro F. Giffuni 
65434f43888SPedro F. Giffuni 	memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize);
655cd3acfe7SFedor Uporov 	header->h_refcount = htole32(le32toh(header->h_refcount) - 1);
65634f43888SPedro F. Giffuni 	bwrite(sbp);
65734f43888SPedro F. Giffuni 
65834f43888SPedro F. Giffuni 	ip->i_facl = facl;
65934f43888SPedro F. Giffuni 	ext2_update(ip->i_vnode, 1);
66034f43888SPedro F. Giffuni 
66134f43888SPedro F. Giffuni 	header = EXT2_HDR(cbp);
662cd3acfe7SFedor Uporov 	header->h_refcount = htole32(1);
66334f43888SPedro F. Giffuni 
66434f43888SPedro F. Giffuni 	*bpp = cbp;
66534f43888SPedro F. Giffuni 
66634f43888SPedro F. Giffuni 	return (0);
66734f43888SPedro F. Giffuni }
66834f43888SPedro F. Giffuni 
66934f43888SPedro F. Giffuni int
ext2_extattr_block_delete(struct inode * ip,int attrnamespace,const char * name)67034f43888SPedro F. Giffuni ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name)
67134f43888SPedro F. Giffuni {
67234f43888SPedro F. Giffuni 	struct m_ext2fs *fs;
67334f43888SPedro F. Giffuni 	struct buf *bp;
67434f43888SPedro F. Giffuni 	struct ext2fs_extattr_header *header;
67534f43888SPedro F. Giffuni 	struct ext2fs_extattr_entry *entry;
67639999a69SPedro F. Giffuni 	const char *attr_name;
67739999a69SPedro F. Giffuni 	int name_len;
67834f43888SPedro F. Giffuni 	int error;
67934f43888SPedro F. Giffuni 
68034f43888SPedro F. Giffuni 	fs = ip->i_e2fs;
68134f43888SPedro F. Giffuni 
68234f43888SPedro F. Giffuni 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
68334f43888SPedro F. Giffuni 	    fs->e2fs_bsize, NOCRED, &bp);
68434f43888SPedro F. Giffuni 	if (error) {
68534f43888SPedro F. Giffuni 		return (error);
68634f43888SPedro F. Giffuni 	}
68734f43888SPedro F. Giffuni 
68834f43888SPedro F. Giffuni 	/* Check attributes magic value */
68934f43888SPedro F. Giffuni 	header = EXT2_HDR(bp);
690cd3acfe7SFedor Uporov 	if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
691cd3acfe7SFedor Uporov 	    le32toh(header->h_blocks) != 1) {
69234f43888SPedro F. Giffuni 		brelse(bp);
69334f43888SPedro F. Giffuni 		return (EINVAL);
69434f43888SPedro F. Giffuni 	}
69534f43888SPedro F. Giffuni 
696512f29d1SFedor Uporov 	error = ext2_extattr_block_check(ip, bp);
69734f43888SPedro F. Giffuni 	if (error) {
69834f43888SPedro F. Giffuni 		brelse(bp);
69934f43888SPedro F. Giffuni 		return (error);
70034f43888SPedro F. Giffuni 	}
70134f43888SPedro F. Giffuni 
702cd3acfe7SFedor Uporov 	if (le32toh(header->h_refcount) > 1) {
70334f43888SPedro F. Giffuni 		error = ext2_extattr_block_clone(ip, &bp);
704ac506a8fSPedro F. Giffuni 		if (error) {
705ac506a8fSPedro F. Giffuni 			brelse(bp);
706ac506a8fSPedro F. Giffuni 			return (error);
707ac506a8fSPedro F. Giffuni 		}
708ac506a8fSPedro F. Giffuni 	}
70934f43888SPedro F. Giffuni 
71034f43888SPedro F. Giffuni 	/* If I am last entry, clean me and free the block */
71134f43888SPedro F. Giffuni 	entry = EXT2_FIRST_ENTRY(bp);
71239999a69SPedro F. Giffuni 	if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry)) &&
71339999a69SPedro F. Giffuni 	    (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
71439999a69SPedro F. Giffuni 	    attrnamespace)) {
71539999a69SPedro F. Giffuni 		name_len = entry->e_name_len;
71639999a69SPedro F. Giffuni 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
71739999a69SPedro F. Giffuni 		    entry->e_name, &name_len);
71839999a69SPedro F. Giffuni 		if (!attr_name) {
71939999a69SPedro F. Giffuni 			brelse(bp);
72039999a69SPedro F. Giffuni 			return (ENOTSUP);
72139999a69SPedro F. Giffuni 		}
72239999a69SPedro F. Giffuni 
72339999a69SPedro F. Giffuni 		if (strlen(name) == name_len &&
72439999a69SPedro F. Giffuni 		    0 == strncmp(attr_name, name, name_len)) {
72534f43888SPedro F. Giffuni 			ip->i_blocks -= btodb(fs->e2fs_bsize);
72634f43888SPedro F. Giffuni 			ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
72734f43888SPedro F. Giffuni 			ip->i_facl = 0;
72834f43888SPedro F. Giffuni 			error = ext2_update(ip->i_vnode, 1);
72934f43888SPedro F. Giffuni 
73034f43888SPedro F. Giffuni 			brelse(bp);
73134f43888SPedro F. Giffuni 			return (error);
73234f43888SPedro F. Giffuni 		}
73334f43888SPedro F. Giffuni 	}
73434f43888SPedro F. Giffuni 
73534f43888SPedro F. Giffuni 	for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
73634f43888SPedro F. Giffuni 	    entry = EXT2_EXTATTR_NEXT(entry)) {
73739999a69SPedro F. Giffuni 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
73839999a69SPedro F. Giffuni 		    attrnamespace)
73934f43888SPedro F. Giffuni 			continue;
74034f43888SPedro F. Giffuni 
74139999a69SPedro F. Giffuni 		name_len = entry->e_name_len;
74239999a69SPedro F. Giffuni 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
74339999a69SPedro F. Giffuni 		    entry->e_name, &name_len);
74439999a69SPedro F. Giffuni 		if (!attr_name) {
74539999a69SPedro F. Giffuni 			brelse(bp);
74639999a69SPedro F. Giffuni 			return (ENOTSUP);
74739999a69SPedro F. Giffuni 		}
74839999a69SPedro F. Giffuni 
74939999a69SPedro F. Giffuni 		if (strlen(name) == name_len &&
75039999a69SPedro F. Giffuni 		    0 == strncmp(attr_name, name, name_len)) {
75134f43888SPedro F. Giffuni 			ext2_extattr_delete_entry(bp->b_data,
75234f43888SPedro F. Giffuni 			    EXT2_FIRST_ENTRY(bp), entry,
75334f43888SPedro F. Giffuni 			    bp->b_data + bp->b_bufsize);
75434f43888SPedro F. Giffuni 
75534f43888SPedro F. Giffuni 			return (bwrite(bp));
756ac506a8fSPedro F. Giffuni 		}
757ac506a8fSPedro F. Giffuni 	}
758ac506a8fSPedro F. Giffuni 
759ac506a8fSPedro F. Giffuni 	brelse(bp);
760ac506a8fSPedro F. Giffuni 
76134f43888SPedro F. Giffuni 	return (ENOATTR);
76234f43888SPedro F. Giffuni }
76334f43888SPedro F. Giffuni 
76434f43888SPedro F. Giffuni static struct ext2fs_extattr_entry *
allocate_entry(const char * name,int attrnamespace,uint16_t offs,uint32_t size,uint32_t hash)76534f43888SPedro F. Giffuni allocate_entry(const char *name, int attrnamespace, uint16_t offs,
76634f43888SPedro F. Giffuni     uint32_t size, uint32_t hash)
76734f43888SPedro F. Giffuni {
76839999a69SPedro F. Giffuni 	const char *attr_name;
76939999a69SPedro F. Giffuni 	int name_len;
77034f43888SPedro F. Giffuni 	struct ext2fs_extattr_entry *entry;
77134f43888SPedro F. Giffuni 
77239999a69SPedro F. Giffuni 	attr_name = ext2_extattr_name_to_linux(attrnamespace, name);
77339999a69SPedro F. Giffuni 	name_len = strlen(attr_name);
77439999a69SPedro F. Giffuni 
77534f43888SPedro F. Giffuni 	entry = malloc(sizeof(struct ext2fs_extattr_entry) + name_len,
77634f43888SPedro F. Giffuni 	    M_TEMP, M_WAITOK);
77734f43888SPedro F. Giffuni 
77834f43888SPedro F. Giffuni 	entry->e_name_len = name_len;
77939999a69SPedro F. Giffuni 	entry->e_name_index = ext2_extattr_attrnamespace_to_linux(attrnamespace, name);
780cd3acfe7SFedor Uporov 	entry->e_value_offs = htole16(offs);
78134f43888SPedro F. Giffuni 	entry->e_value_block = 0;
782cd3acfe7SFedor Uporov 	entry->e_value_size = htole32(size);
783cd3acfe7SFedor Uporov 	entry->e_hash = htole32(hash);
78434f43888SPedro F. Giffuni 	memcpy(entry->e_name, name, name_len);
78534f43888SPedro F. Giffuni 
78634f43888SPedro F. Giffuni 	return (entry);
78734f43888SPedro F. Giffuni }
78834f43888SPedro F. Giffuni 
78934f43888SPedro F. Giffuni static void
free_entry(struct ext2fs_extattr_entry * entry)79034f43888SPedro F. Giffuni free_entry(struct ext2fs_extattr_entry *entry)
79134f43888SPedro F. Giffuni {
79234f43888SPedro F. Giffuni 
79334f43888SPedro F. Giffuni 	free(entry, M_TEMP);
79434f43888SPedro F. Giffuni }
79534f43888SPedro F. Giffuni 
79634f43888SPedro F. Giffuni 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)79734f43888SPedro F. Giffuni ext2_extattr_get_size(struct ext2fs_extattr_entry *first_entry,
79834f43888SPedro F. Giffuni     struct ext2fs_extattr_entry *exist_entry, int header_size,
79934f43888SPedro F. Giffuni     int name_len, int new_size)
80034f43888SPedro F. Giffuni {
80134f43888SPedro F. Giffuni 	struct ext2fs_extattr_entry *entry;
80234f43888SPedro F. Giffuni 	int size;
80334f43888SPedro F. Giffuni 
80434f43888SPedro F. Giffuni 	size = header_size;
80534f43888SPedro F. Giffuni 	size += sizeof(uint32_t);
80634f43888SPedro F. Giffuni 
80734f43888SPedro F. Giffuni 	if (NULL == exist_entry) {
80834f43888SPedro F. Giffuni 		size += EXT2_EXTATTR_LEN(name_len);
80934f43888SPedro F. Giffuni 		size += EXT2_EXTATTR_SIZE(new_size);
81034f43888SPedro F. Giffuni 	}
81134f43888SPedro F. Giffuni 
81234f43888SPedro F. Giffuni 	if (first_entry)
81334f43888SPedro F. Giffuni 		for (entry = first_entry; !EXT2_IS_LAST_ENTRY(entry);
81434f43888SPedro F. Giffuni 		    entry = EXT2_EXTATTR_NEXT(entry)) {
81534f43888SPedro F. Giffuni 			if (entry != exist_entry)
81634f43888SPedro F. Giffuni 				size += EXT2_EXTATTR_LEN(entry->e_name_len) +
817cd3acfe7SFedor Uporov 				    EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size));
81834f43888SPedro F. Giffuni 			else
81934f43888SPedro F. Giffuni 				size += EXT2_EXTATTR_LEN(entry->e_name_len) +
82034f43888SPedro F. Giffuni 				    EXT2_EXTATTR_SIZE(new_size);
82134f43888SPedro F. Giffuni 		}
82234f43888SPedro F. Giffuni 
82334f43888SPedro F. Giffuni 	return (size);
82434f43888SPedro F. Giffuni }
82534f43888SPedro F. Giffuni 
82634f43888SPedro F. Giffuni 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)82734f43888SPedro F. Giffuni ext2_extattr_set_exist_entry(char *off,
82834f43888SPedro F. Giffuni     struct ext2fs_extattr_entry *first_entry,
82934f43888SPedro F. Giffuni     struct ext2fs_extattr_entry *entry,
83034f43888SPedro F. Giffuni     char *end, struct uio *uio)
83134f43888SPedro F. Giffuni {
83234f43888SPedro F. Giffuni 	uint16_t min_offs;
83334f43888SPedro F. Giffuni 
83434f43888SPedro F. Giffuni 	min_offs = ext2_extattr_delete_value(off, first_entry, entry, end);
83534f43888SPedro F. Giffuni 
836cd3acfe7SFedor Uporov 	entry->e_value_size = htole32(uio->uio_resid);
837cd3acfe7SFedor Uporov 	if (le32toh(entry->e_value_size))
838cd3acfe7SFedor Uporov 		entry->e_value_offs = htole16(min_offs -
839cd3acfe7SFedor Uporov 		    EXT2_EXTATTR_SIZE(uio->uio_resid));
84034f43888SPedro F. Giffuni 	else
84134f43888SPedro F. Giffuni 		entry->e_value_offs = 0;
84234f43888SPedro F. Giffuni 
843cd3acfe7SFedor Uporov 	uiomove(off + le16toh(entry->e_value_offs),
844cd3acfe7SFedor Uporov 	    le32toh(entry->e_value_size), uio);
84534f43888SPedro F. Giffuni }
84634f43888SPedro F. Giffuni 
84734f43888SPedro F. Giffuni 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)84834f43888SPedro F. Giffuni ext2_extattr_set_new_entry(char *off, struct ext2fs_extattr_entry *first_entry,
84934f43888SPedro F. Giffuni     const char *name, int attrnamespace, char *end, struct uio *uio)
85034f43888SPedro F. Giffuni {
85134f43888SPedro F. Giffuni 	int name_len;
85234f43888SPedro F. Giffuni 	char *pad;
85334f43888SPedro F. Giffuni 	uint16_t min_offs;
85434f43888SPedro F. Giffuni 	struct ext2fs_extattr_entry *entry;
85534f43888SPedro F. Giffuni 	struct ext2fs_extattr_entry *new_entry;
85634f43888SPedro F. Giffuni 
85734f43888SPedro F. Giffuni 	/* Find pad's */
85834f43888SPedro F. Giffuni 	min_offs = end - off;
85934f43888SPedro F. Giffuni 	entry = first_entry;
86034f43888SPedro F. Giffuni 	while (!EXT2_IS_LAST_ENTRY(entry)) {
861cd3acfe7SFedor Uporov 		if (min_offs > le16toh(entry->e_value_offs) &&
862cd3acfe7SFedor Uporov 		    le16toh(entry->e_value_offs) > 0)
863cd3acfe7SFedor Uporov 			min_offs = le16toh(entry->e_value_offs);
86434f43888SPedro F. Giffuni 
86534f43888SPedro F. Giffuni 		entry = EXT2_EXTATTR_NEXT(entry);
86634f43888SPedro F. Giffuni 	}
86734f43888SPedro F. Giffuni 
86834f43888SPedro F. Giffuni 	pad = (char*)entry + sizeof(uint32_t);
86934f43888SPedro F. Giffuni 
87034f43888SPedro F. Giffuni 	/* Find entry insert position */
87134f43888SPedro F. Giffuni 	name_len = strlen(name);
87234f43888SPedro F. Giffuni 	entry = first_entry;
87334f43888SPedro F. Giffuni 	while (!EXT2_IS_LAST_ENTRY(entry)) {
87434f43888SPedro F. Giffuni 		if (!(attrnamespace - entry->e_name_index) &&
87534f43888SPedro F. Giffuni 		    !(name_len - entry->e_name_len))
87634f43888SPedro F. Giffuni 			if (memcmp(name, entry->e_name, name_len) <= 0)
87734f43888SPedro F. Giffuni 				break;
87834f43888SPedro F. Giffuni 
87934f43888SPedro F. Giffuni 		entry = EXT2_EXTATTR_NEXT(entry);
88034f43888SPedro F. Giffuni 	}
88134f43888SPedro F. Giffuni 
88234f43888SPedro F. Giffuni 	/* Create new entry and insert it */
88334f43888SPedro F. Giffuni 	new_entry = allocate_entry(name, attrnamespace, 0, uio->uio_resid, 0);
88434f43888SPedro F. Giffuni 	memmove((char *)entry + EXT2_EXTATTR_LEN(new_entry->e_name_len), entry,
88534f43888SPedro F. Giffuni 	    pad - (char*)entry);
88634f43888SPedro F. Giffuni 
88734f43888SPedro F. Giffuni 	memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len));
88834f43888SPedro F. Giffuni 	free_entry(new_entry);
88934f43888SPedro F. Giffuni 
89034f43888SPedro F. Giffuni 	new_entry = entry;
891cd3acfe7SFedor Uporov 	if (le32toh(new_entry->e_value_size) > 0)
892cd3acfe7SFedor Uporov 		new_entry->e_value_offs = htole16(min_offs -
893cd3acfe7SFedor Uporov 		    EXT2_EXTATTR_SIZE(le32toh(new_entry->e_value_size)));
89434f43888SPedro F. Giffuni 
895cd3acfe7SFedor Uporov 	uiomove(off + le16toh(new_entry->e_value_offs),
896cd3acfe7SFedor Uporov 	    le32toh(new_entry->e_value_size), uio);
89734f43888SPedro F. Giffuni 
89834f43888SPedro F. Giffuni 	return (new_entry);
89934f43888SPedro F. Giffuni }
90034f43888SPedro F. Giffuni 
90134f43888SPedro F. Giffuni int
ext2_extattr_inode_set(struct inode * ip,int attrnamespace,const char * name,struct uio * uio)90234f43888SPedro F. Giffuni ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
90334f43888SPedro F. Giffuni     const char *name, struct uio *uio)
90434f43888SPedro F. Giffuni {
90534f43888SPedro F. Giffuni 	struct m_ext2fs *fs;
90634f43888SPedro F. Giffuni 	struct buf *bp;
90734f43888SPedro F. Giffuni 	struct ext2fs_extattr_dinode_header *header;
90834f43888SPedro F. Giffuni 	struct ext2fs_extattr_entry *entry;
90939999a69SPedro F. Giffuni 	const char *attr_name;
91039999a69SPedro F. Giffuni 	int name_len;
91134f43888SPedro F. Giffuni 	size_t size = 0, max_size;
91234f43888SPedro F. Giffuni 	int error;
91334f43888SPedro F. Giffuni 
91434f43888SPedro F. Giffuni 	fs = ip->i_e2fs;
91534f43888SPedro F. Giffuni 
91634f43888SPedro F. Giffuni 	if ((error = bread(ip->i_devvp,
91734f43888SPedro F. Giffuni 	    fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
91834f43888SPedro F. Giffuni 	    (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
91934f43888SPedro F. Giffuni 		brelse(bp);
92034f43888SPedro F. Giffuni 		return (error);
92134f43888SPedro F. Giffuni 	}
92234f43888SPedro F. Giffuni 
92334f43888SPedro F. Giffuni 	struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
92434f43888SPedro F. Giffuni 	    ((char *)bp->b_data +
92534f43888SPedro F. Giffuni 	    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
92634f43888SPedro F. Giffuni 
92734f43888SPedro F. Giffuni 	/* Check attributes magic value */
92834f43888SPedro F. Giffuni 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
929cd3acfe7SFedor Uporov 	    E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
93034f43888SPedro F. Giffuni 
931cd3acfe7SFedor Uporov 	if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
93234f43888SPedro F. Giffuni 		brelse(bp);
93334f43888SPedro F. Giffuni 		return (ENOSPC);
93434f43888SPedro F. Giffuni 	}
93534f43888SPedro F. Giffuni 
93634f43888SPedro F. Giffuni 	error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode +
93734f43888SPedro F. Giffuni 	    EXT2_INODE_SIZE(fs));
93834f43888SPedro F. Giffuni 	if (error) {
93934f43888SPedro F. Giffuni 		brelse(bp);
94034f43888SPedro F. Giffuni 		return (error);
94134f43888SPedro F. Giffuni 	}
94234f43888SPedro F. Giffuni 
94334f43888SPedro F. Giffuni 	/* Find if entry exist */
94434f43888SPedro F. Giffuni 	for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
94534f43888SPedro F. Giffuni 	    entry = EXT2_EXTATTR_NEXT(entry)) {
94639999a69SPedro F. Giffuni 		if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
94739999a69SPedro F. Giffuni 		    attrnamespace)
94834f43888SPedro F. Giffuni 			continue;
94934f43888SPedro F. Giffuni 
95039999a69SPedro F. Giffuni 		name_len = entry->e_name_len;
95139999a69SPedro F. Giffuni 		attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
95239999a69SPedro F. Giffuni 		    entry->e_name, &name_len);
95339999a69SPedro F. Giffuni 		if (!attr_name) {
95439999a69SPedro F. Giffuni 			brelse(bp);
95539999a69SPedro F. Giffuni 			return (ENOTSUP);
95639999a69SPedro F. Giffuni 		}
95739999a69SPedro F. Giffuni 
95839999a69SPedro F. Giffuni 		if (strlen(name) == name_len &&
95939999a69SPedro F. Giffuni 		    0 == strncmp(attr_name, name, name_len))
96034f43888SPedro F. Giffuni 			break;
96134f43888SPedro F. Giffuni 	}
96234f43888SPedro F. Giffuni 
96334f43888SPedro F. Giffuni 	max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE -
964cd3acfe7SFedor Uporov 	    le16toh(dinode->e2di_extra_isize);
96534f43888SPedro F. Giffuni 
96634f43888SPedro F. Giffuni 	if (!EXT2_IS_LAST_ENTRY(entry)) {
96734f43888SPedro F. Giffuni 		size = ext2_extattr_get_size(EXT2_IFIRST(header), entry,
96834f43888SPedro F. Giffuni 		    sizeof(struct ext2fs_extattr_dinode_header),
96934f43888SPedro F. Giffuni 		    entry->e_name_len, uio->uio_resid);
97034f43888SPedro F. Giffuni 		if (size > max_size) {
97134f43888SPedro F. Giffuni 			brelse(bp);
97234f43888SPedro F. Giffuni 			return (ENOSPC);
97334f43888SPedro F. Giffuni 		}
97434f43888SPedro F. Giffuni 
97534f43888SPedro F. Giffuni 		ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(header),
97634f43888SPedro F. Giffuni 		    EXT2_IFIRST(header), entry, (char *)header + max_size, uio);
97734f43888SPedro F. Giffuni 	} else {
97834f43888SPedro F. Giffuni 		/* Ensure that the same entry does not exist in the block */
97934f43888SPedro F. Giffuni 		if (ip->i_facl) {
98034f43888SPedro F. Giffuni 			error = ext2_extattr_block_get(ip, attrnamespace, name,
98134f43888SPedro F. Giffuni 			    NULL, &size);
98234f43888SPedro F. Giffuni 			if (error != ENOATTR || size > 0) {
98334f43888SPedro F. Giffuni 				brelse(bp);
98434f43888SPedro F. Giffuni 				if (size > 0)
98534f43888SPedro F. Giffuni 					error = ENOSPC;
98634f43888SPedro F. Giffuni 
98734f43888SPedro F. Giffuni 				return (error);
98834f43888SPedro F. Giffuni 			}
98934f43888SPedro F. Giffuni 		}
99034f43888SPedro F. Giffuni 
99134f43888SPedro F. Giffuni 		size = ext2_extattr_get_size(EXT2_IFIRST(header), NULL,
99234f43888SPedro F. Giffuni 		    sizeof(struct ext2fs_extattr_dinode_header),
99334f43888SPedro F. Giffuni 		    entry->e_name_len, uio->uio_resid);
99434f43888SPedro F. Giffuni 		if (size > max_size) {
99534f43888SPedro F. Giffuni 			brelse(bp);
99634f43888SPedro F. Giffuni 			return (ENOSPC);
99734f43888SPedro F. Giffuni 		}
99834f43888SPedro F. Giffuni 
99934f43888SPedro F. Giffuni 		ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header),
100034f43888SPedro F. Giffuni 		    EXT2_IFIRST(header), name, attrnamespace,
100134f43888SPedro F. Giffuni 		    (char *)header + max_size, uio);
100234f43888SPedro F. Giffuni 	}
100334f43888SPedro F. Giffuni 
100434f43888SPedro F. Giffuni 	return (bwrite(bp));
100534f43888SPedro F. Giffuni }
100634f43888SPedro F. Giffuni 
100734f43888SPedro F. Giffuni static void
ext2_extattr_hash_entry(struct ext2fs_extattr_header * header,struct ext2fs_extattr_entry * entry)100834f43888SPedro F. Giffuni ext2_extattr_hash_entry(struct ext2fs_extattr_header *header,
100934f43888SPedro F. Giffuni     struct ext2fs_extattr_entry *entry)
101034f43888SPedro F. Giffuni {
101134f43888SPedro F. Giffuni 	uint32_t hash = 0;
101234f43888SPedro F. Giffuni 	char *name = entry->e_name;
101334f43888SPedro F. Giffuni 	int n;
101434f43888SPedro F. Giffuni 
101534f43888SPedro F. Giffuni 	for (n=0; n < entry->e_name_len; n++) {
101634f43888SPedro F. Giffuni 		hash = (hash << EXT2_EXTATTR_NAME_HASH_SHIFT) ^
101734f43888SPedro F. Giffuni 		    (hash >> (8*sizeof(hash) - EXT2_EXTATTR_NAME_HASH_SHIFT)) ^
101834f43888SPedro F. Giffuni 		    (*name++);
101934f43888SPedro F. Giffuni 	}
102034f43888SPedro F. Giffuni 
102134f43888SPedro F. Giffuni 	if (entry->e_value_block == 0 && entry->e_value_size != 0) {
1022cd3acfe7SFedor Uporov 		uint32_t *value = (uint32_t *)((char *)header +
1023cd3acfe7SFedor Uporov 		    le16toh(entry->e_value_offs));
1024cd3acfe7SFedor Uporov 		for (n = (le32toh(entry->e_value_size) +
102534f43888SPedro F. Giffuni 		    EXT2_EXTATTR_ROUND) >> EXT2_EXTATTR_PAD_BITS; n; n--) {
102634f43888SPedro F. Giffuni 			hash = (hash << EXT2_EXTATTR_VALUE_HASH_SHIFT) ^
102734f43888SPedro F. Giffuni 			    (hash >> (8*sizeof(hash) - EXT2_EXTATTR_VALUE_HASH_SHIFT)) ^
1028cd3acfe7SFedor Uporov 			    le32toh(*value++);
102934f43888SPedro F. Giffuni 		}
103034f43888SPedro F. Giffuni 	}
103134f43888SPedro F. Giffuni 
1032cd3acfe7SFedor Uporov 	entry->e_hash = htole32(hash);
103334f43888SPedro F. Giffuni }
103434f43888SPedro F. Giffuni 
103534f43888SPedro F. Giffuni static void
ext2_extattr_rehash(struct ext2fs_extattr_header * header,struct ext2fs_extattr_entry * entry)103634f43888SPedro F. Giffuni ext2_extattr_rehash(struct ext2fs_extattr_header *header,
103734f43888SPedro F. Giffuni     struct ext2fs_extattr_entry *entry)
103834f43888SPedro F. Giffuni {
103934f43888SPedro F. Giffuni 	struct ext2fs_extattr_entry *here;
104034f43888SPedro F. Giffuni 	uint32_t hash = 0;
104134f43888SPedro F. Giffuni 
104234f43888SPedro F. Giffuni 	ext2_extattr_hash_entry(header, entry);
104334f43888SPedro F. Giffuni 
104434f43888SPedro F. Giffuni 	here = EXT2_ENTRY(header+1);
104534f43888SPedro F. Giffuni 	while (!EXT2_IS_LAST_ENTRY(here)) {
1046cd3acfe7SFedor Uporov 		if (here->e_hash == 0) {
104734f43888SPedro F. Giffuni 			/* Block is not shared if an entry's hash value == 0 */
104834f43888SPedro F. Giffuni 			hash = 0;
104934f43888SPedro F. Giffuni 			break;
105034f43888SPedro F. Giffuni 		}
105134f43888SPedro F. Giffuni 
105234f43888SPedro F. Giffuni 		hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^
105334f43888SPedro F. Giffuni 		    (hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^
1054cd3acfe7SFedor Uporov 		    le32toh(here->e_hash);
105534f43888SPedro F. Giffuni 
105634f43888SPedro F. Giffuni 		here = EXT2_EXTATTR_NEXT(here);
105734f43888SPedro F. Giffuni 	}
105834f43888SPedro F. Giffuni 
1059cd3acfe7SFedor Uporov 	header->h_hash = htole32(hash);
106034f43888SPedro F. Giffuni }
106134f43888SPedro F. Giffuni 
106234f43888SPedro F. Giffuni int
ext2_extattr_block_set(struct inode * ip,int attrnamespace,const char * name,struct uio * uio)106334f43888SPedro F. Giffuni ext2_extattr_block_set(struct inode *ip, int attrnamespace,
106434f43888SPedro F. Giffuni     const char *name, struct uio *uio)
106534f43888SPedro F. Giffuni {
106634f43888SPedro F. Giffuni 	struct m_ext2fs *fs;
106734f43888SPedro F. Giffuni 	struct buf *bp;
106834f43888SPedro F. Giffuni 	struct ext2fs_extattr_header *header;
106934f43888SPedro F. Giffuni 	struct ext2fs_extattr_entry *entry;
107039999a69SPedro F. Giffuni 	const char *attr_name;
107139999a69SPedro F. Giffuni 	int name_len;
107234f43888SPedro F. Giffuni 	size_t size;
107334f43888SPedro F. Giffuni 	int error;
107434f43888SPedro F. Giffuni 
107534f43888SPedro F. Giffuni 	fs = ip->i_e2fs;
107634f43888SPedro F. Giffuni 
107734f43888SPedro F. Giffuni 	if (ip->i_facl) {
107834f43888SPedro F. Giffuni 		error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
107934f43888SPedro F. Giffuni 		    fs->e2fs_bsize, NOCRED, &bp);
108034f43888SPedro F. Giffuni 		if (error) {
108134f43888SPedro F. Giffuni 			return (error);
108234f43888SPedro F. Giffuni 		}
108334f43888SPedro F. Giffuni 
108434f43888SPedro F. Giffuni 		/* Check attributes magic value */
108534f43888SPedro F. Giffuni 		header = EXT2_HDR(bp);
1086cd3acfe7SFedor Uporov 		if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
1087cd3acfe7SFedor Uporov 		    le32toh(header->h_blocks) != 1) {
108834f43888SPedro F. Giffuni 			brelse(bp);
108934f43888SPedro F. Giffuni 			return (EINVAL);
109034f43888SPedro F. Giffuni 		}
109134f43888SPedro F. Giffuni 
1092512f29d1SFedor Uporov 		error = ext2_extattr_block_check(ip, bp);
109334f43888SPedro F. Giffuni 		if (error) {
109434f43888SPedro F. Giffuni 			brelse(bp);
109534f43888SPedro F. Giffuni 			return (error);
109634f43888SPedro F. Giffuni 		}
109734f43888SPedro F. Giffuni 
1098cd3acfe7SFedor Uporov 		if (le32toh(header->h_refcount) > 1) {
109934f43888SPedro F. Giffuni 			error = ext2_extattr_block_clone(ip, &bp);
110034f43888SPedro F. Giffuni 			if (error) {
110134f43888SPedro F. Giffuni 				brelse(bp);
110234f43888SPedro F. Giffuni 				return (error);
110334f43888SPedro F. Giffuni 			}
110434f43888SPedro F. Giffuni 
110534f43888SPedro F. Giffuni 			header = EXT2_HDR(bp);
110634f43888SPedro F. Giffuni 		}
110734f43888SPedro F. Giffuni 
110834f43888SPedro F. Giffuni 		/* Find if entry exist */
110934f43888SPedro F. Giffuni 		for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
111034f43888SPedro F. Giffuni 		    entry = EXT2_EXTATTR_NEXT(entry)) {
111139999a69SPedro F. Giffuni 			if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
111239999a69SPedro F. Giffuni 			    attrnamespace)
111334f43888SPedro F. Giffuni 				continue;
111434f43888SPedro F. Giffuni 
111539999a69SPedro F. Giffuni 			name_len = entry->e_name_len;
111639999a69SPedro F. Giffuni 			attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
111739999a69SPedro F. Giffuni 			    entry->e_name, &name_len);
111839999a69SPedro F. Giffuni 			if (!attr_name) {
111939999a69SPedro F. Giffuni 				brelse(bp);
112039999a69SPedro F. Giffuni 				return (ENOTSUP);
112139999a69SPedro F. Giffuni 			}
112239999a69SPedro F. Giffuni 
112339999a69SPedro F. Giffuni 			if (strlen(name) == name_len &&
112439999a69SPedro F. Giffuni 			    0 == strncmp(attr_name, name, name_len))
112534f43888SPedro F. Giffuni 				break;
112634f43888SPedro F. Giffuni 		}
112734f43888SPedro F. Giffuni 
112834f43888SPedro F. Giffuni 		if (!EXT2_IS_LAST_ENTRY(entry)) {
112934f43888SPedro F. Giffuni 			size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), entry,
113034f43888SPedro F. Giffuni 			    sizeof(struct ext2fs_extattr_header),
113134f43888SPedro F. Giffuni 			    entry->e_name_len, uio->uio_resid);
113234f43888SPedro F. Giffuni 			if (size > bp->b_bufsize) {
113334f43888SPedro F. Giffuni 				brelse(bp);
113434f43888SPedro F. Giffuni 				return (ENOSPC);
113534f43888SPedro F. Giffuni 			}
113634f43888SPedro F. Giffuni 
113734f43888SPedro F. Giffuni 			ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
113834f43888SPedro F. Giffuni 			    entry, bp->b_data + bp->b_bufsize, uio);
113934f43888SPedro F. Giffuni 		} else {
114034f43888SPedro F. Giffuni 			size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), NULL,
114134f43888SPedro F. Giffuni 			    sizeof(struct ext2fs_extattr_header),
114234f43888SPedro F. Giffuni 			    strlen(name), uio->uio_resid);
114334f43888SPedro F. Giffuni 			if (size > bp->b_bufsize) {
114434f43888SPedro F. Giffuni 				brelse(bp);
114534f43888SPedro F. Giffuni 				return (ENOSPC);
114634f43888SPedro F. Giffuni 			}
114734f43888SPedro F. Giffuni 
114834f43888SPedro F. Giffuni 			entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
114934f43888SPedro F. Giffuni 			    name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
115034f43888SPedro F. Giffuni 
115134f43888SPedro F. Giffuni 			/* Clean the same entry in the inode */
115234f43888SPedro F. Giffuni 			error = ext2_extattr_inode_delete(ip, attrnamespace, name);
115334f43888SPedro F. Giffuni 			if (error && error != ENOATTR) {
115434f43888SPedro F. Giffuni 				brelse(bp);
115534f43888SPedro F. Giffuni 				return (error);
115634f43888SPedro F. Giffuni 			}
115734f43888SPedro F. Giffuni 		}
115834f43888SPedro F. Giffuni 
115934f43888SPedro F. Giffuni 		ext2_extattr_rehash(header, entry);
1160512f29d1SFedor Uporov 		ext2_extattr_blk_csum_set(ip, bp);
116134f43888SPedro F. Giffuni 
116234f43888SPedro F. Giffuni 		return (bwrite(bp));
116334f43888SPedro F. Giffuni 	}
116434f43888SPedro F. Giffuni 
116534f43888SPedro F. Giffuni 	size = ext2_extattr_get_size(NULL, NULL,
116639999a69SPedro F. Giffuni 	    sizeof(struct ext2fs_extattr_header),
116739999a69SPedro F. Giffuni 	    strlen(ext2_extattr_name_to_linux(attrnamespace, name)), uio->uio_resid);
116834f43888SPedro F. Giffuni 	if (size > fs->e2fs_bsize)
116934f43888SPedro F. Giffuni 		return (ENOSPC);
117034f43888SPedro F. Giffuni 
117134f43888SPedro F. Giffuni 	/* Allocate block, fill EA header and insert entry */
1172b394cd1eSFedor Uporov 	ip->i_facl = ext2_alloc_meta(ip);
117334f43888SPedro F. Giffuni 	if (0 == ip->i_facl)
117434f43888SPedro F. Giffuni 		return (ENOSPC);
117534f43888SPedro F. Giffuni 
117634f43888SPedro F. Giffuni 	ip->i_blocks += btodb(fs->e2fs_bsize);
117734f43888SPedro F. Giffuni 	ext2_update(ip->i_vnode, 1);
117834f43888SPedro F. Giffuni 
117934f43888SPedro F. Giffuni 	bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0);
118034f43888SPedro F. Giffuni 	if (!bp) {
118134f43888SPedro F. Giffuni 		ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
118234f43888SPedro F. Giffuni 		ip->i_blocks -= btodb(fs->e2fs_bsize);
118334f43888SPedro F. Giffuni 		ip->i_facl = 0;
118434f43888SPedro F. Giffuni 		ext2_update(ip->i_vnode, 1);
118534f43888SPedro F. Giffuni 		return (EIO);
118634f43888SPedro F. Giffuni 	}
118734f43888SPedro F. Giffuni 
118834f43888SPedro F. Giffuni 	header = EXT2_HDR(bp);
1189cd3acfe7SFedor Uporov 	header->h_magic = htole32(EXTATTR_MAGIC);
1190cd3acfe7SFedor Uporov 	header->h_refcount = htole32(1);
1191cd3acfe7SFedor Uporov 	header->h_blocks = htole32(1);
119234f43888SPedro F. Giffuni 	header->h_hash = 0;
119334f43888SPedro F. Giffuni 	memset(header->h_reserved, 0, sizeof(header->h_reserved));
119434f43888SPedro F. Giffuni 	memcpy(bp->b_data, header, sizeof(struct ext2fs_extattr_header));
119534f43888SPedro F. Giffuni 	memset(EXT2_FIRST_ENTRY(bp), 0, sizeof(uint32_t));
119634f43888SPedro F. Giffuni 
119734f43888SPedro F. Giffuni 	entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
119834f43888SPedro F. Giffuni 	    name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
119934f43888SPedro F. Giffuni 
120034f43888SPedro F. Giffuni 	/* Clean the same entry in the inode */
120134f43888SPedro F. Giffuni 	error = ext2_extattr_inode_delete(ip, attrnamespace, name);
120234f43888SPedro F. Giffuni 	if (error && error != ENOATTR) {
120334f43888SPedro F. Giffuni 		brelse(bp);
120434f43888SPedro F. Giffuni 		return (error);
120534f43888SPedro F. Giffuni 	}
120634f43888SPedro F. Giffuni 
120734f43888SPedro F. Giffuni 	ext2_extattr_rehash(header, entry);
1208512f29d1SFedor Uporov 	ext2_extattr_blk_csum_set(ip, bp);
120934f43888SPedro F. Giffuni 
121034f43888SPedro F. Giffuni 	return (bwrite(bp));
121134f43888SPedro F. Giffuni }
121234f43888SPedro F. Giffuni 
ext2_extattr_free(struct inode * ip)121334f43888SPedro F. Giffuni int ext2_extattr_free(struct inode *ip)
121434f43888SPedro F. Giffuni {
121534f43888SPedro F. Giffuni 	struct m_ext2fs *fs;
121634f43888SPedro F. Giffuni 	struct buf *bp;
121734f43888SPedro F. Giffuni 	struct ext2fs_extattr_header *header;
121834f43888SPedro F. Giffuni 	int error;
121934f43888SPedro F. Giffuni 
122034f43888SPedro F. Giffuni 	fs = ip->i_e2fs;
122134f43888SPedro F. Giffuni 
122234f43888SPedro F. Giffuni 	if (!ip->i_facl)
122334f43888SPedro F. Giffuni 		return (0);
122434f43888SPedro F. Giffuni 
122534f43888SPedro F. Giffuni 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
122634f43888SPedro F. Giffuni 	    fs->e2fs_bsize, NOCRED, &bp);
122734f43888SPedro F. Giffuni 	if (error) {
122834f43888SPedro F. Giffuni 		return (error);
122934f43888SPedro F. Giffuni 	}
123034f43888SPedro F. Giffuni 
123134f43888SPedro F. Giffuni 	/* Check attributes magic value */
123234f43888SPedro F. Giffuni 	header = EXT2_HDR(bp);
1233cd3acfe7SFedor Uporov 	if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
1234cd3acfe7SFedor Uporov 	    le32toh(header->h_blocks) != 1) {
123534f43888SPedro F. Giffuni 		brelse(bp);
123634f43888SPedro F. Giffuni 		return (EINVAL);
123734f43888SPedro F. Giffuni 	}
123834f43888SPedro F. Giffuni 
1239512f29d1SFedor Uporov 	error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp),
1240512f29d1SFedor Uporov 	    bp->b_data + bp->b_bufsize);
124134f43888SPedro F. Giffuni 	if (error) {
124234f43888SPedro F. Giffuni 		brelse(bp);
124334f43888SPedro F. Giffuni 		return (error);
124434f43888SPedro F. Giffuni 	}
124534f43888SPedro F. Giffuni 
1246cd3acfe7SFedor Uporov 	if (le32toh(header->h_refcount) > 1) {
1247cd3acfe7SFedor Uporov 		header->h_refcount = htole32(le32toh(header->h_refcount) - 1);
124834f43888SPedro F. Giffuni 		bwrite(bp);
124934f43888SPedro F. Giffuni 	} else {
125034f43888SPedro F. Giffuni 		ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize);
125134f43888SPedro F. Giffuni 		brelse(bp);
125234f43888SPedro F. Giffuni 	}
125334f43888SPedro F. Giffuni 
125434f43888SPedro F. Giffuni 	ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize);
125534f43888SPedro F. Giffuni 	ip->i_facl = 0;
125634f43888SPedro F. Giffuni 	ext2_update(ip->i_vnode, 1);
125734f43888SPedro F. Giffuni 
1258ac506a8fSPedro F. Giffuni 	return (0);
1259ac506a8fSPedro F. Giffuni }
1260