1/* 2 * Create a squashfs filesystem. This is a highly compressed read only 3 * filesystem. 4 * 5 * Copyright (c) 2008, 2009, 2010 6 * Phillip Lougher <phillip@lougher.demon.co.uk> 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * as published by the Free Software Foundation; either version 2, 11 * or (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 21 * 22 * xattr.c 23 */ 24 25#define TRUE 1 26#define FALSE 0 27 28#include <unistd.h> 29#include <stdio.h> 30#include <sys/types.h> 31#include <sys/stat.h> 32#include <fcntl.h> 33#include <errno.h> 34#include <dirent.h> 35#include <string.h> 36#include <stdlib.h> 37#include <sys/xattr.h> 38 39#include "squashfs_fs.h" 40#include "squashfs_swap.h" 41#include "mksquashfs.h" 42#include "xattr.h" 43 44#ifdef SQUASHFS_TRACE 45#define TRACE(s, args...) \ 46 do { \ 47 printf("mksquashfs: "s, ## args); \ 48 } while(0) 49#else 50#define TRACE(s, args...) 51#endif 52 53#define ERROR(s, args...) \ 54 do { \ 55 fprintf(stderr, s, ## args); \ 56 } while(0) 57 58/* compressed xattr table */ 59static char *xattr_table = NULL; 60static unsigned int xattr_size = 0; 61 62/* cached uncompressed xattr data */ 63static char *data_cache = NULL; 64static int cache_bytes = 0, cache_size = 0; 65 66/* cached uncompressed xattr id table */ 67static struct squashfs_xattr_id *xattr_id_table = NULL; 68static int xattr_ids = 0; 69 70/* saved compressed xattr table */ 71unsigned int sxattr_bytes = 0, stotal_xattr_bytes = 0; 72 73/* saved cached uncompressed xattr data */ 74static char *sdata_cache = NULL; 75static int scache_bytes = 0; 76 77/* saved cached uncompressed xattr id table */ 78static int sxattr_ids = 0; 79 80/* xattr hash table for value duplicate detection */ 81static struct xattr_list *dupl[65536]; 82 83/* xattr hash table for id duplicate detection */ 84static struct dupl_id *dupl_id[65536]; 85 86/* file system globals from mksquashfs.c */ 87extern int no_xattrs, noX; 88extern long long bytes; 89extern int fd; 90extern unsigned int xattr_bytes, total_xattr_bytes; 91 92/* helper functions from mksquashfs.c */ 93extern unsigned short get_checksum(char *, int, unsigned short); 94extern void write_destination(int, long long, int, void *); 95extern long long generic_write_table(int, void *, int, void *, int); 96extern int mangle(char *, char *, int, int, int, int); 97 98/* helper functions and definitions from read_xattrs.c */ 99extern int read_xattrs_from_disk(int, struct squashfs_super_block *); 100extern struct xattr_list *get_xattr(int, unsigned int *); 101extern struct prefix prefix_table[]; 102 103 104static int get_prefix(struct xattr_list *xattr, char *name) 105{ 106 int i; 107 108 xattr->full_name = strdup(name); 109 110 for(i = 0; prefix_table[i].type != -1; i++) { 111 struct prefix *p = &prefix_table[i]; 112 if(strncmp(xattr->full_name, p->prefix, strlen(p->prefix)) == 0) 113 break; 114 } 115 116 if(prefix_table[i].type != -1) { 117 xattr->name = xattr->full_name + strlen(prefix_table[i].prefix); 118 xattr->size = strlen(xattr->name); 119 } 120 121 return prefix_table[i].type; 122} 123 124 125static int read_xattrs_from_system(char *filename, struct xattr_list **xattrs) 126{ 127 ssize_t size, vsize; 128 char *xattr_names, *p; 129 int i; 130 struct xattr_list *xattr_list = NULL; 131 132 while(1) { 133 size = llistxattr(filename, NULL, 0); 134 if(size <= 0) { 135 if(size < 0 && errno != ENOTSUP) 136 ERROR("llistxattr for %s failed in read_attrs," 137 " because %s\n", filename, 138 strerror(errno)); 139 return 0; 140 } 141 142 xattr_names = malloc(size); 143 if(xattr_names == NULL) { 144 ERROR("Out of memory in read_attrs\n"); 145 return 0; 146 } 147 148 size = llistxattr(filename, xattr_names, size); 149 if(size < 0) { 150 free(xattr_names); 151 if(errno == ERANGE) 152 /* xattr list grew? Try again */ 153 continue; 154 else { 155 ERROR("llistxattr for %s failed in read_attrs," 156 " because %s\n", filename, 157 strerror(errno)); 158 return 0; 159 } 160 } 161 162 break; 163 } 164 165 for(i = 0, p = xattr_names; p < xattr_names + size; i++) { 166 struct xattr_list *x = realloc(xattr_list, (i + 1) * 167 sizeof(struct xattr_list)); 168 if(x == NULL) { 169 ERROR("Out of memory in read_attrs\n"); 170 goto failed; 171 } else 172 xattr_list = x; 173 174 xattr_list[i].type = get_prefix(&xattr_list[i], p); 175 p += strlen(p) + 1; 176 if(xattr_list[i].type == -1) { 177 ERROR("Unrecognised xattr prefix %s\n", 178 xattr_list[i].full_name); 179 free(xattr_list[i].full_name); 180 i--; 181 continue; 182 } 183 184 while(1) { 185 vsize = lgetxattr(filename, xattr_list[i].full_name, 186 NULL, 0); 187 if(vsize < 0) { 188 ERROR("lgetxattr failed for %s in read_attrs," 189 " because %s\n", filename, 190 strerror(errno)); 191 free(xattr_list[i].full_name); 192 goto failed; 193 } 194 195 xattr_list[i].value = malloc(vsize); 196 if(xattr_list[i].value == NULL) { 197 ERROR("Out of memory in read_attrs\n"); 198 free(xattr_list[i].full_name); 199 goto failed; 200 } 201 202 vsize = lgetxattr(filename, xattr_list[i].full_name, 203 xattr_list[i].value, vsize); 204 if(vsize < 0) { 205 free(xattr_list[i].value); 206 if(errno == ERANGE) 207 /* xattr grew? Try again */ 208 continue; 209 else { 210 ERROR("lgetxattr failed for %s in " 211 "read_attrs, because %s\n", 212 filename, strerror(errno)); 213 free(xattr_list[i].full_name); 214 goto failed; 215 } 216 } 217 218 break; 219 } 220 xattr_list[i].vsize = vsize; 221 222 TRACE("read_xattrs_from_system: filename %s, xattr name %s," 223 " vsize %d\n", filename, xattr_list[i].full_name, 224 xattr_list[i].vsize); 225 } 226 free(xattr_names); 227 *xattrs = xattr_list; 228 return i; 229 230failed: 231 while(--i >= 0) { 232 free(xattr_list[i].full_name); 233 free(xattr_list[i].value); 234 } 235 free(xattr_list); 236 free(xattr_names); 237 return 0; 238} 239 240 241static int get_xattr_size(struct xattr_list *xattr) 242{ 243 int size = sizeof(struct squashfs_xattr_entry) + 244 sizeof(struct squashfs_xattr_val) + xattr->size; 245 246 if(xattr->type & XATTR_VALUE_OOL) 247 size += XATTR_VALUE_OOL_SIZE; 248 else 249 size += xattr->vsize; 250 251 return size; 252} 253 254 255static void *get_xattr_space(unsigned int req_size, long long *disk) 256{ 257 int data_space; 258 unsigned short c_byte; 259 260 /* 261 * Move and compress cached uncompressed data into xattr table. 262 */ 263 while(cache_bytes >= SQUASHFS_METADATA_SIZE) { 264 if((xattr_size - xattr_bytes) < 265 ((SQUASHFS_METADATA_SIZE << 1)) + 2) { 266 xattr_table = realloc(xattr_table, xattr_size + 267 (SQUASHFS_METADATA_SIZE << 1) + 2); 268 if(xattr_table == NULL) { 269 goto failed; 270 } 271 xattr_size += (SQUASHFS_METADATA_SIZE << 1) + 2; 272 } 273 274 c_byte = mangle(xattr_table + xattr_bytes + BLOCK_OFFSET, 275 data_cache, SQUASHFS_METADATA_SIZE, 276 SQUASHFS_METADATA_SIZE, noX, 0); 277 TRACE("Xattr block @ 0x%x, size %d\n", xattr_bytes, c_byte); 278 SQUASHFS_SWAP_SHORTS(&c_byte, xattr_table + xattr_bytes, 1); 279 xattr_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + BLOCK_OFFSET; 280 memmove(data_cache, data_cache + SQUASHFS_METADATA_SIZE, 281 cache_bytes - SQUASHFS_METADATA_SIZE); 282 cache_bytes -= SQUASHFS_METADATA_SIZE; 283 } 284 285 /* 286 * Ensure there's enough space in the uncompressed data cache 287 */ 288 data_space = cache_size - cache_bytes; 289 if(data_space < req_size) { 290 int realloc_size = req_size - data_space; 291 data_cache = realloc(data_cache, cache_size + 292 realloc_size); 293 if(data_cache == NULL) { 294 goto failed; 295 } 296 cache_size += realloc_size; 297 } 298 299 if(disk) 300 *disk = ((long long) xattr_bytes << 16) | cache_bytes; 301 cache_bytes += req_size; 302 return data_cache + cache_bytes - req_size; 303 304failed: 305 ERROR("Out of memory in inode table reallocation!\n"); 306 return NULL; 307} 308 309 310static struct dupl_id *check_id_dupl(struct xattr_list *xattr_list, int xattrs) 311{ 312 struct dupl_id *entry; 313 int i; 314 unsigned short checksum = 0; 315 316 /* compute checksum over all xattrs */ 317 for(i = 0; i < xattrs; i++) { 318 struct xattr_list *xattr = &xattr_list[i]; 319 320 checksum = get_checksum(xattr->full_name, 321 strlen(xattr->full_name), checksum); 322 checksum = get_checksum(xattr->value, 323 xattr->vsize, checksum); 324 } 325 326 for(entry = dupl_id[checksum]; entry; entry = entry->next) { 327 if (entry->xattrs != xattrs) 328 continue; 329 330 for(i = 0; i < xattrs; i++) { 331 struct xattr_list *xattr = &xattr_list[i]; 332 struct xattr_list *dup_xattr = &entry->xattr_list[i]; 333 334 if(strcmp(xattr->full_name, dup_xattr->full_name)) 335 break; 336 337 if(memcmp(xattr->value, dup_xattr->value, xattr->vsize)) 338 break; 339 } 340 341 if(i == xattrs) 342 break; 343 } 344 345 if(entry == NULL) { 346 /* no duplicate exists */ 347 entry = malloc(sizeof(*entry)); 348 if(entry == NULL) { 349 ERROR("malloc failed in check_ip_dupl\n"); 350 return NULL; 351 } 352 entry->xattrs = xattrs; 353 entry->xattr_list = xattr_list; 354 entry->xattr_id = SQUASHFS_INVALID_XATTR; 355 entry->next = dupl_id[checksum]; 356 dupl_id[checksum] = entry; 357 } 358 359 return entry; 360} 361 362 363static void check_value_dupl(struct xattr_list *xattr) 364{ 365 struct xattr_list *entry; 366 367 if(xattr->vsize < XATTR_VALUE_OOL_SIZE) 368 return; 369 370 /* Check if this is a duplicate of an existing value */ 371 xattr->vchecksum = get_checksum(xattr->value, xattr->vsize, 0); 372 for(entry = dupl[xattr->vchecksum]; entry; entry = entry->vnext) { 373 if(entry->vsize != xattr->vsize) 374 continue; 375 376 if(memcmp(entry->value, xattr->value, xattr->vsize) == 0) 377 break; 378 } 379 380 if(entry == NULL) { 381 /* 382 * No duplicate exists, add to hash table, and mark as 383 * requiring writing 384 */ 385 xattr->vnext = dupl[xattr->vchecksum]; 386 dupl[xattr->vchecksum] = xattr; 387 xattr->ool_value = SQUASHFS_INVALID_BLK; 388 } else { 389 /* 390 * Duplicate exists, make type XATTR_VALUE_OOL, and 391 * remember where the duplicate is 392 */ 393 xattr->type |= XATTR_VALUE_OOL; 394 xattr->ool_value = entry->ool_value; 395 /* on appending don't free duplicate values because the 396 * duplicate value already points to the non-duplicate value */ 397 if(xattr->value != entry->value) { 398 free(xattr->value); 399 xattr->value = entry->value; 400 } 401 } 402} 403 404 405static int get_xattr_id(int xattrs, struct xattr_list *xattr_list, 406 long long xattr_disk, struct dupl_id *xattr_dupl) 407{ 408 int i, size = 0; 409 struct squashfs_xattr_id *xattr_id; 410 411 xattr_id_table = realloc(xattr_id_table, (xattr_ids + 1) * 412 sizeof(struct squashfs_xattr_id)); 413 if(xattr_id_table == NULL) { 414 ERROR("Out of memory in xattr_id_table reallocation!\n"); 415 return -1; 416 } 417 418 /* get total uncompressed size of xattr data, needed for stat */ 419 for(i = 0; i < xattrs; i++) 420 size += strlen(xattr_list[i].full_name) + 1 + 421 xattr_list[i].vsize; 422 423 xattr_id = &xattr_id_table[xattr_ids]; 424 xattr_id->xattr = xattr_disk; 425 xattr_id->count = xattrs; 426 xattr_id->size = size; 427 428 /* 429 * keep track of total uncompressed xattr data, needed for mksquashfs 430 * file system summary 431 */ 432 total_xattr_bytes += size; 433 434 xattr_dupl->xattr_id = xattr_ids ++; 435 return xattr_dupl->xattr_id; 436} 437 438 439long long write_xattrs() 440{ 441 unsigned short c_byte; 442 int i, avail_bytes; 443 char *datap = data_cache; 444 long long start_bytes = bytes; 445 struct squashfs_xattr_table header; 446 447 if(xattr_ids == 0) 448 return SQUASHFS_INVALID_BLK; 449 450 /* 451 * Move and compress cached uncompressed data into xattr table. 452 */ 453 while(cache_bytes) { 454 if((xattr_size - xattr_bytes) < 455 ((SQUASHFS_METADATA_SIZE << 1)) + 2) { 456 xattr_table = realloc(xattr_table, xattr_size + 457 (SQUASHFS_METADATA_SIZE << 1) + 2); 458 if(xattr_table == NULL) { 459 goto failed; 460 } 461 xattr_size += (SQUASHFS_METADATA_SIZE << 1) + 2; 462 } 463 464 avail_bytes = cache_bytes > SQUASHFS_METADATA_SIZE ? 465 SQUASHFS_METADATA_SIZE : cache_bytes; 466 c_byte = mangle(xattr_table + xattr_bytes + BLOCK_OFFSET, datap, 467 avail_bytes, SQUASHFS_METADATA_SIZE, noX, 0); 468 TRACE("Xattr block @ 0x%x, size %d\n", xattr_bytes, c_byte); 469 SQUASHFS_SWAP_SHORTS(&c_byte, xattr_table + xattr_bytes, 1); 470 xattr_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + BLOCK_OFFSET; 471 datap += avail_bytes; 472 cache_bytes -= avail_bytes; 473 } 474 475 /* 476 * Write compressed xattr table to file system 477 */ 478 write_destination(fd, bytes, xattr_bytes, xattr_table); 479 bytes += xattr_bytes; 480 481 /* 482 * Swap if necessary the xattr id table 483 */ 484 for(i = 0; i < xattr_ids; i++) 485 SQUASHFS_INSWAP_XATTR_ID(&xattr_id_table[i]); 486 487 header.xattr_ids = xattr_ids; 488 header.xattr_table_start = start_bytes; 489 SQUASHFS_INSWAP_XATTR_TABLE(&header); 490 491 return generic_write_table(xattr_ids * sizeof(struct squashfs_xattr_id), 492 xattr_id_table, sizeof(header), &header, noX); 493 494failed: 495 ERROR("Out of memory in xattr_table reallocation!\n"); 496 return -1; 497} 498 499 500int generate_xattrs(int xattrs, struct xattr_list *xattr_list) 501{ 502 int total_size, i; 503 int xattr_value_max; 504 void *xp; 505 long long xattr_disk; 506 struct dupl_id *xattr_dupl; 507 508 /* 509 * check if the file xattrs are a complete duplicate of a pre-existing 510 * id 511 */ 512 xattr_dupl = check_id_dupl(xattr_list, xattrs); 513 if (xattr_dupl == NULL) 514 return SQUASHFS_INVALID_XATTR; 515 if(xattr_dupl->xattr_id != SQUASHFS_INVALID_XATTR) 516 return xattr_dupl->xattr_id; 517 518 /* 519 * Scan the xattr_list deciding which type to assign to each 520 * xattr. The choice is fairly straightforward, and depends on the 521 * size of each xattr name/value and the overall size of the 522 * resultant xattr list stored in the xattr metadata table. 523 * 524 * Choices are whether to store data inline or out of line. 525 * 526 * The overall goal is to optimise xattr scanning and lookup, and 527 * to enable the file system layout to scale from a couple of 528 * small xattr name/values to a large number of large xattr 529 * names/values without affecting performance. While hopefully 530 * enabling the common case of a couple of small xattr name/values 531 * to be stored efficiently 532 * 533 * Code repeatedly scans, doing the following 534 * move xattr data out of line if it exceeds 535 * xattr_value_max. Where xattr_value_max is 536 * initially XATTR_INLINE_MAX. If the final uncompressed 537 * xattr list is larger than XATTR_TARGET_MAX then more 538 * aggressively move xattr data out of line by repeatedly 539 * setting inline threshold to 1/2, then 1/4, 1/8 of 540 * XATTR_INLINE_MAX until target achieved or there's 541 * nothing left to move out of line 542 */ 543 xattr_value_max = XATTR_INLINE_MAX; 544 while(1) { 545 for(total_size = 0, i = 0; i < xattrs; i++) { 546 struct xattr_list *xattr = &xattr_list[i]; 547 xattr->type &= XATTR_PREFIX_MASK; /* all inline */ 548 if (xattr->vsize > xattr_value_max) 549 xattr->type |= XATTR_VALUE_OOL; 550 551 total_size += get_xattr_size(xattr); 552 } 553 554 /* 555 * If the total size of the uncompressed xattr list is <= 556 * XATTR_TARGET_MAX we're done 557 */ 558 if(total_size <= XATTR_TARGET_MAX) 559 break; 560 561 if(xattr_value_max == XATTR_VALUE_OOL_SIZE) 562 break; 563 564 /* 565 * Inline target not yet at minimum and so reduce it, and 566 * try again 567 */ 568 xattr_value_max /= 2; 569 if(xattr_value_max < XATTR_VALUE_OOL_SIZE) 570 xattr_value_max = XATTR_VALUE_OOL_SIZE; 571 } 572 573 /* 574 * Check xattr values for duplicates 575 */ 576 for(i = 0; i < xattrs; i++) { 577 check_value_dupl(&xattr_list[i]); 578 } 579 580 /* 581 * Add each out of line value to the file system xattr table 582 * if it doesn't already exist as a duplicate 583 */ 584 for(i = 0; i < xattrs; i++) { 585 struct xattr_list *xattr = &xattr_list[i]; 586 587 if((xattr->type & XATTR_VALUE_OOL) && 588 (xattr->ool_value == SQUASHFS_INVALID_BLK)) { 589 struct squashfs_xattr_val val; 590 int size = sizeof(val) + xattr->vsize; 591 xp = get_xattr_space(size, &xattr->ool_value); 592 val.vsize = xattr->vsize; 593 SQUASHFS_SWAP_XATTR_VAL(&val, xp); 594 memcpy(xp + sizeof(val), xattr->value, xattr->vsize); 595 } 596 } 597 598 /* 599 * Create xattr list and add to file system xattr table 600 */ 601 get_xattr_space(0, &xattr_disk); 602 for(i = 0; i < xattrs; i++) { 603 struct xattr_list *xattr = &xattr_list[i]; 604 struct squashfs_xattr_entry entry; 605 struct squashfs_xattr_val val; 606 607 xp = get_xattr_space(sizeof(entry) + xattr->size, NULL); 608 entry.type = xattr->type; 609 entry.size = xattr->size; 610 SQUASHFS_SWAP_XATTR_ENTRY(&entry, xp); 611 memcpy(xp + sizeof(entry), xattr->name, xattr->size); 612 613 if(xattr->type & XATTR_VALUE_OOL) { 614 int size = sizeof(val) + XATTR_VALUE_OOL_SIZE; 615 xp = get_xattr_space(size, NULL); 616 val.vsize = XATTR_VALUE_OOL_SIZE; 617 SQUASHFS_SWAP_XATTR_VAL(&val, xp); 618 SQUASHFS_SWAP_LONG_LONGS(&xattr->ool_value, xp + 619 sizeof(val), 1); 620 } else { 621 int size = sizeof(val) + xattr->vsize; 622 xp = get_xattr_space(size, &xattr->ool_value); 623 val.vsize = xattr->vsize; 624 SQUASHFS_SWAP_XATTR_VAL(&val, xp); 625 memcpy(xp + sizeof(val), xattr->value, xattr->vsize); 626 } 627 } 628 629 /* 630 * Add to xattr id lookup table 631 */ 632 return get_xattr_id(xattrs, xattr_list, xattr_disk, xattr_dupl); 633} 634 635 636int read_xattrs(void *d) 637{ 638 struct dir_ent *dir_ent = d; 639 struct inode_info *inode = dir_ent->inode; 640 char *filename = dir_ent->pathname; 641 struct xattr_list *xattr_list; 642 int xattrs; 643 644 if(no_xattrs || IS_PSEUDO(inode) || inode->root_entry) 645 return SQUASHFS_INVALID_XATTR; 646 647 xattrs = read_xattrs_from_system(filename, &xattr_list); 648 if(xattrs == 0) 649 return SQUASHFS_INVALID_XATTR; 650 651 return generate_xattrs(xattrs, xattr_list); 652} 653 654 655/* 656 * Add the existing xattr ids and xattr metadata in the file system being 657 * appended to, to the in-memory xattr cache. This allows duplicate checking to 658 * take place against the xattrs already in the file system being appended to, 659 * and ensures the pre-existing xattrs are written out along with any new xattrs 660 */ 661int get_xattrs(int fd, struct squashfs_super_block *sBlk) 662{ 663 int ids, res, i, id; 664 unsigned int count; 665 666 TRACE("get_xattrs\n"); 667 668 res = read_xattrs_from_disk(fd, sBlk); 669 if(res == SQUASHFS_INVALID_BLK || res == 0) 670 goto done; 671 ids = res; 672 673 /* 674 * for each xattr id read and construct its list of xattr 675 * name:value pairs, and add them to the in-memory xattr cache 676 */ 677 for(i = 0; i < ids; i++) { 678 struct xattr_list *xattr_list = get_xattr(i, &count); 679 if(xattr_list == NULL) { 680 res = 0; 681 goto done; 682 } 683 id = generate_xattrs(count, xattr_list); 684 685 /* 686 * Sanity check, the new xattr id should be the same as the 687 * xattr id in the original file system 688 */ 689 if(id != i) { 690 ERROR("BUG, different xattr_id in get_xattrs\n"); 691 res = 0; 692 goto done; 693 } 694 } 695 696done: 697 return res; 698} 699 700 701/* 702 * Save current state of xattrs, needed for restoring state in the event of an 703 * abort in appending 704 */ 705int save_xattrs() 706{ 707 /* save the current state of the compressed xattr data */ 708 sxattr_bytes = xattr_bytes; 709 stotal_xattr_bytes = total_xattr_bytes; 710 711 /* 712 * save the current state of the cached uncompressed xattr data. 713 * Note we have to save the contents of the data cache because future 714 * operations will delete the current contents 715 */ 716 sdata_cache = malloc(cache_bytes); 717 if(sdata_cache == NULL) 718 goto failed; 719 720 memcpy(sdata_cache, data_cache, cache_bytes); 721 scache_bytes = cache_bytes; 722 723 /* save the current state of the xattr id table */ 724 sxattr_ids = xattr_ids; 725 726 return TRUE; 727 728failed: 729 ERROR("Out of memory in save_xattrs\n"); 730 return FALSE; 731} 732 733 734/* 735 * Restore xattrs in the event of an abort in appending 736 */ 737void restore_xattrs() 738{ 739 /* restore the state of the compressed xattr data */ 740 xattr_bytes = sxattr_bytes; 741 total_xattr_bytes = stotal_xattr_bytes; 742 743 /* restore the state of the uncomoressed xattr data */ 744 memcpy(data_cache, sdata_cache, scache_bytes); 745 cache_bytes = scache_bytes; 746 747 /* restore the state of the xattr id table */ 748 xattr_ids = sxattr_ids; 749} 750