xref: /illumos-gate/usr/src/cmd/fs.d/udfs/fsck/pass1.c (revision 7c478bd9)
1 /*
2  * Copyright 1999 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved  	*/
8 
9 /*
10  * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
11  * All rights reserved.
12  *
13  * Redistribution and use in source and binary forms are permitted
14  * provided that: (1) source distributions retain this entire copyright
15  * notice and comment, and (2) distributions including binaries display
16  * the following acknowledgement:  ``This product includes software
17  * developed by the University of California, Berkeley and its contributors''
18  * in the documentation or other materials provided with the distribution
19  * and in all advertising materials mentioning features or use of this
20  * software. Neither the name of the University nor the names of its
21  * contributors may be used to endorse or promote products derived
22  * from this software without specific prior written permission.
23  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <stdio.h>
31 #include <strings.h>
32 #include <malloc.h>
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/sysmacros.h>
36 #include <sys/mntent.h>
37 #include <sys/vnode.h>
38 #include <sys/fs/udf_volume.h>
39 #include <sys/dkio.h>
40 #include <sys/vtoc.h>
41 #include "fsck.h"
42 #include "udfs.h"
43 #include <locale.h>
44 
45 /*
46  * for each large file ( size > MAXOFF_T) this global counter
47  * gets incremented here.
48  */
49 
50 extern unsigned int largefile_count;
51 extern void	pwarn(char *, ...);
52 extern void	pfatal(char *, ...);
53 extern void	errexit(char *, ...);
54 
55 extern int32_t	verifytag(struct tag *, uint32_t, struct tag *, int);
56 extern char	*tagerrs[];
57 extern void	maketag(struct tag *, struct tag *);
58 extern void	flush(int32_t, struct bufarea *);
59 extern void	putfilentry(struct bufarea *);
60 extern int32_t	bread(int32_t, char *, daddr_t, long);
61 extern void	bwrite(int, char *, daddr_t, long);
62 extern int32_t	dofix(struct inodesc *, char *);
63 extern int32_t	reply(char *);
64 extern void	ud_swap_short_ad(short_ad_t *);
65 extern void	ud_swap_long_ad(long_ad_t *);
66 
67 extern void	dump16(char *, char *);
68 
69 static void	adjust(struct fileinfo *);
70 static void	opndir(struct file_entry *);
71 static int32_t	getdir(struct file_entry *, struct bufarea **,
72 	u_offset_t *, struct file_id **);
73 static void ckinode(struct file_entry *);
74 struct bufarea *getfilentry();
75 
76 /* Fields for traversing an allocation extent */
77 static uint32_t dir_adrsize;
78 static uint32_t dir_adrindx;
79 static uint32_t dir_naddrs;
80 static uint8_t *extbuf;
81 static uint8_t *dir_adrlist;
82 
83 /* Keep track of where we are in the directory */
84 static u_offset_t dir_baseoff;
85 static uint32_t dir_basesize;
86 static uint8_t *dirbuf;
87 static uint8_t *dir_fidp;
88 static uint32_t baseblock;
89 
90 #define	MAXFIDSIZE 2048
91 
92 static uint8_t fidbuf[MAXFIDSIZE];
93 
94 void
95 pass1()
96 {
97 	register struct file_entry *fp;
98 	register struct fileinfo *fip;
99 	register struct bufarea *bp;
100 	struct file_id *fidp;
101 	struct bufarea *fbp;
102 	int err;
103 
104 	(void) cachefile(rootblock, rootlen);
105 	fip = &inphead[0];		/* The root */
106 	fip->fe_lseen = 0;		/* Didn't get here through directory */
107 	n_files = n_dirs = 0;
108 	while (fip->fe_block) {
109 		u_offset_t offset, end;
110 
111 		markbusy(fip->fe_block, fip->fe_len);
112 		bp = getfilentry(fip->fe_block, fip->fe_len);
113 		if (bp == NULL) {
114 			pwarn(gettext("Unable to read file entry at %x\n"),
115 				fip->fe_block);
116 			goto next;
117 		}
118 		/* LINTED */
119 		fp = (struct file_entry *)bp->b_un.b_buf;
120 		fip->fe_lcount = fp->fe_lcount;
121 		fip->fe_type = fp->fe_icb_tag.itag_ftype;
122 		if (fp->fe_uniq_id >= maxuniqid)
123 			maxuniqid = fp->fe_uniq_id + 1;
124 
125 		if (fip->fe_block == rootblock &&
126 				fip->fe_type != FTYPE_DIRECTORY)
127 			errexit(gettext("Root file entry is not a "
128 				"directory\n"));
129 
130 		if (debug) {
131 			(void) printf("do %x len %d type %d lcount %d"
132 				" lseen %d end %llx\n",
133 				fip->fe_block, fip->fe_len,
134 				fip->fe_type, fip->fe_lcount,
135 				fip->fe_lseen, fp->fe_info_len);
136 		}
137 		switch (fip->fe_type) {
138 		case FTYPE_DIRECTORY:
139 			n_dirs++;
140 			offset = 0;
141 			end = fp->fe_info_len;
142 			fbp = NULL;
143 			opndir(fp);
144 			for (offset = 0; offset < end;
145 					offset += FID_LENGTH(fidp)) {
146 				err = getdir(fp, &fbp, &offset, &fidp);
147 				if (err) {
148 					pwarn(gettext("Bad directory entry in "
149 						"file %x at offset %llx\n"),
150 						fip->fe_block, offset);
151 					offset = end;
152 				}
153 				if (fidp->fid_flags & FID_DELETED)
154 					continue;
155 				(void) cachefile(fidp->fid_icb.lad_ext_loc,
156 					fidp->fid_icb.lad_ext_len);
157 			}
158 			if (dirbuf) {
159 				free(dirbuf);
160 				dirbuf = NULL;
161 			}
162 			if (fbp)
163 				fbp->b_flags &= ~B_INUSE;
164 			if (debug)
165 				(void) printf("Done %x\n", fip->fe_block);
166 			break;
167 
168 		case FTYPE_FILE:
169 		case FTYPE_SYMLINK:
170 			ckinode(fp);
171 		default:
172 			n_files++;
173 			break;
174 		}
175 		putfilentry(bp);
176 		bp->b_flags &= ~B_INUSE;
177 	next:
178 		/* At end of this set of fips, get the next set */
179 		if ((++fip)->fe_block == (uint32_t)-1)
180 			fip = fip->fe_nexthash;
181 	}
182 
183 	/* Find bad link counts */
184 	fip = &inphead[0];
185 	while (fip->fe_block) {
186 		if (fip->fe_lcount != fip->fe_lseen)
187 			adjust(fip);
188 		/* At end of this set of fips, get the next set */
189 		if ((++fip)->fe_block == (uint32_t)-1)
190 			fip = fip->fe_nexthash;
191 	}
192 }
193 
194 static void
195 opndir(struct file_entry *fp)
196 {
197 	if (dirbuf) {
198 		free(dirbuf);
199 		dirbuf = NULL;
200 	}
201 	if (extbuf) {
202 		free(extbuf);
203 		extbuf = NULL;
204 	}
205 
206 	dir_baseoff = 0;
207 	dir_basesize = 0;
208 	dir_adrindx = 0;
209 
210 	switch (fp->fe_icb_tag.itag_flags & 0x3) {
211 	case ICB_FLAG_SHORT_AD:
212 		dir_adrsize = sizeof (short_ad_t);
213 		dir_naddrs = fp->fe_len_adesc / sizeof (short_ad_t);
214 		dir_adrlist = (uint8_t *)(fp->fe_spec + fp->fe_len_ear);
215 		break;
216 	case ICB_FLAG_LONG_AD:
217 		dir_adrsize = sizeof (long_ad_t);
218 		dir_naddrs = fp->fe_len_adesc / sizeof (long_ad_t);
219 		dir_adrlist = (uint8_t *)(fp->fe_spec + fp->fe_len_ear);
220 		break;
221 	case ICB_FLAG_EXT_AD:
222 		errexit(gettext("Can't handle ext_ads in directories/n"));
223 		break;
224 	case ICB_FLAG_ONE_AD:
225 		dir_adrsize = 0;
226 		dir_naddrs = 0;
227 		dir_adrlist = NULL;
228 		dir_basesize = fp->fe_len_adesc;
229 		dir_fidp = (uint8_t *)(fp->fe_spec + fp->fe_len_ear);
230 		baseblock = fp->fe_tag.tag_loc;
231 		break;
232 	}
233 }
234 
235 /* Allocate and read in an allocation extent */
236 /* ARGSUSED */
237 int
238 getallocext(struct file_entry *fp, uint32_t loc, uint32_t len)
239 {
240 	uint32_t nb;
241 	uint8_t *ap;
242 	int i;
243 	int err;
244 	struct alloc_ext_desc *aep;
245 
246 	if (debug)
247 		(void) printf(" allocext loc %x len %x\n", loc, len);
248 	nb = roundup(len, secsize);
249 	if (extbuf)
250 		free(extbuf);
251 	extbuf = (uint8_t *)malloc(nb);
252 	if (extbuf == NULL)
253 		errexit(gettext("Can't allocate directory extent buffer\n"));
254 	if (bread(fsreadfd, (char *)extbuf,
255 			fsbtodb(loc + part_start), nb) != 0) {
256 		(void) fprintf(stderr,
257 			gettext("Can't read allocation extent\n"));
258 		return (1);
259 	}
260 	/* LINTED */
261 	aep = (struct alloc_ext_desc *)extbuf;
262 	err = verifytag(&aep->aed_tag, loc, &aep->aed_tag, UD_ALLOC_EXT_DESC);
263 	if (err) {
264 		(void) printf(
265 			gettext("Bad tag on alloc extent: %s\n"), tagerrs[err]);
266 		free(extbuf);
267 		return (1);
268 	}
269 	dir_adrlist = (uint8_t *)(aep + 1);
270 	dir_naddrs = aep->aed_len_aed / dir_adrsize;
271 	dir_adrindx = 0;
272 
273 	/* Swap the descriptors */
274 	for (i = 0, ap = dir_adrlist; i < dir_naddrs; i++, ap += dir_adrsize) {
275 		if (dir_adrsize == sizeof (short_ad_t)) {
276 			/* LINTED */
277 			ud_swap_short_ad((short_ad_t *)ap);
278 		} else if (dir_adrsize == sizeof (long_ad_t)) {
279 			/* LINTED */
280 			ud_swap_long_ad((long_ad_t *)ap);
281 		}
282 	}
283 
284 	return (0);
285 }
286 
287 /*
288  * Variables used in this function and their relationships:
289  *  *poffset - read pointer in the directory
290  *  dir_baseoff - offset at start of dirbuf
291  *  dir_baselen - length of valid data in current extent
292  *  dir_adrindx - index into current allocation extent for location of
293  *	dir_baseoff
294  *  dir_naddrs - number of entries in current allocation extent
295  *  dir_fidp - pointer to dirbuf or immediate data in file entry
296  *  baseblock - block address of dir_baseoff
297  *  newoff - *poffset - dir_baseoff
298  */
299 /* ARGSUSED1 */
300 static int32_t
301 getdir(struct file_entry *fp, struct bufarea **fbp,
302 	u_offset_t *poffset, struct file_id **fidpp)
303 {
304 	/* LINTED */
305 	register struct file_id *fidp = (struct file_id *)fidbuf;
306 	register struct short_ad *sap;
307 	register struct long_ad *lap;
308 	register int i, newoff, xoff = 0;
309 	uint32_t block = 0, nb, len, left;
310 	u_offset_t offset;
311 	int err, type;
312 
313 
314 again:
315 	offset = *poffset;
316 again2:
317 	if (debug)
318 		(void) printf("getdir %llx\n", offset);
319 	newoff = offset - dir_baseoff;
320 	if (newoff >= dir_basesize) {
321 		if (dirbuf) {
322 			free(dirbuf);
323 			dirbuf = NULL;
324 		}
325 	} else {
326 		if (block == 0)
327 			block = baseblock + (newoff / secsize);
328 		goto nextone;
329 	}
330 
331 again3:
332 	switch (fp->fe_icb_tag.itag_flags & 0x3) {
333 	case ICB_FLAG_SHORT_AD:
334 		/* LINTED */
335 		sap = &((short_ad_t *)dir_adrlist)[dir_adrindx];
336 		for (i = dir_adrindx; i < dir_naddrs; i++, sap++) {
337 			len = EXTLEN(sap->sad_ext_len);
338 			type = EXTYPE(sap->sad_ext_len);
339 			if (type == 3) {
340 				if (i < dir_naddrs - 1)
341 					errexit(gettext("Allocation extent not "
342 						"at end of list\n"));
343 				markbusy(sap->sad_ext_loc, len);
344 				if (getallocext(fp, sap->sad_ext_loc, len))
345 					return (1);
346 				goto again3;
347 			}
348 			if (newoff < len)
349 				break;
350 			newoff -= len;
351 			dir_baseoff += len;
352 			if (debug)
353 				(void) printf(
354 				    " loc %x len %x\n", sap->sad_ext_loc,
355 					len);
356 		}
357 		dir_adrindx = i;
358 		if (debug)
359 			(void) printf(" loc %x len %x\n", sap->sad_ext_loc,
360 				sap->sad_ext_len);
361 		baseblock = sap->sad_ext_loc;
362 		if (block == 0)
363 			block = baseblock;
364 		dir_basesize = len;
365 		if (type < 2)
366 			markbusy(sap->sad_ext_loc, len);
367 		if (type != 0) {
368 			*poffset += dir_basesize;
369 			goto again;
370 		}
371 		nb = roundup(len, secsize);
372 		dirbuf = (uint8_t *)malloc(nb);
373 		if (dirbuf == NULL)
374 			errexit(gettext("Can't allocate directory extent "
375 				"buffer\n"));
376 		if (bread(fsreadfd, (char *)dirbuf,
377 				fsbtodb(baseblock + part_start), nb) != 0) {
378 			errexit(gettext("Can't read directory extent\n"));
379 		}
380 		dir_fidp = dirbuf;
381 		break;
382 	case ICB_FLAG_LONG_AD:
383 		/* LINTED */
384 		lap = &((long_ad_t *)dir_adrlist)[dir_adrindx];
385 		for (i = dir_adrindx; i < dir_naddrs; i++, lap++) {
386 			len = EXTLEN(lap->lad_ext_len);
387 			type = EXTYPE(lap->lad_ext_len);
388 			if (type == 3) {
389 				if (i < dir_naddrs - 1)
390 					errexit(gettext("Allocation extent not "
391 						"at end of list\n"));
392 				markbusy(lap->lad_ext_loc, len);
393 				if (getallocext(fp, lap->lad_ext_loc, len))
394 					return (1);
395 				goto again3;
396 			}
397 			if (newoff < len)
398 				break;
399 			newoff -= len;
400 			dir_baseoff += len;
401 			if (debug)
402 				(void) printf(
403 				    " loc %x len %x\n", lap->lad_ext_loc,
404 					len);
405 		}
406 		dir_adrindx = i;
407 		if (debug)
408 			(void) printf(" loc %x len %x\n", lap->lad_ext_loc,
409 				lap->lad_ext_len);
410 		baseblock = lap->lad_ext_loc;
411 		if (block == 0)
412 			block = baseblock;
413 		dir_basesize = len;
414 		if (type < 2)
415 			markbusy(lap->lad_ext_loc, len);
416 		if (type != 0) {
417 			*poffset += dir_basesize;
418 			goto again;
419 		}
420 		nb = roundup(len, secsize);
421 		dirbuf = (uint8_t *)malloc(nb);
422 		if (dirbuf == NULL)
423 			errexit(gettext("Can't allocate directory extent "
424 				"buffer\n"));
425 		if (bread(fsreadfd, (char *)dirbuf,
426 				fsbtodb(baseblock + part_start), nb) != 0) {
427 			errexit(gettext("Can't read directory extent\n"));
428 		}
429 		dir_fidp = dirbuf;
430 		break;
431 	case ICB_FLAG_EXT_AD:
432 		break;
433 	case ICB_FLAG_ONE_AD:
434 		errexit(gettext("Logic error in getdir - at ICB_FLAG_ONE_AD "
435 			"case\n"));
436 		break;
437 	}
438 nextone:
439 	if (debug)
440 		(void) printf("getdirend blk %x dir_baseoff %llx newoff %x\n",
441 			block, dir_baseoff, newoff);
442 	left = dir_basesize - newoff;
443 	if (xoff + left > MAXFIDSIZE)
444 		left = MAXFIDSIZE - xoff;
445 	bcopy((char *)dir_fidp + newoff, (char *)fidbuf + xoff, left);
446 	xoff += left;
447 	/*
448 	 * If we have a fid that crosses an extent boundary, then force
449 	 * a read of the next extent, and fill up the rest of the fid.
450 	 */
451 	if (xoff < sizeof (fidp->fid_tag) ||
452 	    xoff < sizeof (fidp->fid_tag) + SWAP16(fidp->fid_tag.tag_crc_len)) {
453 		offset += left;
454 		if (debug)
455 			(void) printf("block crossing at offset %llx\n",
456 				offset);
457 		goto again2;
458 	}
459 	err = verifytag(&fidp->fid_tag, block, &fidp->fid_tag, UD_FILE_ID_DESC);
460 	if (debug) {
461 		dump16((char *)fidp, "\n");
462 	}
463 	if (err) {
464 		pwarn(gettext("Bad directory tag: %s\n"), tagerrs[err]);
465 		return (err);
466 	}
467 	*fidpp = fidp;
468 	return (0);
469 }
470 
471 static void
472 ckinode(struct file_entry *fp)
473 {
474 	register struct short_ad *sap;
475 	register struct long_ad *lap;
476 	register int i, type, len;
477 
478 	switch (fp->fe_icb_tag.itag_flags & 0x3) {
479 	case ICB_FLAG_SHORT_AD:
480 		dir_adrsize = sizeof (short_ad_t);
481 		dir_naddrs = fp->fe_len_adesc / sizeof (short_ad_t);
482 		/* LINTED */
483 		sap = (short_ad_t *)(fp->fe_spec + fp->fe_len_ear);
484 again1:
485 		for (i = 0; i < dir_naddrs; i++, sap++) {
486 			len = EXTLEN(sap->sad_ext_len);
487 			type = EXTYPE(sap->sad_ext_len);
488 			if (type < 2)
489 				markbusy(sap->sad_ext_loc, len);
490 			if (debug)
491 				(void) printf(
492 				    " loc %x len %x\n", sap->sad_ext_loc,
493 					sap->sad_ext_len);
494 			if (type == 3) {
495 				markbusy(sap->sad_ext_loc, len);
496 				/* This changes dir_naddrs and dir_adrlist */
497 				if (getallocext(fp, sap->sad_ext_loc, len))
498 					break;
499 				/* LINTED */
500 				sap = (short_ad_t *)dir_adrlist;
501 				goto again1;
502 			}
503 		}
504 		break;
505 	case ICB_FLAG_LONG_AD:
506 		dir_adrsize = sizeof (long_ad_t);
507 		dir_naddrs = fp->fe_len_adesc / sizeof (long_ad_t);
508 		/* LINTED */
509 		lap = (long_ad_t *)(fp->fe_spec + fp->fe_len_ear);
510 again2:
511 		for (i = 0; i < dir_naddrs; i++, lap++) {
512 			len = EXTLEN(lap->lad_ext_len);
513 			type = EXTYPE(lap->lad_ext_len);
514 			if (type < 2)
515 				markbusy(lap->lad_ext_loc, len);
516 			if (debug)
517 				(void) printf(
518 				    " loc %x len %x\n", lap->lad_ext_loc,
519 					lap->lad_ext_len);
520 			if (type == 3) {
521 				markbusy(sap->sad_ext_loc, len);
522 				/* This changes dir_naddrs and dir_adrlist */
523 				if (getallocext(fp, lap->lad_ext_loc, len))
524 					break;
525 				/* LINTED */
526 				lap = (long_ad_t *)dir_adrlist;
527 				goto again2;
528 			}
529 		}
530 		break;
531 	case ICB_FLAG_EXT_AD:
532 		break;
533 	case ICB_FLAG_ONE_AD:
534 		break;
535 	}
536 }
537 
538 static void
539 adjust(struct fileinfo *fip)
540 {
541 	register struct file_entry *fp;
542 	register struct bufarea *bp;
543 
544 	bp = getfilentry(fip->fe_block, fip->fe_len);
545 	if (bp == NULL)
546 		errexit(gettext("Unable to read file entry at %x\n"),
547 			fip->fe_block);
548 	/* LINTED */
549 	fp = (struct file_entry *)bp->b_un.b_buf;
550 	pwarn(gettext("LINK COUNT %s I=%x"),
551 		fip->fe_type == FTYPE_DIRECTORY ? "DIR" :
552 		fip->fe_type == FTYPE_SYMLINK ? "SYM" :
553 		fip->fe_type == FTYPE_FILE ? "FILE" : "???", fip->fe_block);
554 	(void) printf(gettext(" COUNT %d SHOULD BE %d"),
555 		fip->fe_lcount, fip->fe_lseen);
556 	if (preen) {
557 		if (fip->fe_lseen > fip->fe_lcount) {
558 			(void) printf("\n");
559 			pfatal(gettext("LINK COUNT INCREASING"));
560 		}
561 		(void) printf(gettext(" (ADJUSTED)\n"));
562 	}
563 	if (preen || reply(gettext("ADJUST")) == 1) {
564 		fp->fe_lcount = fip->fe_lseen;
565 		putfilentry(bp);
566 		dirty(bp);
567 		flush(fswritefd, bp);
568 	}
569 	bp->b_flags &= ~B_INUSE;
570 }
571 
572 void
573 dofreemap()
574 {
575 	register int i;
576 	register char *bp, *fp;
577 	struct inodesc idesc;
578 
579 	if (freemap == NULL)
580 		return;
581 
582 	/* Flip bits in the busy map */
583 	bp = busymap;
584 	for (i = 0, bp = busymap; i < part_bmp_bytes; i++, bp++)
585 		*bp = ~*bp;
586 
587 	/* Mark leftovers in byte as allocated */
588 	if (part_len % NBBY)
589 		bp[-1] &= (unsigned)0xff >> (NBBY - part_len % NBBY);
590 	bp = busymap;
591 	fp = freemap;
592 	bzero((char *)&idesc, sizeof (struct inodesc));
593 	idesc.id_type = ADDR;
594 	if (bcmp(bp, fp, part_bmp_bytes) != 0 &&
595 		dofix(&idesc, gettext("BLK(S) MISSING IN FREE BITMAP"))) {
596 		bcopy(bp, fp, part_bmp_bytes);
597 		maketag(&spacep->sbd_tag, &spacep->sbd_tag);
598 		bwrite(fswritefd, (char *)spacep, fsbtodb(part_bmp_loc),
599 			part_bmp_sectors * secsize);
600 	}
601 }
602 
603 void
604 dolvint()
605 {
606 	struct lvid_iu *lviup;
607 	struct inodesc idesc;
608 
609 	bzero((char *)&idesc, sizeof (struct inodesc));
610 	idesc.id_type = ADDR;
611 	lviup = (struct lvid_iu *)&lvintp->lvid_fst[2];
612 	if ((lvintp->lvid_fst[0] != part_len - n_blks ||
613 	    lvintp->lvid_int_type != LVI_CLOSE ||
614 	    lviup->lvidiu_nfiles != n_files ||
615 	    lviup->lvidiu_ndirs != n_dirs ||
616 	    lvintp->lvid_uniqid < maxuniqid) &&
617 	    dofix(&idesc, gettext("LOGICAL VOLUME INTEGRITY COUNTS WRONG"))) {
618 		lvintp->lvid_int_type = LVI_CLOSE;
619 		lvintp->lvid_fst[0] = part_len - n_blks;
620 		lviup->lvidiu_nfiles = n_files;
621 		lviup->lvidiu_ndirs = n_dirs;
622 		lvintp->lvid_uniqid = maxuniqid;
623 		maketag(&lvintp->lvid_tag, &lvintp->lvid_tag);
624 		bwrite(fswritefd, (char *)lvintp, fsbtodb(lvintblock),
625 			lvintlen);
626 	}
627 }
628