xref: /freebsd/sys/fs/ext2fs/ext2_extattr.c (revision ac506a8f)
1 /*-
2  * Copyright (c) 2017, Fedor Uporov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/types.h>
32 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/vnode.h>
35 #include <sys/bio.h>
36 #include <sys/buf.h>
37 #include <sys/endian.h>
38 #include <sys/conf.h>
39 #include <sys/extattr.h>
40 
41 #include <fs/ext2fs/fs.h>
42 #include <fs/ext2fs/ext2fs.h>
43 #include <fs/ext2fs/inode.h>
44 #include <fs/ext2fs/ext2_dinode.h>
45 #include <fs/ext2fs/ext2_mount.h>
46 #include <fs/ext2fs/ext2_extattr.h>
47 
48 
49 static int
50 ext2_extattr_index_to_bsd(int index)
51 {
52 	switch (index) {
53 		case EXT4_XATTR_INDEX_USER:
54 			return EXTATTR_NAMESPACE_USER;
55 
56 		case EXT4_XATTR_INDEX_SYSTEM:
57 			return EXTATTR_NAMESPACE_SYSTEM;
58 
59 		default:
60 			return EXTATTR_NAMESPACE_EMPTY;
61 	}
62 }
63 
64 int
65 ext2_extattr_inode_list(struct inode *ip, int attrnamespace,
66     struct uio *uio, size_t *size)
67 {
68 	struct m_ext2fs *fs;
69 	struct buf *bp;
70 	struct ext2fs_extattr_dinode_header *header;
71 	struct ext2fs_extattr_entry *entry;
72 	struct ext2fs_extattr_entry *next;
73 	char *end;
74 	int error;
75 
76 	fs = ip->i_e2fs;
77 
78 	if ((error = bread(ip->i_devvp,
79 	    fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
80 	    (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
81 		brelse(bp);
82 		return (error);
83 	}
84 
85 	struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
86 	    ((char *)bp->b_data +
87 	    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
88 
89 	/* Check attributes magic value */
90 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
91 	    E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
92 
93 	if (header->h_magic != EXTATTR_MAGIC) {
94 		brelse(bp);
95 		return (0);
96 	}
97 
98 	/* Check attributes integrity */
99 	entry = EXT2_IFIRST(header);
100 	end = (char *)dinode + EXT2_INODE_SIZE(fs);
101 	while (!EXT2_IS_LAST_ENTRY(entry)) {
102 		next = EXT2_EXTATTR_NEXT(entry);
103 		if ((char *)next >= end) {
104 			brelse(bp);
105 			return (EIO);
106 		}
107 
108 		entry = next;
109 	}
110 
111 	for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
112 		entry = EXT2_EXTATTR_NEXT(entry)) {
113 		if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
114 			continue;
115 
116 		if (uio == NULL)
117 			*size += entry->e_name_len + 1;
118 		else {
119 			char *attr_name = malloc(entry->e_name_len + 1, M_TEMP, M_WAITOK);
120 			attr_name[0] = entry->e_name_len;
121 			memcpy(&attr_name[1], entry->e_name, entry->e_name_len);
122 			error = uiomove(attr_name, entry->e_name_len + 1, uio);
123 			free(attr_name, M_TEMP);
124 		}
125 	}
126 
127 	brelse(bp);
128 
129 	return (0);
130 }
131 
132 int
133 ext2_extattr_block_list(struct inode *ip, int attrnamespace,
134     struct uio *uio, size_t *size)
135 {
136 	struct m_ext2fs *fs;
137 	struct buf *bp;
138 	struct ext2fs_extattr_header *header;
139 	struct ext2fs_extattr_entry *entry;
140 	struct ext2fs_extattr_entry *next;
141 	char *end;
142 	int error;
143 
144 	fs = ip->i_e2fs;
145 
146 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
147 	    fs->e2fs_bsize, NOCRED, &bp);
148 	if (error) {
149 		brelse(bp);
150 		return (error);
151 	}
152 
153 	/* Check attributes magic value */
154 	header = EXT2_HDR(bp);
155 	if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
156 		brelse(bp);
157 		return (EINVAL);
158 	}
159 
160 	/* Check attributes integrity */
161 	end = bp->b_data + bp->b_bufsize;
162 	entry = EXT2_FIRST_ENTRY(bp);
163 	while (!EXT2_IS_LAST_ENTRY(entry)) {
164 		next = EXT2_EXTATTR_NEXT(entry);
165 		if ((char *)next >= end) {
166 			brelse(bp);
167 			return (EIO);
168 		}
169 
170 		entry = next;
171 	}
172 
173 	for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
174 	    entry = EXT2_EXTATTR_NEXT(entry)) {
175 		if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
176 			continue;
177 
178 		if (uio == NULL)
179 			*size += entry->e_name_len + 1;
180 		else {
181 			char *attr_name = malloc(entry->e_name_len + 1, M_TEMP, M_WAITOK);
182 			attr_name[0] = entry->e_name_len;
183 			memcpy(&attr_name[1], entry->e_name, entry->e_name_len);
184 			error = uiomove(attr_name, entry->e_name_len + 1, uio);
185 			free(attr_name, M_TEMP);
186 		}
187 	}
188 
189 	brelse(bp);
190 
191 	return (0);
192 }
193 
194 int
195 ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
196     const char *name, struct uio *uio, size_t *size)
197 {
198 	struct m_ext2fs *fs;
199 	struct buf *bp;
200 	struct ext2fs_extattr_dinode_header *header;
201 	struct ext2fs_extattr_entry *entry;
202 	struct ext2fs_extattr_entry *next;
203 	char *end;
204 	int error;
205 
206 	fs = ip->i_e2fs;
207 
208 	if ((error = bread(ip->i_devvp,
209 	    fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
210 	    (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
211 		brelse(bp);
212 		return (error);
213 	}
214 
215 	struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
216 	    ((char *)bp->b_data +
217 	    EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
218 
219 	/* Check attributes magic value */
220 	header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
221 	    E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
222 
223 	if (header->h_magic != EXTATTR_MAGIC) {
224 		brelse(bp);
225 		return (0);
226 	}
227 
228 	/* Check attributes integrity */
229 	entry = EXT2_IFIRST(header);
230 	end = (char *)dinode + EXT2_INODE_SIZE(fs);
231 	while (!EXT2_IS_LAST_ENTRY(entry)) {
232 		next = EXT2_EXTATTR_NEXT(entry);
233 		if ((char *)next >= end) {
234 			brelse(bp);
235 			return (EIO);
236 		}
237 
238 		entry = next;
239 	}
240 
241 	for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
242 	    entry = EXT2_EXTATTR_NEXT(entry)) {
243 		if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
244 			continue;
245 
246 		if (strlen(name) == entry->e_name_len &&
247 		    0 == strncmp(entry->e_name, name, entry->e_name_len)) {
248 			if (uio == NULL)
249 				*size += entry->e_value_size;
250 			else {
251 				error = uiomove(((char *)EXT2_IFIRST(header)) + entry->e_value_offs,
252 				    entry->e_value_size, uio);
253 				if (error) {
254 					brelse(bp);
255 					return (error);
256 				}
257 			}
258 		}
259 	 }
260 
261 	brelse(bp);
262 
263 	return (0);
264 }
265 
266 int
267 ext2_extattr_block_get(struct inode *ip, int attrnamespace,
268     const char *name, struct uio *uio, size_t *size)
269 {
270 	struct m_ext2fs *fs;
271 	struct buf *bp;
272 	struct ext2fs_extattr_header *header;
273 	struct ext2fs_extattr_entry *entry;
274 	struct ext2fs_extattr_entry *next;
275 	char *end;
276 	int error;
277 
278 	fs = ip->i_e2fs;
279 
280 	error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
281 	    fs->e2fs_bsize, NOCRED, &bp);
282 	if (error) {
283 		brelse(bp);
284 		return (error);
285 	}
286 
287 	/* Check attributes magic value */
288 	header = EXT2_HDR(bp);
289 	if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
290 		brelse(bp);
291 		return (EINVAL);
292 	}
293 
294 	/* Check attributes integrity */
295 	end = bp->b_data + bp->b_bufsize;
296 	entry = EXT2_FIRST_ENTRY(bp);
297 	while (!EXT2_IS_LAST_ENTRY(entry)) {
298 		next = EXT2_EXTATTR_NEXT(entry);
299 		if ((char *)next >= end) {
300 			brelse(bp);
301 			return (EIO);
302 		}
303 
304 		entry = next;
305 	}
306 
307 	for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
308 	    entry = EXT2_EXTATTR_NEXT(entry)) {
309 		if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace)
310 			continue;
311 
312 		if (strlen(name) == entry->e_name_len &&
313 		    0 == strncmp(entry->e_name, name, entry->e_name_len)) {
314 			if (uio == NULL)
315 				*size += entry->e_value_size;
316 			else {
317 				error = uiomove(bp->b_data + entry->e_value_offs,
318 				    entry->e_value_size, uio);
319 				if (error) {
320 					brelse(bp);
321 					return (error);
322 				}
323 			}
324 		}
325 	 }
326 
327 	brelse(bp);
328 
329 	return (0);
330 }
331