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