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 50 static int 51 ext2_extattr_index_to_bsd(int index) 52 { 53 switch (index) { 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 61 return (EXTATTR_NAMESPACE_EMPTY); 62 } 63 64 static int 65 ext2_extattr_index_to_linux(int index) 66 { 67 switch (index) { 68 case EXTATTR_NAMESPACE_SYSTEM: 69 return (EXT4_XATTR_INDEX_SYSTEM); 70 71 case EXTATTR_NAMESPACE_USER: 72 return (EXT4_XATTR_INDEX_USER); 73 } 74 75 return (-1); 76 } 77 78 int 79 ext2_extattr_valid_attrname(int attrnamespace, const char *attrname) 80 { 81 if (attrnamespace == EXTATTR_NAMESPACE_EMPTY) 82 return (EINVAL); 83 84 if (strlen(attrname) == 0) 85 return (EINVAL); 86 87 if (strlen(attrname) + 1 > EXT2_EXTATTR_NAMELEN_MAX) 88 return (ENAMETOOLONG); 89 90 return (0); 91 } 92 93 static int 94 ext2_extattr_check(struct ext2fs_extattr_entry *entry, char *end) 95 { 96 struct ext2fs_extattr_entry *next; 97 98 while (!EXT2_IS_LAST_ENTRY(entry)) { 99 next = EXT2_EXTATTR_NEXT(entry); 100 if ((char *)next >= end) 101 return (EIO); 102 103 entry = next; 104 } 105 106 return (0); 107 } 108 109 int 110 ext2_extattr_inode_list(struct inode *ip, int attrnamespace, 111 struct uio *uio, size_t *size) 112 { 113 struct m_ext2fs *fs; 114 struct buf *bp; 115 struct ext2fs_extattr_dinode_header *header; 116 struct ext2fs_extattr_entry *entry; 117 int error; 118 119 fs = ip->i_e2fs; 120 121 if ((error = bread(ip->i_devvp, 122 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), 123 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { 124 brelse(bp); 125 return (error); 126 } 127 128 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *) 129 ((char *)bp->b_data + 130 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)); 131 132 /* Check attributes magic value */ 133 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode + 134 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize); 135 136 if (header->h_magic != EXTATTR_MAGIC) { 137 brelse(bp); 138 return (0); 139 } 140 141 error = ext2_extattr_check(EXT2_IFIRST(header), 142 (char *)dinode + EXT2_INODE_SIZE(fs)); 143 if (error) { 144 brelse(bp); 145 return (error); 146 } 147 148 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry); 149 entry = EXT2_EXTATTR_NEXT(entry)) { 150 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) 151 continue; 152 153 if (uio == NULL) 154 *size += entry->e_name_len + 1; 155 else { 156 char *attr_name = malloc(entry->e_name_len + 1, M_TEMP, M_WAITOK); 157 attr_name[0] = entry->e_name_len; 158 memcpy(&attr_name[1], entry->e_name, entry->e_name_len); 159 error = uiomove(attr_name, entry->e_name_len + 1, uio); 160 free(attr_name, M_TEMP); 161 if (error) 162 break; 163 } 164 } 165 166 brelse(bp); 167 168 return (error); 169 } 170 171 int 172 ext2_extattr_block_list(struct inode *ip, int attrnamespace, 173 struct uio *uio, size_t *size) 174 { 175 struct m_ext2fs *fs; 176 struct buf *bp; 177 struct ext2fs_extattr_header *header; 178 struct ext2fs_extattr_entry *entry; 179 int error; 180 181 fs = ip->i_e2fs; 182 183 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl), 184 fs->e2fs_bsize, NOCRED, &bp); 185 if (error) { 186 brelse(bp); 187 return (error); 188 } 189 190 /* Check attributes magic value */ 191 header = EXT2_HDR(bp); 192 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) { 193 brelse(bp); 194 return (EINVAL); 195 } 196 197 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize); 198 if (error) { 199 brelse(bp); 200 return (error); 201 } 202 203 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry); 204 entry = EXT2_EXTATTR_NEXT(entry)) { 205 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) 206 continue; 207 208 if (uio == NULL) 209 *size += entry->e_name_len + 1; 210 else { 211 char *attr_name = malloc(entry->e_name_len + 1, M_TEMP, M_WAITOK); 212 attr_name[0] = entry->e_name_len; 213 memcpy(&attr_name[1], entry->e_name, entry->e_name_len); 214 error = uiomove(attr_name, entry->e_name_len + 1, uio); 215 free(attr_name, M_TEMP); 216 if (error) 217 break; 218 } 219 } 220 221 brelse(bp); 222 223 return (error); 224 } 225 226 int 227 ext2_extattr_inode_get(struct inode *ip, int attrnamespace, 228 const char *name, struct uio *uio, size_t *size) 229 { 230 struct m_ext2fs *fs; 231 struct buf *bp; 232 struct ext2fs_extattr_dinode_header *header; 233 struct ext2fs_extattr_entry *entry; 234 int error; 235 236 fs = ip->i_e2fs; 237 238 if ((error = bread(ip->i_devvp, 239 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), 240 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { 241 brelse(bp); 242 return (error); 243 } 244 245 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *) 246 ((char *)bp->b_data + 247 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)); 248 249 /* Check attributes magic value */ 250 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode + 251 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize); 252 253 if (header->h_magic != EXTATTR_MAGIC) { 254 brelse(bp); 255 return (ENOATTR); 256 } 257 258 error = ext2_extattr_check(EXT2_IFIRST(header), 259 (char *)dinode + EXT2_INODE_SIZE(fs)); 260 if (error) { 261 brelse(bp); 262 return (error); 263 } 264 265 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry); 266 entry = EXT2_EXTATTR_NEXT(entry)) { 267 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) 268 continue; 269 270 if (strlen(name) == entry->e_name_len && 271 0 == strncmp(entry->e_name, name, entry->e_name_len)) { 272 if (uio == NULL) 273 *size += entry->e_value_size; 274 else { 275 error = uiomove(((char *)EXT2_IFIRST(header)) + 276 entry->e_value_offs, entry->e_value_size, uio); 277 } 278 279 brelse(bp); 280 return (error); 281 } 282 } 283 284 brelse(bp); 285 286 return (ENOATTR); 287 } 288 289 int 290 ext2_extattr_block_get(struct inode *ip, int attrnamespace, 291 const char *name, struct uio *uio, size_t *size) 292 { 293 struct m_ext2fs *fs; 294 struct buf *bp; 295 struct ext2fs_extattr_header *header; 296 struct ext2fs_extattr_entry *entry; 297 int error; 298 299 fs = ip->i_e2fs; 300 301 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl), 302 fs->e2fs_bsize, NOCRED, &bp); 303 if (error) { 304 brelse(bp); 305 return (error); 306 } 307 308 /* Check attributes magic value */ 309 header = EXT2_HDR(bp); 310 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) { 311 brelse(bp); 312 return (EINVAL); 313 } 314 315 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize); 316 if (error) { 317 brelse(bp); 318 return (error); 319 } 320 321 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry); 322 entry = EXT2_EXTATTR_NEXT(entry)) { 323 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) 324 continue; 325 326 if (strlen(name) == entry->e_name_len && 327 0 == strncmp(entry->e_name, name, entry->e_name_len)) { 328 if (uio == NULL) 329 *size += entry->e_value_size; 330 else { 331 error = uiomove(bp->b_data + entry->e_value_offs, 332 entry->e_value_size, uio); 333 } 334 335 brelse(bp); 336 return (error); 337 } 338 } 339 340 brelse(bp); 341 342 return (ENOATTR); 343 } 344 345 static uint16_t 346 ext2_extattr_delete_value(char *off, 347 struct ext2fs_extattr_entry *first_entry, 348 struct ext2fs_extattr_entry *entry, char *end) 349 { 350 uint16_t min_offs; 351 struct ext2fs_extattr_entry *next; 352 353 min_offs = end - off; 354 next = first_entry; 355 while (!EXT2_IS_LAST_ENTRY(next)) { 356 if (min_offs > next->e_value_offs && next->e_value_offs > 0) 357 min_offs = next->e_value_offs; 358 359 next = EXT2_EXTATTR_NEXT(next); 360 } 361 362 if (entry->e_value_size == 0) 363 return (min_offs); 364 365 memmove(off + min_offs + EXT2_EXTATTR_SIZE(entry->e_value_size), 366 off + min_offs, entry->e_value_offs - min_offs); 367 368 /* Adjust all value offsets */ 369 next = first_entry; 370 while (!EXT2_IS_LAST_ENTRY(next)) 371 { 372 if (next->e_value_offs > 0 && 373 next->e_value_offs < entry->e_value_offs) 374 next->e_value_offs += 375 EXT2_EXTATTR_SIZE(entry->e_value_size); 376 377 next = EXT2_EXTATTR_NEXT(next); 378 } 379 380 min_offs += EXT2_EXTATTR_SIZE(entry->e_value_size); 381 382 return (min_offs); 383 } 384 385 static void 386 ext2_extattr_delete_entry(char *off, 387 struct ext2fs_extattr_entry *first_entry, 388 struct ext2fs_extattr_entry *entry, char *end) 389 { 390 char *pad; 391 struct ext2fs_extattr_entry *next; 392 393 /* Clean entry value */ 394 ext2_extattr_delete_value(off, first_entry, entry, end); 395 396 /* Clean the entry */ 397 next = first_entry; 398 while (!EXT2_IS_LAST_ENTRY(next)) 399 next = EXT2_EXTATTR_NEXT(next); 400 401 pad = (char*)next + sizeof(uint32_t); 402 403 memmove(entry, (char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len), 404 pad - ((char *)entry + EXT2_EXTATTR_LEN(entry->e_name_len))); 405 } 406 407 int 408 ext2_extattr_inode_delete(struct inode *ip, int attrnamespace, const char *name) 409 { 410 struct m_ext2fs *fs; 411 struct buf *bp; 412 struct ext2fs_extattr_dinode_header *header; 413 struct ext2fs_extattr_entry *entry; 414 int error; 415 416 fs = ip->i_e2fs; 417 418 if ((error = bread(ip->i_devvp, 419 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), 420 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { 421 brelse(bp); 422 return (error); 423 } 424 425 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *) 426 ((char *)bp->b_data + 427 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)); 428 429 /* Check attributes magic value */ 430 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode + 431 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize); 432 433 if (header->h_magic != EXTATTR_MAGIC) { 434 brelse(bp); 435 return (ENOATTR); 436 } 437 438 error = ext2_extattr_check(EXT2_IFIRST(header), 439 (char *)dinode + EXT2_INODE_SIZE(fs)); 440 if (error) { 441 brelse(bp); 442 return (error); 443 } 444 445 /* If I am last entry, just make magic zero */ 446 entry = EXT2_IFIRST(header); 447 if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) { 448 if (strlen(name) == entry->e_name_len && 449 0 == strncmp(entry->e_name, name, entry->e_name_len)) { 450 memset(header, 0, sizeof(struct ext2fs_extattr_dinode_header)); 451 452 return (bwrite(bp)); 453 } 454 } 455 456 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry); 457 entry = EXT2_EXTATTR_NEXT(entry)) { 458 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) 459 continue; 460 461 if (strlen(name) == entry->e_name_len && 462 0 == strncmp(entry->e_name, name, entry->e_name_len)) { 463 ext2_extattr_delete_entry((char *)EXT2_IFIRST(header), 464 EXT2_IFIRST(header), entry, 465 (char *)dinode + EXT2_INODE_SIZE(fs)); 466 467 return (bwrite(bp)); 468 } 469 } 470 471 brelse(bp); 472 473 return (ENOATTR); 474 } 475 476 static int 477 ext2_extattr_block_clone(struct inode *ip, struct buf **bpp) 478 { 479 struct m_ext2fs *fs; 480 struct buf *sbp; 481 struct buf *cbp; 482 struct ext2fs_extattr_header *header; 483 uint64_t facl; 484 485 fs = ip->i_e2fs; 486 sbp = *bpp; 487 488 header = EXT2_HDR(sbp); 489 if (header->h_magic != EXTATTR_MAGIC || header->h_refcount == 1) 490 return (EINVAL); 491 492 facl = ext2_allocfacl(ip); 493 if (!facl) 494 return (ENOSPC); 495 496 cbp = getblk(ip->i_devvp, fsbtodb(fs, facl), fs->e2fs_bsize, 0, 0, 0); 497 if (!cbp) { 498 ext2_blkfree(ip, facl, fs->e2fs_bsize); 499 return (EIO); 500 } 501 502 memcpy(cbp->b_data, sbp->b_data, fs->e2fs_bsize); 503 header->h_refcount--; 504 bwrite(sbp); 505 506 ip->i_facl = facl; 507 ext2_update(ip->i_vnode, 1); 508 509 header = EXT2_HDR(cbp); 510 header->h_refcount = 1; 511 512 *bpp = cbp; 513 514 return (0); 515 } 516 517 int 518 ext2_extattr_block_delete(struct inode *ip, int attrnamespace, const char *name) 519 { 520 struct m_ext2fs *fs; 521 struct buf *bp; 522 struct ext2fs_extattr_header *header; 523 struct ext2fs_extattr_entry *entry; 524 int error; 525 526 fs = ip->i_e2fs; 527 528 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl), 529 fs->e2fs_bsize, NOCRED, &bp); 530 if (error) { 531 brelse(bp); 532 return (error); 533 } 534 535 /* Check attributes magic value */ 536 header = EXT2_HDR(bp); 537 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) { 538 brelse(bp); 539 return (EINVAL); 540 } 541 542 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize); 543 if (error) { 544 brelse(bp); 545 return (error); 546 } 547 548 if (header->h_refcount > 1) { 549 error = ext2_extattr_block_clone(ip, &bp); 550 if (error) { 551 brelse(bp); 552 return (error); 553 } 554 } 555 556 /* If I am last entry, clean me and free the block */ 557 entry = EXT2_FIRST_ENTRY(bp); 558 if (EXT2_IS_LAST_ENTRY(EXT2_EXTATTR_NEXT(entry))) { 559 if (strlen(name) == entry->e_name_len && 560 0 == strncmp(entry->e_name, name, entry->e_name_len)) { 561 ip->i_blocks -= btodb(fs->e2fs_bsize); 562 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize); 563 ip->i_facl = 0; 564 error = ext2_update(ip->i_vnode, 1); 565 566 brelse(bp); 567 return (error); 568 } 569 } 570 571 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry); 572 entry = EXT2_EXTATTR_NEXT(entry)) { 573 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) 574 continue; 575 576 if (strlen(name) == entry->e_name_len && 577 0 == strncmp(entry->e_name, name, entry->e_name_len)) { 578 ext2_extattr_delete_entry(bp->b_data, 579 EXT2_FIRST_ENTRY(bp), entry, 580 bp->b_data + bp->b_bufsize); 581 582 return (bwrite(bp)); 583 } 584 } 585 586 brelse(bp); 587 588 return (ENOATTR); 589 } 590 591 static struct ext2fs_extattr_entry * 592 allocate_entry(const char *name, int attrnamespace, uint16_t offs, 593 uint32_t size, uint32_t hash) 594 { 595 size_t name_len; 596 struct ext2fs_extattr_entry *entry; 597 598 name_len = strlen(name); 599 entry = malloc(sizeof(struct ext2fs_extattr_entry) + name_len, 600 M_TEMP, M_WAITOK); 601 602 entry->e_name_len = name_len; 603 entry->e_name_index = ext2_extattr_index_to_linux(attrnamespace); 604 entry->e_value_offs = offs; 605 entry->e_value_block = 0; 606 entry->e_value_size = size; 607 entry->e_hash = hash; 608 memcpy(entry->e_name, name, name_len); 609 610 return (entry); 611 } 612 613 static void 614 free_entry(struct ext2fs_extattr_entry *entry) 615 { 616 617 free(entry, M_TEMP); 618 } 619 620 static int 621 ext2_extattr_get_size(struct ext2fs_extattr_entry *first_entry, 622 struct ext2fs_extattr_entry *exist_entry, int header_size, 623 int name_len, int new_size) 624 { 625 struct ext2fs_extattr_entry *entry; 626 int size; 627 628 size = header_size; 629 size += sizeof(uint32_t); 630 631 if (NULL == exist_entry) { 632 size += EXT2_EXTATTR_LEN(name_len); 633 size += EXT2_EXTATTR_SIZE(new_size); 634 } 635 636 if (first_entry) 637 for (entry = first_entry; !EXT2_IS_LAST_ENTRY(entry); 638 entry = EXT2_EXTATTR_NEXT(entry)) { 639 if (entry != exist_entry) 640 size += EXT2_EXTATTR_LEN(entry->e_name_len) + 641 EXT2_EXTATTR_SIZE(entry->e_value_size); 642 else 643 size += EXT2_EXTATTR_LEN(entry->e_name_len) + 644 EXT2_EXTATTR_SIZE(new_size); 645 } 646 647 return (size); 648 } 649 650 static void 651 ext2_extattr_set_exist_entry(char *off, 652 struct ext2fs_extattr_entry *first_entry, 653 struct ext2fs_extattr_entry *entry, 654 char *end, struct uio *uio) 655 { 656 uint16_t min_offs; 657 658 min_offs = ext2_extattr_delete_value(off, first_entry, entry, end); 659 660 entry->e_value_size = uio->uio_resid; 661 if (entry->e_value_size) 662 entry->e_value_offs = min_offs - 663 EXT2_EXTATTR_SIZE(uio->uio_resid); 664 else 665 entry->e_value_offs = 0; 666 667 uiomove(off + entry->e_value_offs, entry->e_value_size, uio); 668 } 669 670 static struct ext2fs_extattr_entry * 671 ext2_extattr_set_new_entry(char *off, struct ext2fs_extattr_entry *first_entry, 672 const char *name, int attrnamespace, char *end, struct uio *uio) 673 { 674 int name_len; 675 char *pad; 676 uint16_t min_offs; 677 struct ext2fs_extattr_entry *entry; 678 struct ext2fs_extattr_entry *new_entry; 679 680 /* Find pad's */ 681 min_offs = end - off; 682 entry = first_entry; 683 while (!EXT2_IS_LAST_ENTRY(entry)) { 684 if (min_offs > entry->e_value_offs && entry->e_value_offs > 0) 685 min_offs = entry->e_value_offs; 686 687 entry = EXT2_EXTATTR_NEXT(entry); 688 } 689 690 pad = (char*)entry + sizeof(uint32_t); 691 692 /* Find entry insert position */ 693 name_len = strlen(name); 694 entry = first_entry; 695 while (!EXT2_IS_LAST_ENTRY(entry)) { 696 if (!(attrnamespace - entry->e_name_index) && 697 !(name_len - entry->e_name_len)) 698 if (memcmp(name, entry->e_name, name_len) <= 0) 699 break; 700 701 entry = EXT2_EXTATTR_NEXT(entry); 702 } 703 704 /* Create new entry and insert it */ 705 new_entry = allocate_entry(name, attrnamespace, 0, uio->uio_resid, 0); 706 memmove((char *)entry + EXT2_EXTATTR_LEN(new_entry->e_name_len), entry, 707 pad - (char*)entry); 708 709 memcpy(entry, new_entry, EXT2_EXTATTR_LEN(new_entry->e_name_len)); 710 free_entry(new_entry); 711 712 new_entry = entry; 713 if (new_entry->e_value_size > 0) 714 new_entry->e_value_offs = min_offs - 715 EXT2_EXTATTR_SIZE(new_entry->e_value_size); 716 717 uiomove(off + new_entry->e_value_offs, new_entry->e_value_size, uio); 718 719 return (new_entry); 720 } 721 722 int 723 ext2_extattr_inode_set(struct inode *ip, int attrnamespace, 724 const char *name, struct uio *uio) 725 { 726 struct m_ext2fs *fs; 727 struct buf *bp; 728 struct ext2fs_extattr_dinode_header *header; 729 struct ext2fs_extattr_entry *entry; 730 size_t size = 0, max_size; 731 int error; 732 733 fs = ip->i_e2fs; 734 735 if ((error = bread(ip->i_devvp, 736 fsbtodb(fs, ino_to_fsba(fs, ip->i_number)), 737 (int)fs->e2fs_bsize, NOCRED, &bp)) != 0) { 738 brelse(bp); 739 return (error); 740 } 741 742 struct ext2fs_dinode *dinode = (struct ext2fs_dinode *) 743 ((char *)bp->b_data + 744 EXT2_INODE_SIZE(fs) * ino_to_fsbo(fs, ip->i_number)); 745 746 /* Check attributes magic value */ 747 header = (struct ext2fs_extattr_dinode_header *)((char *)dinode + 748 E2FS_REV0_INODE_SIZE + dinode->e2di_extra_isize); 749 750 if (header->h_magic != EXTATTR_MAGIC) { 751 brelse(bp); 752 return (ENOSPC); 753 } 754 755 error = ext2_extattr_check(EXT2_IFIRST(header), (char *)dinode + 756 EXT2_INODE_SIZE(fs)); 757 if (error) { 758 brelse(bp); 759 return (error); 760 } 761 762 /* Find if entry exist */ 763 for (entry = EXT2_IFIRST(header); !EXT2_IS_LAST_ENTRY(entry); 764 entry = EXT2_EXTATTR_NEXT(entry)) { 765 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) 766 continue; 767 768 if (strlen(name) == entry->e_name_len && 769 0 == strncmp(entry->e_name, name, entry->e_name_len)) 770 break; 771 } 772 773 max_size = EXT2_INODE_SIZE(fs) - E2FS_REV0_INODE_SIZE - 774 dinode->e2di_extra_isize; 775 776 if (!EXT2_IS_LAST_ENTRY(entry)) { 777 size = ext2_extattr_get_size(EXT2_IFIRST(header), entry, 778 sizeof(struct ext2fs_extattr_dinode_header), 779 entry->e_name_len, uio->uio_resid); 780 if (size > max_size) { 781 brelse(bp); 782 return (ENOSPC); 783 } 784 785 ext2_extattr_set_exist_entry((char *)EXT2_IFIRST(header), 786 EXT2_IFIRST(header), entry, (char *)header + max_size, uio); 787 } else { 788 /* Ensure that the same entry does not exist in the block */ 789 if (ip->i_facl) { 790 error = ext2_extattr_block_get(ip, attrnamespace, name, 791 NULL, &size); 792 if (error != ENOATTR || size > 0) { 793 brelse(bp); 794 if (size > 0) 795 error = ENOSPC; 796 797 return (error); 798 } 799 } 800 801 size = ext2_extattr_get_size(EXT2_IFIRST(header), NULL, 802 sizeof(struct ext2fs_extattr_dinode_header), 803 entry->e_name_len, uio->uio_resid); 804 if (size > max_size) { 805 brelse(bp); 806 return (ENOSPC); 807 } 808 809 ext2_extattr_set_new_entry((char *)EXT2_IFIRST(header), 810 EXT2_IFIRST(header), name, attrnamespace, 811 (char *)header + max_size, uio); 812 } 813 814 return (bwrite(bp)); 815 } 816 817 static void 818 ext2_extattr_hash_entry(struct ext2fs_extattr_header *header, 819 struct ext2fs_extattr_entry *entry) 820 { 821 uint32_t hash = 0; 822 char *name = entry->e_name; 823 int n; 824 825 for (n=0; n < entry->e_name_len; n++) { 826 hash = (hash << EXT2_EXTATTR_NAME_HASH_SHIFT) ^ 827 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_NAME_HASH_SHIFT)) ^ 828 (*name++); 829 } 830 831 if (entry->e_value_block == 0 && entry->e_value_size != 0) { 832 uint32_t *value = (uint32_t *)((char *)header + entry->e_value_offs); 833 for (n = (entry->e_value_size + 834 EXT2_EXTATTR_ROUND) >> EXT2_EXTATTR_PAD_BITS; n; n--) { 835 hash = (hash << EXT2_EXTATTR_VALUE_HASH_SHIFT) ^ 836 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_VALUE_HASH_SHIFT)) ^ 837 (*value++); 838 } 839 } 840 841 entry->e_hash = hash; 842 } 843 844 static void 845 ext2_extattr_rehash(struct ext2fs_extattr_header *header, 846 struct ext2fs_extattr_entry *entry) 847 { 848 struct ext2fs_extattr_entry *here; 849 uint32_t hash = 0; 850 851 ext2_extattr_hash_entry(header, entry); 852 853 here = EXT2_ENTRY(header+1); 854 while (!EXT2_IS_LAST_ENTRY(here)) { 855 if (!here->e_hash) { 856 /* Block is not shared if an entry's hash value == 0 */ 857 hash = 0; 858 break; 859 } 860 861 hash = (hash << EXT2_EXTATTR_BLOCK_HASH_SHIFT) ^ 862 (hash >> (8*sizeof(hash) - EXT2_EXTATTR_BLOCK_HASH_SHIFT)) ^ 863 here->e_hash; 864 865 here = EXT2_EXTATTR_NEXT(here); 866 } 867 868 header->h_hash = hash; 869 } 870 871 int 872 ext2_extattr_block_set(struct inode *ip, int attrnamespace, 873 const char *name, struct uio *uio) 874 { 875 struct m_ext2fs *fs; 876 struct buf *bp; 877 struct ext2fs_extattr_header *header; 878 struct ext2fs_extattr_entry *entry; 879 size_t size; 880 int error; 881 882 fs = ip->i_e2fs; 883 884 if (ip->i_facl) { 885 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl), 886 fs->e2fs_bsize, NOCRED, &bp); 887 if (error) { 888 brelse(bp); 889 return (error); 890 } 891 892 /* Check attributes magic value */ 893 header = EXT2_HDR(bp); 894 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) { 895 brelse(bp); 896 return (EINVAL); 897 } 898 899 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), 900 bp->b_data + bp->b_bufsize); 901 if (error) { 902 brelse(bp); 903 return (error); 904 } 905 906 if (header->h_refcount > 1) { 907 error = ext2_extattr_block_clone(ip, &bp); 908 if (error) { 909 brelse(bp); 910 return (error); 911 } 912 913 header = EXT2_HDR(bp); 914 } 915 916 /* Find if entry exist */ 917 for (entry = EXT2_FIRST_ENTRY(bp); !EXT2_IS_LAST_ENTRY(entry); 918 entry = EXT2_EXTATTR_NEXT(entry)) { 919 if (ext2_extattr_index_to_bsd(entry->e_name_index) != attrnamespace) 920 continue; 921 922 if (strlen(name) == entry->e_name_len && 923 0 == strncmp(entry->e_name, name, entry->e_name_len)) 924 break; 925 } 926 927 if (!EXT2_IS_LAST_ENTRY(entry)) { 928 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), entry, 929 sizeof(struct ext2fs_extattr_header), 930 entry->e_name_len, uio->uio_resid); 931 if (size > bp->b_bufsize) { 932 brelse(bp); 933 return (ENOSPC); 934 } 935 936 ext2_extattr_set_exist_entry(bp->b_data, EXT2_FIRST_ENTRY(bp), 937 entry, bp->b_data + bp->b_bufsize, uio); 938 } else { 939 size = ext2_extattr_get_size(EXT2_FIRST_ENTRY(bp), NULL, 940 sizeof(struct ext2fs_extattr_header), 941 strlen(name), uio->uio_resid); 942 if (size > bp->b_bufsize) { 943 brelse(bp); 944 return (ENOSPC); 945 } 946 947 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp), 948 name, attrnamespace, bp->b_data + bp->b_bufsize, uio); 949 950 /* Clean the same entry in the inode */ 951 error = ext2_extattr_inode_delete(ip, attrnamespace, name); 952 if (error && error != ENOATTR) { 953 brelse(bp); 954 return (error); 955 } 956 } 957 958 ext2_extattr_rehash(header, entry); 959 960 return (bwrite(bp)); 961 } 962 963 size = ext2_extattr_get_size(NULL, NULL, 964 sizeof(struct ext2fs_extattr_header), strlen(name), uio->uio_resid); 965 if (size > fs->e2fs_bsize) 966 return (ENOSPC); 967 968 /* Allocate block, fill EA header and insert entry */ 969 ip->i_facl = ext2_allocfacl(ip); 970 if (0 == ip->i_facl) 971 return (ENOSPC); 972 973 ip->i_blocks += btodb(fs->e2fs_bsize); 974 ext2_update(ip->i_vnode, 1); 975 976 bp = getblk(ip->i_devvp, fsbtodb(fs, ip->i_facl), fs->e2fs_bsize, 0, 0, 0); 977 if (!bp) { 978 ext2_blkfree(ip, ip->i_facl, fs->e2fs_bsize); 979 ip->i_blocks -= btodb(fs->e2fs_bsize); 980 ip->i_facl = 0; 981 ext2_update(ip->i_vnode, 1); 982 return (EIO); 983 } 984 985 header = EXT2_HDR(bp); 986 header->h_magic = EXTATTR_MAGIC; 987 header->h_refcount = 1; 988 header->h_blocks = 1; 989 header->h_hash = 0; 990 memset(header->h_reserved, 0, sizeof(header->h_reserved)); 991 memcpy(bp->b_data, header, sizeof(struct ext2fs_extattr_header)); 992 memset(EXT2_FIRST_ENTRY(bp), 0, sizeof(uint32_t)); 993 994 entry = ext2_extattr_set_new_entry(bp->b_data, EXT2_FIRST_ENTRY(bp), 995 name, attrnamespace, bp->b_data + bp->b_bufsize, uio); 996 997 /* Clean the same entry in the inode */ 998 error = ext2_extattr_inode_delete(ip, attrnamespace, name); 999 if (error && error != ENOATTR) { 1000 brelse(bp); 1001 return (error); 1002 } 1003 1004 ext2_extattr_rehash(header, entry); 1005 1006 return (bwrite(bp)); 1007 } 1008 1009 int ext2_extattr_free(struct inode *ip) 1010 { 1011 struct m_ext2fs *fs; 1012 struct buf *bp; 1013 struct ext2fs_extattr_header *header; 1014 int error; 1015 1016 fs = ip->i_e2fs; 1017 1018 if (!ip->i_facl) 1019 return (0); 1020 1021 error = bread(ip->i_devvp, fsbtodb(fs, ip->i_facl), 1022 fs->e2fs_bsize, NOCRED, &bp); 1023 if (error) { 1024 brelse(bp); 1025 return (error); 1026 } 1027 1028 /* Check attributes magic value */ 1029 header = EXT2_HDR(bp); 1030 if (header->h_magic != EXTATTR_MAGIC || header->h_blocks != 1) { 1031 brelse(bp); 1032 return (EINVAL); 1033 } 1034 1035 error = ext2_extattr_check(EXT2_FIRST_ENTRY(bp), bp->b_data + bp->b_bufsize); 1036 if (error) { 1037 brelse(bp); 1038 return (error); 1039 } 1040 1041 if (header->h_refcount > 1) { 1042 header->h_refcount--; 1043 bwrite(bp); 1044 } else { 1045 ext2_blkfree(ip, ip->i_facl, ip->i_e2fs->e2fs_bsize); 1046 brelse(bp); 1047 } 1048 1049 ip->i_blocks -= btodb(ip->i_e2fs->e2fs_bsize); 1050 ip->i_facl = 0; 1051 ext2_update(ip->i_vnode, 1); 1052 1053 return (0); 1054 } 1055