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