6253 F_GETLK doesn't always return lock owner
[illumos-gate.git] / usr / src / cmd / fs.d / cachefs / fsck / res.c
blob6331f213abdf4ac299311a94befa1ebf1b170e6a
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * res.c
30 * Implements routines to create a cache resource file.
33 #pragma ident "%Z%%M% %I% %E% SMI"
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"
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 */
59 #define MAGIC 8272
60 #define precond(A) assert(A)
61 #define MININDEX 1
63 #define RL_HEAD(resp, type) \
64 (&(resp->p_linfop->rl_items[CACHEFS_RL_INDEX(type)]))
65 #define CVBLKS(nbytes) ((nbytes + MAXBSIZE - 1) / MAXBSIZE)
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);
76 * res_create
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)
93 res *
94 res_create(char *namep, int entries, int verbose)
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;
107 precond(namep);
108 precond(entries > MININDEX);
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;
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))) {
122 /* remove the resource file */
123 xx = unlink(namep);
124 if ((xx == -1) && (errno != ENOENT))
125 return (NULL);
127 /* create and open the file */
128 fd = open(namep, O_CREAT | O_RDWR, 0600);
129 if (fd == -1)
130 return (NULL);
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);
146 /* else open the file */
147 else {
148 fd = open(namep, O_RDWR);
149 if (fd == -1)
150 return (NULL);
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);
160 /* close the file descriptor, we do not need it anymore */
161 close(fd);
163 /* allocate memory for the res object */
164 resp = malloc(sizeof (res));
165 if (resp == NULL) {
166 munmap(addrp, size);
167 return (NULL);
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);
183 /* reset the resource file in preperation to rebuild it */
184 res_reset(resp);
186 /* return the object */
187 return (resp);
192 * res_destroy
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)
205 void
206 res_destroy(res *resp)
208 precond(resp);
209 precond(resp->p_magic == MAGIC);
211 /* unmap the file */
212 munmap(resp->p_addrp, resp->p_size);
214 /* if res_done not performed */
215 if (resp->p_done == 0) {
216 /* remove the resource file */
217 unlink(resp->p_name);
220 /* destroy the object */
221 resp->p_magic = -MAGIC;
222 free(resp);
225 rl_entry_t *
226 res_rlent_get(res *resp, uint_t entno)
228 rl_entry_t *rlentp, *window;
229 uint_t whichwindow, winoffset;
231 precond((entno >= MININDEX) && (entno < resp->p_totentries));
233 whichwindow = entno / CACHEFS_RLPMBS;
234 winoffset = entno % CACHEFS_RLPMBS;
236 window = (rl_entry_t *)
237 (((caddr_t)resp->p_rlentp) + (MAXBSIZE * whichwindow));
238 rlentp = window + winoffset;
240 return (rlentp);
245 * res_reset
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)
256 void
257 res_reset(res *resp)
259 int index;
260 rl_entry_t *rlentp;
261 int ret;
262 cachefs_rl_listhead_t *lhp;
264 precond(resp);
265 precond(resp->p_magic == MAGIC);
267 resp->p_cusagep->cu_blksused = 0;
268 resp->p_cusagep->cu_filesused = 0;
269 resp->p_cusagep->cu_flags = CUSAGE_ACTIVE; /* dirty cache */
271 /* clear out the non-pointer info */
272 for (index = MININDEX; index < resp->p_totentries; index++) {
273 rlentp = res_rlent_get(resp, index);
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;
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);
298 /* if an error occurred on one of the lists */
299 if (ret == 0) {
300 res_clear(resp);
301 return;
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;
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));
324 * res_listcheck
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)
338 res_listcheck(res *resp, enum cachefs_rl_type type)
340 rl_entry_t *rlentp;
341 int previndex, index;
342 cachefs_rl_listhead_t *lhp;
343 int itemcnt = 0;
345 lhp = RL_HEAD(resp, type);
346 index = lhp->rli_front;
347 previndex = 0;
349 /* walk the list */
350 while (index != 0) {
351 itemcnt++;
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);
360 /* get pointer to rl_entry object */
361 rlentp = res_rlent_get(resp, index);
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);
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);
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);
391 /* indicate we have seen this pointer */
392 rlentp->rl_fsck = 1;
393 previndex = index;
394 index = rlentp->rl_bkwd_idx;
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);
405 return (1);
410 * res_clear
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)
421 void
422 res_clear(res *resp)
424 memset(resp->p_addrp, 0, resp->p_size);
430 * res_done
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)
446 res_done(res *resp)
448 rl_entry_t *rlentp;
449 int index;
450 int xx;
451 int ret;
453 precond(resp);
454 precond(resp->p_magic == MAGIC);
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;
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);
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);
477 rlentp->rl_fsck = 0; /* prepare to re-check */
481 * Sanity check that we do not have an internal error in
482 * fsck. Eventually turn this stuff off.
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
505 /* indicate the cache is clean */
506 resp->p_cusagep->cu_flags &= ~CUSAGE_ACTIVE;
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);
513 resp->p_done = 1;
515 /* return success */
516 return (0);
521 * res_addfile
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)
533 void
534 res_addfile(res *resp, long nbytes)
536 precond(resp);
537 precond(resp->p_magic == MAGIC);
539 /* update resource counts */
540 resp->p_cusagep->cu_blksused += CVBLKS(nbytes);
541 resp->p_cusagep->cu_filesused += 1;
546 * res_addident
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)
566 res_addident(res *resp, int index, rl_entry_t *dp, long nbytes, int file)
568 rl_entry_t *rlentp;
570 precond(resp);
571 precond(resp->p_magic == MAGIC);
572 precond(dp);
574 /* check index for sanity */
575 if ((index < MININDEX) || (index >= resp->p_totentries)) {
576 return (-1);
579 /* get pointer to ident */
580 rlentp = res_rlent_get(resp, index);
582 /* if something already there */
583 if (rlentp->rl_fsid != 0LL) {
584 return (-1);
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));
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;
597 /* update resource counts */
598 resp->p_cusagep->cu_blksused += CVBLKS(nbytes);
599 resp->p_cusagep->cu_filesused += file;
601 /* return success */
602 return (0);
607 * res_clearident
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)
625 void
626 res_clearident(res *resp, int index, int nbytes, int file)
628 rl_entry_t *rlentp;
630 precond(resp);
631 precond(resp->p_magic == MAGIC);
632 precond((index >= MININDEX) && (index < resp->p_totentries));
634 /* get pointer to ident */
635 rlentp = res_rlent_get(resp, index);
636 precond(rlentp->rl_fsid != 0LL);
638 /* clear the ident */
639 rlentp->rl_fsid = 0LL;
640 rlentp->rl_fileno = 0;
641 rlentp->rl_attrc = 0;
642 rlentp->rl_local = 0;
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);
651 * This function moves an RL entry from whereever it currently is to
652 * the requested list.
655 void
656 res_rlent_moveto(res *resp, enum cachefs_rl_type type, uint_t entno, long blks)
658 rl_entry_t *rl_ent;
659 uint_t prev, next;
660 cachefs_rl_listhead_t *lhp;
661 enum cachefs_rl_type otype;
663 precond((CACHEFS_RL_START <= type) && (type <= CACHEFS_RL_END));
664 precond((entno >= MININDEX) && (entno < resp->p_totentries));
666 rl_ent = res_rlent_get(resp, entno);
667 if (rl_ent->rl_fsck) {
668 /* remove entry from its previous list */
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));
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));
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;
688 if (next != 0) {
689 rl_ent = res_rlent_get(resp, next);
690 rl_ent->rl_bkwd_idx = prev;
692 lhp->rli_blkcnt -= blks;
693 lhp->rli_itemcnt--;
696 /* add entry to its new list */
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;
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;
712 lhp->rli_back = entno;
713 lhp->rli_blkcnt += blks;
714 lhp->rli_itemcnt++;
716 rl_ent = res_rlent_get(resp, entno);
717 rl_ent->rl_current = type;
718 rl_ent->rl_fsck = 1;