1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2017, Fedor Uporov
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/types.h>
32 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/vnode.h>
35 #include <sys/bio.h>
36 #include <sys/buf.h>
37 #include <sys/endian.h>
38 #include <sys/conf.h>
39 #include <sys/extattr.h>
40 #include <sys/sdt.h>
41
42 #include <fs/ext2fs/fs.h>
43 #include <fs/ext2fs/ext2fs.h>
44 #include <fs/ext2fs/inode.h>
45 #include <fs/ext2fs/ext2_dinode.h>
46 #include <fs/ext2fs/ext2_mount.h>
47 #include <fs/ext2fs/ext2_extattr.h>
48 #include <fs/ext2fs/ext2_extern.h>
49
50 SDT_PROVIDER_DECLARE(ext2fs);
51 /*
52 * ext2fs trace probe:
53 * arg0: verbosity. Higher numbers give more verbose messages
54 * arg1: Textual message
55 */
56 SDT_PROBE_DEFINE2(ext2fs, , trace, extattr, "int", "char*");
57
58 static int
ext2_extattr_attrnamespace_to_bsd(int attrnamespace)59 ext2_extattr_attrnamespace_to_bsd(int attrnamespace)
60 {
61
62 switch (attrnamespace) {
63 case EXT4_XATTR_INDEX_SYSTEM:
64 return (EXTATTR_NAMESPACE_SYSTEM);
65
66 case EXT4_XATTR_INDEX_USER:
67 return (EXTATTR_NAMESPACE_USER);
68
69 case EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT:
70 return (POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE);
71
72 case EXT4_XATTR_INDEX_POSIX_ACL_ACCESS:
73 return (POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE);
74 }
75
76 return (EXTATTR_NAMESPACE_EMPTY);
77 }
78
79 static const char *
ext2_extattr_name_to_bsd(int attrnamespace,const char * name,int * name_len)80 ext2_extattr_name_to_bsd(int attrnamespace, const char *name, int* name_len)
81 {
82
83 if (attrnamespace == EXT4_XATTR_INDEX_SYSTEM)
84 return (name);
85 else if (attrnamespace == EXT4_XATTR_INDEX_USER)
86 return (name);
87 else if (attrnamespace == EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT) {
88 *name_len = strlen(POSIX1E_ACL_DEFAULT_EXTATTR_NAME);
89 return (POSIX1E_ACL_DEFAULT_EXTATTR_NAME);
90 } else if (attrnamespace == EXT4_XATTR_INDEX_POSIX_ACL_ACCESS) {
91 *name_len = strlen(POSIX1E_ACL_ACCESS_EXTATTR_NAME);
92 return (POSIX1E_ACL_ACCESS_EXTATTR_NAME);
93 }
94
95 /*
96 * XXX: Not all linux namespaces are mapped to bsd for now,
97 * return NULL, which will be converted to ENOTSUP on upper layer.
98 */
99 SDT_PROBE2(ext2fs, , trace, extattr, 1,
100 "can not convert ext2fs name to bsd namespace");
101
102 return (NULL);
103 }
104
105 static int
ext2_extattr_attrnamespace_to_linux(int attrnamespace,const char * name)106 ext2_extattr_attrnamespace_to_linux(int attrnamespace, const char *name)
107 {
108
109 if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE &&
110 !strcmp(name, POSIX1E_ACL_DEFAULT_EXTATTR_NAME))
111 return (EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT);
112
113 if (attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE &&
114 !strcmp(name, POSIX1E_ACL_ACCESS_EXTATTR_NAME))
115 return (EXT4_XATTR_INDEX_POSIX_ACL_ACCESS);
116
117 switch (attrnamespace) {
118 case EXTATTR_NAMESPACE_SYSTEM:
119 return (EXT4_XATTR_INDEX_SYSTEM);
120
121 case EXTATTR_NAMESPACE_USER:
122 return (EXT4_XATTR_INDEX_USER);
123 }
124
125 /*
126 * In this case namespace conversion should be unique,
127 * so this point is unreachable.
128 */
129 return (-1);
130 }
131
132 static const char *
ext2_extattr_name_to_linux(int attrnamespace,const char * name)133 ext2_extattr_name_to_linux(int attrnamespace, const char *name)
134 {
135
136 if (attrnamespace == POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE ||
137 attrnamespace == POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE)
138 return ("");
139 else
140 return (name);
141 }
142
143 int
ext2_extattr_valid_attrname(int attrnamespace,const char * attrname)144 ext2_extattr_valid_attrname(int attrnamespace, const char *attrname)
145 {
146 if (attrnamespace == EXTATTR_NAMESPACE_EMPTY)
147 return (EINVAL);
148
149 if (strlen(attrname) == 0)
150 return (EINVAL);
151
152 if (strlen(attrname) + 1 > EXT2_EXTATTR_NAMELEN_MAX)
153 return (ENAMETOOLONG);
154
155 return (0);
156 }
157
158 static int
ext2_extattr_check(struct ext2fs_extattr_entry * entry,char * end)159 ext2_extattr_check(struct ext2fs_extattr_entry *entry, char *end)
160 {
161 struct ext2fs_extattr_entry *next;
162
163 while (!EXT2_IS_LAST_ENTRY(entry)) {
164 next = EXT2_EXTATTR_NEXT(entry);
165 if ((char *)next >= end)
166 return (EIO);
167
168 entry = next;
169 }
170
171 return (0);
172 }
173
174 static int
ext2_extattr_block_check(struct inode * ip,struct buf * bp)175 ext2_extattr_block_check(struct inode *ip, struct buf *bp)
176 {
177 struct ext2fs_extattr_header *header;
178 int error;
179
180 header = (struct ext2fs_extattr_header *)bp->b_data;
181
182 error = ext2_extattr_check(EXT2_IFIRST(header),
183 bp->b_data + bp->b_bufsize);
184 if (error)
185 return (error);
186
187 return (ext2_extattr_blk_csum_verify(ip, bp));
188 }
189
190 int
ext2_extattr_inode_list(struct inode * ip,int attrnamespace,struct uio * uio,size_t * size)191 ext2_extattr_inode_list(struct inode *ip, int attrnamespace,
192 struct uio *uio, size_t *size)
193 {
194 struct m_ext2fs *fs;
195 struct buf *bp;
196 struct ext2fs_extattr_dinode_header *header;
197 struct ext2fs_extattr_entry *entry;
198 const char *attr_name;
199 int name_len;
200 int error;
201
202 fs = ip->i_e2fs;
203
204 if ((error = bread(ip->i_devvp,
205 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
206 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
207 brelse(bp);
208 return (error);
209 }
210
211 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
212 ((char *)bp->b_data +
213 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
214
215 /* Check attributes magic value */
216 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
217 E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
218
219 if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
220 brelse(bp);
221 return (0);
222 }
223
224 error = ext2_extattr_check(EXT2_IFIRST(header),
225 (char *)dinode + EXT2_INODE_SIZE(fs));
226 if (error) {
227 brelse(bp);
228 return (error);
229 }
230
231 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
232 entry = EXT2_EXTATTR_NEXT(entry)) {
233 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
234 attrnamespace)
235 continue;
236
237 name_len = entry->e_name_len;
238 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
239 entry->e_name, &name_len);
240 if (!attr_name) {
241 brelse(bp);
242 return (ENOTSUP);
243 }
244
245 if (size != NULL)
246 *size += name_len + 1;
247
248 if (uio != NULL) {
249 char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
250 name[0] = name_len;
251 memcpy(&name[1], attr_name, name_len);
252 error = uiomove(name, name_len + 1, uio);
253 free(name, M_TEMP);
254 if (error)
255 break;
256 }
257 }
258
259 brelse(bp);
260
261 return (error);
262 }
263
264 int
ext2_extattr_block_list(struct inode * ip,int attrnamespace,struct uio * uio,size_t * size)265 ext2_extattr_block_list(struct inode *ip, int attrnamespace,
266 struct uio *uio, size_t *size)
267 {
268 struct m_ext2fs *fs;
269 struct buf *bp;
270 struct ext2fs_extattr_header *header;
271 struct ext2fs_extattr_entry *entry;
272 const char *attr_name;
273 int name_len;
274 int error;
275
276 fs = ip->i_e2fs;
277
278 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
279 fs->e2fs_bsize, NOCRED, &bp);
280 if (error) {
281 return (error);
282 }
283
284 /* Check attributes magic value */
285 header = EXT2_HDR(bp);
286 if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
287 le32toh(header->h_blocks) != 1) {
288 brelse(bp);
289 return (EINVAL);
290 }
291
292 error = ext2_extattr_block_check(ip, bp);
293 if (error) {
294 brelse(bp);
295 return (error);
296 }
297
298 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
299 entry = EXT2_EXTATTR_NEXT(entry)) {
300 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
301 attrnamespace)
302 continue;
303
304 name_len = entry->e_name_len;
305 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
306 entry->e_name, &name_len);
307 if (!attr_name) {
308 brelse(bp);
309 return (ENOTSUP);
310 }
311
312 if (size != NULL)
313 *size += name_len + 1;
314
315 if (uio != NULL) {
316 char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
317 name[0] = name_len;
318 memcpy(&name[1], attr_name, name_len);
319 error = uiomove(name, name_len + 1, uio);
320 free(name, M_TEMP);
321 if (error)
322 break;
323 }
324 }
325
326 brelse(bp);
327
328 return (error);
329 }
330
331 int
ext2_extattr_inode_get(struct inode * ip,int attrnamespace,const char * name,struct uio * uio,size_t * size)332 ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
333 const char *name, struct uio *uio, size_t *size)
334 {
335 struct m_ext2fs *fs;
336 struct buf *bp;
337 struct ext2fs_extattr_dinode_header *header;
338 struct ext2fs_extattr_entry *entry;
339 const char *attr_name;
340 int name_len;
341 int error;
342
343 fs = ip->i_e2fs;
344
345 if ((error = bread(ip->i_devvp,
346 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
347 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
348 brelse(bp);
349 return (error);
350 }
351
352 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
353 ((char *)bp->b_data +
354 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
355
356 /* Check attributes magic value */
357 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
358 E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
359
360 if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
361 brelse(bp);
362 return (ENOATTR);
363 }
364
365 error = ext2_extattr_check(EXT2_IFIRST(header),
366 (char *)dinode + EXT2_INODE_SIZE(fs));
367 if (error) {
368 brelse(bp);
369 return (error);
370 }
371
372 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
373 entry = EXT2_EXTATTR_NEXT(entry)) {
374 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
375 attrnamespace)
376 continue;
377
378 name_len = entry->e_name_len;
379 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
380 entry->e_name, &name_len);
381 if (!attr_name) {
382 brelse(bp);
383 return (ENOTSUP);
384 }
385
386 if (strlen(name) == name_len &&
387 0 == strncmp(attr_name, name, name_len)) {
388 if (size != NULL)
389 *size += le32toh(entry->e_value_size);
390
391 if (uio != NULL)
392 error = uiomove(((char *)EXT2_IFIRST(header)) +
393 le16toh(entry->e_value_offs),
394 le32toh(entry->e_value_size), uio);
395
396 brelse(bp);
397 return (error);
398 }
399 }
400
401 brelse(bp);
402
403 return (ENOATTR);
404 }
405
406 int
ext2_extattr_block_get(struct inode * ip,int attrnamespace,const char * name,struct uio * uio,size_t * size)407 ext2_extattr_block_get(struct inode *ip, int attrnamespace,
408 const char *name, struct uio *uio, size_t *size)
409 {
410 struct m_ext2fs *fs;
411 struct buf *bp;
412 struct ext2fs_extattr_header *header;
413 struct ext2fs_extattr_entry *entry;
414 const char *attr_name;
415 int name_len;
416 int error;
417
418 fs = ip->i_e2fs;
419
420 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
421 fs->e2fs_bsize, NOCRED, &bp);
422 if (error) {
423 return (error);
424 }
425
426 /* Check attributes magic value */
427 header = EXT2_HDR(bp);
428 if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
429 le32toh(header->h_blocks) != 1) {
430 brelse(bp);
431 return (EINVAL);
432 }
433
434 error = ext2_extattr_block_check(ip, bp);
435 if (error) {
436 brelse(bp);
437 return (error);
438 }
439
440 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
441 entry = EXT2_EXTATTR_NEXT(entry)) {
442 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
443 attrnamespace)
444 continue;
445
446 name_len = entry->e_name_len;
447 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
448 entry->e_name, &name_len);
449 if (!attr_name) {
450 brelse(bp);
451 return (ENOTSUP);
452 }
453
454 if (strlen(name) == name_len &&
455 0 == strncmp(attr_name, name, name_len)) {
456 if (size != NULL)
457 *size += le32toh(entry->e_value_size);
458
459 if (uio != NULL)
460 error = uiomove(bp->b_data +
461 le16toh(entry->e_value_offs),
462 le32toh(entry->e_value_size), uio);
463
464 brelse(bp);
465 return (error);
466 }
467 }
468
469 brelse(bp);
470
471 return (ENOATTR);
472 }
473
474 static uint16_t
ext2_extattr_delete_value(char * off,struct ext2fs_extattr_entry * first_entry,struct ext2fs_extattr_entry * entry,char * end)475 ext2_extattr_delete_value(char *off,
476 struct ext2fs_extattr_entry *first_entry,
477 struct ext2fs_extattr_entry *entry, char *end)
478 {
479 uint16_t min_offs;
480 struct ext2fs_extattr_entry *next;
481
482 min_offs = end - off;
483 next = first_entry;
484 while (!EXT2_IS_LAST_ENTRY(next)) {
485 if (min_offs > le16toh(next->e_value_offs) &&
486 le16toh(next->e_value_offs) > 0)
487 min_offs = le16toh(next->e_value_offs);
488
489 next = EXT2_EXTATTR_NEXT(next);
490 }
491
492 if (entry->e_value_size == 0)
493 return (min_offs);
494
495 memmove(off + min_offs + EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size)),
496 off + min_offs, le16toh(entry->e_value_offs) - min_offs);
497
498 /* Adjust all value offsets */
499 next = first_entry;
500 while (!EXT2_IS_LAST_ENTRY(next))
501 {
502 if (le16toh(next->e_value_offs) > 0 &&
503 le16toh(next->e_value_offs) < le16toh(entry->e_value_offs))
504 next->e_value_offs = htole16(le16toh(next->e_value_offs) +
505 EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size)));
506
507 next = EXT2_EXTATTR_NEXT(next);
508 }
509
510 min_offs += EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size));
511
512 return (min_offs);
513 }
514
515 static void
ext2_extattr_delete_entry(char * off,struct ext2fs_extattr_entry * first_entry,struct ext2fs_extattr_entry * entry,char * end)516 ext2_extattr_delete_entry(char *off,
517 struct ext2fs_extattr_entry *first_entry,
518 struct ext2fs_extattr_entry *entry, char *end)
519 {
520 char *pad;
521 struct ext2fs_extattr_entry *next;
522
523 /* Clean entry value */
524 ext2_extattr_delete_value(off, first_entry, entry, end);
525
526 /* Clean the entry */
527 next = first_entry;
528 while (!EXT2_IS_LAST_ENTRY(next))
529 next = EXT2_EXTATTR_NEXT(next);
530
531 pad = (char*)next + sizeof(uint32_t);
532
533 memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len),
534 pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len)));
535 }
536
537 int
ext2_extattr_inode_delete(struct inode * ip,int attrnamespace,const char * name)538 ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name)
539 {
540 struct m_ext2fs *fs;
541 struct buf *bp;
542 struct ext2fs_extattr_dinode_header *header;
543 struct ext2fs_extattr_entry *entry;
544 const char *attr_name;
545 int name_len;
546 int error;
547
548 fs = ip->i_e2fs;
549
550 if ((error = bread(ip->i_devvp,
551 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
552 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
553 brelse(bp);
554 return (error);
555 }
556
557 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
558 ((char *)bp->b_data +
559 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
560
561 /* Check attributes magic value */
562 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
563 E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
564
565 if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
566 brelse(bp);
567 return (ENOATTR);
568 }
569
570 error = ext2_extattr_check(EXT2_IFIRST(header),
571 (char *)dinode + EXT2_INODE_SIZE(fs));
572 if (error) {
573 brelse(bp);
574 return (error);
575 }
576
577 /* If I am last entry, just make magic zero */
578 entry = EXT2_IFIRST(header);
579 if ((EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) &&
580 (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
581 attrnamespace)) {
582 name_len = entry->e_name_len;
583 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
584 entry->e_name, &name_len);
585 if (!attr_name) {
586 brelse(bp);
587 return (ENOTSUP);
588 }
589
590 if (strlen(name) == name_len &&
591 0 == strncmp(attr_name, name, name_len)) {
592 memset(header, 0, sizeof(struct ext2fs_extattr_dinode_header));
593
594 return (bwrite(bp));
595 }
596 }
597
598 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
599 entry = EXT2_EXTATTR_NEXT(entry)) {
600 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
601 attrnamespace)
602 continue;
603
604 name_len = entry->e_name_len;
605 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
606 entry->e_name, &name_len);
607 if (!attr_name) {
608 brelse(bp);
609 return (ENOTSUP);
610 }
611
612 if (strlen(name) == name_len &&
613 0 == strncmp(attr_name, name, name_len)) {
614 ext2_extattr_delete_entry((char *)EXT2_IFIRST(header),
615 EXT2_IFIRST(header), entry,
616 (char *)dinode + EXT2_INODE_SIZE(fs));
617
618 return (bwrite(bp));
619 }
620 }
621
622 brelse(bp);
623
624 return (ENOATTR);
625 }
626
627 static int
ext2_extattr_block_clone(struct inode * ip,struct buf ** bpp)628 ext2_extattr_block_clone(struct inode *ip, struct buf **bpp)
629 {
630 struct m_ext2fs *fs;
631 struct buf *sbp;
632 struct buf *cbp;
633 struct ext2fs_extattr_header *header;
634 uint64_t facl;
635
636 fs = ip->i_e2fs;
637 sbp = *bpp;
638
639 header = EXT2_HDR(sbp);
640 if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
641 le32toh(header->h_refcount) == 1)
642 return (EINVAL);
643
644 facl = ext2_alloc_meta(ip);
645 if (!facl)
646 return (ENOSPC);
647
648 cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0);
649 if (!cbp) {
650 ext2_blkfree(ip, facl, fs->e2fs_bsize);
651 return (EIO);
652 }
653
654 memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize);
655 header->h_refcount = htole32(le32toh(header->h_refcount) - 1);
656 bwrite(sbp);
657
658 ip->i_facl = facl;
659 ext2_update(ip->i_vnode, 1);
660
661 header = EXT2_HDR(cbp);
662 header->h_refcount = htole32(1);
663
664 *bpp = cbp;
665
666 return (0);
667 }
668
669 int
ext2_extattr_block_delete(struct inode * ip,int attrnamespace,const char * name)670 ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name)
671 {
672 struct m_ext2fs *fs;
673 struct buf *bp;
674 struct ext2fs_extattr_header *header;
675 struct ext2fs_extattr_entry *entry;
676 const char *attr_name;
677 int name_len;
678 int error;
679
680 fs = ip->i_e2fs;
681
682 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
683 fs->e2fs_bsize, NOCRED, &bp);
684 if (error) {
685 return (error);
686 }
687
688 /* Check attributes magic value */
689 header = EXT2_HDR(bp);
690 if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
691 le32toh(header->h_blocks) != 1) {
692 brelse(bp);
693 return (EINVAL);
694 }
695
696 error = ext2_extattr_block_check(ip, bp);
697 if (error) {
698 brelse(bp);
699 return (error);
700 }
701
702 if (le32toh(header->h_refcount) > 1) {
703 error = ext2_extattr_block_clone(ip, &bp);
704 if (error) {
705 brelse(bp);
706 return (error);
707 }
708 }
709
710 /* If I am last entry, clean me and free the block */
711 entry = EXT2_FIRST_ENTRY(bp);
712 if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry)) &&
713 (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
714 attrnamespace)) {
715 name_len = entry->e_name_len;
716 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
717 entry->e_name, &name_len);
718 if (!attr_name) {
719 brelse(bp);
720 return (ENOTSUP);
721 }
722
723 if (strlen(name) == name_len &&
724 0 == strncmp(attr_name, name, name_len)) {
725 ip->i_blocks -= btodb(fs->e2fs_bsize);
726 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
727 ip->i_facl = 0;
728 error = ext2_update(ip->i_vnode, 1);
729
730 brelse(bp);
731 return (error);
732 }
733 }
734
735 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
736 entry = EXT2_EXTATTR_NEXT(entry)) {
737 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
738 attrnamespace)
739 continue;
740
741 name_len = entry->e_name_len;
742 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
743 entry->e_name, &name_len);
744 if (!attr_name) {
745 brelse(bp);
746 return (ENOTSUP);
747 }
748
749 if (strlen(name) == name_len &&
750 0 == strncmp(attr_name, name, name_len)) {
751 ext2_extattr_delete_entry(bp->b_data,
752 EXT2_FIRST_ENTRY(bp), entry,
753 bp->b_data + bp->b_bufsize);
754
755 return (bwrite(bp));
756 }
757 }
758
759 brelse(bp);
760
761 return (ENOATTR);
762 }
763
764 static struct ext2fs_extattr_entry *
allocate_entry(const char * name,int attrnamespace,uint16_t offs,uint32_t size,uint32_t hash)765 allocate_entry(const char *name, int attrnamespace, uint16_t offs,
766 uint32_t size, uint32_t hash)
767 {
768 const char *attr_name;
769 int name_len;
770 struct ext2fs_extattr_entry *entry;
771
772 attr_name = ext2_extattr_name_to_linux(attrnamespace, name);
773 name_len = strlen(attr_name);
774
775 entry = malloc(sizeof(struct ext2fs_extattr_entry) + name_len,
776 M_TEMP, M_WAITOK);
777
778 entry->e_name_len = name_len;
779 entry->e_name_index = ext2_extattr_attrnamespace_to_linux(attrnamespace, name);
780 entry->e_value_offs = htole16(offs);
781 entry->e_value_block = 0;
782 entry->e_value_size = htole32(size);
783 entry->e_hash = htole32(hash);
784 memcpy(entry->e_name, name, name_len);
785
786 return (entry);
787 }
788
789 static void
free_entry(struct ext2fs_extattr_entry * entry)790 free_entry(struct ext2fs_extattr_entry *entry)
791 {
792
793 free(entry, M_TEMP);
794 }
795
796 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)797 ext2_extattr_get_size(struct ext2fs_extattr_entry *first_entry,
798 struct ext2fs_extattr_entry *exist_entry, int header_size,
799 int name_len, int new_size)
800 {
801 struct ext2fs_extattr_entry *entry;
802 int size;
803
804 size = header_size;
805 size += sizeof(uint32_t);
806
807 if (NULL == exist_entry) {
808 size += EXT2_EXTATTR_LEN(name_len);
809 size += EXT2_EXTATTR_SIZE(new_size);
810 }
811
812 if (first_entry)
813 for (entry = first_entry; !EXT2_IS_LAST_ENTRY(entry);
814 entry = EXT2_EXTATTR_NEXT(entry)) {
815 if (entry != exist_entry)
816 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
817 EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size));
818 else
819 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
820 EXT2_EXTATTR_SIZE(new_size);
821 }
822
823 return (size);
824 }
825
826 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)827 ext2_extattr_set_exist_entry(char *off,
828 struct ext2fs_extattr_entry *first_entry,
829 struct ext2fs_extattr_entry *entry,
830 char *end, struct uio *uio)
831 {
832 uint16_t min_offs;
833
834 min_offs = ext2_extattr_delete_value(off, first_entry, entry, end);
835
836 entry->e_value_size = htole32(uio->uio_resid);
837 if (le32toh(entry->e_value_size))
838 entry->e_value_offs = htole16(min_offs -
839 EXT2_EXTATTR_SIZE(uio->uio_resid));
840 else
841 entry->e_value_offs = 0;
842
843 uiomove(off + le16toh(entry->e_value_offs),
844 le32toh(entry->e_value_size), uio);
845 }
846
847 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)848 ext2_extattr_set_new_entry(char *off, struct ext2fs_extattr_entry *first_entry,
849 const char *name, int attrnamespace, char *end, struct uio *uio)
850 {
851 int name_len;
852 char *pad;
853 uint16_t min_offs;
854 struct ext2fs_extattr_entry *entry;
855 struct ext2fs_extattr_entry *new_entry;
856
857 /* Find pad's */
858 min_offs = end - off;
859 entry = first_entry;
860 while (!EXT2_IS_LAST_ENTRY(entry)) {
861 if (min_offs > le16toh(entry->e_value_offs) &&
862 le16toh(entry->e_value_offs) > 0)
863 min_offs = le16toh(entry->e_value_offs);
864
865 entry = EXT2_EXTATTR_NEXT(entry);
866 }
867
868 pad = (char*)entry + sizeof(uint32_t);
869
870 /* Find entry insert position */
871 name_len = strlen(name);
872 entry = first_entry;
873 while (!EXT2_IS_LAST_ENTRY(entry)) {
874 if (!(attrnamespace - entry->e_name_index) &&
875 !(name_len - entry->e_name_len))
876 if (memcmp(name, entry->e_name, name_len) <= 0)
877 break;
878
879 entry = EXT2_EXTATTR_NEXT(entry);
880 }
881
882 /* Create new entry and insert it */
883 new_entry = allocate_entry(name, attrnamespace, 0, uio->uio_resid, 0);
884 memmove((char *)entry + EXT2_EXTATTR_LEN(new_entry->e_name_len), entry,
885 pad - (char*)entry);
886
887 memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len));
888 free_entry(new_entry);
889
890 new_entry = entry;
891 if (le32toh(new_entry->e_value_size) > 0)
892 new_entry->e_value_offs = htole16(min_offs -
893 EXT2_EXTATTR_SIZE(le32toh(new_entry->e_value_size)));
894
895 uiomove(off + le16toh(new_entry->e_value_offs),
896 le32toh(new_entry->e_value_size), uio);
897
898 return (new_entry);
899 }
900
901 int
ext2_extattr_inode_set(struct inode * ip,int attrnamespace,const char * name,struct uio * uio)902 ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
903 const char *name, struct uio *uio)
904 {
905 struct m_ext2fs *fs;
906 struct buf *bp;
907 struct ext2fs_extattr_dinode_header *header;
908 struct ext2fs_extattr_entry *entry;
909 const char *attr_name;
910 int name_len;
911 size_t size = 0, max_size;
912 int error;
913
914 fs = ip->i_e2fs;
915
916 if ((error = bread(ip->i_devvp,
917 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
918 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
919 brelse(bp);
920 return (error);
921 }
922
923 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
924 ((char *)bp->b_data +
925 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
926
927 /* Check attributes magic value */
928 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
929 E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
930
931 if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
932 brelse(bp);
933 return (ENOSPC);
934 }
935
936 error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode +
937 EXT2_INODE_SIZE(fs));
938 if (error) {
939 brelse(bp);
940 return (error);
941 }
942
943 /* Find if entry exist */
944 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
945 entry = EXT2_EXTATTR_NEXT(entry)) {
946 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
947 attrnamespace)
948 continue;
949
950 name_len = entry->e_name_len;
951 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
952 entry->e_name, &name_len);
953 if (!attr_name) {
954 brelse(bp);
955 return (ENOTSUP);
956 }
957
958 if (strlen(name) == name_len &&
959 0 == strncmp(attr_name, name, name_len))
960 break;
961 }
962
963 max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE -
964 le16toh(dinode->e2di_extra_isize);
965
966 if (!EXT2_IS_LAST_ENTRY(entry)) {
967 size = ext2_extattr_get_size(EXT2_IFIRST(header), entry,
968 sizeof(struct ext2fs_extattr_dinode_header),
969 entry->e_name_len, uio->uio_resid);
970 if (size > max_size) {
971 brelse(bp);
972 return (ENOSPC);
973 }
974
975 ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(header),
976 EXT2_IFIRST(header), entry, (char *)header + max_size, uio);
977 } else {
978 /* Ensure that the same entry does not exist in the block */
979 if (ip->i_facl) {
980 error = ext2_extattr_block_get(ip, attrnamespace, name,
981 NULL, &size);
982 if (error != ENOATTR || size > 0) {
983 brelse(bp);
984 if (size > 0)
985 error = ENOSPC;
986
987 return (error);
988 }
989 }
990
991 size = ext2_extattr_get_size(EXT2_IFIRST(header), NULL,
992 sizeof(struct ext2fs_extattr_dinode_header),
993 entry->e_name_len, uio->uio_resid);
994 if (size > max_size) {
995 brelse(bp);
996 return (ENOSPC);
997 }
998
999 ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header),
1000 EXT2_IFIRST(header), name, attrnamespace,
1001 (char *)header + max_size, uio);
1002 }
1003
1004 return (bwrite(bp));
1005 }
1006
1007 static void
ext2_extattr_hash_entry(struct ext2fs_extattr_header * header,struct ext2fs_extattr_entry * entry)1008 ext2_extattr_hash_entry(struct ext2fs_extattr_header *header,
1009 struct ext2fs_extattr_entry *entry)
1010 {
1011 uint32_t hash = 0;
1012 char *name = entry->e_name;
1013 int n;
1014
1015 for (n=0; n < entry->e_name_len; n++) {
1016 hash = (hash << EXT2_EXTATTR_NAME_HASH_SHIFT) ^
1017 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_NAME_HASH_SHIFT)) ^
1018 (*name++);
1019 }
1020
1021 if (entry->e_value_block == 0 && entry->e_value_size != 0) {
1022 uint32_t *value = (uint32_t *)((char *)header +
1023 le16toh(entry->e_value_offs));
1024 for (n = (le32toh(entry->e_value_size) +
1025 EXT2_EXTATTR_ROUND) >> EXT2_EXTATTR_PAD_BITS; n; n--) {
1026 hash = (hash << EXT2_EXTATTR_VALUE_HASH_SHIFT) ^
1027 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_VALUE_HASH_SHIFT)) ^
1028 le32toh(*value++);
1029 }
1030 }
1031
1032 entry->e_hash = htole32(hash);
1033 }
1034
1035 static void
ext2_extattr_rehash(struct ext2fs_extattr_header * header,struct ext2fs_extattr_entry * entry)1036 ext2_extattr_rehash(struct ext2fs_extattr_header *header,
1037 struct ext2fs_extattr_entry *entry)
1038 {
1039 struct ext2fs_extattr_entry *here;
1040 uint32_t hash = 0;
1041
1042 ext2_extattr_hash_entry(header, entry);
1043
1044 here = EXT2_ENTRY(header+1);
1045 while (!EXT2_IS_LAST_ENTRY(here)) {
1046 if (here->e_hash == 0) {
1047 /* Block is not shared if an entry's hash value == 0 */
1048 hash = 0;
1049 break;
1050 }
1051
1052 hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^
1053 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^
1054 le32toh(here->e_hash);
1055
1056 here = EXT2_EXTATTR_NEXT(here);
1057 }
1058
1059 header->h_hash = htole32(hash);
1060 }
1061
1062 int
ext2_extattr_block_set(struct inode * ip,int attrnamespace,const char * name,struct uio * uio)1063 ext2_extattr_block_set(struct inode *ip, int attrnamespace,
1064 const char *name, struct uio *uio)
1065 {
1066 struct m_ext2fs *fs;
1067 struct buf *bp;
1068 struct ext2fs_extattr_header *header;
1069 struct ext2fs_extattr_entry *entry;
1070 const char *attr_name;
1071 int name_len;
1072 size_t size;
1073 int error;
1074
1075 fs = ip->i_e2fs;
1076
1077 if (ip->i_facl) {
1078 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1079 fs->e2fs_bsize, NOCRED, &bp);
1080 if (error) {
1081 return (error);
1082 }
1083
1084 /* Check attributes magic value */
1085 header = EXT2_HDR(bp);
1086 if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
1087 le32toh(header->h_blocks) != 1) {
1088 brelse(bp);
1089 return (EINVAL);
1090 }
1091
1092 error = ext2_extattr_block_check(ip, bp);
1093 if (error) {
1094 brelse(bp);
1095 return (error);
1096 }
1097
1098 if (le32toh(header->h_refcount) > 1) {
1099 error = ext2_extattr_block_clone(ip, &bp);
1100 if (error) {
1101 brelse(bp);
1102 return (error);
1103 }
1104
1105 header = EXT2_HDR(bp);
1106 }
1107
1108 /* Find if entry exist */
1109 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
1110 entry = EXT2_EXTATTR_NEXT(entry)) {
1111 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
1112 attrnamespace)
1113 continue;
1114
1115 name_len = entry->e_name_len;
1116 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
1117 entry->e_name, &name_len);
1118 if (!attr_name) {
1119 brelse(bp);
1120 return (ENOTSUP);
1121 }
1122
1123 if (strlen(name) == name_len &&
1124 0 == strncmp(attr_name, name, name_len))
1125 break;
1126 }
1127
1128 if (!EXT2_IS_LAST_ENTRY(entry)) {
1129 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), entry,
1130 sizeof(struct ext2fs_extattr_header),
1131 entry->e_name_len, uio->uio_resid);
1132 if (size > bp->b_bufsize) {
1133 brelse(bp);
1134 return (ENOSPC);
1135 }
1136
1137 ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1138 entry, bp->b_data + bp->b_bufsize, uio);
1139 } else {
1140 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), NULL,
1141 sizeof(struct ext2fs_extattr_header),
1142 strlen(name), uio->uio_resid);
1143 if (size > bp->b_bufsize) {
1144 brelse(bp);
1145 return (ENOSPC);
1146 }
1147
1148 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1149 name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1150
1151 /* Clean the same entry in the inode */
1152 error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1153 if (error && error != ENOATTR) {
1154 brelse(bp);
1155 return (error);
1156 }
1157 }
1158
1159 ext2_extattr_rehash(header, entry);
1160 ext2_extattr_blk_csum_set(ip, bp);
1161
1162 return (bwrite(bp));
1163 }
1164
1165 size = ext2_extattr_get_size(NULL, NULL,
1166 sizeof(struct ext2fs_extattr_header),
1167 strlen(ext2_extattr_name_to_linux(attrnamespace, name)), uio->uio_resid);
1168 if (size > fs->e2fs_bsize)
1169 return (ENOSPC);
1170
1171 /* Allocate block, fill EA header and insert entry */
1172 ip->i_facl = ext2_alloc_meta(ip);
1173 if (0 == ip->i_facl)
1174 return (ENOSPC);
1175
1176 ip->i_blocks += btodb(fs->e2fs_bsize);
1177 ext2_update(ip->i_vnode, 1);
1178
1179 bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0);
1180 if (!bp) {
1181 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
1182 ip->i_blocks -= btodb(fs->e2fs_bsize);
1183 ip->i_facl = 0;
1184 ext2_update(ip->i_vnode, 1);
1185 return (EIO);
1186 }
1187
1188 header = EXT2_HDR(bp);
1189 header->h_magic = htole32(EXTATTR_MAGIC);
1190 header->h_refcount = htole32(1);
1191 header->h_blocks = htole32(1);
1192 header->h_hash = 0;
1193 memset(header->h_reserved, 0, sizeof(header->h_reserved));
1194 memcpy(bp->b_data, header, sizeof(struct ext2fs_extattr_header));
1195 memset(EXT2_FIRST_ENTRY(bp), 0, sizeof(uint32_t));
1196
1197 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1198 name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1199
1200 /* Clean the same entry in the inode */
1201 error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1202 if (error && error != ENOATTR) {
1203 brelse(bp);
1204 return (error);
1205 }
1206
1207 ext2_extattr_rehash(header, entry);
1208 ext2_extattr_blk_csum_set(ip, bp);
1209
1210 return (bwrite(bp));
1211 }
1212
ext2_extattr_free(struct inode * ip)1213 int ext2_extattr_free(struct inode *ip)
1214 {
1215 struct m_ext2fs *fs;
1216 struct buf *bp;
1217 struct ext2fs_extattr_header *header;
1218 int error;
1219
1220 fs = ip->i_e2fs;
1221
1222 if (!ip->i_facl)
1223 return (0);
1224
1225 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1226 fs->e2fs_bsize, NOCRED, &bp);
1227 if (error) {
1228 return (error);
1229 }
1230
1231 /* Check attributes magic value */
1232 header = EXT2_HDR(bp);
1233 if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
1234 le32toh(header->h_blocks) != 1) {
1235 brelse(bp);
1236 return (EINVAL);
1237 }
1238
1239 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp),
1240 bp->b_data + bp->b_bufsize);
1241 if (error) {
1242 brelse(bp);
1243 return (error);
1244 }
1245
1246 if (le32toh(header->h_refcount) > 1) {
1247 header->h_refcount = htole32(le32toh(header->h_refcount) - 1);
1248 bwrite(bp);
1249 } else {
1250 ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize);
1251 brelse(bp);
1252 }
1253
1254 ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize);
1255 ip->i_facl = 0;
1256 ext2_update(ip->i_vnode, 1);
1257
1258 return (0);
1259 }
1260