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 *
res_create(char * namep,int entries,int verbose)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
res_destroy(res * resp)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 *
res_rlent_get(res * resp,uint_t entno)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
res_reset(res * resp)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
res_listcheck(res * resp,enum cachefs_rl_type type)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
res_clear(res * resp)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
res_done(res * resp)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
res_addfile(res * resp,long nbytes)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
res_addident(res * resp,int index,rl_entry_t * dp,long nbytes,int file)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
res_clearident(res * resp,int index,int nbytes,int file)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
res_rlent_moveto(res * resp,enum cachefs_rl_type type,uint_t entno,long blks)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