/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <strings.h>
#include <malloc.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/mntent.h>
#include <sys/vnode.h>
#include <sys/fs/udf_volume.h>
#include <sys/dkio.h>
#include <sys/vtoc.h>
#include "fsck.h"
#include "udfs.h"
#include <locale.h>
/*
* 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()
{
register struct file_entry *fp;
register struct fileinfo *fip;
register 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;
}
/* LINTED */
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);
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);
}
/* LINTED */
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)) {
/* LINTED */
ud_swap_short_ad((short_ad_t *)ap);
} else if (dir_adrsize == sizeof (long_ad_t)) {
/* LINTED */
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)
{
/* LINTED */
register struct file_id *fidp = (struct file_id *)fidbuf;
register struct short_ad *sap;
register struct long_ad *lap;
register int i, newoff, xoff = 0;
uint32_t block = 0, nb, len, left;
u_offset_t offset;
int err, type;
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:
/* LINTED */
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:
/* LINTED */
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)
{
register struct short_ad *sap;
register struct long_ad *lap;
register 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);
/* LINTED */
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;
/* LINTED */
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);
/* LINTED */
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;
/* LINTED */
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)
{
register struct file_entry *fp;
register 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);
/* LINTED */
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()
{
register int i;
register 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()
{
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);
}
}