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