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