]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/ext2fs/ext2_extattr.c
Merge ^/vendor/libc++/dist up to its last change, and resolve conflicts.
[FreeBSD/FreeBSD.git] / sys / fs / ext2fs / ext2_extattr.c
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                 return (error);
284         }
285
286         /* Check attributes magic value */
287         header = EXT2_HDR(bp);
288         if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
289                 brelse(bp);
290                 return (EINVAL);
291         }
292
293         error = ext2_extattr_block_check(ip, bp);
294         if (error) {
295                 brelse(bp);
296                 return (error);
297         }
298
299         for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
300             entry = EXT2_EXTATTR_NEXT(entry)) {
301                 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
302                     attrnamespace)
303                         continue;
304
305                 name_len = entry->e_name_len;
306                 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
307                     entry->e_name, &name_len);
308                 if (!attr_name) {
309                         brelse(bp);
310                         return (ENOTSUP);
311                 }
312
313                 if (size != NULL)
314                         *size += name_len + 1;
315
316                 if (uio != NULL) {
317                         char *name = malloc(name_len + 1, M_TEMP, M_WAITOK);
318                         name[0] = name_len;
319                         memcpy(&name[1], attr_name, name_len);
320                         error = uiomove(name, name_len + 1, uio);
321                         free(name, M_TEMP);
322                         if (error)
323                                 break;
324                 }
325         }
326
327         brelse(bp);
328
329         return (error);
330 }
331
332 int
333 ext2_extattr_inode_get(struct inode *ip, int attrnamespace,
334     const char *name, struct uio *uio, size_t *size)
335 {
336         struct m_ext2fs *fs;
337         struct buf *bp;
338         struct ext2fs_extattr_dinode_header *header;
339         struct ext2fs_extattr_entry *entry;
340         const char *attr_name;
341         int name_len;
342         int error;
343
344         fs = ip->i_e2fs;
345
346         if ((error = bread(ip->i_devvp,
347             fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
348             (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
349                 brelse(bp);
350                 return (error);
351         }
352
353         struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
354             ((char *)bp->b_data +
355             EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
356
357         /* Check attributes magic value */
358         header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
359             E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
360
361         if (header->h_magic != EXTATTR_MAGIC) {
362                 brelse(bp);
363                 return (ENOATTR);
364         }
365
366         error = ext2_extattr_check(EXT2_IFIRST(header),
367             (char *)dinode + EXT2_INODE_SIZE(fs));
368         if (error) {
369                 brelse(bp);
370                 return (error);
371         }
372
373         for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
374             entry = EXT2_EXTATTR_NEXT(entry)) {
375                 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
376                     attrnamespace)
377                         continue;
378
379                 name_len = entry->e_name_len;
380                 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
381                     entry->e_name, &name_len);
382                 if (!attr_name) {
383                         brelse(bp);
384                         return (ENOTSUP);
385                 }
386
387                 if (strlen(name) == name_len &&
388                     0 == strncmp(attr_name, name, name_len)) {
389                         if (size != NULL)
390                                 *size += entry->e_value_size;
391
392                         if (uio != NULL)
393                                 error = uiomove(((char *)EXT2_IFIRST(header)) +
394                                     entry->e_value_offs, entry->e_value_size, uio);
395
396                         brelse(bp);
397                         return (error);
398                 }
399          }
400
401         brelse(bp);
402
403         return (ENOATTR);
404 }
405
406 int
407 ext2_extattr_block_get(struct inode *ip, int attrnamespace,
408     const char *name, struct uio *uio, size_t *size)
409 {
410         struct m_ext2fs *fs;
411         struct buf *bp;
412         struct ext2fs_extattr_header *header;
413         struct ext2fs_extattr_entry *entry;
414         const char *attr_name;
415         int name_len;
416         int error;
417
418         fs = ip->i_e2fs;
419
420         error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
421             fs->e2fs_bsize, NOCRED, &bp);
422         if (error) {
423                 return (error);
424         }
425
426         /* Check attributes magic value */
427         header = EXT2_HDR(bp);
428         if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
429                 brelse(bp);
430                 return (EINVAL);
431         }
432
433         error = ext2_extattr_block_check(ip, bp);
434         if (error) {
435                 brelse(bp);
436                 return (error);
437         }
438
439         for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
440             entry = EXT2_EXTATTR_NEXT(entry)) {
441                 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
442                     attrnamespace)
443                         continue;
444
445                 name_len = entry->e_name_len;
446                 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
447                     entry->e_name, &name_len);
448                 if (!attr_name) {
449                         brelse(bp);
450                         return (ENOTSUP);
451                 }
452
453                 if (strlen(name) == name_len &&
454                     0 == strncmp(attr_name, name, name_len)) {
455                         if (size != NULL)
456                                 *size += entry->e_value_size;
457
458                         if (uio != NULL)
459                                 error = uiomove(bp->b_data + entry->e_value_offs,
460                                     entry->e_value_size, uio);
461
462                         brelse(bp);
463                         return (error);
464                 }
465          }
466
467         brelse(bp);
468
469         return (ENOATTR);
470 }
471
472 static uint16_t
473 ext2_extattr_delete_value(char *off,
474     struct ext2fs_extattr_entry *first_entry,
475     struct ext2fs_extattr_entry *entry, char *end)
476 {
477         uint16_t min_offs;
478         struct ext2fs_extattr_entry *next;
479
480         min_offs = end - off;
481         next = first_entry;
482         while (!EXT2_IS_LAST_ENTRY(next)) {
483                 if (min_offs > next->e_value_offs && next->e_value_offs > 0)
484                         min_offs = next->e_value_offs;
485
486                 next = EXT2_EXTATTR_NEXT(next);
487         }
488
489         if (entry->e_value_size == 0)
490                 return (min_offs);
491
492         memmove(off + min_offs + EXT2_EXTATTR_SIZE(entry->e_value_size),
493             off + min_offs, entry->e_value_offs - min_offs);
494
495         /* Adjust all value offsets */
496         next = first_entry;
497         while (!EXT2_IS_LAST_ENTRY(next))
498         {
499                 if (next->e_value_offs > 0 &&
500                     next->e_value_offs < entry->e_value_offs)
501                         next->e_value_offs +=
502                             EXT2_EXTATTR_SIZE(entry->e_value_size);
503
504                 next = EXT2_EXTATTR_NEXT(next);
505         }
506
507         min_offs += EXT2_EXTATTR_SIZE(entry->e_value_size);
508
509         return (min_offs);
510 }
511
512 static void
513 ext2_extattr_delete_entry(char *off,
514     struct ext2fs_extattr_entry *first_entry,
515     struct ext2fs_extattr_entry *entry, char *end)
516 {
517         char *pad;
518         struct ext2fs_extattr_entry *next;
519
520         /* Clean entry value */
521         ext2_extattr_delete_value(off, first_entry, entry, end);
522
523         /* Clean the entry */
524         next = first_entry;
525         while (!EXT2_IS_LAST_ENTRY(next))
526                 next = EXT2_EXTATTR_NEXT(next);
527
528         pad = (char*)next + sizeof(uint32_t);
529
530         memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len),
531             pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len)));
532 }
533
534 int
535 ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name)
536 {
537         struct m_ext2fs *fs;
538         struct buf *bp;
539         struct ext2fs_extattr_dinode_header *header;
540         struct ext2fs_extattr_entry *entry;
541         const char *attr_name;
542         int name_len;
543         int error;
544
545         fs = ip->i_e2fs;
546
547         if ((error = bread(ip->i_devvp,
548             fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
549             (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
550                 brelse(bp);
551                 return (error);
552         }
553
554         struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
555             ((char *)bp->b_data +
556             EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
557
558         /* Check attributes magic value */
559         header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
560             E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
561
562         if (header->h_magic != EXTATTR_MAGIC) {
563                 brelse(bp);
564                 return (ENOATTR);
565         }
566
567         error = ext2_extattr_check(EXT2_IFIRST(header),
568             (char *)dinode + EXT2_INODE_SIZE(fs));
569         if (error) {
570                 brelse(bp);
571                 return (error);
572         }
573
574         /* If I am last entry, just make magic zero */
575         entry = EXT2_IFIRST(header);
576         if ((EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) &&
577             (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
578             attrnamespace)) {
579
580                 name_len = entry->e_name_len;
581                 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
582                     entry->e_name, &name_len);
583                 if (!attr_name) {
584                         brelse(bp);
585                         return (ENOTSUP);
586                 }
587
588                 if (strlen(name) == name_len &&
589                     0 == strncmp(attr_name, name, name_len)) {
590                         memset(header, 0, sizeof(struct ext2fs_extattr_dinode_header));
591
592                         return (bwrite(bp));
593                 }
594         }
595
596         for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
597             entry = EXT2_EXTATTR_NEXT(entry)) {
598                 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
599                     attrnamespace)
600                         continue;
601
602                 name_len = entry->e_name_len;
603                 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
604                     entry->e_name, &name_len);
605                 if (!attr_name) {
606                         brelse(bp);
607                         return (ENOTSUP);
608                 }
609
610                 if (strlen(name) == name_len &&
611                     0 == strncmp(attr_name, name, name_len)) {
612                         ext2_extattr_delete_entry((char *)EXT2_IFIRST(header),
613                             EXT2_IFIRST(header), entry,
614                             (char *)dinode + EXT2_INODE_SIZE(fs));
615
616                         return (bwrite(bp));
617                 }
618         }
619
620         brelse(bp);
621
622         return (ENOATTR);
623 }
624
625 static int
626 ext2_extattr_block_clone(struct inode *ip, struct buf **bpp)
627 {
628         struct m_ext2fs *fs;
629         struct buf *sbp;
630         struct buf *cbp;
631         struct ext2fs_extattr_header *header;
632         uint64_t facl;
633
634         fs = ip->i_e2fs;
635         sbp = *bpp;
636
637         header = EXT2_HDR(sbp);
638         if (header->h_magic != EXTATTR_MAGIC || header->h_refcount == 1)
639                 return (EINVAL);
640
641         facl = ext2_alloc_meta(ip);
642         if (!facl)
643                 return (ENOSPC);
644
645         cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0);
646         if (!cbp) {
647                 ext2_blkfree(ip, facl, fs->e2fs_bsize);
648                 return (EIO);
649         }
650
651         memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize);
652         header->h_refcount--;
653         bwrite(sbp);
654
655         ip->i_facl = facl;
656         ext2_update(ip->i_vnode, 1);
657
658         header = EXT2_HDR(cbp);
659         header->h_refcount = 1;
660
661         *bpp = cbp;
662
663         return (0);
664 }
665
666 int
667 ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name)
668 {
669         struct m_ext2fs *fs;
670         struct buf *bp;
671         struct ext2fs_extattr_header *header;
672         struct ext2fs_extattr_entry *entry;
673         const char *attr_name;
674         int name_len;
675         int error;
676
677         fs = ip->i_e2fs;
678
679         error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
680             fs->e2fs_bsize, NOCRED, &bp);
681         if (error) {
682                 return (error);
683         }
684
685         /* Check attributes magic value */
686         header = EXT2_HDR(bp);
687         if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
688                 brelse(bp);
689                 return (EINVAL);
690         }
691
692         error = ext2_extattr_block_check(ip, bp);
693         if (error) {
694                 brelse(bp);
695                 return (error);
696         }
697
698         if (header->h_refcount > 1) {
699                 error = ext2_extattr_block_clone(ip, &bp);
700                 if (error) {
701                         brelse(bp);
702                         return (error);
703                 }
704         }
705
706         /* If I am last entry, clean me and free the block */
707         entry = EXT2_FIRST_ENTRY(bp);
708         if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry)) &&
709             (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
710             attrnamespace)) {
711
712                 name_len = entry->e_name_len;
713                 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
714                     entry->e_name, &name_len);
715                 if (!attr_name) {
716                         brelse(bp);
717                         return (ENOTSUP);
718                 }
719
720                 if (strlen(name) == name_len &&
721                     0 == strncmp(attr_name, name, name_len)) {
722                         ip->i_blocks -= btodb(fs->e2fs_bsize);
723                         ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
724                         ip->i_facl = 0;
725                         error = ext2_update(ip->i_vnode, 1);
726
727                         brelse(bp);
728                         return (error);
729                 }
730         }
731
732         for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
733             entry = EXT2_EXTATTR_NEXT(entry)) {
734                 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
735                     attrnamespace)
736                         continue;
737
738                 name_len = entry->e_name_len;
739                 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
740                     entry->e_name, &name_len);
741                 if (!attr_name) {
742                         brelse(bp);
743                         return (ENOTSUP);
744                 }
745
746                 if (strlen(name) == name_len &&
747                     0 == strncmp(attr_name, name, name_len)) {
748                         ext2_extattr_delete_entry(bp->b_data,
749                             EXT2_FIRST_ENTRY(bp), entry,
750                             bp->b_data + bp->b_bufsize);
751
752                         return (bwrite(bp));
753                 }
754         }
755
756         brelse(bp);
757
758         return (ENOATTR);
759 }
760
761 static struct ext2fs_extattr_entry *
762 allocate_entry(const char *name, int attrnamespace, uint16_t offs,
763     uint32_t size, uint32_t hash)
764 {
765         const char *attr_name;
766         int name_len;
767         struct ext2fs_extattr_entry *entry;
768
769         attr_name = ext2_extattr_name_to_linux(attrnamespace, name);
770         name_len = strlen(attr_name);
771
772         entry = malloc(sizeof(struct ext2fs_extattr_entry) + name_len,
773             M_TEMP, M_WAITOK);
774
775         entry->e_name_len = name_len;
776         entry->e_name_index = ext2_extattr_attrnamespace_to_linux(attrnamespace, name);
777         entry->e_value_offs = offs;
778         entry->e_value_block = 0;
779         entry->e_value_size = size;
780         entry->e_hash = hash;
781         memcpy(entry->e_name, name, name_len);
782
783         return (entry);
784 }
785
786 static void
787 free_entry(struct ext2fs_extattr_entry *entry)
788 {
789
790         free(entry, M_TEMP);
791 }
792
793 static int
794 ext2_extattr_get_size(struct ext2fs_extattr_entry *first_entry,
795     struct ext2fs_extattr_entry *exist_entry, int header_size,
796     int name_len, int new_size)
797 {
798         struct ext2fs_extattr_entry *entry;
799         int size;
800
801         size = header_size;
802         size += sizeof(uint32_t);
803
804         if (NULL == exist_entry) {
805                 size += EXT2_EXTATTR_LEN(name_len);
806                 size += EXT2_EXTATTR_SIZE(new_size);
807         }
808
809         if (first_entry)
810                 for (entry = first_entry; !EXT2_IS_LAST_ENTRY(entry);
811                     entry = EXT2_EXTATTR_NEXT(entry)) {
812                         if (entry != exist_entry)
813                                 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
814                                     EXT2_EXTATTR_SIZE(entry->e_value_size);
815                         else
816                                 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
817                                     EXT2_EXTATTR_SIZE(new_size);
818                 }
819
820         return (size);
821 }
822
823 static void
824 ext2_extattr_set_exist_entry(char *off,
825     struct ext2fs_extattr_entry *first_entry,
826     struct ext2fs_extattr_entry *entry,
827     char *end, struct uio *uio)
828 {
829         uint16_t min_offs;
830
831         min_offs = ext2_extattr_delete_value(off, first_entry, entry, end);
832
833         entry->e_value_size = uio->uio_resid;
834         if (entry->e_value_size)
835                 entry->e_value_offs = min_offs -
836                     EXT2_EXTATTR_SIZE(uio->uio_resid);
837         else
838                 entry->e_value_offs = 0;
839
840         uiomove(off + entry->e_value_offs, entry->e_value_size, uio);
841 }
842
843 static struct ext2fs_extattr_entry *
844 ext2_extattr_set_new_entry(char *off, struct ext2fs_extattr_entry *first_entry,
845     const char *name, int attrnamespace, char *end, struct uio *uio)
846 {
847         int name_len;
848         char *pad;
849         uint16_t min_offs;
850         struct ext2fs_extattr_entry *entry;
851         struct ext2fs_extattr_entry *new_entry;
852
853         /* Find pad's */
854         min_offs = end - off;
855         entry = first_entry;
856         while (!EXT2_IS_LAST_ENTRY(entry)) {
857                 if (min_offs > entry->e_value_offs && entry->e_value_offs > 0)
858                         min_offs = entry->e_value_offs;
859
860                 entry = EXT2_EXTATTR_NEXT(entry);
861         }
862
863         pad = (char*)entry + sizeof(uint32_t);
864
865         /* Find entry insert position */
866         name_len = strlen(name);
867         entry = first_entry;
868         while (!EXT2_IS_LAST_ENTRY(entry)) {
869                 if (!(attrnamespace - entry->e_name_index) &&
870                     !(name_len - entry->e_name_len))
871                         if (memcmp(name, entry->e_name, name_len) <= 0)
872                                 break;
873
874                 entry = EXT2_EXTATTR_NEXT(entry);
875         }
876
877         /* Create new entry and insert it */
878         new_entry = allocate_entry(name, attrnamespace, 0, uio->uio_resid, 0);
879         memmove((char *)entry + EXT2_EXTATTR_LEN(new_entry->e_name_len), entry,
880             pad - (char*)entry);
881
882         memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len));
883         free_entry(new_entry);
884
885         new_entry = entry;
886         if (new_entry->e_value_size > 0)
887                 new_entry->e_value_offs = min_offs -
888                     EXT2_EXTATTR_SIZE(new_entry->e_value_size);
889
890         uiomove(off + new_entry->e_value_offs, new_entry->e_value_size, uio);
891
892         return (new_entry);
893 }
894
895 int
896 ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
897     const char *name, struct uio *uio)
898 {
899         struct m_ext2fs *fs;
900         struct buf *bp;
901         struct ext2fs_extattr_dinode_header *header;
902         struct ext2fs_extattr_entry *entry;
903         const char *attr_name;
904         int name_len;
905         size_t size = 0, max_size;
906         int error;
907
908         fs = ip->i_e2fs;
909
910         if ((error = bread(ip->i_devvp,
911             fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
912             (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
913                 brelse(bp);
914                 return (error);
915         }
916
917         struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
918             ((char *)bp->b_data +
919             EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
920
921         /* Check attributes magic value */
922         header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
923             E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize);
924
925         if (header->h_magic != EXTATTR_MAGIC) {
926                 brelse(bp);
927                 return (ENOSPC);
928         }
929
930         error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode +
931             EXT2_INODE_SIZE(fs));
932         if (error) {
933                 brelse(bp);
934                 return (error);
935         }
936
937         /* Find if entry exist */
938         for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
939             entry = EXT2_EXTATTR_NEXT(entry)) {
940                 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
941                     attrnamespace)
942                         continue;
943
944                 name_len = entry->e_name_len;
945                 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
946                     entry->e_name, &name_len);
947                 if (!attr_name) {
948                         brelse(bp);
949                         return (ENOTSUP);
950                 }
951
952                 if (strlen(name) == name_len &&
953                     0 == strncmp(attr_name, name, name_len))
954                         break;
955         }
956
957         max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE -
958             dinode->e2di_extra_isize;
959
960         if (!EXT2_IS_LAST_ENTRY(entry)) {
961                 size = ext2_extattr_get_size(EXT2_IFIRST(header), entry,
962                     sizeof(struct ext2fs_extattr_dinode_header),
963                     entry->e_name_len, uio->uio_resid);
964                 if (size > max_size) {
965                         brelse(bp);
966                         return (ENOSPC);
967                 }
968
969                 ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(header),
970                     EXT2_IFIRST(header), entry, (char *)header + max_size, uio);
971         } else {
972                 /* Ensure that the same entry does not exist in the block */
973                 if (ip->i_facl) {
974                         error = ext2_extattr_block_get(ip, attrnamespace, name,
975                             NULL, &size);
976                         if (error != ENOATTR || size > 0) {
977                                 brelse(bp);
978                                 if (size > 0)
979                                         error = ENOSPC;
980
981                                 return (error);
982                         }
983                 }
984
985                 size = ext2_extattr_get_size(EXT2_IFIRST(header), NULL,
986                     sizeof(struct ext2fs_extattr_dinode_header),
987                     entry->e_name_len, uio->uio_resid);
988                 if (size > max_size) {
989                         brelse(bp);
990                         return (ENOSPC);
991                 }
992
993                 ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header),
994                     EXT2_IFIRST(header), name, attrnamespace,
995                     (char *)header + max_size, uio);
996         }
997
998         return (bwrite(bp));
999 }
1000
1001 static void
1002 ext2_extattr_hash_entry(struct ext2fs_extattr_header *header,
1003     struct ext2fs_extattr_entry *entry)
1004 {
1005         uint32_t hash = 0;
1006         char *name = entry->e_name;
1007         int n;
1008
1009         for (n=0; n < entry->e_name_len; n++) {
1010                 hash = (hash << EXT2_EXTATTR_NAME_HASH_SHIFT) ^
1011                     (hash >> (8*sizeof(hash) - EXT2_EXTATTR_NAME_HASH_SHIFT)) ^
1012                     (*name++);
1013         }
1014
1015         if (entry->e_value_block == 0 && entry->e_value_size != 0) {
1016                 uint32_t *value = (uint32_t *)((char *)header + entry->e_value_offs);
1017                 for (n = (entry->e_value_size +
1018                     EXT2_EXTATTR_ROUND) >> EXT2_EXTATTR_PAD_BITS; n; n--) {
1019                         hash = (hash << EXT2_EXTATTR_VALUE_HASH_SHIFT) ^
1020                             (hash >> (8*sizeof(hash) - EXT2_EXTATTR_VALUE_HASH_SHIFT)) ^
1021                             (*value++);
1022                 }
1023         }
1024
1025         entry->e_hash = hash;
1026 }
1027
1028 static void
1029 ext2_extattr_rehash(struct ext2fs_extattr_header *header,
1030     struct ext2fs_extattr_entry *entry)
1031 {
1032         struct ext2fs_extattr_entry *here;
1033         uint32_t hash = 0;
1034
1035         ext2_extattr_hash_entry(header, entry);
1036
1037         here = EXT2_ENTRY(header+1);
1038         while (!EXT2_IS_LAST_ENTRY(here)) {
1039                 if (!here->e_hash) {
1040                         /* Block is not shared if an entry's hash value == 0 */
1041                         hash = 0;
1042                         break;
1043                 }
1044
1045                 hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^
1046                     (hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^
1047                     here->e_hash;
1048
1049                 here = EXT2_EXTATTR_NEXT(here);
1050         }
1051
1052         header->h_hash = hash;
1053 }
1054
1055 int
1056 ext2_extattr_block_set(struct inode *ip, int attrnamespace,
1057     const char *name, struct uio *uio)
1058 {
1059         struct m_ext2fs *fs;
1060         struct buf *bp;
1061         struct ext2fs_extattr_header *header;
1062         struct ext2fs_extattr_entry *entry;
1063         const char *attr_name;
1064         int name_len;
1065         size_t size;
1066         int error;
1067
1068         fs = ip->i_e2fs;
1069
1070         if (ip->i_facl) {
1071                 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1072                     fs->e2fs_bsize, NOCRED, &bp);
1073                 if (error) {
1074                         return (error);
1075                 }
1076
1077                 /* Check attributes magic value */
1078                 header = EXT2_HDR(bp);
1079                 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1080                         brelse(bp);
1081                         return (EINVAL);
1082                 }
1083
1084                 error = ext2_extattr_block_check(ip, bp);
1085                 if (error) {
1086                         brelse(bp);
1087                         return (error);
1088                 }
1089
1090                 if (header->h_refcount > 1) {
1091                         error = ext2_extattr_block_clone(ip, &bp);
1092                         if (error) {
1093                                 brelse(bp);
1094                                 return (error);
1095                         }
1096
1097                         header = EXT2_HDR(bp);
1098                 }
1099
1100                 /* Find if entry exist */
1101                 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
1102                     entry = EXT2_EXTATTR_NEXT(entry)) {
1103                         if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
1104                             attrnamespace)
1105                                 continue;
1106
1107                         name_len = entry->e_name_len;
1108                         attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
1109                             entry->e_name, &name_len);
1110                         if (!attr_name) {
1111                                 brelse(bp);
1112                                 return (ENOTSUP);
1113                         }
1114
1115                         if (strlen(name) == name_len &&
1116                             0 == strncmp(attr_name, name, name_len))
1117                                 break;
1118                 }
1119
1120                 if (!EXT2_IS_LAST_ENTRY(entry)) {
1121                         size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), entry,
1122                             sizeof(struct ext2fs_extattr_header),
1123                             entry->e_name_len, uio->uio_resid);
1124                         if (size > bp->b_bufsize) {
1125                                 brelse(bp);
1126                                 return (ENOSPC);
1127                         }
1128
1129                         ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1130                             entry, bp->b_data + bp->b_bufsize, uio);
1131                 } else {
1132                         size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), NULL,
1133                             sizeof(struct ext2fs_extattr_header),
1134                             strlen(name), uio->uio_resid);
1135                         if (size > bp->b_bufsize) {
1136                                 brelse(bp);
1137                                 return (ENOSPC);
1138                         }
1139
1140                         entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1141                             name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1142
1143                         /* Clean the same entry in the inode */
1144                         error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1145                         if (error && error != ENOATTR) {
1146                                 brelse(bp);
1147                                 return (error);
1148                         }
1149                 }
1150
1151                 ext2_extattr_rehash(header, entry);
1152                 ext2_extattr_blk_csum_set(ip, bp);
1153
1154                 return (bwrite(bp));
1155         }
1156
1157         size = ext2_extattr_get_size(NULL, NULL,
1158             sizeof(struct ext2fs_extattr_header),
1159             strlen(ext2_extattr_name_to_linux(attrnamespace, name)), uio->uio_resid);
1160         if (size > fs->e2fs_bsize)
1161                 return (ENOSPC);
1162
1163         /* Allocate block, fill EA header and insert entry */
1164         ip->i_facl = ext2_alloc_meta(ip);
1165         if (0 == ip->i_facl)
1166                 return (ENOSPC);
1167
1168         ip->i_blocks += btodb(fs->e2fs_bsize);
1169         ext2_update(ip->i_vnode, 1);
1170
1171         bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0);
1172         if (!bp) {
1173                 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
1174                 ip->i_blocks -= btodb(fs->e2fs_bsize);
1175                 ip->i_facl = 0;
1176                 ext2_update(ip->i_vnode, 1);
1177                 return (EIO);
1178         }
1179
1180         header = EXT2_HDR(bp);
1181         header->h_magic = EXTATTR_MAGIC;
1182         header->h_refcount = 1;
1183         header->h_blocks = 1;
1184         header->h_hash = 0;
1185         memset(header->h_reserved, 0, sizeof(header->h_reserved));
1186         memcpy(bp->b_data, header, sizeof(struct ext2fs_extattr_header));
1187         memset(EXT2_FIRST_ENTRY(bp), 0, sizeof(uint32_t));
1188
1189         entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1190             name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1191
1192         /* Clean the same entry in the inode */
1193         error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1194         if (error && error != ENOATTR) {
1195                 brelse(bp);
1196                 return (error);
1197         }
1198
1199         ext2_extattr_rehash(header, entry);
1200         ext2_extattr_blk_csum_set(ip, bp);
1201
1202         return (bwrite(bp));
1203 }
1204
1205 int ext2_extattr_free(struct inode *ip)
1206 {
1207         struct m_ext2fs *fs;
1208         struct buf *bp;
1209         struct ext2fs_extattr_header *header;
1210         int error;
1211
1212         fs = ip->i_e2fs;
1213
1214         if (!ip->i_facl)
1215                 return (0);
1216
1217         error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1218             fs->e2fs_bsize, NOCRED, &bp);
1219         if (error) {
1220                 return (error);
1221         }
1222
1223         /* Check attributes magic value */
1224         header = EXT2_HDR(bp);
1225         if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) {
1226                 brelse(bp);
1227                 return (EINVAL);
1228         }
1229
1230         error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp),
1231             bp->b_data + bp->b_bufsize);
1232         if (error) {
1233                 brelse(bp);
1234                 return (error);
1235         }
1236
1237         if (header->h_refcount > 1) {
1238                 header->h_refcount--;
1239                 bwrite(bp);
1240         } else {
1241                 ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize);
1242                 brelse(bp);
1243         }
1244
1245         ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize);
1246         ip->i_facl = 0;
1247         ext2_update(ip->i_vnode, 1);
1248
1249         return (0);
1250 }