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