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 + dinode->e2di_extra_isize);
220
221 if (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 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 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 + dinode->e2di_extra_isize);
361
362 if (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 += entry->e_value_size;
392
393 if (uio != NULL)
394 error = uiomove(((char *)EXT2_IFIRST(header)) +
395 entry->e_value_offs, entry->e_value_size, uio);
396
397 brelse(bp);
398 return (error);
399 }
400 }
401
402 brelse(bp);
403
404 return (ENOATTR);
405 }
406
407 int
408 ext2_extattr_block_get(struct inode *ip, int attrnamespace,
409 const char *name, struct uio *uio, size_t *size)
410 {
411 struct m_ext2fs *fs;
412 struct buf *bp;
413 struct ext2fs_extattr_header *header;
414 struct ext2fs_extattr_entry *entry;
415 const char *attr_name;
416 int name_len;
417 int error;
418
419 fs = ip->i_e2fs;
420
421 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
422 fs->e2fs_bsize, NOCRED, &bp);
423 if (error) {
424 brelse(bp);
425 return (error);
426 }
427
428 /* Check attributes magic value */
429 header = EXT2_HDR(bp);
430 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
431 brelse(bp);
432 return (EINVAL);
433 }
434
435 error = ext2_extattr_block_check(ip, bp);
436 if (error) {
437 brelse(bp);
438 return (error);
439 }
440
441 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
442 entry = EXT2_EXTATTR_NEXT(entry)) {
443 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
444 attrnamespace)
445 continue;
446
447 name_len = entry->e_name_len;
448 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
449 entry->e_name, &name_len);
450 if (!attr_name) {
451 brelse(bp);
452 return (ENOTSUP);
453 }
454
455 if (strlen(name) == name_len &&
456 0 == strncmp(attr_name, name, name_len)) {
457 if (size != NULL)
458 *size += entry->e_value_size;
459
460 if (uio != NULL)
461 error = uiomove(bp->b_data + entry->e_value_offs,
462 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
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 > next->e_value_offs && next->e_value_offs > 0)
486 min_offs = next->e_value_offs;
487
488 next = EXT2_EXTATTR_NEXT(next);
489 }
490
491 if (entry->e_value_size == 0)
492 return (min_offs);
493
494 memmove(off + min_offs + EXT2_EXTATTR_SIZE(entry->e_value_size),
495 off + min_offs, entry->e_value_offs - min_offs);
496
497 /* Adjust all value offsets */
498 next = first_entry;
499 while (!EXT2_IS_LAST_ENTRY(next))
500 {
501 if (next->e_value_offs > 0 &&
502 next->e_value_offs < entry->e_value_offs)
503 next->e_value_offs +=
504 EXT2_EXTATTR_SIZE(entry->e_value_size);
505
506 next = EXT2_EXTATTR_NEXT(next);
507 }
508
509 min_offs += EXT2_EXTATTR_SIZE(entry->e_value_size);
510
511 return (min_offs);
512 }
513
514 static void
515 ext2_extattr_delete_entry(char *off,
516 struct ext2fs_extattr_entry *first_entry,
517 struct ext2fs_extattr_entry *entry, char *end)
518 {
519 char *pad;
520 struct ext2fs_extattr_entry *next;
521
522 /* Clean entry value */
523 ext2_extattr_delete_value(off, first_entry, entry, end);
524
525 /* Clean the entry */
526 next = first_entry;
527 while (!EXT2_IS_LAST_ENTRY(next))
528 next = EXT2_EXTATTR_NEXT(next);
529
530 pad = (char*)next + sizeof(uint32_t);
531
532 memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len),
533 pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len)));
534 }
535
536 int
537 ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name)
538 {
539 struct m_ext2fs *fs;
540 struct buf *bp;
541 struct ext2fs_extattr_dinode_header *header;
542 struct ext2fs_extattr_entry *entry;
543 const char *attr_name;
544 int name_len;
545 int error;
546
547 fs = ip->i_e2fs;
548
549 if ((error = bread(ip->i_devvp,
550 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
551 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
552 brelse(bp);
553 return (error);
554 }
555
556 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
557 ((char *)bp->b_data +
558 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
559
560 /* Check attributes magic value */
561 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
562 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
563
564 if (header->h_magic != EXTATTR_MAGIC) {
565 brelse(bp);
566 return (ENOATTR);
567 }
568
569 error = ext2_extattr_check(EXT2_IFIRST(header),
570 (char *)dinode + EXT2_INODE_SIZE(fs));
571 if (error) {
572 brelse(bp);
573 return (error);
574 }
575
576 /* If I am last entry, just make magic zero */
577 entry = EXT2_IFIRST(header);
578 if ((EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) &&
579 (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
580 attrnamespace)) {
581
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
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 (header->h_magic != EXTATTR_MAGIC || header->h_refcount == 1)
641 return (EINVAL);
642
643 facl = ext2_alloc_meta(ip);
644 if (!facl)
645 return (ENOSPC);
646
647 cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0);
648 if (!cbp) {
649 ext2_blkfree(ip, facl, fs->e2fs_bsize);
650 return (EIO);
651 }
652
653 memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize);
654 header->h_refcount--;
655 bwrite(sbp);
656
657 ip->i_facl = facl;
658 ext2_update(ip->i_vnode, 1);
659
660 header = EXT2_HDR(cbp);
661 header->h_refcount = 1;
662
663 *bpp = cbp;
664
665 return (0);
666 }
667
668 int
669 ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name)
670 {
671 struct m_ext2fs *fs;
672 struct buf *bp;
673 struct ext2fs_extattr_header *header;
674 struct ext2fs_extattr_entry *entry;
675 const char *attr_name;
676 int name_len;
677 int error;
678
679 fs = ip->i_e2fs;
680
681 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
682 fs->e2fs_bsize, NOCRED, &bp);
683 if (error) {
684 brelse(bp);
685 return (error);
686 }
687
688 /* Check attributes magic value */
689 header = EXT2_HDR(bp);
690 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
691 brelse(bp);
692 return (EINVAL);
693 }
694
695 error = ext2_extattr_block_check(ip, bp);
696 if (error) {
697 brelse(bp);
698 return (error);
699 }
700
701 if (header->h_refcount > 1) {
702 error = ext2_extattr_block_clone(ip, &bp);
703 if (error) {
704 brelse(bp);
705 return (error);
706 }
707 }
708
709 /* If I am last entry, clean me and free the block */
710 entry = EXT2_FIRST_ENTRY(bp);
711 if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry)) &&
712 (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
713 attrnamespace)) {
714
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 *
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 = offs;
781 entry->e_value_block = 0;
782 entry->e_value_size = size;
783 entry->e_hash = hash;
784 memcpy(entry->e_name, name, name_len);
785
786 return (entry);
787 }
788
789 static void
790 free_entry(struct ext2fs_extattr_entry *entry)
791 {
792
793 free(entry, M_TEMP);
794 }
795
796 static int
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(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
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 = uio->uio_resid;
837 if (entry->e_value_size)
838 entry->e_value_offs = min_offs -
839 EXT2_EXTATTR_SIZE(uio->uio_resid);
840 else
841 entry->e_value_offs = 0;
842
843 uiomove(off + entry->e_value_offs, entry->e_value_size, uio);
844 }
845
846 static struct ext2fs_extattr_entry *
847 ext2_extattr_set_new_entry(char *off, struct ext2fs_extattr_entry *first_entry,
848 const char *name, int attrnamespace, char *end, struct uio *uio)
849 {
850 int name_len;
851 char *pad;
852 uint16_t min_offs;
853 struct ext2fs_extattr_entry *entry;
854 struct ext2fs_extattr_entry *new_entry;
855
856 /* Find pad's */
857 min_offs = end - off;
858 entry = first_entry;
859 while (!EXT2_IS_LAST_ENTRY(entry)) {
860 if (min_offs > entry->e_value_offs && entry->e_value_offs > 0)
861 min_offs = entry->e_value_offs;
862
863 entry = EXT2_EXTATTR_NEXT(entry);
864 }
865
866 pad = (char*)entry + sizeof(uint32_t);
867
868 /* Find entry insert position */
869 name_len = strlen(name);
870 entry = first_entry;
871 while (!EXT2_IS_LAST_ENTRY(entry)) {
872 if (!(attrnamespace - entry->e_name_index) &&
873 !(name_len - entry->e_name_len))
874 if (memcmp(name, entry->e_name, name_len) <= 0)
875 break;
876
877 entry = EXT2_EXTATTR_NEXT(entry);
878 }
879
880 /* Create new entry and insert it */
881 new_entry = allocate_entry(name, attrnamespace, 0, uio->uio_resid, 0);
882 memmove((char *)entry + EXT2_EXTATTR_LEN(new_entry->e_name_len), entry,
883 pad - (char*)entry);
884
885 memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len));
886 free_entry(new_entry);
887
888 new_entry = entry;
889 if (new_entry->e_value_size > 0)
890 new_entry->e_value_offs = min_offs -
891 EXT2_EXTATTR_SIZE(new_entry->e_value_size);
892
893 uiomove(off + new_entry->e_value_offs, new_entry->e_value_size, uio);
894
895 return (new_entry);
896 }
897
898 int
899 ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
900 const char *name, struct uio *uio)
901 {
902 struct m_ext2fs *fs;
903 struct buf *bp;
904 struct ext2fs_extattr_dinode_header *header;
905 struct ext2fs_extattr_entry *entry;
906 const char *attr_name;
907 int name_len;
908 size_t size = 0, max_size;
909 int error;
910
911 fs = ip->i_e2fs;
912
913 if ((error = bread(ip->i_devvp,
914 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
915 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
916 brelse(bp);
917 return (error);
918 }
919
920 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
921 ((char *)bp->b_data +
922 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
923
924 /* Check attributes magic value */
925 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
926 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
927
928 if (header->h_magic != EXTATTR_MAGIC) {
929 brelse(bp);
930 return (ENOSPC);
931 }
932
933 error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode +
934 EXT2_INODE_SIZE(fs));
935 if (error) {
936 brelse(bp);
937 return (error);
938 }
939
940 /* Find if entry exist */
941 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
942 entry = EXT2_EXTATTR_NEXT(entry)) {
943 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
944 attrnamespace)
945 continue;
946
947 name_len = entry->e_name_len;
948 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
949 entry->e_name, &name_len);
950 if (!attr_name) {
951 brelse(bp);
952 return (ENOTSUP);
953 }
954
955 if (strlen(name) == name_len &&
956 0 == strncmp(attr_name, name, name_len))
957 break;
958 }
959
960 max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE -
961 dinode->e2di_extra_isize;
962
963 if (!EXT2_IS_LAST_ENTRY(entry)) {
964 size = ext2_extattr_get_size(EXT2_IFIRST(header), entry,
965 sizeof(struct ext2fs_extattr_dinode_header),
966 entry->e_name_len, uio->uio_resid);
967 if (size > max_size) {
968 brelse(bp);
969 return (ENOSPC);
970 }
971
972 ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(header),
973 EXT2_IFIRST(header), entry, (char *)header + max_size, uio);
974 } else {
975 /* Ensure that the same entry does not exist in the block */
976 if (ip->i_facl) {
977 error = ext2_extattr_block_get(ip, attrnamespace, name,
978 NULL, &size);
979 if (error != ENOATTR || size > 0) {
980 brelse(bp);
981 if (size > 0)
982 error = ENOSPC;
983
984 return (error);
985 }
986 }
987
988 size = ext2_extattr_get_size(EXT2_IFIRST(header), NULL,
989 sizeof(struct ext2fs_extattr_dinode_header),
990 entry->e_name_len, uio->uio_resid);
991 if (size > max_size) {
992 brelse(bp);
993 return (ENOSPC);
994 }
995
996 ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header),
997 EXT2_IFIRST(header), name, attrnamespace,
998 (char *)header + max_size, uio);
999 }
1000
1001 return (bwrite(bp));
1002 }
1003
1004 static void
1005 ext2_extattr_hash_entry(struct ext2fs_extattr_header *header,
1006 struct ext2fs_extattr_entry *entry)
1007 {
1008 uint32_t hash = 0;
1009 char *name = entry->e_name;
1010 int n;
1011
1012 for (n=0; n < entry->e_name_len; n++) {
1013 hash = (hash << EXT2_EXTATTR_NAME_HASH_SHIFT) ^
1014 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_NAME_HASH_SHIFT)) ^
1015 (*name++);
1016 }
1017
1018 if (entry->e_value_block == 0 && entry->e_value_size != 0) {
1019 uint32_t *value = (uint32_t *)((char *)header + entry->e_value_offs);
1020 for (n = (entry->e_value_size +
1021 EXT2_EXTATTR_ROUND) >> EXT2_EXTATTR_PAD_BITS; n; n--) {
1022 hash = (hash << EXT2_EXTATTR_VALUE_HASH_SHIFT) ^
1023 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_VALUE_HASH_SHIFT)) ^
1024 (*value++);
1025 }
1026 }
1027
1028 entry->e_hash = hash;
1029 }
1030
1031 static void
1032 ext2_extattr_rehash(struct ext2fs_extattr_header *header,
1033 struct ext2fs_extattr_entry *entry)
1034 {
1035 struct ext2fs_extattr_entry *here;
1036 uint32_t hash = 0;
1037
1038 ext2_extattr_hash_entry(header, entry);
1039
1040 here = EXT2_ENTRY(header+1);
1041 while (!EXT2_IS_LAST_ENTRY(here)) {
1042 if (!here->e_hash) {
1043 /* Block is not shared if an entry's hash value == 0 */
1044 hash = 0;
1045 break;
1046 }
1047
1048 hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^
1049 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^
1050 here->e_hash;
1051
1052 here = EXT2_EXTATTR_NEXT(here);
1053 }
1054
1055 header->h_hash = hash;
1056 }
1057
1058 int
1059 ext2_extattr_block_set(struct inode *ip, int attrnamespace,
1060 const char *name, struct uio *uio)
1061 {
1062 struct m_ext2fs *fs;
1063 struct buf *bp;
1064 struct ext2fs_extattr_header *header;
1065 struct ext2fs_extattr_entry *entry;
1066 const char *attr_name;
1067 int name_len;
1068 size_t size;
1069 int error;
1070
1071 fs = ip->i_e2fs;
1072
1073 if (ip->i_facl) {
1074 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1075 fs->e2fs_bsize, NOCRED, &bp);
1076 if (error) {
1077 brelse(bp);
1078 return (error);
1079 }
1080
1081 /* Check attributes magic value */
1082 header = EXT2_HDR(bp);
1083 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1084 brelse(bp);
1085 return (EINVAL);
1086 }
1087
1088 error = ext2_extattr_block_check(ip, bp);
1089 if (error) {
1090 brelse(bp);
1091 return (error);
1092 }
1093
1094 if (header->h_refcount > 1) {
1095 error = ext2_extattr_block_clone(ip, &bp);
1096 if (error) {
1097 brelse(bp);
1098 return (error);
1099 }
1100
1101 header = EXT2_HDR(bp);
1102 }
1103
1104 /* Find if entry exist */
1105 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
1106 entry = EXT2_EXTATTR_NEXT(entry)) {
1107 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
1108 attrnamespace)
1109 continue;
1110
1111 name_len = entry->e_name_len;
1112 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
1113 entry->e_name, &name_len);
1114 if (!attr_name) {
1115 brelse(bp);
1116 return (ENOTSUP);
1117 }
1118
1119 if (strlen(name) == name_len &&
1120 0 == strncmp(attr_name, name, name_len))
1121 break;
1122 }
1123
1124 if (!EXT2_IS_LAST_ENTRY(entry)) {
1125 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), entry,
1126 sizeof(struct ext2fs_extattr_header),
1127 entry->e_name_len, uio->uio_resid);
1128 if (size > bp->b_bufsize) {
1129 brelse(bp);
1130 return (ENOSPC);
1131 }
1132
1133 ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1134 entry, bp->b_data + bp->b_bufsize, uio);
1135 } else {
1136 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), NULL,
1137 sizeof(struct ext2fs_extattr_header),
1138 strlen(name), uio->uio_resid);
1139 if (size > bp->b_bufsize) {
1140 brelse(bp);
1141 return (ENOSPC);
1142 }
1143
1144 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1145 name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1146
1147 /* Clean the same entry in the inode */
1148 error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1149 if (error && error != ENOATTR) {
1150 brelse(bp);
1151 return (error);
1152 }
1153 }
1154
1155 ext2_extattr_rehash(header, entry);
1156 ext2_extattr_blk_csum_set(ip, bp);
1157
1158 return (bwrite(bp));
1159 }
1160
1161 size = ext2_extattr_get_size(NULL, NULL,
1162 sizeof(struct ext2fs_extattr_header),
1163 strlen(ext2_extattr_name_to_linux(attrnamespace, name)), uio->uio_resid);
1164 if (size > fs->e2fs_bsize)
1165 return (ENOSPC);
1166
1167 /* Allocate block, fill EA header and insert entry */
1168 ip->i_facl = ext2_alloc_meta(ip);
1169 if (0 == ip->i_facl)
1170 return (ENOSPC);
1171
1172 ip->i_blocks += btodb(fs->e2fs_bsize);
1173 ext2_update(ip->i_vnode, 1);
1174
1175 bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0);
1176 if (!bp) {
1177 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
1178 ip->i_blocks -= btodb(fs->e2fs_bsize);
1179 ip->i_facl = 0;
1180 ext2_update(ip->i_vnode, 1);
1181 return (EIO);
1182 }
1183
1184 header = EXT2_HDR(bp);
1185 header->h_magic = EXTATTR_MAGIC;
1186 header->h_refcount = 1;
1187 header->h_blocks = 1;
1188 header->h_hash = 0;
1189 memset(header->h_reserved, 0, sizeof(header->h_reserved));
1190 memcpy(bp->b_data, header, sizeof(struct ext2fs_extattr_header));
1191 memset(EXT2_FIRST_ENTRY(bp), 0, sizeof(uint32_t));
1192
1193 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1194 name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1195
1196 /* Clean the same entry in the inode */
1197 error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1198 if (error && error != ENOATTR) {
1199 brelse(bp);
1200 return (error);
1201 }
1202
1203 ext2_extattr_rehash(header, entry);
1204 ext2_extattr_blk_csum_set(ip, bp);
1205
1206 return (bwrite(bp));
1207 }
1208
1209 int ext2_extattr_free(struct inode *ip)
1210 {
1211 struct m_ext2fs *fs;
1212 struct buf *bp;
1213 struct ext2fs_extattr_header *header;
1214 int error;
1215
1216 fs = ip->i_e2fs;
1217
1218 if (!ip->i_facl)
1219 return (0);
1220
1221 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1222 fs->e2fs_bsize, NOCRED, &bp);
1223 if (error) {
1224 brelse(bp);
1225 return (error);
1226 }
1227
1228 /* Check attributes magic value */
1229 header = EXT2_HDR(bp);
1230 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1231 brelse(bp);
1232 return (EINVAL);
1233 }
1234
1235 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp),
1236 bp->b_data + bp->b_bufsize);
1237 if (error) {
1238 brelse(bp);
1239 return (error);
1240 }
1241
1242 if (header->h_refcount > 1) {
1243 header->h_refcount--;
1244 bwrite(bp);
1245 } else {
1246 ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize);
1247 brelse(bp);
1248 }
1249
1250 ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize);
1251 ip->i_facl = 0;
1252 ext2_update(ip->i_vnode, 1);
1253
1254 return (0);
1255 }
Cache object: 4b8f63a6a156ddf1a7fdbf2371d4c593
|