]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/ext2fs/ext2_extattr.c
MFV 364468:
[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
585                 name_len = entry->e_name_len;
586                 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
587                     entry->e_name, &name_len);
588                 if (!attr_name) {
589                         brelse(bp);
590                         return (ENOTSUP);
591                 }
592
593                 if (strlen(name) == name_len &&
594                     0 == strncmp(attr_name, name, name_len)) {
595                         memset(header, 0, sizeof(struct ext2fs_extattr_dinode_header));
596
597                         return (bwrite(bp));
598                 }
599         }
600
601         for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
602             entry = EXT2_EXTATTR_NEXT(entry)) {
603                 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
604                     attrnamespace)
605                         continue;
606
607                 name_len = entry->e_name_len;
608                 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
609                     entry->e_name, &name_len);
610                 if (!attr_name) {
611                         brelse(bp);
612                         return (ENOTSUP);
613                 }
614
615                 if (strlen(name) == name_len &&
616                     0 == strncmp(attr_name, name, name_len)) {
617                         ext2_extattr_delete_entry((char *)EXT2_IFIRST(header),
618                             EXT2_IFIRST(header), entry,
619                             (char *)dinode + EXT2_INODE_SIZE(fs));
620
621                         return (bwrite(bp));
622                 }
623         }
624
625         brelse(bp);
626
627         return (ENOATTR);
628 }
629
630 static int
631 ext2_extattr_block_clone(struct inode *ip, struct buf **bpp)
632 {
633         struct m_ext2fs *fs;
634         struct buf *sbp;
635         struct buf *cbp;
636         struct ext2fs_extattr_header *header;
637         uint64_t facl;
638
639         fs = ip->i_e2fs;
640         sbp = *bpp;
641
642         header = EXT2_HDR(sbp);
643         if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
644             le32toh(header->h_refcount) == 1)
645                 return (EINVAL);
646
647         facl = ext2_alloc_meta(ip);
648         if (!facl)
649                 return (ENOSPC);
650
651         cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0);
652         if (!cbp) {
653                 ext2_blkfree(ip, facl, fs->e2fs_bsize);
654                 return (EIO);
655         }
656
657         memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize);
658         header->h_refcount = htole32(le32toh(header->h_refcount) - 1);
659         bwrite(sbp);
660
661         ip->i_facl = facl;
662         ext2_update(ip->i_vnode, 1);
663
664         header = EXT2_HDR(cbp);
665         header->h_refcount = htole32(1);
666
667         *bpp = cbp;
668
669         return (0);
670 }
671
672 int
673 ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name)
674 {
675         struct m_ext2fs *fs;
676         struct buf *bp;
677         struct ext2fs_extattr_header *header;
678         struct ext2fs_extattr_entry *entry;
679         const char *attr_name;
680         int name_len;
681         int error;
682
683         fs = ip->i_e2fs;
684
685         error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
686             fs->e2fs_bsize, NOCRED, &bp);
687         if (error) {
688                 return (error);
689         }
690
691         /* Check attributes magic value */
692         header = EXT2_HDR(bp);
693         if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
694             le32toh(header->h_blocks) != 1) {
695                 brelse(bp);
696                 return (EINVAL);
697         }
698
699         error = ext2_extattr_block_check(ip, bp);
700         if (error) {
701                 brelse(bp);
702                 return (error);
703         }
704
705         if (le32toh(header->h_refcount) > 1) {
706                 error = ext2_extattr_block_clone(ip, &bp);
707                 if (error) {
708                         brelse(bp);
709                         return (error);
710                 }
711         }
712
713         /* If I am last entry, clean me and free the block */
714         entry = EXT2_FIRST_ENTRY(bp);
715         if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry)) &&
716             (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) ==
717             attrnamespace)) {
718
719                 name_len = entry->e_name_len;
720                 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
721                     entry->e_name, &name_len);
722                 if (!attr_name) {
723                         brelse(bp);
724                         return (ENOTSUP);
725                 }
726
727                 if (strlen(name) == name_len &&
728                     0 == strncmp(attr_name, name, name_len)) {
729                         ip->i_blocks -= btodb(fs->e2fs_bsize);
730                         ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
731                         ip->i_facl = 0;
732                         error = ext2_update(ip->i_vnode, 1);
733
734                         brelse(bp);
735                         return (error);
736                 }
737         }
738
739         for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
740             entry = EXT2_EXTATTR_NEXT(entry)) {
741                 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
742                     attrnamespace)
743                         continue;
744
745                 name_len = entry->e_name_len;
746                 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
747                     entry->e_name, &name_len);
748                 if (!attr_name) {
749                         brelse(bp);
750                         return (ENOTSUP);
751                 }
752
753                 if (strlen(name) == name_len &&
754                     0 == strncmp(attr_name, name, name_len)) {
755                         ext2_extattr_delete_entry(bp->b_data,
756                             EXT2_FIRST_ENTRY(bp), entry,
757                             bp->b_data + bp->b_bufsize);
758
759                         return (bwrite(bp));
760                 }
761         }
762
763         brelse(bp);
764
765         return (ENOATTR);
766 }
767
768 static struct ext2fs_extattr_entry *
769 allocate_entry(const char *name, int attrnamespace, uint16_t offs,
770     uint32_t size, uint32_t hash)
771 {
772         const char *attr_name;
773         int name_len;
774         struct ext2fs_extattr_entry *entry;
775
776         attr_name = ext2_extattr_name_to_linux(attrnamespace, name);
777         name_len = strlen(attr_name);
778
779         entry = malloc(sizeof(struct ext2fs_extattr_entry) + name_len,
780             M_TEMP, M_WAITOK);
781
782         entry->e_name_len = name_len;
783         entry->e_name_index = ext2_extattr_attrnamespace_to_linux(attrnamespace, name);
784         entry->e_value_offs = htole16(offs);
785         entry->e_value_block = 0;
786         entry->e_value_size = htole32(size);
787         entry->e_hash = htole32(hash);
788         memcpy(entry->e_name, name, name_len);
789
790         return (entry);
791 }
792
793 static void
794 free_entry(struct ext2fs_extattr_entry *entry)
795 {
796
797         free(entry, M_TEMP);
798 }
799
800 static int
801 ext2_extattr_get_size(struct ext2fs_extattr_entry *first_entry,
802     struct ext2fs_extattr_entry *exist_entry, int header_size,
803     int name_len, int new_size)
804 {
805         struct ext2fs_extattr_entry *entry;
806         int size;
807
808         size = header_size;
809         size += sizeof(uint32_t);
810
811         if (NULL == exist_entry) {
812                 size += EXT2_EXTATTR_LEN(name_len);
813                 size += EXT2_EXTATTR_SIZE(new_size);
814         }
815
816         if (first_entry)
817                 for (entry = first_entry; !EXT2_IS_LAST_ENTRY(entry);
818                     entry = EXT2_EXTATTR_NEXT(entry)) {
819                         if (entry != exist_entry)
820                                 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
821                                     EXT2_EXTATTR_SIZE(le32toh(entry->e_value_size));
822                         else
823                                 size += EXT2_EXTATTR_LEN(entry->e_name_len) +
824                                     EXT2_EXTATTR_SIZE(new_size);
825                 }
826
827         return (size);
828 }
829
830 static void
831 ext2_extattr_set_exist_entry(char *off,
832     struct ext2fs_extattr_entry *first_entry,
833     struct ext2fs_extattr_entry *entry,
834     char *end, struct uio *uio)
835 {
836         uint16_t min_offs;
837
838         min_offs = ext2_extattr_delete_value(off, first_entry, entry, end);
839
840         entry->e_value_size = htole32(uio->uio_resid);
841         if (le32toh(entry->e_value_size))
842                 entry->e_value_offs = htole16(min_offs -
843                     EXT2_EXTATTR_SIZE(uio->uio_resid));
844         else
845                 entry->e_value_offs = 0;
846
847         uiomove(off + le16toh(entry->e_value_offs),
848             le32toh(entry->e_value_size), uio);
849 }
850
851 static struct ext2fs_extattr_entry *
852 ext2_extattr_set_new_entry(char *off, struct ext2fs_extattr_entry *first_entry,
853     const char *name, int attrnamespace, char *end, struct uio *uio)
854 {
855         int name_len;
856         char *pad;
857         uint16_t min_offs;
858         struct ext2fs_extattr_entry *entry;
859         struct ext2fs_extattr_entry *new_entry;
860
861         /* Find pad's */
862         min_offs = end - off;
863         entry = first_entry;
864         while (!EXT2_IS_LAST_ENTRY(entry)) {
865                 if (min_offs > le16toh(entry->e_value_offs) &&
866                     le16toh(entry->e_value_offs) > 0)
867                         min_offs = le16toh(entry->e_value_offs);
868
869                 entry = EXT2_EXTATTR_NEXT(entry);
870         }
871
872         pad = (char*)entry + sizeof(uint32_t);
873
874         /* Find entry insert position */
875         name_len = strlen(name);
876         entry = first_entry;
877         while (!EXT2_IS_LAST_ENTRY(entry)) {
878                 if (!(attrnamespace - entry->e_name_index) &&
879                     !(name_len - entry->e_name_len))
880                         if (memcmp(name, entry->e_name, name_len) <= 0)
881                                 break;
882
883                 entry = EXT2_EXTATTR_NEXT(entry);
884         }
885
886         /* Create new entry and insert it */
887         new_entry = allocate_entry(name, attrnamespace, 0, uio->uio_resid, 0);
888         memmove((char *)entry + EXT2_EXTATTR_LEN(new_entry->e_name_len), entry,
889             pad - (char*)entry);
890
891         memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len));
892         free_entry(new_entry);
893
894         new_entry = entry;
895         if (le32toh(new_entry->e_value_size) > 0)
896                 new_entry->e_value_offs = htole16(min_offs -
897                     EXT2_EXTATTR_SIZE(le32toh(new_entry->e_value_size)));
898
899         uiomove(off + le16toh(new_entry->e_value_offs),
900             le32toh(new_entry->e_value_size), uio);
901
902         return (new_entry);
903 }
904
905 int
906 ext2_extattr_inode_set(struct inode *ip, int attrnamespace,
907     const char *name, struct uio *uio)
908 {
909         struct m_ext2fs *fs;
910         struct buf *bp;
911         struct ext2fs_extattr_dinode_header *header;
912         struct ext2fs_extattr_entry *entry;
913         const char *attr_name;
914         int name_len;
915         size_t size = 0, max_size;
916         int error;
917
918         fs = ip->i_e2fs;
919
920         if ((error = bread(ip->i_devvp,
921             fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
922             (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) {
923                 brelse(bp);
924                 return (error);
925         }
926
927         struct ext2fs_dinode *dinode = (struct ext2fs_dinode *)
928             ((char *)bp->b_data +
929             EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number));
930
931         /* Check attributes magic value */
932         header = (struct ext2fs_extattr_dinode_header *)((char *)dinode +
933             E2FS_REV0_INODE_SIZE + le16toh(dinode->e2di_extra_isize));
934
935         if (le32toh(header->h_magic) != EXTATTR_MAGIC) {
936                 brelse(bp);
937                 return (ENOSPC);
938         }
939
940         error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode +
941             EXT2_INODE_SIZE(fs));
942         if (error) {
943                 brelse(bp);
944                 return (error);
945         }
946
947         /* Find if entry exist */
948         for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry);
949             entry = EXT2_EXTATTR_NEXT(entry)) {
950                 if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
951                     attrnamespace)
952                         continue;
953
954                 name_len = entry->e_name_len;
955                 attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
956                     entry->e_name, &name_len);
957                 if (!attr_name) {
958                         brelse(bp);
959                         return (ENOTSUP);
960                 }
961
962                 if (strlen(name) == name_len &&
963                     0 == strncmp(attr_name, name, name_len))
964                         break;
965         }
966
967         max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE -
968             le16toh(dinode->e2di_extra_isize);
969
970         if (!EXT2_IS_LAST_ENTRY(entry)) {
971                 size = ext2_extattr_get_size(EXT2_IFIRST(header), entry,
972                     sizeof(struct ext2fs_extattr_dinode_header),
973                     entry->e_name_len, uio->uio_resid);
974                 if (size > max_size) {
975                         brelse(bp);
976                         return (ENOSPC);
977                 }
978
979                 ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(header),
980                     EXT2_IFIRST(header), entry, (char *)header + max_size, uio);
981         } else {
982                 /* Ensure that the same entry does not exist in the block */
983                 if (ip->i_facl) {
984                         error = ext2_extattr_block_get(ip, attrnamespace, name,
985                             NULL, &size);
986                         if (error != ENOATTR || size > 0) {
987                                 brelse(bp);
988                                 if (size > 0)
989                                         error = ENOSPC;
990
991                                 return (error);
992                         }
993                 }
994
995                 size = ext2_extattr_get_size(EXT2_IFIRST(header), NULL,
996                     sizeof(struct ext2fs_extattr_dinode_header),
997                     entry->e_name_len, uio->uio_resid);
998                 if (size > max_size) {
999                         brelse(bp);
1000                         return (ENOSPC);
1001                 }
1002
1003                 ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header),
1004                     EXT2_IFIRST(header), name, attrnamespace,
1005                     (char *)header + max_size, uio);
1006         }
1007
1008         return (bwrite(bp));
1009 }
1010
1011 static void
1012 ext2_extattr_hash_entry(struct ext2fs_extattr_header *header,
1013     struct ext2fs_extattr_entry *entry)
1014 {
1015         uint32_t hash = 0;
1016         char *name = entry->e_name;
1017         int n;
1018
1019         for (n=0; n < entry->e_name_len; n++) {
1020                 hash = (hash << EXT2_EXTATTR_NAME_HASH_SHIFT) ^
1021                     (hash >> (8*sizeof(hash) - EXT2_EXTATTR_NAME_HASH_SHIFT)) ^
1022                     (*name++);
1023         }
1024
1025         if (entry->e_value_block == 0 && entry->e_value_size != 0) {
1026                 uint32_t *value = (uint32_t *)((char *)header +
1027                     le16toh(entry->e_value_offs));
1028                 for (n = (le32toh(entry->e_value_size) +
1029                     EXT2_EXTATTR_ROUND) >> EXT2_EXTATTR_PAD_BITS; n; n--) {
1030                         hash = (hash << EXT2_EXTATTR_VALUE_HASH_SHIFT) ^
1031                             (hash >> (8*sizeof(hash) - EXT2_EXTATTR_VALUE_HASH_SHIFT)) ^
1032                             le32toh(*value++);
1033                 }
1034         }
1035
1036         entry->e_hash = htole32(hash);
1037 }
1038
1039 static void
1040 ext2_extattr_rehash(struct ext2fs_extattr_header *header,
1041     struct ext2fs_extattr_entry *entry)
1042 {
1043         struct ext2fs_extattr_entry *here;
1044         uint32_t hash = 0;
1045
1046         ext2_extattr_hash_entry(header, entry);
1047
1048         here = EXT2_ENTRY(header+1);
1049         while (!EXT2_IS_LAST_ENTRY(here)) {
1050                 if (here->e_hash == 0) {
1051                         /* Block is not shared if an entry's hash value == 0 */
1052                         hash = 0;
1053                         break;
1054                 }
1055
1056                 hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^
1057                     (hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^
1058                     le32toh(here->e_hash);
1059
1060                 here = EXT2_EXTATTR_NEXT(here);
1061         }
1062
1063         header->h_hash = htole32(hash);
1064 }
1065
1066 int
1067 ext2_extattr_block_set(struct inode *ip, int attrnamespace,
1068     const char *name, struct uio *uio)
1069 {
1070         struct m_ext2fs *fs;
1071         struct buf *bp;
1072         struct ext2fs_extattr_header *header;
1073         struct ext2fs_extattr_entry *entry;
1074         const char *attr_name;
1075         int name_len;
1076         size_t size;
1077         int error;
1078
1079         fs = ip->i_e2fs;
1080
1081         if (ip->i_facl) {
1082                 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1083                     fs->e2fs_bsize, NOCRED, &bp);
1084                 if (error) {
1085                         return (error);
1086                 }
1087
1088                 /* Check attributes magic value */
1089                 header = EXT2_HDR(bp);
1090                 if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
1091                     le32toh(header->h_blocks) != 1) {
1092                         brelse(bp);
1093                         return (EINVAL);
1094                 }
1095
1096                 error = ext2_extattr_block_check(ip, bp);
1097                 if (error) {
1098                         brelse(bp);
1099                         return (error);
1100                 }
1101
1102                 if (le32toh(header->h_refcount) > 1) {
1103                         error = ext2_extattr_block_clone(ip, &bp);
1104                         if (error) {
1105                                 brelse(bp);
1106                                 return (error);
1107                         }
1108
1109                         header = EXT2_HDR(bp);
1110                 }
1111
1112                 /* Find if entry exist */
1113                 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry);
1114                     entry = EXT2_EXTATTR_NEXT(entry)) {
1115                         if (ext2_extattr_attrnamespace_to_bsd(entry->e_name_index) !=
1116                             attrnamespace)
1117                                 continue;
1118
1119                         name_len = entry->e_name_len;
1120                         attr_name = ext2_extattr_name_to_bsd(entry->e_name_index,
1121                             entry->e_name, &name_len);
1122                         if (!attr_name) {
1123                                 brelse(bp);
1124                                 return (ENOTSUP);
1125                         }
1126
1127                         if (strlen(name) == name_len &&
1128                             0 == strncmp(attr_name, name, name_len))
1129                                 break;
1130                 }
1131
1132                 if (!EXT2_IS_LAST_ENTRY(entry)) {
1133                         size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), entry,
1134                             sizeof(struct ext2fs_extattr_header),
1135                             entry->e_name_len, uio->uio_resid);
1136                         if (size > bp->b_bufsize) {
1137                                 brelse(bp);
1138                                 return (ENOSPC);
1139                         }
1140
1141                         ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1142                             entry, bp->b_data + bp->b_bufsize, uio);
1143                 } else {
1144                         size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), NULL,
1145                             sizeof(struct ext2fs_extattr_header),
1146                             strlen(name), uio->uio_resid);
1147                         if (size > bp->b_bufsize) {
1148                                 brelse(bp);
1149                                 return (ENOSPC);
1150                         }
1151
1152                         entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1153                             name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1154
1155                         /* Clean the same entry in the inode */
1156                         error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1157                         if (error && error != ENOATTR) {
1158                                 brelse(bp);
1159                                 return (error);
1160                         }
1161                 }
1162
1163                 ext2_extattr_rehash(header, entry);
1164                 ext2_extattr_blk_csum_set(ip, bp);
1165
1166                 return (bwrite(bp));
1167         }
1168
1169         size = ext2_extattr_get_size(NULL, NULL,
1170             sizeof(struct ext2fs_extattr_header),
1171             strlen(ext2_extattr_name_to_linux(attrnamespace, name)), uio->uio_resid);
1172         if (size > fs->e2fs_bsize)
1173                 return (ENOSPC);
1174
1175         /* Allocate block, fill EA header and insert entry */
1176         ip->i_facl = ext2_alloc_meta(ip);
1177         if (0 == ip->i_facl)
1178                 return (ENOSPC);
1179
1180         ip->i_blocks += btodb(fs->e2fs_bsize);
1181         ext2_update(ip->i_vnode, 1);
1182
1183         bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0);
1184         if (!bp) {
1185                 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize);
1186                 ip->i_blocks -= btodb(fs->e2fs_bsize);
1187                 ip->i_facl = 0;
1188                 ext2_update(ip->i_vnode, 1);
1189                 return (EIO);
1190         }
1191
1192         header = EXT2_HDR(bp);
1193         header->h_magic = htole32(EXTATTR_MAGIC);
1194         header->h_refcount = htole32(1);
1195         header->h_blocks = htole32(1);
1196         header->h_hash = 0;
1197         memset(header->h_reserved, 0, sizeof(header->h_reserved));
1198         memcpy(bp->b_data, header, sizeof(struct ext2fs_extattr_header));
1199         memset(EXT2_FIRST_ENTRY(bp), 0, sizeof(uint32_t));
1200
1201         entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp),
1202             name, attrnamespace, bp->b_data + bp->b_bufsize, uio);
1203
1204         /* Clean the same entry in the inode */
1205         error = ext2_extattr_inode_delete(ip, attrnamespace, name);
1206         if (error && error != ENOATTR) {
1207                 brelse(bp);
1208                 return (error);
1209         }
1210
1211         ext2_extattr_rehash(header, entry);
1212         ext2_extattr_blk_csum_set(ip, bp);
1213
1214         return (bwrite(bp));
1215 }
1216
1217 int ext2_extattr_free(struct inode *ip)
1218 {
1219         struct m_ext2fs *fs;
1220         struct buf *bp;
1221         struct ext2fs_extattr_header *header;
1222         int error;
1223
1224         fs = ip->i_e2fs;
1225
1226         if (!ip->i_facl)
1227                 return (0);
1228
1229         error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl),
1230             fs->e2fs_bsize, NOCRED, &bp);
1231         if (error) {
1232                 return (error);
1233         }
1234
1235         /* Check attributes magic value */
1236         header = EXT2_HDR(bp);
1237         if (le32toh(header->h_magic) != EXTATTR_MAGIC ||
1238             le32toh(header->h_blocks) != 1) {
1239                 brelse(bp);
1240                 return (EINVAL);
1241         }
1242
1243         error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp),
1244             bp->b_data + bp->b_bufsize);
1245         if (error) {
1246                 brelse(bp);
1247                 return (error);
1248         }
1249
1250         if (le32toh(header->h_refcount) > 1) {
1251                 header->h_refcount = htole32(le32toh(header->h_refcount) - 1);
1252                 bwrite(bp);
1253         } else {
1254                 ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize);
1255                 brelse(bp);
1256         }
1257
1258         ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize);
1259         ip->i_facl = 0;
1260         ext2_update(ip->i_vnode, 1);
1261
1262         return (0);
1263 }