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
fs_getmntinfo(buf,name,type)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 *
get_fs_info(lstatfsp,use_mmap)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
reread_fs_info(fsp,use_mmap)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
get_superblock(fsp,sbp)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
get_ifile(fsp,use_mmap)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
lfs_segmapv(fsp,seg,seg_buf,blocks,bcount)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
add_blocks(fsp,bip,countp,sp,seg_buf,segaddr,psegaddr)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
add_inodes(fsp,bip,countp,sp,seg_buf,seg_addr)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
pseg_valid(fsp,ssp)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
mmap_segment(fsp,segment,segbuf,use_mmap)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
munmap_segment(fsp,seg_buf,use_mmap)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
print_SEGSUM(lfsp,p)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
bi_compare(a,b)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
bi_toss(dummy,a,b)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
toss(p,nump,size,dotoss,client)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