]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/ext2fs/ext2_extattr.c
MFV r347989:
[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                 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 }