1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* 27 * 28 * res.c 29 * 30 * Implements routines to create a cache resource file. 31 */ 32 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 #include <assert.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <errno.h> 39 #include <sys/stat.h> 40 #include <sys/param.h> 41 #include <sys/fcntl.h> 42 #include <sys/mman.h> 43 #include <sys/fs/cachefs_fs.h> 44 #include "res.h" 45 46 struct res { 47 int p_magic; /* magic number */ 48 int p_done:1; /* 1 if res_done called */ 49 int p_verbose:1; /* 1 means print errors */ 50 void *p_addrp; /* address of mapped file */ 51 long p_size; /* size of mapped file */ 52 struct cache_usage *p_cusagep; /* ptr to cache_usage */ 53 struct cachefs_rl_info *p_linfop; /* ptr to rl_info */ 54 rl_entry_t *p_rlentp; /* ptr to first rl_entry */ 55 int p_totentries; /* max number of rl entries */ 56 char p_name[MAXPATHLEN]; /* name of resource file */ 57 }; 58 59 #define MAGIC 8272 60 #define precond(A) assert(A) 61 #define MININDEX 1 62 63 #define RL_HEAD(resp, type) \ 64 (&(resp->p_linfop->rl_items[CACHEFS_RL_INDEX(type)])) 65 #define CVBLKS(nbytes) ((nbytes + MAXBSIZE - 1) / MAXBSIZE) 66 67 /* forward references */ 68 void res_rlent_moveto(res *resp, enum cachefs_rl_type type, uint_t entno, 69 long blks); 70 void res_reset(res *resp); 71 void res_clear(res *resp); 72 int res_listcheck(res *, enum cachefs_rl_type); 73 74 /* 75 * 76 * res_create 77 * 78 * Description: 79 * Creates a res object and returns a pointer to it. 80 * The specified file is used to store resource file data. 81 * Arguments: 82 * namep name of the resource file 83 * entries max number of rl entries in the file 84 * verbose 1 means print out error messages 85 * Returns: 86 * Returns a pointer to the object or NULL if an error occurred. 87 * Preconditions: 88 * precond(namep) 89 * precond(entries > 3) 90 * precond(strlen(namep) < MAXPATHLEN) 91 */ 92 93 res * 94 res_create(char *namep, int entries, int verbose) 95 { 96 int xx; 97 long size; 98 int fd; 99 char buf[1024]; 100 long cnt; 101 unsigned int amt; 102 ssize_t result; 103 void *addrp; 104 res *resp; 105 struct stat64 statinfo; 106 107 precond(namep); 108 precond(entries > MININDEX); 109 110 /* determine the size needed for the resource file */ 111 size = MAXBSIZE; 112 size += MAXBSIZE * (entries / CACHEFS_RLPMBS); 113 if ((entries % CACHEFS_RLPMBS) != 0) 114 size += MAXBSIZE; 115 116 /* if the file does not exist or is the wrong size/type */ 117 xx = lstat64(namep, &statinfo); 118 /* resource file will be <2GB */ 119 if ((xx == -1) || (statinfo.st_size != (offset_t)size) || 120 !(S_ISREG(statinfo.st_mode))) { 121 122 /* remove the resource file */ 123 xx = unlink(namep); 124 if ((xx == -1) && (errno != ENOENT)) 125 return (NULL); 126 127 /* create and open the file */ 128 fd = open(namep, O_CREAT | O_RDWR, 0600); 129 if (fd == -1) 130 return (NULL); 131 132 /* fill the file with zeros */ 133 memset(buf, 0, sizeof (buf)); 134 for (cnt = size; cnt > 0; cnt -= result) { 135 amt = sizeof (buf); 136 if (amt > cnt) 137 amt = cnt; 138 result = write(fd, buf, amt); 139 if (result == -1) { 140 close(fd); 141 return (NULL); 142 } 143 } 144 } 145 146 /* else open the file */ 147 else { 148 fd = open(namep, O_RDWR); 149 if (fd == -1) 150 return (NULL); 151 } 152 153 /* mmap the file into our address space */ 154 addrp = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 155 if (addrp == (void *)-1) { 156 close(fd); 157 return (NULL); 158 } 159 160 /* close the file descriptor, we do not need it anymore */ 161 close(fd); 162 163 /* allocate memory for the res object */ 164 resp = malloc(sizeof (res)); 165 if (resp == NULL) { 166 munmap(addrp, size); 167 return (NULL); 168 } 169 170 /* initialize the object */ 171 resp->p_magic = MAGIC; 172 resp->p_done = 0; 173 resp->p_addrp = addrp; 174 resp->p_size = size; 175 resp->p_verbose = verbose; 176 resp->p_cusagep = (struct cache_usage *)addrp; 177 resp->p_linfop = (struct cachefs_rl_info *)((char *)addrp + 178 sizeof (struct cache_usage)); 179 resp->p_rlentp = (rl_entry_t *)((char *)addrp + MAXBSIZE); 180 resp->p_totentries = entries; 181 strcpy(resp->p_name, namep); 182 183 /* reset the resource file in preperation to rebuild it */ 184 res_reset(resp); 185 186 /* return the object */ 187 return (resp); 188 } 189 190 /* 191 * 192 * res_destroy 193 * 194 * Description: 195 * Destroys the specifed res object. 196 * If res_done has not been called on the object or if res_done 197 * failed, then the resource file will be deleted. 198 * Arguments: 199 * resp object to destroy 200 * Returns: 201 * Preconditions: 202 * precond(resp is a valid res object) 203 */ 204 205 void 206 res_destroy(res *resp) 207 { 208 precond(resp); 209 precond(resp->p_magic == MAGIC); 210 211 /* unmap the file */ 212 munmap(resp->p_addrp, resp->p_size); 213 214 /* if res_done not performed */ 215 if (resp->p_done == 0) { 216 /* remove the resource file */ 217 unlink(resp->p_name); 218 } 219 220 /* destroy the object */ 221 resp->p_magic = -MAGIC; 222 free(resp); 223 } 224 225 rl_entry_t * 226 res_rlent_get(res *resp, uint_t entno) 227 { 228 rl_entry_t *rlentp, *window; 229 uint_t whichwindow, winoffset; 230 231 precond((entno >= MININDEX) && (entno < resp->p_totentries)); 232 233 whichwindow = entno / CACHEFS_RLPMBS; 234 winoffset = entno % CACHEFS_RLPMBS; 235 236 window = (rl_entry_t *) 237 (((caddr_t)resp->p_rlentp) + (MAXBSIZE * whichwindow)); 238 rlentp = window + winoffset; 239 240 return (rlentp); 241 } 242 243 /* 244 * 245 * res_reset 246 * 247 * Description: 248 * Resets the resource file in preparation to rebuild it. 249 * Arguments: 250 * resp res object 251 * Returns: 252 * Preconditions: 253 * precond(resp is a valid res object) 254 */ 255 256 void 257 res_reset(res *resp) 258 { 259 int index; 260 rl_entry_t *rlentp; 261 int ret; 262 cachefs_rl_listhead_t *lhp; 263 264 precond(resp); 265 precond(resp->p_magic == MAGIC); 266 267 resp->p_cusagep->cu_blksused = 0; 268 resp->p_cusagep->cu_filesused = 0; 269 resp->p_cusagep->cu_flags = CUSAGE_ACTIVE; /* dirty cache */ 270 271 /* clear out the non-pointer info */ 272 for (index = MININDEX; index < resp->p_totentries; index++) { 273 rlentp = res_rlent_get(resp, index); 274 275 rlentp->rl_attrc = 0; 276 rlentp->rl_fsck = 0; 277 rlentp->rl_local = 0; 278 rlentp->rl_fsid = 0LL; 279 rlentp->rl_fileno = 0; 280 } 281 282 /* verify validity of the various lists */ 283 ret = res_listcheck(resp, CACHEFS_RL_GC); 284 if (ret == 1) { 285 ret = res_listcheck(resp, CACHEFS_RL_ATTRFILE); 286 if (ret == 1) { 287 ret = res_listcheck(resp, CACHEFS_RL_MODIFIED); 288 if (ret == 1) { 289 ret = res_listcheck(resp, CACHEFS_RL_PACKED); 290 if (ret == 1) { 291 ret = res_listcheck(resp, 292 CACHEFS_RL_PACKED_PENDING); 293 } 294 } 295 } 296 } 297 298 /* if an error occurred on one of the lists */ 299 if (ret == 0) { 300 res_clear(resp); 301 return; 302 } 303 304 /* zero out total sizes, they get fixed up as we add items */ 305 RL_HEAD(resp, CACHEFS_RL_GC)->rli_blkcnt = 0; 306 RL_HEAD(resp, CACHEFS_RL_ATTRFILE)->rli_blkcnt = 0; 307 RL_HEAD(resp, CACHEFS_RL_MODIFIED)->rli_blkcnt = 0; 308 RL_HEAD(resp, CACHEFS_RL_PACKED)->rli_blkcnt = 0; 309 RL_HEAD(resp, CACHEFS_RL_PACKED_PENDING)->rli_blkcnt = 0; 310 311 /* null out the heads of the lists we do not want to preserve */ 312 lhp = RL_HEAD(resp, CACHEFS_RL_FREE); 313 memset(lhp, 0, sizeof (cachefs_rl_listhead_t)); 314 lhp = RL_HEAD(resp, CACHEFS_RL_NONE); 315 memset(lhp, 0, sizeof (cachefs_rl_listhead_t)); 316 lhp = RL_HEAD(resp, CACHEFS_RL_MF); 317 memset(lhp, 0, sizeof (cachefs_rl_listhead_t)); 318 lhp = RL_HEAD(resp, CACHEFS_RL_ACTIVE); 319 memset(lhp, 0, sizeof (cachefs_rl_listhead_t)); 320 } 321 322 /* 323 * 324 * res_listcheck 325 * 326 * Description: 327 * Checks the specified list. 328 * Arguments: 329 * resp res object 330 * type list to check 331 * Returns: 332 * Returns 1 if the list is ok, 0 if there is a problem. 333 * Preconditions: 334 * precond(resp is a valid res object) 335 */ 336 337 int 338 res_listcheck(res *resp, enum cachefs_rl_type type) 339 { 340 rl_entry_t *rlentp; 341 int previndex, index; 342 cachefs_rl_listhead_t *lhp; 343 int itemcnt = 0; 344 345 lhp = RL_HEAD(resp, type); 346 index = lhp->rli_front; 347 previndex = 0; 348 349 /* walk the list */ 350 while (index != 0) { 351 itemcnt++; 352 353 /* make sure offset is in bounds */ 354 if ((index < MININDEX) || (index >= resp->p_totentries)) { 355 if (resp->p_verbose) 356 pr_err("index out of bounds %d", index); 357 return (0); 358 } 359 360 /* get pointer to rl_entry object */ 361 rlentp = res_rlent_get(resp, index); 362 363 /* check forward pointer */ 364 if (rlentp->rl_fwd_idx != previndex) { 365 /* bad back pointer in rl list */ 366 if (resp->p_verbose) 367 pr_err(gettext("bad forward pointer %d %d"), 368 rlentp->rl_fwd_idx, previndex); 369 return (0); 370 } 371 372 /* check for cycle */ 373 if (rlentp->rl_fsck) { 374 /* cycle found in list */ 375 if (resp->p_verbose) 376 pr_err(gettext("cycle found in list %d"), 377 index); 378 return (0); 379 } 380 381 /* check type */ 382 if (rlentp->rl_current != type) { 383 /* entry doesn't belong here */ 384 if (resp->p_verbose) 385 pr_err(gettext( 386 "bad entry %d type %d in list type %d"), 387 index, (int)rlentp->rl_current, (int)type); 388 return (0); 389 } 390 391 /* indicate we have seen this pointer */ 392 rlentp->rl_fsck = 1; 393 previndex = index; 394 index = rlentp->rl_bkwd_idx; 395 } 396 397 /* verify number of items match */ 398 if (itemcnt != lhp->rli_itemcnt) { 399 if (resp->p_verbose) 400 pr_err(gettext("itemcnt wrong old %d new %d"), 401 lhp->rli_itemcnt, itemcnt); 402 return (0); 403 } 404 405 return (1); 406 } 407 408 /* 409 * 410 * res_clear 411 * 412 * Description: 413 * Deletes all information from the resource file. 414 * Arguments: 415 * resp res object 416 * Returns: 417 * Preconditions: 418 * precond(resp is a valid res object) 419 */ 420 421 void 422 res_clear(res *resp) 423 { 424 memset(resp->p_addrp, 0, resp->p_size); 425 } 426 427 428 /* 429 * 430 * res_done 431 * 432 * Description: 433 * Called when through performing res_addfile and res_addident 434 * to complete the resource file and flush the contents to 435 * the disk file. 436 * Arguments: 437 * resp res object 438 * Returns: 439 * Returns 0 for success, -1 for an error with errno set 440 * appropriatly. 441 * Preconditions: 442 * precond(resp is a valid res object) 443 */ 444 445 int 446 res_done(res *resp) 447 { 448 rl_entry_t *rlentp; 449 int index; 450 int xx; 451 int ret; 452 453 precond(resp); 454 precond(resp->p_magic == MAGIC); 455 456 /* scan the ident list to find the max allocated entry */ 457 resp->p_linfop->rl_entries = 0; 458 for (index = MININDEX; index < resp->p_totentries; index++) { 459 rlentp = res_rlent_get(resp, index); 460 if (rlentp->rl_fsid && (ino64_t)rlentp->rl_fsck) { 461 resp->p_linfop->rl_entries = index; 462 } 463 } 464 465 /* scan the ident list to fix up the free list */ 466 for (index = MININDEX; index < resp->p_totentries; index++) { 467 rlentp = res_rlent_get(resp, index); 468 469 /* if entry is not valid */ 470 if ((rlentp->rl_fsid == 0LL) || (rlentp->rl_fsck == 0)) { 471 /* if entry should appear on the free list */ 472 if (index <= resp->p_linfop->rl_entries) { 473 res_rlent_moveto(resp, 474 CACHEFS_RL_FREE, index, 0); 475 } 476 } 477 rlentp->rl_fsck = 0; /* prepare to re-check */ 478 } 479 480 /* 481 * Sanity check that we do not have an internal error in 482 * fsck. Eventually turn this stuff off. 483 */ 484 #if 1 485 ret = res_listcheck(resp, CACHEFS_RL_GC); 486 assert(ret == 1); 487 ret = res_listcheck(resp, CACHEFS_RL_ATTRFILE); 488 assert(ret == 1); 489 ret = res_listcheck(resp, CACHEFS_RL_MODIFIED); 490 assert(ret == 1); 491 ret = res_listcheck(resp, CACHEFS_RL_PACKED); 492 assert(ret == 1); 493 ret = res_listcheck(resp, CACHEFS_RL_PACKED_PENDING); 494 assert(ret == 1); 495 ret = res_listcheck(resp, CACHEFS_RL_FREE); 496 assert(ret == 1); 497 ret = res_listcheck(resp, CACHEFS_RL_NONE); 498 assert(ret == 1); 499 ret = res_listcheck(resp, CACHEFS_RL_MF); 500 assert(ret == 1); 501 ret = res_listcheck(resp, CACHEFS_RL_ACTIVE); 502 assert(ret == 1); 503 #endif 504 505 /* indicate the cache is clean */ 506 resp->p_cusagep->cu_flags &= ~CUSAGE_ACTIVE; 507 508 /* sync the data to the file */ 509 xx = msync(resp->p_addrp, resp->p_size, MS_SYNC); 510 if (xx == -1) 511 return (-1); 512 513 resp->p_done = 1; 514 515 /* return success */ 516 return (0); 517 } 518 519 /* 520 * 521 * res_addfile 522 * 523 * Description: 524 * Increments the number of files and blocks resource counts. 525 * Arguments: 526 * resp res object 527 * nbytes number of bytes in the file 528 * Returns: 529 * Preconditions: 530 * precond(resp is a valid res object) 531 */ 532 533 void 534 res_addfile(res *resp, long nbytes) 535 { 536 precond(resp); 537 precond(resp->p_magic == MAGIC); 538 539 /* update resource counts */ 540 resp->p_cusagep->cu_blksused += CVBLKS(nbytes); 541 resp->p_cusagep->cu_filesused += 1; 542 } 543 544 /* 545 * 546 * res_addident 547 * 548 * Description: 549 * Adds the specified file to the ident list. 550 * Updates resource counts. 551 * Arguments: 552 * resp res object 553 * index index into idents/pointers tables 554 * dp ident information 555 * nbytes number of bytes of item 556 * file number of files of item 557 * Returns: 558 * Returns 0 for success or -1 if the index is already in use 559 * or is not valid. 560 * Preconditions: 561 * precond(resp is a valid res object) 562 * precond(dp) 563 */ 564 565 int 566 res_addident(res *resp, int index, rl_entry_t *dp, long nbytes, int file) 567 { 568 rl_entry_t *rlentp; 569 570 precond(resp); 571 precond(resp->p_magic == MAGIC); 572 precond(dp); 573 574 /* check index for sanity */ 575 if ((index < MININDEX) || (index >= resp->p_totentries)) { 576 return (-1); 577 } 578 579 /* get pointer to ident */ 580 rlentp = res_rlent_get(resp, index); 581 582 /* if something already there */ 583 if (rlentp->rl_fsid != 0LL) { 584 return (-1); 585 } 586 587 /* if not on the right list, move it there */ 588 if ((rlentp->rl_fsck == 0) || (rlentp->rl_current != dp->rl_current)) 589 res_rlent_moveto(resp, dp->rl_current, index, CVBLKS(nbytes)); 590 591 rlentp->rl_fsck = 1; 592 rlentp->rl_local = dp->rl_local; 593 rlentp->rl_attrc = dp->rl_attrc; 594 rlentp->rl_fsid = dp->rl_fsid; 595 rlentp->rl_fileno = dp->rl_fileno; 596 597 /* update resource counts */ 598 resp->p_cusagep->cu_blksused += CVBLKS(nbytes); 599 resp->p_cusagep->cu_filesused += file; 600 601 /* return success */ 602 return (0); 603 } 604 605 /* 606 * 607 * res_clearident 608 * 609 * Description: 610 * Removes the specified file from the ident list. 611 * Updates resource counts. 612 * Arguments: 613 * resp res object 614 * index index into idents/pointers tables 615 * nbytes number of bytes in the file 616 * file number of files 617 * Returns: 618 * Returns 0. 619 * Preconditions: 620 * precond(resp is a valid res object) 621 * precond(index is valid) 622 * precond(ident is in use) 623 */ 624 625 void 626 res_clearident(res *resp, int index, int nbytes, int file) 627 { 628 rl_entry_t *rlentp; 629 630 precond(resp); 631 precond(resp->p_magic == MAGIC); 632 precond((index >= MININDEX) && (index < resp->p_totentries)); 633 634 /* get pointer to ident */ 635 rlentp = res_rlent_get(resp, index); 636 precond(rlentp->rl_fsid != 0LL); 637 638 /* clear the ident */ 639 rlentp->rl_fsid = 0LL; 640 rlentp->rl_fileno = 0; 641 rlentp->rl_attrc = 0; 642 rlentp->rl_local = 0; 643 644 /* update resource counts */ 645 resp->p_cusagep->cu_blksused -= CVBLKS(nbytes); 646 resp->p_cusagep->cu_filesused -= file; 647 assert(resp->p_cusagep->cu_blksused >= 0); 648 } 649 650 /* 651 * This function moves an RL entry from whereever it currently is to 652 * the requested list. 653 */ 654 655 void 656 res_rlent_moveto(res *resp, enum cachefs_rl_type type, uint_t entno, long blks) 657 { 658 rl_entry_t *rl_ent; 659 uint_t prev, next; 660 cachefs_rl_listhead_t *lhp; 661 enum cachefs_rl_type otype; 662 663 precond((CACHEFS_RL_START <= type) && (type <= CACHEFS_RL_END)); 664 precond((entno >= MININDEX) && (entno < resp->p_totentries)); 665 666 rl_ent = res_rlent_get(resp, entno); 667 if (rl_ent->rl_fsck) { 668 /* remove entry from its previous list */ 669 670 next = rl_ent->rl_fwd_idx; 671 prev = rl_ent->rl_bkwd_idx; 672 otype = rl_ent->rl_current; 673 assert((CACHEFS_RL_START <= otype) && 674 (otype <= CACHEFS_RL_END)); 675 676 lhp = RL_HEAD(resp, otype); 677 if ((lhp->rli_back == 0) || (lhp->rli_front == 0)) 678 assert((lhp->rli_back == 0) && (lhp->rli_front == 0)); 679 680 if (lhp->rli_back == entno) 681 lhp->rli_back = next; 682 if (lhp->rli_front == entno) 683 lhp->rli_front = prev; 684 if (prev != 0) { 685 rl_ent = res_rlent_get(resp, prev); 686 rl_ent->rl_fwd_idx = next; 687 } 688 if (next != 0) { 689 rl_ent = res_rlent_get(resp, next); 690 rl_ent->rl_bkwd_idx = prev; 691 } 692 lhp->rli_blkcnt -= blks; 693 lhp->rli_itemcnt--; 694 } 695 696 /* add entry to its new list */ 697 698 lhp = RL_HEAD(resp, type); 699 rl_ent = res_rlent_get(resp, entno); 700 rl_ent->rl_current = type; 701 rl_ent->rl_bkwd_idx = 0; 702 rl_ent->rl_fwd_idx = lhp->rli_back; 703 704 if (lhp->rli_back != 0) { 705 assert(lhp->rli_front != 0); 706 rl_ent = res_rlent_get(resp, lhp->rli_back); 707 rl_ent->rl_bkwd_idx = entno; 708 } else { 709 assert(lhp->rli_front == 0); 710 lhp->rli_front = entno; 711 } 712 lhp->rli_back = entno; 713 lhp->rli_blkcnt += blks; 714 lhp->rli_itemcnt++; 715 716 rl_ent = res_rlent_get(resp, entno); 717 rl_ent->rl_current = type; 718 rl_ent->rl_fsck = 1; 719 } 720