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