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 } 1264