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