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