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