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