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