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