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