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