xref: /titanic_44/usr/src/cmd/fs.d/cachefs/fsck/res.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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