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