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