/* * Copyright 1999 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980, 1986, 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by the University of California, Berkeley and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include #include #include #include #include #include #include #include #include #include "fsck.h" #include "udfs.h" #include uint64_t maxuniqid; /* maximum unique id on medium */ /* * for each large file ( size > MAXOFF_T) this global counter * gets incremented here. */ extern unsigned int largefile_count; extern void pwarn(char *, ...); extern void pfatal(char *, ...); extern void errexit(char *, ...); extern int32_t verifytag(struct tag *, uint32_t, struct tag *, int); extern char *tagerrs[]; extern void maketag(struct tag *, struct tag *); extern void flush(int32_t, struct bufarea *); extern void putfilentry(struct bufarea *); extern int32_t bread(int32_t, char *, daddr_t, long); extern void bwrite(int, char *, daddr_t, long); extern int32_t dofix(struct inodesc *, char *); extern int32_t reply(char *); extern void ud_swap_short_ad(short_ad_t *); extern void ud_swap_long_ad(long_ad_t *); extern void dump16(char *, char *); static void adjust(struct fileinfo *); static void opndir(struct file_entry *); static int32_t getdir(struct file_entry *, struct bufarea **, u_offset_t *, struct file_id **); static void ckinode(struct file_entry *); struct bufarea *getfilentry(); /* Fields for traversing an allocation extent */ static uint32_t dir_adrsize; static uint32_t dir_adrindx; static uint32_t dir_naddrs; static uint8_t *extbuf; static uint8_t *dir_adrlist; /* Keep track of where we are in the directory */ static u_offset_t dir_baseoff; static uint32_t dir_basesize; static uint8_t *dirbuf; static uint8_t *dir_fidp; static uint32_t baseblock; #define MAXFIDSIZE 2048 static uint8_t fidbuf[MAXFIDSIZE]; void pass1(void) { struct file_entry *fp; struct fileinfo *fip; struct bufarea *bp; struct file_id *fidp; struct bufarea *fbp; int err; (void) cachefile(rootblock, rootlen); fip = &inphead[0]; /* The root */ fip->fe_lseen = 0; /* Didn't get here through directory */ n_files = n_dirs = 0; while (fip->fe_block) { u_offset_t offset, end; markbusy(fip->fe_block, fip->fe_len); bp = getfilentry(fip->fe_block, fip->fe_len); if (bp == NULL) { pwarn(gettext("Unable to read file entry at %x\n"), fip->fe_block); goto next; } fp = (struct file_entry *)bp->b_un.b_buf; fip->fe_lcount = fp->fe_lcount; fip->fe_type = fp->fe_icb_tag.itag_ftype; if (fp->fe_uniq_id >= maxuniqid) maxuniqid = fp->fe_uniq_id + 1; if (fip->fe_block == rootblock && fip->fe_type != FTYPE_DIRECTORY) errexit(gettext("Root file entry is not a " "directory\n")); if (debug) { (void) printf("do %x len %d type %d lcount %d" " lseen %d end %llx\n", fip->fe_block, fip->fe_len, fip->fe_type, fip->fe_lcount, fip->fe_lseen, fp->fe_info_len); } switch (fip->fe_type) { case FTYPE_DIRECTORY: n_dirs++; offset = 0; end = fp->fe_info_len; fbp = NULL; opndir(fp); for (offset = 0; offset < end; offset += FID_LENGTH(fidp)) { err = getdir(fp, &fbp, &offset, &fidp); if (err) { pwarn(gettext("Bad directory entry in " "file %x at offset %llx\n"), fip->fe_block, offset); offset = end; } if (fidp->fid_flags & FID_DELETED) continue; (void) cachefile(fidp->fid_icb.lad_ext_loc, fidp->fid_icb.lad_ext_len); } if (dirbuf) { free(dirbuf); dirbuf = NULL; } if (fbp) fbp->b_flags &= ~B_INUSE; if (debug) (void) printf("Done %x\n", fip->fe_block); break; case FTYPE_FILE: case FTYPE_SYMLINK: ckinode(fp); /* FALLTHROUGH */ default: n_files++; break; } putfilentry(bp); bp->b_flags &= ~B_INUSE; next: /* At end of this set of fips, get the next set */ if ((++fip)->fe_block == (uint32_t)-1) fip = fip->fe_nexthash; } /* Find bad link counts */ fip = &inphead[0]; while (fip->fe_block) { if (fip->fe_lcount != fip->fe_lseen) adjust(fip); /* At end of this set of fips, get the next set */ if ((++fip)->fe_block == (uint32_t)-1) fip = fip->fe_nexthash; } } static void opndir(struct file_entry *fp) { if (dirbuf) { free(dirbuf); dirbuf = NULL; } if (extbuf) { free(extbuf); extbuf = NULL; } dir_baseoff = 0; dir_basesize = 0; dir_adrindx = 0; switch (fp->fe_icb_tag.itag_flags & 0x3) { case ICB_FLAG_SHORT_AD: dir_adrsize = sizeof (short_ad_t); dir_naddrs = fp->fe_len_adesc / sizeof (short_ad_t); dir_adrlist = (uint8_t *)(fp->fe_spec + fp->fe_len_ear); break; case ICB_FLAG_LONG_AD: dir_adrsize = sizeof (long_ad_t); dir_naddrs = fp->fe_len_adesc / sizeof (long_ad_t); dir_adrlist = (uint8_t *)(fp->fe_spec + fp->fe_len_ear); break; case ICB_FLAG_EXT_AD: errexit(gettext("Can't handle ext_ads in directories/n")); break; case ICB_FLAG_ONE_AD: dir_adrsize = 0; dir_naddrs = 0; dir_adrlist = NULL; dir_basesize = fp->fe_len_adesc; dir_fidp = (uint8_t *)(fp->fe_spec + fp->fe_len_ear); baseblock = fp->fe_tag.tag_loc; break; } } /* Allocate and read in an allocation extent */ /* ARGSUSED */ int getallocext(struct file_entry *fp, uint32_t loc, uint32_t len) { uint32_t nb; uint8_t *ap; int i; int err; struct alloc_ext_desc *aep; if (debug) (void) printf(" allocext loc %x len %x\n", loc, len); nb = roundup(len, secsize); if (extbuf) free(extbuf); extbuf = (uint8_t *)malloc(nb); if (extbuf == NULL) errexit(gettext("Can't allocate directory extent buffer\n")); if (bread(fsreadfd, (char *)extbuf, fsbtodb(loc + part_start), nb) != 0) { (void) fprintf(stderr, gettext("Can't read allocation extent\n")); return (1); } aep = (struct alloc_ext_desc *)extbuf; err = verifytag(&aep->aed_tag, loc, &aep->aed_tag, UD_ALLOC_EXT_DESC); if (err) { (void) printf( gettext("Bad tag on alloc extent: %s\n"), tagerrs[err]); free(extbuf); return (1); } dir_adrlist = (uint8_t *)(aep + 1); dir_naddrs = aep->aed_len_aed / dir_adrsize; dir_adrindx = 0; /* Swap the descriptors */ for (i = 0, ap = dir_adrlist; i < dir_naddrs; i++, ap += dir_adrsize) { if (dir_adrsize == sizeof (short_ad_t)) { ud_swap_short_ad((short_ad_t *)ap); } else if (dir_adrsize == sizeof (long_ad_t)) { ud_swap_long_ad((long_ad_t *)ap); } } return (0); } /* * Variables used in this function and their relationships: * *poffset - read pointer in the directory * dir_baseoff - offset at start of dirbuf * dir_baselen - length of valid data in current extent * dir_adrindx - index into current allocation extent for location of * dir_baseoff * dir_naddrs - number of entries in current allocation extent * dir_fidp - pointer to dirbuf or immediate data in file entry * baseblock - block address of dir_baseoff * newoff - *poffset - dir_baseoff */ /* ARGSUSED1 */ static int32_t getdir(struct file_entry *fp, struct bufarea **fbp, u_offset_t *poffset, struct file_id **fidpp) { struct file_id *fidp = (struct file_id *)fidbuf; struct short_ad *sap; struct long_ad *lap; int i, newoff, xoff = 0; uint32_t block = 0, nb, len = 0, left; u_offset_t offset; int err, type = 0; again: offset = *poffset; again2: if (debug) (void) printf("getdir %llx\n", offset); newoff = offset - dir_baseoff; if (newoff >= dir_basesize) { if (dirbuf) { free(dirbuf); dirbuf = NULL; } } else { if (block == 0) block = baseblock + (newoff / secsize); goto nextone; } again3: switch (fp->fe_icb_tag.itag_flags & 0x3) { case ICB_FLAG_SHORT_AD: sap = &((short_ad_t *)dir_adrlist)[dir_adrindx]; for (i = dir_adrindx; i < dir_naddrs; i++, sap++) { len = EXTLEN(sap->sad_ext_len); type = EXTYPE(sap->sad_ext_len); if (type == 3) { if (i < dir_naddrs - 1) errexit(gettext("Allocation extent not " "at end of list\n")); markbusy(sap->sad_ext_loc, len); if (getallocext(fp, sap->sad_ext_loc, len)) return (1); goto again3; } if (newoff < len) break; newoff -= len; dir_baseoff += len; if (debug) (void) printf( " loc %x len %x\n", sap->sad_ext_loc, len); } dir_adrindx = i; if (debug) (void) printf(" loc %x len %x\n", sap->sad_ext_loc, sap->sad_ext_len); baseblock = sap->sad_ext_loc; if (block == 0) block = baseblock; dir_basesize = len; if (type < 2) markbusy(sap->sad_ext_loc, len); if (type != 0) { *poffset += dir_basesize; goto again; } nb = roundup(len, secsize); dirbuf = (uint8_t *)malloc(nb); if (dirbuf == NULL) errexit(gettext("Can't allocate directory extent " "buffer\n")); if (bread(fsreadfd, (char *)dirbuf, fsbtodb(baseblock + part_start), nb) != 0) { errexit(gettext("Can't read directory extent\n")); } dir_fidp = dirbuf; break; case ICB_FLAG_LONG_AD: lap = &((long_ad_t *)dir_adrlist)[dir_adrindx]; for (i = dir_adrindx; i < dir_naddrs; i++, lap++) { len = EXTLEN(lap->lad_ext_len); type = EXTYPE(lap->lad_ext_len); if (type == 3) { if (i < dir_naddrs - 1) errexit(gettext("Allocation extent not " "at end of list\n")); markbusy(lap->lad_ext_loc, len); if (getallocext(fp, lap->lad_ext_loc, len)) return (1); goto again3; } if (newoff < len) break; newoff -= len; dir_baseoff += len; if (debug) (void) printf( " loc %x len %x\n", lap->lad_ext_loc, len); } dir_adrindx = i; if (debug) (void) printf(" loc %x len %x\n", lap->lad_ext_loc, lap->lad_ext_len); baseblock = lap->lad_ext_loc; if (block == 0) block = baseblock; dir_basesize = len; if (type < 2) markbusy(lap->lad_ext_loc, len); if (type != 0) { *poffset += dir_basesize; goto again; } nb = roundup(len, secsize); dirbuf = (uint8_t *)malloc(nb); if (dirbuf == NULL) errexit(gettext("Can't allocate directory extent " "buffer\n")); if (bread(fsreadfd, (char *)dirbuf, fsbtodb(baseblock + part_start), nb) != 0) { errexit(gettext("Can't read directory extent\n")); } dir_fidp = dirbuf; break; case ICB_FLAG_EXT_AD: break; case ICB_FLAG_ONE_AD: errexit(gettext("Logic error in getdir - at ICB_FLAG_ONE_AD " "case\n")); break; } nextone: if (debug) (void) printf("getdirend blk %x dir_baseoff %llx newoff %x\n", block, dir_baseoff, newoff); left = dir_basesize - newoff; if (xoff + left > MAXFIDSIZE) left = MAXFIDSIZE - xoff; bcopy((char *)dir_fidp + newoff, (char *)fidbuf + xoff, left); xoff += left; /* * If we have a fid that crosses an extent boundary, then force * a read of the next extent, and fill up the rest of the fid. */ if (xoff < sizeof (fidp->fid_tag) || xoff < sizeof (fidp->fid_tag) + SWAP16(fidp->fid_tag.tag_crc_len)) { offset += left; if (debug) (void) printf("block crossing at offset %llx\n", offset); goto again2; } err = verifytag(&fidp->fid_tag, block, &fidp->fid_tag, UD_FILE_ID_DESC); if (debug) { dump16((char *)fidp, "\n"); } if (err) { pwarn(gettext("Bad directory tag: %s\n"), tagerrs[err]); return (err); } *fidpp = fidp; return (0); } static void ckinode(struct file_entry *fp) { struct short_ad *sap = NULL; struct long_ad *lap; int i, type, len; switch (fp->fe_icb_tag.itag_flags & 0x3) { case ICB_FLAG_SHORT_AD: dir_adrsize = sizeof (short_ad_t); dir_naddrs = fp->fe_len_adesc / sizeof (short_ad_t); sap = (short_ad_t *)(fp->fe_spec + fp->fe_len_ear); again1: for (i = 0; i < dir_naddrs; i++, sap++) { len = EXTLEN(sap->sad_ext_len); type = EXTYPE(sap->sad_ext_len); if (type < 2) markbusy(sap->sad_ext_loc, len); if (debug) (void) printf( " loc %x len %x\n", sap->sad_ext_loc, sap->sad_ext_len); if (type == 3) { markbusy(sap->sad_ext_loc, len); /* This changes dir_naddrs and dir_adrlist */ if (getallocext(fp, sap->sad_ext_loc, len)) break; sap = (short_ad_t *)dir_adrlist; goto again1; } } break; case ICB_FLAG_LONG_AD: dir_adrsize = sizeof (long_ad_t); dir_naddrs = fp->fe_len_adesc / sizeof (long_ad_t); lap = (long_ad_t *)(fp->fe_spec + fp->fe_len_ear); again2: for (i = 0; i < dir_naddrs; i++, lap++) { len = EXTLEN(lap->lad_ext_len); type = EXTYPE(lap->lad_ext_len); if (type < 2) markbusy(lap->lad_ext_loc, len); if (debug) (void) printf( " loc %x len %x\n", lap->lad_ext_loc, lap->lad_ext_len); if (type == 3) { markbusy(sap->sad_ext_loc, len); /* This changes dir_naddrs and dir_adrlist */ if (getallocext(fp, lap->lad_ext_loc, len)) break; lap = (long_ad_t *)dir_adrlist; goto again2; } } break; case ICB_FLAG_EXT_AD: break; case ICB_FLAG_ONE_AD: break; } } static void adjust(struct fileinfo *fip) { struct file_entry *fp; struct bufarea *bp; bp = getfilentry(fip->fe_block, fip->fe_len); if (bp == NULL) errexit(gettext("Unable to read file entry at %x\n"), fip->fe_block); fp = (struct file_entry *)bp->b_un.b_buf; pwarn(gettext("LINK COUNT %s I=%x"), fip->fe_type == FTYPE_DIRECTORY ? "DIR" : fip->fe_type == FTYPE_SYMLINK ? "SYM" : fip->fe_type == FTYPE_FILE ? "FILE" : "???", fip->fe_block); (void) printf(gettext(" COUNT %d SHOULD BE %d"), fip->fe_lcount, fip->fe_lseen); if (preen) { if (fip->fe_lseen > fip->fe_lcount) { (void) printf("\n"); pfatal(gettext("LINK COUNT INCREASING")); } (void) printf(gettext(" (ADJUSTED)\n")); } if (preen || reply(gettext("ADJUST")) == 1) { fp->fe_lcount = fip->fe_lseen; putfilentry(bp); dirty(bp); flush(fswritefd, bp); } bp->b_flags &= ~B_INUSE; } void dofreemap(void) { int i; char *bp, *fp; struct inodesc idesc; if (freemap == NULL) return; /* Flip bits in the busy map */ bp = busymap; for (i = 0, bp = busymap; i < part_bmp_bytes; i++, bp++) *bp = ~*bp; /* Mark leftovers in byte as allocated */ if (part_len % NBBY) bp[-1] &= (unsigned)0xff >> (NBBY - part_len % NBBY); bp = busymap; fp = freemap; bzero((char *)&idesc, sizeof (struct inodesc)); idesc.id_type = ADDR; if (bcmp(bp, fp, part_bmp_bytes) != 0 && dofix(&idesc, gettext("BLK(S) MISSING IN FREE BITMAP"))) { bcopy(bp, fp, part_bmp_bytes); maketag(&spacep->sbd_tag, &spacep->sbd_tag); bwrite(fswritefd, (char *)spacep, fsbtodb(part_bmp_loc), part_bmp_sectors * secsize); } } void dolvint(void) { struct lvid_iu *lviup; struct inodesc idesc; bzero((char *)&idesc, sizeof (struct inodesc)); idesc.id_type = ADDR; lviup = (struct lvid_iu *)&lvintp->lvid_fst[2]; if ((lvintp->lvid_fst[0] != part_len - n_blks || lvintp->lvid_int_type != LVI_CLOSE || lviup->lvidiu_nfiles != n_files || lviup->lvidiu_ndirs != n_dirs || lvintp->lvid_uniqid < maxuniqid) && dofix(&idesc, gettext("LOGICAL VOLUME INTEGRITY COUNTS WRONG"))) { lvintp->lvid_int_type = LVI_CLOSE; lvintp->lvid_fst[0] = part_len - n_blks; lviup->lvidiu_nfiles = n_files; lviup->lvidiu_ndirs = n_dirs; lvintp->lvid_uniqid = maxuniqid; maketag(&lvintp->lvid_tag, &lvintp->lvid_tag); bwrite(fswritefd, (char *)lvintp, fsbtodb(lvintblock), lvintlen); } }