1 /*-
2  * Copyright (c) 1992, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)library.c	8.3 (Berkeley) 05/24/95";
10 #endif /* not lint */
11 
12 #include <sys/param.h>
13 #include <sys/time.h>
14 #include <sys/stat.h>
15 #include <sys/mount.h>
16 #include <sys/types.h>
17 #include <sys/mman.h>
18 
19 #include <ufs/ufs/dinode.h>
20 #include <ufs/lfs/lfs.h>
21 
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #include "clean.h"
29 
30 void	 add_blocks __P((FS_INFO *, BLOCK_INFO *, int *, SEGSUM *, caddr_t,
31 	     daddr_t, daddr_t));
32 void	 add_inodes __P((FS_INFO *, BLOCK_INFO *, int *, SEGSUM *, caddr_t,
33 	     daddr_t));
34 int	 bi_compare __P((const void *, const void *));
35 int	 bi_toss __P((const void *, const void *, const void *));
36 void	 get_ifile __P((FS_INFO *, int));
37 int	 get_superblock __P((FS_INFO *, struct lfs *));
38 int	 pseg_valid __P((FS_INFO *, SEGSUM *));
39 
40 /*
41  * This function will get information on a a filesystem which matches
42  * the name and type given.  If a "name" is in a filesystem of the given
43  * type, then buf is filled with that filesystem's info, and the
44  * a non-zero value is returned.
45  */
46 int
47 fs_getmntinfo(buf, name, type)
48 	struct	statfs	**buf;
49 	char	*name;
50 	char	*type;
51 {
52 	/* allocate space for the filesystem info */
53 	*buf = (struct statfs *)malloc(sizeof(struct statfs));
54 	if (*buf == NULL)
55 		return 0;
56 
57 	/* grab the filesystem info */
58 	if (statfs(name, *buf) < 0) {
59 		free(*buf);
60 		return 0;
61 	}
62 
63 	/* check to see if it's the one we want */
64 	if (strcmp((*buf)->f_fstypename, type) ||
65 	    strncmp(name, (*buf)->f_mntonname, MNAMELEN)) {
66 		/* "this is not the filesystem you're looking for */
67 		free(*buf);
68 		return 0;
69 	}
70 
71 	return 1;
72 }
73 
74 /*
75  * Get all the information available on an LFS file system.
76  * Returns an pointer to an FS_INFO structure, NULL on error.
77  */
78 FS_INFO *
79 get_fs_info (lstatfsp, use_mmap)
80 	struct statfs *lstatfsp;	/* IN: pointer to statfs struct */
81 	int use_mmap;			/* IN: mmap or read */
82 {
83 	FS_INFO	*fsp;
84 	int	i;
85 
86 	fsp = (FS_INFO *)malloc(sizeof(FS_INFO));
87 	if (fsp == NULL)
88 		return NULL;
89 	bzero(fsp, sizeof(FS_INFO));
90 
91 	fsp->fi_statfsp = lstatfsp;
92 	if (get_superblock (fsp, &fsp->fi_lfs))
93 		err(1, "get_fs_info: get_superblock failed");
94 	fsp->fi_daddr_shift =
95 	     fsp->fi_lfs.lfs_bshift - fsp->fi_lfs.lfs_fsbtodb;
96 	get_ifile (fsp, use_mmap);
97 	return (fsp);
98 }
99 
100 /*
101  * If we are reading the ifile then we need to refresh it.  Even if
102  * we are mmapping it, it might have grown.  Finally, we need to
103  * refresh the file system information (statfs) info.
104  */
105 void
106 reread_fs_info(fsp, use_mmap)
107 	FS_INFO *fsp;	/* IN: prointer fs_infos to reread */
108 	int use_mmap;
109 {
110 	int i;
111 
112 	if (statfs(fsp->fi_statfsp->f_mntonname, fsp->fi_statfsp))
113 		err(1, "reread_fs_info: statfs failed");
114 	get_ifile (fsp, use_mmap);
115 }
116 
117 /*
118  * Gets the superblock from disk (possibly in face of errors)
119  */
120 int
121 get_superblock (fsp, sbp)
122 	FS_INFO *fsp;		/* local file system info structure */
123 	struct lfs *sbp;
124 {
125 	char mntfromname[MNAMELEN+1];
126         int fid;
127 
128 	strcpy(mntfromname, "/dev/r");
129 	strcat(mntfromname, fsp->fi_statfsp->f_mntfromname+5);
130 
131 	if ((fid = open(mntfromname, O_RDONLY, (mode_t)0)) < 0) {
132 		err(0, "get_superblock: bad open");
133 		return (-1);
134 	}
135 
136 	get(fid, LFS_LABELPAD, sbp, sizeof(struct lfs));
137 	close (fid);
138 
139 	return (0);
140 }
141 
142 /*
143  * This function will map the ifile into memory.  It causes a
144  * fatal error on failure.
145  */
146 void
147 get_ifile (fsp, use_mmap)
148 	FS_INFO	*fsp;
149 	int use_mmap;
150 
151 {
152 	struct stat file_stat;
153 	caddr_t ifp;
154 	char *ifile_name;
155 	int count, fid;
156 
157 	ifp = NULL;
158 	ifile_name = malloc(strlen(fsp->fi_statfsp->f_mntonname) +
159 	    strlen(IFILE_NAME)+2);
160 	strcat(strcat(strcpy(ifile_name, fsp->fi_statfsp->f_mntonname), "/"),
161 	    IFILE_NAME);
162 
163 	if ((fid = open(ifile_name, O_RDWR, (mode_t)0)) < 0)
164 		err(1, "get_ifile: bad open");
165 
166 	if (fstat (fid, &file_stat))
167 		err(1, "get_ifile: fstat failed");
168 
169 	if (use_mmap && file_stat.st_size == fsp->fi_ifile_length) {
170 		(void) close(fid);
171 		return;
172 	}
173 
174 	/* get the ifile */
175 	if (use_mmap) {
176 		if (fsp->fi_cip)
177 			munmap((caddr_t)fsp->fi_cip, fsp->fi_ifile_length);
178 		ifp = mmap ((caddr_t)0, file_stat.st_size,
179 		    PROT_READ|PROT_WRITE, 0, fid, (off_t)0);
180 		if (ifp ==  (caddr_t)(-1))
181 			err(1, "get_ifile: mmap failed");
182 	} else {
183 		if (fsp->fi_cip)
184 			free(fsp->fi_cip);
185 		if (!(ifp = malloc (file_stat.st_size)))
186 			err (1, "get_ifile: malloc failed");
187 redo_read:
188 		count = read (fid, ifp, (size_t) file_stat.st_size);
189 
190 		if (count < 0)
191 			err(1, "get_ifile: bad ifile read");
192 		else if (count < file_stat.st_size) {
193 			err(0, "get_ifile");
194 			if (lseek(fid, 0, SEEK_SET) < 0)
195 				err(1, "get_ifile: bad ifile lseek");
196 			goto redo_read;
197 		}
198 	}
199 	fsp->fi_ifile_length = file_stat.st_size;
200 	close (fid);
201 
202 	fsp->fi_cip = (CLEANERINFO *)ifp;
203 	fsp->fi_segusep = (SEGUSE *)(ifp + CLEANSIZE(fsp));
204 	fsp->fi_ifilep  = (IFILE *)((caddr_t)fsp->fi_segusep + SEGTABSIZE(fsp));
205 
206 	/*
207 	 * The number of ifile entries is equal to the number of blocks
208 	 * blocks in the ifile minus the ones allocated to cleaner info
209 	 * and segment usage table multiplied by the number of ifile
210 	 * entries per page.
211 	 */
212 	fsp->fi_ifile_count = (fsp->fi_ifile_length >> fsp->fi_lfs.lfs_bshift -
213 	    fsp->fi_lfs.lfs_cleansz - fsp->fi_lfs.lfs_segtabsz) *
214 	    fsp->fi_lfs.lfs_ifpb;
215 
216 	free (ifile_name);
217 }
218 
219 /*
220  * This function will scan a segment and return a list of
221  * <inode, blocknum> pairs which indicate which blocks were
222  * contained as live data within the segment when the segment
223  * summary was read (it may have "died" since then).  Any given
224  * pair will be listed at most once.
225  */
226 int
227 lfs_segmapv(fsp, seg, seg_buf, blocks, bcount)
228 	FS_INFO *fsp;		/* pointer to local file system information */
229 	int seg;		/* the segment number */
230 	caddr_t seg_buf;	/* the buffer containing the segment's data */
231 	BLOCK_INFO **blocks;	/* OUT: array of block_info for live blocks */
232 	int *bcount;		/* OUT: number of active blocks in segment */
233 {
234 	BLOCK_INFO *bip;
235 	SEGSUM *sp;
236 	SEGUSE *sup;
237 	FINFO *fip;
238 	struct lfs *lfsp;
239 	caddr_t s, segend;
240 	daddr_t pseg_addr, seg_addr;
241 	int i, nelem, nblocks, nsegs, sumsize;
242 	time_t timestamp;
243 
244 	lfsp = &fsp->fi_lfs;
245 	nelem = 2 * lfsp->lfs_ssize;
246 	if (!(bip = malloc(nelem * sizeof(BLOCK_INFO))))
247 		goto err0;
248 
249 	sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, seg);
250 	s = seg_buf + (sup->su_flags & SEGUSE_SUPERBLOCK ? LFS_SBPAD : 0);
251 	seg_addr = sntoda(lfsp, seg);
252 	pseg_addr = seg_addr + (sup->su_flags & SEGUSE_SUPERBLOCK ? btodb(LFS_SBPAD) : 0);
253 #ifdef VERBOSE
254 		printf("\tsegment buffer at: 0x%x\tseg_addr 0x%x\n", s, seg_addr);
255 #endif /* VERBOSE */
256 
257 	*bcount = 0;
258 	for (nsegs = 0, timestamp = 0; nsegs < sup->su_nsums; nsegs++) {
259 		sp = (SEGSUM *)s;
260 
261 		nblocks = pseg_valid(fsp, sp);
262 		if (nblocks <= 0) {
263 			printf("Warning: invalid segment summary at 0x%x\n",
264 			    pseg_addr);
265 			break;
266 		}
267 
268 #ifdef VERBOSE
269 		printf("\tpartial at: 0x%x\n", pseg_addr);
270 		print_SEGSUM(lfsp, sp);
271 		fflush(stdout);
272 #endif /* VERBOSE */
273 
274 		/* Check if we have hit old data */
275 		if (timestamp > ((SEGSUM*)s)->ss_create)
276 			break;
277 		timestamp = ((SEGSUM*)s)->ss_create;
278 
279 #ifdef DIAGNOSTIC
280 		/* Verfiy size of summary block */
281 		sumsize = sizeof(SEGSUM) +
282 		    (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp);
283 		for (i = 0, fip = (FINFO *)(sp + 1); i < sp->ss_nfinfo; ++i) {
284 			sumsize += sizeof(FINFO) +
285 			    (fip->fi_nblocks - 1) * sizeof(daddr_t);
286 			fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks]);
287 		}
288 		if (sumsize > LFS_SUMMARY_SIZE) {
289 			fprintf(stderr,
290 			    "Segment %d summary block too big: %d\n",
291 			    seg, sumsize);
292 			exit(1);
293 		}
294 #endif
295 
296 		if (*bcount + nblocks + sp->ss_ninos > nelem) {
297 			nelem = *bcount + nblocks + sp->ss_ninos;
298 			bip = realloc (bip, nelem * sizeof(BLOCK_INFO));
299 			if (!bip)
300 				goto err0;
301 		}
302 		add_blocks(fsp, bip, bcount, sp, seg_buf, seg_addr, pseg_addr);
303 		add_inodes(fsp, bip, bcount, sp, seg_buf, seg_addr);
304 		pseg_addr += fsbtodb(lfsp, nblocks) +
305 		    bytetoda(fsp, LFS_SUMMARY_SIZE);
306 		s += (nblocks << lfsp->lfs_bshift) + LFS_SUMMARY_SIZE;
307 	}
308 	qsort(bip, *bcount, sizeof(BLOCK_INFO), bi_compare);
309 	toss(bip, bcount, sizeof(BLOCK_INFO), bi_toss, NULL);
310 #ifdef VERBOSE
311 	{
312 		BLOCK_INFO *_bip;
313 		int i;
314 
315 		printf("BLOCK INFOS\n");
316 		for (_bip = bip, i=0; i < *bcount; ++_bip, ++i)
317 			PRINT_BINFO(_bip);
318 	}
319 #endif
320 	*blocks = bip;
321 	return (0);
322 
323 err0:	*bcount = 0;
324 	return (-1);
325 
326 }
327 
328 /*
329  * This will parse a partial segment and fill in BLOCK_INFO structures
330  * for each block described in the segment summary.  It will not include
331  * blocks or inodes from files with new version numbers.
332  */
333 void
334 add_blocks (fsp, bip, countp, sp, seg_buf, segaddr, psegaddr)
335 	FS_INFO *fsp;		/* pointer to super block */
336 	BLOCK_INFO *bip;	/* Block info array */
337 	int *countp;		/* IN/OUT: number of blocks in array */
338 	SEGSUM	*sp;		/* segment summmary pointer */
339 	caddr_t seg_buf;	/* buffer containing segment */
340 	daddr_t segaddr;	/* address of this segment */
341 	daddr_t psegaddr;	/* address of this partial segment */
342 {
343 	IFILE	*ifp;
344 	FINFO	*fip;
345 	caddr_t	bp;
346 	daddr_t	*dp, *iaddrp;
347 	int db_per_block, i, j;
348 	int db_frag;
349 	u_long page_size;
350 long *lp;
351 
352 #ifdef VERBOSE
353 	printf("FILE INFOS\n");
354 #endif
355 	db_per_block = fsbtodb(&fsp->fi_lfs, 1);
356 	page_size = fsp->fi_lfs.lfs_bsize;
357 	bp = seg_buf + datobyte(fsp, psegaddr - segaddr) + LFS_SUMMARY_SIZE;
358 	bip += *countp;
359 	psegaddr += bytetoda(fsp, LFS_SUMMARY_SIZE);
360 	iaddrp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE);
361 	--iaddrp;
362 	for (fip = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo;
363 	    ++i, fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks])) {
364 
365 		ifp = IFILE_ENTRY(&fsp->fi_lfs, fsp->fi_ifilep, fip->fi_ino);
366 		PRINT_FINFO(fip, ifp);
367 		if (ifp->if_version > fip->fi_version)
368 			continue;
369 		dp = &(fip->fi_blocks[0]);
370 		for (j = 0; j < fip->fi_nblocks; j++, dp++) {
371 			while (psegaddr == *iaddrp) {
372 				psegaddr += db_per_block;
373 				bp += page_size;
374 				--iaddrp;
375 			}
376 			bip->bi_inode = fip->fi_ino;
377 			bip->bi_lbn = *dp;
378 			bip->bi_daddr = psegaddr;
379 			bip->bi_segcreate = (time_t)(sp->ss_create);
380 			bip->bi_bp = bp;
381 			bip->bi_version = ifp->if_version;
382 			if (fip->fi_lastlength == page_size) {
383 				bip->bi_size = page_size;
384 				psegaddr += db_per_block;
385 				bp += page_size;
386 			} else {
387 				db_frag = fragstodb(&(fsp->fi_lfs),
388 				    numfrags(&(fsp->fi_lfs),
389 				    fip->fi_lastlength));
390 #ifdef VERBOSE
391 				printf("lastlength, frags: %d, %d, %d\n",
392 				    fip->fi_lastlength, temp,
393 				    bytetoda(fsp, temp));
394 				fflush(stdout);
395 #endif
396 				bip->bi_size = fip->fi_lastlength;
397 				bp += fip->fi_lastlength;
398 				psegaddr += db_frag;
399 			}
400 			++bip;
401 			++(*countp);
402 		}
403 	}
404 }
405 
406 /*
407  * For a particular segment summary, reads the inode blocks and adds
408  * INODE_INFO structures to the array.  Returns the number of inodes
409  * actually added.
410  */
411 void
412 add_inodes (fsp, bip, countp, sp, seg_buf, seg_addr)
413 	FS_INFO *fsp;		/* pointer to super block */
414 	BLOCK_INFO *bip;	/* block info array */
415 	int *countp;		/* pointer to current number of inodes */
416 	SEGSUM *sp;		/* segsum pointer */
417 	caddr_t	seg_buf;	/* the buffer containing the segment's data */
418 	daddr_t	seg_addr;	/* disk address of seg_buf */
419 {
420 	struct dinode *di;
421 	struct lfs *lfsp;
422 	IFILE *ifp;
423 	BLOCK_INFO *bp;
424 	daddr_t	*daddrp;
425 	ino_t inum;
426 	int i;
427 
428 	if (sp->ss_ninos <= 0)
429 		return;
430 
431 	bp = bip + *countp;
432 	lfsp = &fsp->fi_lfs;
433 #ifdef VERBOSE
434 	(void) printf("INODES:\n");
435 #endif
436 	daddrp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE);
437 	for (i = 0; i < sp->ss_ninos; ++i) {
438 		if (i % INOPB(lfsp) == 0) {
439 			--daddrp;
440 			di = (struct dinode *)(seg_buf +
441 			    ((*daddrp - seg_addr) << fsp->fi_daddr_shift));
442 		} else
443 			++di;
444 
445 		inum = di->di_inumber;
446 		bp->bi_lbn = LFS_UNUSED_LBN;
447 		bp->bi_inode = inum;
448 		bp->bi_daddr = *daddrp;
449 		bp->bi_bp = di;
450 		bp->bi_segcreate = sp->ss_create;
451 
452 		if (inum == LFS_IFILE_INUM) {
453 			bp->bi_version = 1;	/* Ifile version should be 1 */
454 			bp++;
455 			++(*countp);
456 			PRINT_INODE(1, bp);
457 		} else {
458 			ifp = IFILE_ENTRY(lfsp, fsp->fi_ifilep, inum);
459 			PRINT_INODE(ifp->if_daddr == *daddrp, bp);
460 			bp->bi_version = ifp->if_version;
461 			if (ifp->if_daddr == *daddrp) {
462 				bp++;
463 				++(*countp);
464 			}
465 		}
466 	}
467 }
468 
469 /*
470  * Checks the summary checksum and the data checksum to determine if the
471  * segment is valid or not.  Returns the size of the partial segment if it
472  * is valid, * and 0 otherwise.  Use dump_summary to figure out size of the
473  * the partial as well as whether or not the checksum is valid.
474  */
475 int
476 pseg_valid (fsp, ssp)
477 	FS_INFO *fsp;   /* pointer to file system info */
478 	SEGSUM *ssp;	/* pointer to segment summary block */
479 {
480 	caddr_t	p;
481 	int i, nblocks;
482 	u_long *datap;
483 
484 	if (ssp->ss_magic != SS_MAGIC)
485 		return(0);
486 
487 	if ((nblocks = dump_summary(&fsp->fi_lfs, ssp, 0, NULL)) <= 0 ||
488 	    nblocks > fsp->fi_lfs.lfs_ssize - 1)
489 		return(0);
490 
491 	/* check data/inode block(s) checksum too */
492 	datap = (u_long *)malloc(nblocks * sizeof(u_long));
493 	p = (caddr_t)ssp + LFS_SUMMARY_SIZE;
494 	for (i = 0; i < nblocks; ++i) {
495 		datap[i] = *((u_long *)p);
496 		p += fsp->fi_lfs.lfs_bsize;
497 	}
498 	if (cksum ((void *)datap, nblocks * sizeof(u_long)) != ssp->ss_datasum)
499 		return (0);
500 
501 	return (nblocks);
502 }
503 
504 
505 /* #define MMAP_SEGMENT */
506 /*
507  * read a segment into a memory buffer
508  */
509 int
510 mmap_segment (fsp, segment, segbuf, use_mmap)
511 	FS_INFO *fsp;		/* file system information */
512 	int segment;		/* segment number */
513 	caddr_t *segbuf;	/* pointer to buffer area */
514 	int use_mmap;		/* mmap instead of read */
515 {
516 	struct lfs *lfsp;
517 	int fid;		/* fildes for file system device */
518 	daddr_t seg_daddr;	/* base disk address of segment */
519 	off_t seg_byte;
520 	size_t ssize;
521 	char mntfromname[MNAMELEN+2];
522 
523 	lfsp = &fsp->fi_lfs;
524 
525 	/* get the disk address of the beginning of the segment */
526 	seg_daddr = sntoda(lfsp, segment);
527 	seg_byte = datobyte(fsp, seg_daddr);
528 	ssize = seg_size(lfsp);
529 
530 	strcpy(mntfromname, "/dev/r");
531 	strcat(mntfromname, fsp->fi_statfsp->f_mntfromname+5);
532 
533 	if ((fid = open(mntfromname, O_RDONLY, (mode_t)0)) < 0) {
534 		err(0, "mmap_segment: bad open");
535 		return (-1);
536 	}
537 
538 	if (use_mmap) {
539 		*segbuf = mmap ((caddr_t)0, seg_size(lfsp), PROT_READ,
540 		    0, fid, seg_byte);
541 		if (*(long *)segbuf < 0) {
542 			err(0, "mmap_segment: mmap failed");
543 			return (NULL);
544 		}
545 	} else {
546 #ifdef VERBOSE
547 		printf("mmap_segment\tseg_daddr: %lu\tseg_size: %lu\tseg_offset: %qu\n",
548 		    seg_daddr, ssize, seg_byte);
549 #endif
550 		/* malloc the space for the buffer */
551 		*segbuf = malloc(ssize);
552 		if (!*segbuf) {
553 			err(0, "mmap_segment: malloc failed");
554 			return(NULL);
555 		}
556 
557 		/* read the segment data into the buffer */
558 		if (lseek (fid, seg_byte, SEEK_SET) != seg_byte) {
559 			err (0, "mmap_segment: bad lseek");
560 			free(*segbuf);
561 			return (-1);
562 		}
563 
564 		if (read (fid, *segbuf, ssize) != ssize) {
565 			err (0, "mmap_segment: bad read");
566 			free(*segbuf);
567 			return (-1);
568 		}
569 	}
570 	close (fid);
571 
572 	return (0);
573 }
574 
575 void
576 munmap_segment (fsp, seg_buf, use_mmap)
577 	FS_INFO *fsp;		/* file system information */
578 	caddr_t seg_buf;	/* pointer to buffer area */
579 	int use_mmap;		/* mmap instead of read/write */
580 {
581 	if (use_mmap)
582 		munmap (seg_buf, seg_size(&fsp->fi_lfs));
583 	else
584 		free (seg_buf);
585 }
586 
587 
588 /*
589  * USEFUL DEBUGGING TOOLS:
590  */
591 void
592 print_SEGSUM (lfsp, p)
593 	struct lfs *lfsp;
594 	SEGSUM	*p;
595 {
596 	if (p)
597 		(void) dump_summary(lfsp, p, DUMP_ALL, NULL);
598 	else printf("0x0");
599 	fflush(stdout);
600 }
601 
602 int
603 bi_compare(a, b)
604 	const void *a;
605 	const void *b;
606 {
607 	const BLOCK_INFO *ba, *bb;
608 	int diff;
609 
610 	ba = a;
611 	bb = b;
612 
613 	if (diff = (int)(ba->bi_inode - bb->bi_inode))
614 		return (diff);
615 	if (diff = (int)(ba->bi_lbn - bb->bi_lbn)) {
616 		if (ba->bi_lbn == LFS_UNUSED_LBN)
617 			return(-1);
618 		else if (bb->bi_lbn == LFS_UNUSED_LBN)
619 			return(1);
620 		else if (ba->bi_lbn < 0 && bb->bi_lbn >= 0)
621 			return(1);
622 		else if (bb->bi_lbn < 0 && ba->bi_lbn >= 0)
623 			return(-1);
624 		else
625 			return (diff);
626 	}
627 	if (diff = (int)(ba->bi_segcreate - bb->bi_segcreate))
628 		return (diff);
629 	diff = (int)(ba->bi_daddr - bb->bi_daddr);
630 	return (diff);
631 }
632 
633 int
634 bi_toss(dummy, a, b)
635 	const void *dummy;
636 	const void *a;
637 	const void *b;
638 {
639 	const BLOCK_INFO *ba, *bb;
640 
641 	ba = a;
642 	bb = b;
643 
644 	return(ba->bi_inode == bb->bi_inode && ba->bi_lbn == bb->bi_lbn);
645 }
646 
647 void
648 toss(p, nump, size, dotoss, client)
649 	void *p;
650 	int *nump;
651 	size_t size;
652 	int (*dotoss) __P((const void *, const void *, const void *));
653 	void *client;
654 {
655 	int i;
656 	void *p1;
657 
658 	if (*nump == 0)
659 		return;
660 
661 	for (i = *nump; --i > 0;) {
662 		p1 = p + size;
663 		if (dotoss(client, p, p1)) {
664 			memmove(p, p1, i * size);
665 			--(*nump);
666 		} else
667 			p += size;
668 	}
669 }
670