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