xref: /illumos-gate/usr/src/cmd/fs.d/udfs/fsck/pass1.c (revision dd72704bd9e794056c558153663c739e2012d721)
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 #include <stdio.h>
29 #include <strings.h>
30 #include <malloc.h>
31 #include <sys/param.h>
32 #include <sys/types.h>
33 #include <sys/sysmacros.h>
34 #include <sys/mntent.h>
35 #include <sys/vnode.h>
36 #include <sys/fs/udf_volume.h>
37 #include <sys/dkio.h>
38 #include <sys/vtoc.h>
39 #include "fsck.h"
40 #include "udfs.h"
41 #include <locale.h>
42 
43 uint64_t maxuniqid;	/* maximum unique id on medium */
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(void)
96 {
97 	struct file_entry *fp;
98 	struct fileinfo *fip;
99 	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 		fp = (struct file_entry *)bp->b_un.b_buf;
119 		fip->fe_lcount = fp->fe_lcount;
120 		fip->fe_type = fp->fe_icb_tag.itag_ftype;
121 		if (fp->fe_uniq_id >= maxuniqid)
122 			maxuniqid = fp->fe_uniq_id + 1;
123 
124 		if (fip->fe_block == rootblock &&
125 				fip->fe_type != FTYPE_DIRECTORY)
126 			errexit(gettext("Root file entry is not a "
127 				"directory\n"));
128 
129 		if (debug) {
130 			(void) printf("do %x len %d type %d lcount %d"
131 				" lseen %d end %llx\n",
132 				fip->fe_block, fip->fe_len,
133 				fip->fe_type, fip->fe_lcount,
134 				fip->fe_lseen, fp->fe_info_len);
135 		}
136 		switch (fip->fe_type) {
137 		case FTYPE_DIRECTORY:
138 			n_dirs++;
139 			offset = 0;
140 			end = fp->fe_info_len;
141 			fbp = NULL;
142 			opndir(fp);
143 			for (offset = 0; offset < end;
144 					offset += FID_LENGTH(fidp)) {
145 				err = getdir(fp, &fbp, &offset, &fidp);
146 				if (err) {
147 					pwarn(gettext("Bad directory entry in "
148 						"file %x at offset %llx\n"),
149 						fip->fe_block, offset);
150 					offset = end;
151 				}
152 				if (fidp->fid_flags & FID_DELETED)
153 					continue;
154 				(void) cachefile(fidp->fid_icb.lad_ext_loc,
155 					fidp->fid_icb.lad_ext_len);
156 			}
157 			if (dirbuf) {
158 				free(dirbuf);
159 				dirbuf = NULL;
160 			}
161 			if (fbp)
162 				fbp->b_flags &= ~B_INUSE;
163 			if (debug)
164 				(void) printf("Done %x\n", fip->fe_block);
165 			break;
166 
167 		case FTYPE_FILE:
168 		case FTYPE_SYMLINK:
169 			ckinode(fp);
170 			/* FALLTHROUGH */
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 	aep = (struct alloc_ext_desc *)extbuf;
261 	err = verifytag(&aep->aed_tag, loc, &aep->aed_tag, UD_ALLOC_EXT_DESC);
262 	if (err) {
263 		(void) printf(
264 			gettext("Bad tag on alloc extent: %s\n"), tagerrs[err]);
265 		free(extbuf);
266 		return (1);
267 	}
268 	dir_adrlist = (uint8_t *)(aep + 1);
269 	dir_naddrs = aep->aed_len_aed / dir_adrsize;
270 	dir_adrindx = 0;
271 
272 	/* Swap the descriptors */
273 	for (i = 0, ap = dir_adrlist; i < dir_naddrs; i++, ap += dir_adrsize) {
274 		if (dir_adrsize == sizeof (short_ad_t)) {
275 			ud_swap_short_ad((short_ad_t *)ap);
276 		} else if (dir_adrsize == sizeof (long_ad_t)) {
277 			ud_swap_long_ad((long_ad_t *)ap);
278 		}
279 	}
280 
281 	return (0);
282 }
283 
284 /*
285  * Variables used in this function and their relationships:
286  *  *poffset - read pointer in the directory
287  *  dir_baseoff - offset at start of dirbuf
288  *  dir_baselen - length of valid data in current extent
289  *  dir_adrindx - index into current allocation extent for location of
290  *	dir_baseoff
291  *  dir_naddrs - number of entries in current allocation extent
292  *  dir_fidp - pointer to dirbuf or immediate data in file entry
293  *  baseblock - block address of dir_baseoff
294  *  newoff - *poffset - dir_baseoff
295  */
296 /* ARGSUSED1 */
297 static int32_t
298 getdir(struct file_entry *fp, struct bufarea **fbp,
299 	u_offset_t *poffset, struct file_id **fidpp)
300 {
301 	struct file_id *fidp = (struct file_id *)fidbuf;
302 	struct short_ad *sap;
303 	struct long_ad *lap;
304 	int i, newoff, xoff = 0;
305 	uint32_t block = 0, nb, len = 0, left;
306 	u_offset_t offset;
307 	int err, type = 0;
308 
309 
310 again:
311 	offset = *poffset;
312 again2:
313 	if (debug)
314 		(void) printf("getdir %llx\n", offset);
315 	newoff = offset - dir_baseoff;
316 	if (newoff >= dir_basesize) {
317 		if (dirbuf) {
318 			free(dirbuf);
319 			dirbuf = NULL;
320 		}
321 	} else {
322 		if (block == 0)
323 			block = baseblock + (newoff / secsize);
324 		goto nextone;
325 	}
326 
327 again3:
328 	switch (fp->fe_icb_tag.itag_flags & 0x3) {
329 	case ICB_FLAG_SHORT_AD:
330 		sap = &((short_ad_t *)dir_adrlist)[dir_adrindx];
331 		for (i = dir_adrindx; i < dir_naddrs; i++, sap++) {
332 			len = EXTLEN(sap->sad_ext_len);
333 			type = EXTYPE(sap->sad_ext_len);
334 			if (type == 3) {
335 				if (i < dir_naddrs - 1)
336 					errexit(gettext("Allocation extent not "
337 						"at end of list\n"));
338 				markbusy(sap->sad_ext_loc, len);
339 				if (getallocext(fp, sap->sad_ext_loc, len))
340 					return (1);
341 				goto again3;
342 			}
343 			if (newoff < len)
344 				break;
345 			newoff -= len;
346 			dir_baseoff += len;
347 			if (debug)
348 				(void) printf(
349 				    " loc %x len %x\n", sap->sad_ext_loc,
350 					len);
351 		}
352 		dir_adrindx = i;
353 		if (debug)
354 			(void) printf(" loc %x len %x\n", sap->sad_ext_loc,
355 				sap->sad_ext_len);
356 		baseblock = sap->sad_ext_loc;
357 		if (block == 0)
358 			block = baseblock;
359 		dir_basesize = len;
360 		if (type < 2)
361 			markbusy(sap->sad_ext_loc, len);
362 		if (type != 0) {
363 			*poffset += dir_basesize;
364 			goto again;
365 		}
366 		nb = roundup(len, secsize);
367 		dirbuf = (uint8_t *)malloc(nb);
368 		if (dirbuf == NULL)
369 			errexit(gettext("Can't allocate directory extent "
370 				"buffer\n"));
371 		if (bread(fsreadfd, (char *)dirbuf,
372 				fsbtodb(baseblock + part_start), nb) != 0) {
373 			errexit(gettext("Can't read directory extent\n"));
374 		}
375 		dir_fidp = dirbuf;
376 		break;
377 	case ICB_FLAG_LONG_AD:
378 		lap = &((long_ad_t *)dir_adrlist)[dir_adrindx];
379 		for (i = dir_adrindx; i < dir_naddrs; i++, lap++) {
380 			len = EXTLEN(lap->lad_ext_len);
381 			type = EXTYPE(lap->lad_ext_len);
382 			if (type == 3) {
383 				if (i < dir_naddrs - 1)
384 					errexit(gettext("Allocation extent not "
385 						"at end of list\n"));
386 				markbusy(lap->lad_ext_loc, len);
387 				if (getallocext(fp, lap->lad_ext_loc, len))
388 					return (1);
389 				goto again3;
390 			}
391 			if (newoff < len)
392 				break;
393 			newoff -= len;
394 			dir_baseoff += len;
395 			if (debug)
396 				(void) printf(
397 				    " loc %x len %x\n", lap->lad_ext_loc,
398 					len);
399 		}
400 		dir_adrindx = i;
401 		if (debug)
402 			(void) printf(" loc %x len %x\n", lap->lad_ext_loc,
403 				lap->lad_ext_len);
404 		baseblock = lap->lad_ext_loc;
405 		if (block == 0)
406 			block = baseblock;
407 		dir_basesize = len;
408 		if (type < 2)
409 			markbusy(lap->lad_ext_loc, len);
410 		if (type != 0) {
411 			*poffset += dir_basesize;
412 			goto again;
413 		}
414 		nb = roundup(len, secsize);
415 		dirbuf = (uint8_t *)malloc(nb);
416 		if (dirbuf == NULL)
417 			errexit(gettext("Can't allocate directory extent "
418 				"buffer\n"));
419 		if (bread(fsreadfd, (char *)dirbuf,
420 				fsbtodb(baseblock + part_start), nb) != 0) {
421 			errexit(gettext("Can't read directory extent\n"));
422 		}
423 		dir_fidp = dirbuf;
424 		break;
425 	case ICB_FLAG_EXT_AD:
426 		break;
427 	case ICB_FLAG_ONE_AD:
428 		errexit(gettext("Logic error in getdir - at ICB_FLAG_ONE_AD "
429 			"case\n"));
430 		break;
431 	}
432 nextone:
433 	if (debug)
434 		(void) printf("getdirend blk %x dir_baseoff %llx newoff %x\n",
435 			block, dir_baseoff, newoff);
436 	left = dir_basesize - newoff;
437 	if (xoff + left > MAXFIDSIZE)
438 		left = MAXFIDSIZE - xoff;
439 	bcopy((char *)dir_fidp + newoff, (char *)fidbuf + xoff, left);
440 	xoff += left;
441 	/*
442 	 * If we have a fid that crosses an extent boundary, then force
443 	 * a read of the next extent, and fill up the rest of the fid.
444 	 */
445 	if (xoff < sizeof (fidp->fid_tag) ||
446 	    xoff < sizeof (fidp->fid_tag) + SWAP16(fidp->fid_tag.tag_crc_len)) {
447 		offset += left;
448 		if (debug)
449 			(void) printf("block crossing at offset %llx\n",
450 				offset);
451 		goto again2;
452 	}
453 	err = verifytag(&fidp->fid_tag, block, &fidp->fid_tag, UD_FILE_ID_DESC);
454 	if (debug) {
455 		dump16((char *)fidp, "\n");
456 	}
457 	if (err) {
458 		pwarn(gettext("Bad directory tag: %s\n"), tagerrs[err]);
459 		return (err);
460 	}
461 	*fidpp = fidp;
462 	return (0);
463 }
464 
465 static void
466 ckinode(struct file_entry *fp)
467 {
468 	struct short_ad *sap = NULL;
469 	struct long_ad *lap;
470 	int i, type, len;
471 
472 	switch (fp->fe_icb_tag.itag_flags & 0x3) {
473 	case ICB_FLAG_SHORT_AD:
474 		dir_adrsize = sizeof (short_ad_t);
475 		dir_naddrs = fp->fe_len_adesc / sizeof (short_ad_t);
476 		sap = (short_ad_t *)(fp->fe_spec + fp->fe_len_ear);
477 again1:
478 		for (i = 0; i < dir_naddrs; i++, sap++) {
479 			len = EXTLEN(sap->sad_ext_len);
480 			type = EXTYPE(sap->sad_ext_len);
481 			if (type < 2)
482 				markbusy(sap->sad_ext_loc, len);
483 			if (debug)
484 				(void) printf(
485 				    " loc %x len %x\n", sap->sad_ext_loc,
486 					sap->sad_ext_len);
487 			if (type == 3) {
488 				markbusy(sap->sad_ext_loc, len);
489 				/* This changes dir_naddrs and dir_adrlist */
490 				if (getallocext(fp, sap->sad_ext_loc, len))
491 					break;
492 				sap = (short_ad_t *)dir_adrlist;
493 				goto again1;
494 			}
495 		}
496 		break;
497 	case ICB_FLAG_LONG_AD:
498 		dir_adrsize = sizeof (long_ad_t);
499 		dir_naddrs = fp->fe_len_adesc / sizeof (long_ad_t);
500 		lap = (long_ad_t *)(fp->fe_spec + fp->fe_len_ear);
501 again2:
502 		for (i = 0; i < dir_naddrs; i++, lap++) {
503 			len = EXTLEN(lap->lad_ext_len);
504 			type = EXTYPE(lap->lad_ext_len);
505 			if (type < 2)
506 				markbusy(lap->lad_ext_loc, len);
507 			if (debug)
508 				(void) printf(
509 				    " loc %x len %x\n", lap->lad_ext_loc,
510 					lap->lad_ext_len);
511 			if (type == 3) {
512 				markbusy(sap->sad_ext_loc, len);
513 				/* This changes dir_naddrs and dir_adrlist */
514 				if (getallocext(fp, lap->lad_ext_loc, len))
515 					break;
516 				lap = (long_ad_t *)dir_adrlist;
517 				goto again2;
518 			}
519 		}
520 		break;
521 	case ICB_FLAG_EXT_AD:
522 		break;
523 	case ICB_FLAG_ONE_AD:
524 		break;
525 	}
526 }
527 
528 static void
529 adjust(struct fileinfo *fip)
530 {
531 	struct file_entry *fp;
532 	struct bufarea *bp;
533 
534 	bp = getfilentry(fip->fe_block, fip->fe_len);
535 	if (bp == NULL)
536 		errexit(gettext("Unable to read file entry at %x\n"),
537 			fip->fe_block);
538 	fp = (struct file_entry *)bp->b_un.b_buf;
539 	pwarn(gettext("LINK COUNT %s I=%x"),
540 		fip->fe_type == FTYPE_DIRECTORY ? "DIR" :
541 		fip->fe_type == FTYPE_SYMLINK ? "SYM" :
542 		fip->fe_type == FTYPE_FILE ? "FILE" : "???", fip->fe_block);
543 	(void) printf(gettext(" COUNT %d SHOULD BE %d"),
544 		fip->fe_lcount, fip->fe_lseen);
545 	if (preen) {
546 		if (fip->fe_lseen > fip->fe_lcount) {
547 			(void) printf("\n");
548 			pfatal(gettext("LINK COUNT INCREASING"));
549 		}
550 		(void) printf(gettext(" (ADJUSTED)\n"));
551 	}
552 	if (preen || reply(gettext("ADJUST")) == 1) {
553 		fp->fe_lcount = fip->fe_lseen;
554 		putfilentry(bp);
555 		dirty(bp);
556 		flush(fswritefd, bp);
557 	}
558 	bp->b_flags &= ~B_INUSE;
559 }
560 
561 void
562 dofreemap(void)
563 {
564 	int i;
565 	char *bp, *fp;
566 	struct inodesc idesc;
567 
568 	if (freemap == NULL)
569 		return;
570 
571 	/* Flip bits in the busy map */
572 	bp = busymap;
573 	for (i = 0, bp = busymap; i < part_bmp_bytes; i++, bp++)
574 		*bp = ~*bp;
575 
576 	/* Mark leftovers in byte as allocated */
577 	if (part_len % NBBY)
578 		bp[-1] &= (unsigned)0xff >> (NBBY - part_len % NBBY);
579 	bp = busymap;
580 	fp = freemap;
581 	bzero((char *)&idesc, sizeof (struct inodesc));
582 	idesc.id_type = ADDR;
583 	if (bcmp(bp, fp, part_bmp_bytes) != 0 &&
584 		dofix(&idesc, gettext("BLK(S) MISSING IN FREE BITMAP"))) {
585 		bcopy(bp, fp, part_bmp_bytes);
586 		maketag(&spacep->sbd_tag, &spacep->sbd_tag);
587 		bwrite(fswritefd, (char *)spacep, fsbtodb(part_bmp_loc),
588 			part_bmp_sectors * secsize);
589 	}
590 }
591 
592 void
593 dolvint(void)
594 {
595 	struct lvid_iu *lviup;
596 	struct inodesc idesc;
597 
598 	bzero((char *)&idesc, sizeof (struct inodesc));
599 	idesc.id_type = ADDR;
600 	lviup = (struct lvid_iu *)&lvintp->lvid_fst[2];
601 	if ((lvintp->lvid_fst[0] != part_len - n_blks ||
602 	    lvintp->lvid_int_type != LVI_CLOSE ||
603 	    lviup->lvidiu_nfiles != n_files ||
604 	    lviup->lvidiu_ndirs != n_dirs ||
605 	    lvintp->lvid_uniqid < maxuniqid) &&
606 	    dofix(&idesc, gettext("LOGICAL VOLUME INTEGRITY COUNTS WRONG"))) {
607 		lvintp->lvid_int_type = LVI_CLOSE;
608 		lvintp->lvid_fst[0] = part_len - n_blks;
609 		lviup->lvidiu_nfiles = n_files;
610 		lviup->lvidiu_ndirs = n_dirs;
611 		lvintp->lvid_uniqid = maxuniqid;
612 		maketag(&lvintp->lvid_tag, &lvintp->lvid_tag);
613 		bwrite(fswritefd, (char *)lvintp, fsbtodb(lvintblock),
614 			lvintlen);
615 	}
616 }
617