# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (the "License"). You may not use this file except in compliance # with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 1999 by Sun Microsystems, Inc. # All rights reserved. # # Copyright (c) 2018, Joyent, Inc. FSTYPE= udfs LIBPROG= fsck ATTMK= $(LIBPROG) include ../../Makefile.fstype FSCKOBJS= main.o setup.o utilities.o pass1.o inode.o FSCKSRCS= $(FSCKOBJS:%.o=%.c) UDFSDIR= ../mkfs UDFSOBJS= udfslib.o #UDFSSRCS= $(UDFSOBJS:%.o=%.c) CERRWARN += -Wno-parentheses # not linted SMATCH=off OBJS= $(FSCKOBJS) $(UDFSOBJS) SRCS= $(FSCKSRCS) $(UDFSSRCS) ../mkfs/udfslib.c #CPPFLAGS += -D_LARGEFILE64_SOURCE CPPFLAGS += -I../mkfs LDLIBS += -ladm $(LIBPROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) %.o: $(UDFSDIR)/%.c $(COMPILE.c) $< # for messaging catalog # POFILE= fsck.po # for messaging catalog # catalog: $(POFILE) $(POFILE): $(SRCS) $(RM) $@ $(COMPILE.cpp) $(SRCS) > $(POFILE).i $(XGETTEXT) $(XGETFLAGS) $(POFILE).i sed "/^domain/d" messages.po > $@ $(RM) $(POFILE).i messages.po clean: $(RM) $(FSCKOBJS) $(UDFSOBJS) * 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. PORTIONS OF UDFS FUNCTIONALITY /* Copyright (c) 1984, 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. */ /* * Copyright (c) 1996, 1998-1999 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _FSCK_H #define _FSCK_H #ifdef __cplusplus extern "C" { #endif #define MAXDUP 10 /* limit on dup blks (per inode) */ #define MAXBAD 10 /* limit on bad blks (per inode) */ #define MAXBUFSPACE 256*1024 /* maximum space to allocate */ /* to buffers */ #define INOBUFSIZE 256*1024 /* size of buffer to read */ /* inodes in pass1 */ #define MAXBSIZE 8192 /* maximum allowed block size */ #define FIRSTAVDP 256 #ifndef BUFSIZ #define BUFSIZ 1024 #endif #ifdef sparc #define SWAP16(x) (((x) & 0xff) << 8 | ((x) >> 8) & 0xff) #define SWAP32(x) (((x) & 0xff) << 24 | ((x) & 0xff00) << 8 | \ ((x) & 0xff0000) >> 8 | ((x) >> 24) & 0xff) #define SWAP64(x) (SWAP32((x) >> 32) & 0xffffffff | SWAP32(x) << 32) #else #define SWAP16(x) (x) #define SWAP32(x) (x) #define SWAP64(x) (x) #endif #define NOTBUSY 00 /* Not busy when busymarked is set */ #define USTATE 01 /* inode not allocated */ #define FSTATE 02 /* inode is file */ #define DSTATE 03 /* inode is directory */ #define DFOUND 04 /* directory found during descent */ #define DCLEAR 05 /* directory is to be cleared */ #define FCLEAR 06 /* file is to be cleared */ #define SSTATE 07 /* inode is a shadow */ #define SCLEAR 010 /* shadow is to be cleared */ #define ESTATE 011 /* Inode extension */ #define ECLEAR 012 /* inode extension is to be cleared */ #define IBUSY 013 /* inode is marked busy by first pass */ #define LSTATE 014 /* Link tags */ struct dinode { int dummy; }; /* * buffer cache structure. */ struct bufarea { struct bufarea *b_next; /* free list queue */ struct bufarea *b_prev; /* free list queue */ daddr_t b_bno; int b_size; int b_errs; int b_flags; union { char *b_buf; /* buffer space */ daddr_t *b_indir; /* indirect block */ struct fs *b_fs; /* super block */ struct cg *b_cg; /* cylinder group */ struct dinode *b_dinode; /* inode block */ } b_un; char b_dirty; }; #define B_INUSE 1 #define MINBUFS 5 /* minimum number of buffers required */ extern struct log_vol_int_desc *lvintp; extern struct lvid_iu *lviup; extern struct space_bmap_desc *spacep; #define dirty(bp) (bp)->b_dirty = isdirty = 1 #define initbarea(bp) \ (bp)->b_dirty = 0; \ (bp)->b_bno = (daddr_t)-1; \ (bp)->b_flags = 0; enum fixstate {DONTKNOW, NOFIX, FIX}; struct inodesc { enum fixstate id_fix; /* policy on fixing errors */ int (*id_func)(); /* function to be applied to blocks of inode */ ino_t id_number; /* inode number described */ ino_t id_parent; /* for DATA nodes, their parent */ daddr_t id_blkno; /* current block number being examined */ int id_numfrags; /* number of frags contained in block */ offset_t id_filesize; /* for DATA nodes, the size of the directory */ int id_loc; /* for DATA nodes, current location in dir */ int id_entryno; /* for DATA nodes, current entry number */ struct direct *id_dirp; /* for DATA nodes, ptr to current entry */ char *id_name; /* for DATA nodes, name to find or enter */ char id_type; /* type of descriptor, DATA or ADDR */ }; /* file types */ #define DATA 1 #define ADDR 2 /* * File entry cache structures. */ typedef struct fileinfo { struct fileinfo *fe_nexthash; /* next entry in hash chain */ uint32_t fe_block; /* location of this file entry */ uint16_t fe_len; /* size of file entry */ uint16_t fe_lseen; /* number of links seen */ uint16_t fe_lcount; /* count from the file entry */ uint8_t fe_type; /* type of file entry */ uint8_t fe_state; /* flag bits */ } fileinfo_t; extern fileinfo_t *inphead, **inphash, *inpnext, *inplast; extern long listmax; #define FEGROW 512 extern char *devname; /* name of device being checked */ extern long secsize; /* actual disk sector size */ extern long fsbsize; /* file system block size (same as secsize) */ extern char nflag; /* assume a no response */ extern char yflag; /* assume a yes response */ extern int debug; /* output debugging info */ extern int rflag; /* check raw file systems */ extern int fflag; /* check regardless of clean flag (force) */ extern char preen; /* just fix normal inconsistencies */ extern char mountedfs; /* checking mounted device */ extern int exitstat; /* exit status (set to 8 if 'No' response) */ extern int fsmodified; /* 1 => write done to file system */ extern int fsreadfd; /* file descriptor for reading file system */ extern int fswritefd; /* file descriptor for writing file system */ extern int iscorrupt; /* known to be corrupt/inconsistent */ extern int isdirty; /* 1 => write pending to file system */ extern char mountpoint[100]; /* string set to contain mount point */ extern char *busymap; /* ptr to primary blk busy map */ extern char *freemap; /* ptr to copy of disk map */ extern uint32_t part_start; extern uint32_t part_len; extern uint32_t part_bmp_bytes; extern uint32_t part_bmp_sectors; extern uint32_t part_bmp_loc; extern uint32_t rootblock; extern uint32_t rootlen; extern uint32_t lvintblock; extern uint32_t lvintlen; extern daddr_t n_blks; /* number of blocks in use */ extern daddr_t n_files; /* number of files in use */ extern daddr_t n_dirs; /* number of dirs in use */ /* * bit map related macros */ #define bitloc(a, i) ((a)[(i)/NBBY]) #define setbit(a, i) ((a)[(i)/NBBY] |= 1<<((i)%NBBY)) #define clrbit(a, i) ((a)[(i)/NBBY] &= ~(1<<((i)%NBBY))) #define isset(a, i) ((a)[(i)/NBBY] & (1<<((i)%NBBY))) #define isclr(a, i) (((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0) #define setbmap(blkno) setbit(blockmap, blkno) #define testbmap(blkno) isset(blockmap, blkno) #define clrbmap(blkno) clrbit(blockmap, blkno) #define setbusy(blkno) setbit(busymap, blkno) #define testbusy(blkno) isset(busymap, blkno) #define clrbusy(blkno) clrbit(busymap, blkno) #define fsbtodb(blkno) ((blkno) * (fsbsize / DEV_BSIZE)) #define dbtofsb(blkno) ((blkno) / (fsbsize / DEV_BSIZE)) #define STOP 0x01 #define SKIP 0x02 #define KEEPON 0x04 #define ALTERED 0x08 #define FOUND 0x10 time_t time(); struct dinode *ginode(); struct inoinfo *getinoinfo(); struct fileinfo *cachefile(); ino_t allocino(); int findino(); char *setup(); void markbusy(daddr_t, long); #ifndef MNTTYPE_UDFS #define MNTTYPE_UDFS "udfs" #endif #define SPACEMAP_OFF 24 #define FID_LENGTH(fid) (((sizeof (struct file_id) + \ (fid)->fid_iulen + (fid)->fid_idlen - 2) + 3) & ~3) #define EXTYPE(len) (((len) >> 30) & 3) #define EXTLEN(len) ((len) & 0x3fffffff) /* Integrity descriptor types */ #define LVI_OPEN 0 #define LVI_CLOSE 1 #ifdef __cplusplus } #endif #endif /* _FSCK_H */ /* * 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 #include extern void errexit(char *, ...); extern unsigned int largefile_count; /* * Enter inodes into the cache. */ struct fileinfo * cachefile(feblock, len) uint32_t feblock; uint32_t len; { register struct fileinfo *inp; struct fileinfo **inpp; inpp = &inphash[feblock % listmax]; for (inp = *inpp; inp; inp = inp->fe_nexthash) { if (inp->fe_block == feblock) break; } if (!inp) { if (inpnext >= inplast) { inpnext = (struct fileinfo *)calloc(FEGROW + 1, sizeof (struct fileinfo)); if (inpnext == NULL) errexit(gettext("Cannot grow inphead list\n")); /* Link at extra entry so that we can find them */ inplast->fe_nexthash = inpnext; inplast->fe_block = (uint32_t)-1; inplast = &inpnext[FEGROW]; } inp = inpnext++; inp->fe_block = feblock; inp->fe_len = (uint16_t)len; inp->fe_lseen = 1; inp->fe_nexthash = *inpp; *inpp = inp; if (debug) { (void) printf("cacheing %x\n", feblock); } } else { inp->fe_lseen++; if (debug) { (void) printf("cache hit %x lcount %d lseen %d\n", feblock, inp->fe_lcount, inp->fe_lseen); } } return (inp); } /* * Copyright 2005 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 /* use isdigit macro rather than 4.1 libc routine */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fsck.h" #include int debug; char nflag; char yflag; int rflag; static int wflag; /* check only writable filesystems */ int fflag; static int sflag; /* print status flag */ int isdirty; int fsmodified; int iscorrupt; int exitstat; uint32_t part_len; daddr_t n_blks; daddr_t n_files; daddr_t n_dirs; char preen; char mountpoint[100]; char mountedfs; char *devname; struct log_vol_int_desc *lvintp; extern int32_t writable(char *); extern void pfatal(char *, ...); extern void printfree(); extern void pwarn(char *, ...); extern void pass1(); extern void dofreemap(); extern void dolvint(); extern char *getfullblkname(); extern char *getfullrawname(); static int mflag = 0; /* sanity check only */ char *mntopt(); void catch(), catchquit(), voidquit(); int returntosingle; static void checkfilesys(); static void check_sanity(); static void usage(); static char *subopts [] = { #define PREEN 0 "p", #define DEBUG 1 "d", #define READ_ONLY 2 "r", #define ONLY_WRITES 3 "w", #define FORCE 4 /* force checking, even if clean */ "f", #define STATS 5 /* print time and busy stats */ "s", NULL }; uint32_t ecma_version = 2; int main(int argc, char *argv[]) { int c; char *suboptions, *value; int suboption; (void) setlocale(LC_ALL, ""); while ((c = getopt(argc, argv, "mnNo:VyYz")) != EOF) { switch (c) { case 'm': mflag++; break; case 'n': /* default no answer flag */ case 'N': nflag++; yflag = 0; break; case 'o': /* * udfs specific options. */ suboptions = optarg; while (*suboptions != '\0') { suboption = getsubopt(&suboptions, subopts, &value); switch (suboption) { case PREEN: preen++; break; case DEBUG: debug++; break; case READ_ONLY: break; case ONLY_WRITES: /* check only writable filesystems */ wflag++; break; case FORCE: fflag++; break; case STATS: sflag++; break; default: usage(); } } break; case 'V': { int opt_count; char *opt_text; (void) fprintf(stdout, "fsck -F udfs "); for (opt_count = 1; opt_count < argc; opt_count++) { opt_text = argv[opt_count]; if (opt_text) (void) fprintf(stdout, " %s ", opt_text); } (void) fprintf(stdout, "\n"); } break; case 'y': /* default yes answer flag */ case 'Y': yflag++; nflag = 0; break; case '?': usage(); } } argc -= optind; argv = &argv[optind]; rflag++; /* check raw devices */ if (signal(SIGINT, SIG_IGN) != SIG_IGN) { (void) signal(SIGINT, catch); } if (preen) { (void) signal(SIGQUIT, catchquit); } if (argc) { while (argc-- > 0) { if (wflag && !writable(*argv)) { (void) fprintf(stderr, gettext("not writeable '%s'\n"), *argv); argv++; } else checkfilesys(*argv++); } exit(exitstat); } return (0); } static void checkfilesys(char *filesys) { char *devstr; mountedfs = 0; iscorrupt = 1; if ((devstr = setup(filesys)) == 0) { if (iscorrupt == 0) return; if (preen) pfatal(gettext("CAN'T CHECK FILE SYSTEM.")); if ((exitstat == 0) && (mflag)) exitstat = 32; exit(exitstat); } else devname = devstr; if (mflag) check_sanity(filesys); /* this never returns */ iscorrupt = 0; /* * 1: scan inodes tallying blocks used */ if (preen == 0) { if (mountedfs) (void) printf(gettext("** Currently Mounted on %s\n"), mountpoint); if (mflag) { (void) printf( gettext("** Phase 1 - Sanity Check only\n")); return; } else (void) printf( gettext("** Phase 1 - Check Directories " "and Blocks\n")); } pass1(); if (sflag) { if (preen) (void) printf("%s: ", devname); else (void) printf("** "); } if (debug) (void) printf("pass1 isdirty %d\n", isdirty); if (debug) printfree(); dofreemap(); dolvint(); /* * print out summary statistics */ pwarn(gettext("%d files, %d dirs, %d used, %d free\n"), n_files, n_dirs, n_blks, part_len - n_blks); if (iscorrupt) exitstat = 36; if (!fsmodified) return; if (!preen) (void) printf( gettext("\n***** FILE SYSTEM WAS MODIFIED *****\n")); if (mountedfs) { exitstat = 40; } } /* * exit 0 - file system is unmounted and okay * exit 32 - file system is unmounted and needs checking * exit 33 - file system is mounted * for root file system * exit 34 - cannot stat device */ static void check_sanity(char *filename) { struct stat stbd, stbr; struct ustat usb; char *devname; struct vfstab vfsbuf; FILE *vfstab; int is_root = 0; int is_usr = 0; int is_block = 0; if (stat(filename, &stbd) < 0) { (void) fprintf(stderr, gettext("udfs fsck: sanity check failed : cannot stat " "%s\n"), filename); exit(34); } if ((stbd.st_mode & S_IFMT) == S_IFBLK) is_block = 1; else if ((stbd.st_mode & S_IFMT) == S_IFCHR) is_block = 0; else { (void) fprintf(stderr, gettext("udfs fsck: sanity check failed: %s not " "block or character device\n"), filename); exit(34); } /* * Determine if this is the root file system via vfstab. Give up * silently on failures. The whole point of this is not to care * if the root file system is already mounted. * * XXX - similar for /usr. This should be fixed to simply return * a new code indicating, mounted and needs to be checked. */ if ((vfstab = fopen(VFSTAB, "r")) != 0) { if (getvfsfile(vfstab, &vfsbuf, "/") == 0) { if (is_block) devname = vfsbuf.vfs_special; else devname = vfsbuf.vfs_fsckdev; if (stat(devname, &stbr) == 0) if (stbr.st_rdev == stbd.st_rdev) is_root = 1; } if (getvfsfile(vfstab, &vfsbuf, "/usr") == 0) { if (is_block) devname = vfsbuf.vfs_special; else devname = vfsbuf.vfs_fsckdev; if (stat(devname, &stbr) == 0) if (stbr.st_rdev == stbd.st_rdev) is_usr = 1; } } /* * XXX - only works if filename is a block device or if * character and block device has the same dev_t value */ if (is_root == 0 && is_usr == 0 && ustat(stbd.st_rdev, &usb) == 0) { (void) fprintf(stderr, gettext("udfs fsck: sanity check: %s " "already mounted\n"), filename); exit(33); } if (lvintp->lvid_int_type == LVI_CLOSE) { (void) fprintf(stderr, gettext("udfs fsck: sanity check: %s okay\n"), filename); } else { (void) fprintf(stderr, gettext("udfs fsck: sanity check: %s needs checking\n"), filename); exit(32); } exit(0); } char * unrawname(char *name) { char *dp; if ((dp = getfullblkname(name)) == NULL) return (""); return (dp); } char * rawname(char *name) { char *dp; if ((dp = getfullrawname(name)) == NULL) return (""); return (dp); } char * hasvfsopt(struct vfstab *vfs, char *opt) { char *f, *opts; static char *tmpopts; if (vfs->vfs_mntopts == NULL) return (NULL); if (tmpopts == 0) { tmpopts = (char *)calloc(256, sizeof (char)); if (tmpopts == 0) return (0); } (void) strncpy(tmpopts, vfs->vfs_mntopts, (sizeof (tmpopts) - 1)); opts = tmpopts; f = mntopt(&opts); for (; *f; f = mntopt(&opts)) { if (strncmp(opt, f, strlen(opt)) == 0) return (f - tmpopts + vfs->vfs_mntopts); } return (NULL); } static void usage() { (void) fprintf(stderr, gettext("udfs usage: fsck [-F udfs] " "[generic options] [-o p,w,s] [special ....]\n")); exit(31+1); } /* * 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); } } /* * Copyright 2005 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. */ #define DKTYPENAMES #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for ENDIAN defines */ #include #include #include #include #include #include #include #include #include #include #include "fsck.h" extern void errexit(char *, ...); extern int32_t mounted(char *); extern void pwarn(char *, ...); extern void pfatal(char *, ...); extern void printclean(); extern void bufinit(); extern void ckfini(); extern int32_t bread(int32_t, char *, daddr_t, long); extern int32_t reply(char *); static int32_t readvolseq(int32_t); static uint32_t get_last_block(); extern int32_t verifytag(struct tag *, uint32_t, struct tag *, int); extern char *tagerrs[]; #define POWEROF2(num) (((num) & ((num) - 1)) == 0) extern int mflag; long fsbsize; static char hotroot; /* checking root device */ char *freemap; char *busymap; uint32_t part_start; uint32_t lvintlen; uint32_t lvintblock; uint32_t rootblock; uint32_t rootlen; uint32_t part_bmp_bytes; uint32_t part_bmp_sectors; uint32_t part_bmp_loc; int fsreadfd; int fswritefd; long secsize; long numdirs, numfiles, listmax; struct fileinfo *inphead, **inphash, *inpnext, *inplast; struct space_bmap_desc *spacep; static struct unall_desc *unallp; static struct pri_vol_desc *pvolp; static struct part_desc *partp; static struct phdr_desc *pheadp; static struct log_vol_desc *logvp; struct lvid_iu *lviup; static struct vdp_desc *volp; static struct anch_vol_desc_ptr *avdp; static struct iuvd_desc *iudp; char avdbuf[MAXBSIZE]; /* buffer for anchor volume descriptor */ char *main_vdbuf; /* buffer for entire main volume sequence */ char *res_vdbuf; /* buffer for reserved volume sequence */ int serialnum = -1; /* set from primary volume descriptor */ char * setup(char *dev) { dev_t rootdev; struct stat statb; static char devstr[MAXPATHLEN]; char *raw, *rawname(), *unrawname(); struct ustat ustatb; if (stat("/", &statb) < 0) errexit(gettext("Can't stat root\n")); rootdev = statb.st_dev; devname = devstr; (void) strncpy(devstr, dev, sizeof (devstr)); restat: if (stat(devstr, &statb) < 0) { (void) printf(gettext("Can't stat %s\n"), devstr); exitstat = 34; return (0); } /* * A mount point is specified. But the mount point doesn't * match entries in the /etc/vfstab. * Search mnttab, because if the fs is error locked, it is * allowed to be fsck'd while mounted. */ if ((statb.st_mode & S_IFMT) == S_IFDIR) { (void) printf(gettext("%s is not a block or " "character device\n"), dev); return (0); } if ((statb.st_mode & S_IFMT) == S_IFBLK) { if (rootdev == statb.st_rdev) hotroot++; else if (ustat(statb.st_rdev, &ustatb) == 0) { (void) printf(gettext("%s is a mounted file system, " "ignored\n"), dev); exitstat = 33; return (0); } } if ((statb.st_mode & S_IFMT) == S_IFDIR) { FILE *vfstab; struct vfstab vfsbuf; /* * Check vfstab for a mount point with this name */ if ((vfstab = fopen(VFSTAB, "r")) == NULL) { errexit(gettext("Can't open checklist file: %s\n"), VFSTAB); } while (getvfsent(vfstab, &vfsbuf) == 0) { if (strcmp(devstr, vfsbuf.vfs_mountp) == 0) { if (strcmp(vfsbuf.vfs_fstype, MNTTYPE_UDFS) != 0) { /* * found the entry but it is not a * udfs filesystem, don't check it */ (void) fclose(vfstab); return (0); } (void) strcpy(devstr, vfsbuf.vfs_special); if (rflag) { raw = rawname( unrawname(vfsbuf.vfs_special)); (void) strcpy(devstr, raw); } goto restat; } } (void) fclose(vfstab); } else if (((statb.st_mode & S_IFMT) != S_IFBLK) && ((statb.st_mode & S_IFMT) != S_IFCHR)) { if (preen) pwarn(gettext("file is not a block or " "character device.\n")); else if (reply(gettext("file is not a block or " "character device; OK")) == 0) return (0); /* * To fsck regular files (fs images) * we need to clear the rflag since * regular files don't have raw names. --CW */ rflag = 0; } if (mounted(devstr)) { if (rflag) mountedfs++; else { (void) printf(gettext("%s is mounted, fsck on BLOCK " "device ignored\n"), devstr); exit(33); } sync(); /* call sync, only when devstr's mounted */ } if (rflag) { char blockname[MAXPATHLEN]; /* * For root device check, must check * block devices. */ (void) strcpy(blockname, devstr); if (stat(unrawname(blockname), &statb) < 0) { (void) printf(gettext("Can't stat %s\n"), blockname); exitstat = 34; return (0); } } if (rootdev == statb.st_rdev) hotroot++; if ((fsreadfd = open(devstr, O_RDONLY)) < 0) { (void) printf(gettext("Can't open %s\n"), devstr); exitstat = 34; return (0); } if (preen == 0 || debug != 0) (void) printf("** %s", devstr); if (nflag || (fswritefd = open(devstr, O_WRONLY)) < 0) { fswritefd = -1; if (preen && !debug) pfatal(gettext("(NO WRITE ACCESS)\n")); (void) printf(gettext(" (NO WRITE)")); } if (preen == 0) (void) printf("\n"); if (debug && (hotroot || mountedfs)) { (void) printf("** %s", devstr); if (hotroot) (void) printf(" is root fs%s", mountedfs? " and": ""); if (mountedfs) (void) printf(" is mounted"); (void) printf(".\n"); } fsmodified = 0; if (readvolseq(1) == 0) return (0); if (fflag == 0 && preen && lvintp->lvid_int_type == LVI_CLOSE) { iscorrupt = 0; printclean(); return (0); } listmax = FEGROW; inphash = (struct fileinfo **)calloc(FEGROW, sizeof (struct fileinfo *)); inphead = (struct fileinfo *)calloc(FEGROW + 1, sizeof (struct fileinfo)); if (inphead == NULL || inphash == NULL) { (void) printf(gettext("cannot alloc %ld bytes for inphead\n"), listmax * sizeof (struct fileinfo)); goto badsb; } inpnext = inphead; inplast = &inphead[listmax]; bufinit(); return (devstr); badsb: ckfini(); exitstat = 39; return (0); } static int check_pri_vol_desc(struct tag *tp) { pvolp = (struct pri_vol_desc *)tp; return (0); } static int check_avdp(struct tag *tp) { avdp = (struct anch_vol_desc_ptr *)tp; return (0); } static int check_vdp(struct tag *tp) { volp = (struct vdp_desc *)tp; return (0); } static int check_iuvd(struct tag *tp) { iudp = (struct iuvd_desc *)tp; return (0); } static int check_part_desc(struct tag *tp) { partp = (struct part_desc *)tp; /* LINTED */ pheadp = (struct phdr_desc *)&partp->pd_pc_use; part_start = partp->pd_part_start; part_len = partp->pd_part_length; if (debug) (void) printf("partition start %x len %x\n", part_start, part_len); return (0); } static int check_log_desc(struct tag *tp) { logvp = (struct log_vol_desc *)tp; return (0); } static int check_unall_desc(struct tag *tp) { unallp = (struct unall_desc *)tp; return (0); } /* ARGSUSED */ static int check_term_desc(struct tag *tp) { return (0); } static int check_lvint(struct tag *tp) { /* LINTED */ lvintp = (struct log_vol_int_desc *)tp; return (0); } void dump16(char *cp, char *nl) { int i; long *ptr; for (i = 0; i < 16; i += 4) { /* LINTED */ ptr = (long *)(cp + i); (void) printf("%08lx ", *ptr); } (void) printf(nl); } /* * Read in the super block and its summary info. */ /* ARGSUSED */ static int readvolseq(int32_t listerr) { struct tag *tp; long_ad_t *lap; struct anch_vol_desc_ptr *avp; uint8_t *cp, *end; daddr_t nextblock; int err; long freelen; daddr_t avdp; struct file_set_desc *fileset; uint32_t filesetblock; uint32_t filesetlen; if (debug) (void) printf("Disk partition size: %x\n", get_last_block()); /* LINTED */ avp = (struct anch_vol_desc_ptr *)avdbuf; tp = &avp->avd_tag; for (fsbsize = 512; fsbsize <= MAXBSIZE; fsbsize <<= 1) { avdp = FIRSTAVDP * fsbsize / DEV_BSIZE; if (bread(fsreadfd, avdbuf, avdp, fsbsize) != 0) return (0); err = verifytag(tp, FIRSTAVDP, tp, UD_ANCH_VOL_DESC); if (debug) (void) printf("bsize %ld tp->tag %d, %s\n", fsbsize, tp->tag_id, tagerrs[err]); if (err == 0) break; } if (fsbsize > MAXBSIZE) errexit(gettext("Can't find anchor volume descriptor\n")); secsize = fsbsize; if (debug) (void) printf("fsbsize = %ld\n", fsbsize); main_vdbuf = malloc(avp->avd_main_vdse.ext_len); res_vdbuf = malloc(avp->avd_res_vdse.ext_len); if (main_vdbuf == NULL || res_vdbuf == NULL) errexit("cannot allocate space for volume sequences\n"); if (debug) (void) printf("reading volume sequences " "(%d bytes at %x and %x)\n", avp->avd_main_vdse.ext_len, avp->avd_main_vdse.ext_loc, avp->avd_res_vdse.ext_loc); if (bread(fsreadfd, main_vdbuf, fsbtodb(avp->avd_main_vdse.ext_loc), avp->avd_main_vdse.ext_len) != 0) return (0); if (bread(fsreadfd, res_vdbuf, fsbtodb(avp->avd_res_vdse.ext_loc), avp->avd_res_vdse.ext_len) != 0) return (0); end = (uint8_t *)main_vdbuf + avp->avd_main_vdse.ext_len; nextblock = avp->avd_main_vdse.ext_loc; for (cp = (uint8_t *)main_vdbuf; cp < end; cp += fsbsize, nextblock++) { /* LINTED */ tp = (struct tag *)cp; err = verifytag(tp, nextblock, tp, 0); if (debug) { dump16((char *)cp, ""); (void) printf("blk %lx err %s tag %d\n", nextblock, tagerrs[err], tp->tag_id); } if (err == 0) { if (serialnum >= 0 && tp->tag_sno != serialnum) { (void) printf(gettext("serial number mismatch " "tag type %d, block %lx\n"), tp->tag_id, nextblock); continue; } switch (tp->tag_id) { case UD_PRI_VOL_DESC: serialnum = tp->tag_sno; if (debug) { (void) printf("serial number = %d\n", serialnum); } err = check_pri_vol_desc(tp); break; case UD_ANCH_VOL_DESC: err = check_avdp(tp); break; case UD_VOL_DESC_PTR: err = check_vdp(tp); break; case UD_IMPL_USE_DESC: err = check_iuvd(tp); break; case UD_PART_DESC: err = check_part_desc(tp); break; case UD_LOG_VOL_DESC: err = check_log_desc(tp); break; case UD_UNALL_SPA_DESC: err = check_unall_desc(tp); break; case UD_TERM_DESC: err = check_term_desc(tp); goto done; break; case UD_LOG_VOL_INT: err = check_lvint(tp); break; default: (void) printf(gettext("Invalid volume " "sequence tag %d\n"), tp->tag_id); } } else { (void) printf(gettext("Volume sequence tag error %s\n"), tagerrs[err]); } } done: if (!partp || !logvp) { (void) printf(gettext("Missing partition header or" " logical volume descriptor\n")); return (0); } /* Get the logical volume integrity descriptor */ lvintblock = logvp->lvd_int_seq_ext.ext_loc; lvintlen = logvp->lvd_int_seq_ext.ext_len; lvintp = (struct log_vol_int_desc *)malloc(lvintlen); if (debug) (void) printf("Logvolint at %x for %d bytes\n", lvintblock, lvintlen); if (lvintp == NULL) { (void) printf(gettext("Can't allocate space for logical" " volume integrity sequence\n")); return (0); } if (bread(fsreadfd, (char *)lvintp, fsbtodb(lvintblock), lvintlen) != 0) { return (0); } err = verifytag(&lvintp->lvid_tag, lvintblock, &lvintp->lvid_tag, UD_LOG_VOL_INT); if (debug) { dump16((char *)lvintp, "\n"); } if (err) { (void) printf(gettext("Log_vol_int tag error: %s, tag = %d\n"), tagerrs[err], lvintp->lvid_tag.tag_id); return (0); } /* Get pointer to implementation use area */ lviup = (struct lvid_iu *)&lvintp->lvid_fst[lvintp->lvid_npart*2]; if (debug) { (void) printf("free space %d total %d ", lvintp->lvid_fst[0], lvintp->lvid_fst[1]); (void) printf(gettext("nfiles %d ndirs %d\n"), lviup->lvidiu_nfiles, lviup->lvidiu_ndirs); } /* Set up free block map and read in the existing free space map */ freelen = pheadp->phdr_usb.sad_ext_len; if (freelen == 0) { (void) printf(gettext("No partition free map\n")); } part_bmp_bytes = (part_len + NBBY - 1) / NBBY; busymap = calloc((unsigned)part_bmp_bytes, sizeof (char)); if (busymap == NULL) { (void) printf(gettext("Can't allocate free block bitmap\n")); return (0); } if (freelen) { part_bmp_sectors = (part_bmp_bytes + SPACEMAP_OFF + secsize - 1) / secsize; part_bmp_loc = pheadp->phdr_usb.sad_ext_loc + part_start; /* Mark the partition map blocks busy */ markbusy(pheadp->phdr_usb.sad_ext_loc, part_bmp_sectors * secsize); spacep = (struct space_bmap_desc *) malloc(secsize*part_bmp_sectors); if (spacep == NULL) { (void) printf(gettext("Can't allocate partition " "map\n")); return (0); } if (bread(fsreadfd, (char *)spacep, fsbtodb(part_bmp_loc), part_bmp_sectors * secsize) != 0) return (0); cp = (uint8_t *)spacep; err = verifytag(&spacep->sbd_tag, pheadp->phdr_usb.sad_ext_loc, &spacep->sbd_tag, UD_SPA_BMAP_DESC); if (debug) { dump16((char *)cp, ""); (void) printf("blk %x err %s tag %d\n", part_bmp_loc, tagerrs[err], spacep->sbd_tag.tag_id); } freemap = (char *)cp + SPACEMAP_OFF; if (debug) (void) printf("err %s tag %x space bitmap at %x" " length %d nbits %d nbytes %d\n", tagerrs[err], spacep->sbd_tag.tag_id, part_bmp_loc, part_bmp_sectors, spacep->sbd_nbits, spacep->sbd_nbytes); if (err) { (void) printf(gettext("Space bitmap tag error, %s, " "tag = %d\n"), tagerrs[err], spacep->sbd_tag.tag_id); return (0); } } /* Get the fileset descriptor */ lap = (long_ad_t *)&logvp->lvd_lvcu; filesetblock = lap->lad_ext_loc; filesetlen = lap->lad_ext_len; markbusy(filesetblock, filesetlen); if (debug) (void) printf("Fileset descriptor at %x for %d bytes\n", filesetblock, filesetlen); if (!filesetlen) { (void) printf(gettext("No file set descriptor found\n")); return (0); } fileset = (struct file_set_desc *)malloc(filesetlen); if (fileset == NULL) { (void) printf(gettext("Unable to allocate fileset\n")); return (0); } if (bread(fsreadfd, (char *)fileset, fsbtodb(filesetblock + part_start), filesetlen) != 0) { return (0); } err = verifytag(&fileset->fsd_tag, filesetblock, &fileset->fsd_tag, UD_FILE_SET_DESC); if (err) { (void) printf(gettext("Fileset tag error, tag = %d, %s\n"), fileset->fsd_tag.tag_id, tagerrs[err]); return (0); } /* Get the address of the root file entry */ lap = (long_ad_t *)&fileset->fsd_root_icb; rootblock = lap->lad_ext_loc; rootlen = lap->lad_ext_len; if (debug) (void) printf("Root at %x for %d bytes\n", rootblock, rootlen); return (1); } uint32_t get_last_block() { struct vtoc vtoc; struct dk_cinfo dki_info; if (ioctl(fsreadfd, DKIOCGVTOC, (intptr_t)&vtoc) != 0) { (void) fprintf(stderr, gettext("Unable to read VTOC\n")); return (0); } if (vtoc.v_sanity != VTOC_SANE) { (void) fprintf(stderr, gettext("Vtoc.v_sanity != VTOC_SANE\n")); return (0); } if (ioctl(fsreadfd, DKIOCINFO, (intptr_t)&dki_info) != 0) { (void) fprintf(stderr, gettext("Could not get the slice information\n")); return (0); } if (dki_info.dki_partition > V_NUMPAR) { (void) fprintf(stderr, gettext("dki_info.dki_partition > V_NUMPAR\n")); return (0); } return ((uint32_t)vtoc.v_part[dki_info.dki_partition].p_size); } /* * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. */ /* 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 #include #include #include #include #include #include #include #include #include #include #include #include "fsck.h" #include #include static struct bufarea bufhead; extern int32_t verifytag(struct tag *, uint32_t, struct tag *, int); extern char *tagerrs[]; extern void maketag(struct tag *, struct tag *); extern char *hasvfsopt(struct vfstab *, char *); static struct bufarea *getdatablk(daddr_t, long); static struct bufarea *getblk(struct bufarea *, daddr_t, long); void flush(int32_t, struct bufarea *); int32_t bread(int32_t, char *, daddr_t, long); void bwrite(int, char *, daddr_t, long); static int32_t getaline(FILE *, char *, int32_t); void errexit(char *, ...) __NORETURN; static long diskreads, totalreads; /* Disk cache statistics */ offset_t llseek(); extern unsigned int largefile_count; /* * An unexpected inconsistency occured. * Die if preening, otherwise just print message and continue. */ /* VARARGS1 */ void pfatal(char *fmt, ...) { va_list args; va_start(args, fmt); if (preen) { (void) printf("%s: ", devname); (void) vprintf(fmt, args); (void) printf("\n"); (void) printf( gettext("%s: UNEXPECTED INCONSISTENCY; RUN fsck " "MANUALLY.\n"), devname); va_end(args); exit(36); } (void) vprintf(fmt, args); va_end(args); } /* * Pwarn just prints a message when not preening, * or a warning (preceded by filename) when preening. */ /* VARARGS1 */ void pwarn(char *fmt, ...) { va_list args; va_start(args, fmt); if (preen) (void) printf("%s: ", devname); (void) vprintf(fmt, args); va_end(args); } /* VARARGS1 */ void errexit(char *fmt, ...) { va_list args; va_start(args, fmt); (void) vprintf(fmt, args); va_end(args); exit(39); } void markbusy(daddr_t block, long count) { register int i; count = roundup(count, secsize) / secsize; for (i = 0; i < count; i++, block++) { if ((unsigned)block > part_len) { pwarn(gettext("Block %lx out of range\n"), block); break; } if (testbusy(block)) pwarn(gettext("Dup block %lx\n"), block); else { n_blks++; setbusy(block); } } } void printfree() { int i, startfree, endfree; startfree = -1; endfree = -1; for (i = 0; i < part_len; i++) { if (!testbusy(i)) { if (startfree <= 0) startfree = i; endfree = i; } else if (startfree >= 0) { (void) printf("free: %x-%x\n", startfree, endfree - 1); startfree = -1; } } if (startfree >= 0) { (void) printf("free: %x-%x\n", startfree, endfree); } } struct bufarea * getfilentry(uint32_t block, int len) { struct bufarea *bp; struct file_entry *fp; int err; if (len > fsbsize) { (void) printf(gettext("File entry at %x is too long " "(%d bytes)\n"), block, len); len = fsbsize; } bp = getdatablk((daddr_t)(block + part_start), fsbsize); if (bp->b_errs) { bp->b_flags &= ~B_INUSE; return (NULL); } /* LINTED */ fp = (struct file_entry *)bp->b_un.b_buf; err = verifytag(&fp->fe_tag, block, &fp->fe_tag, UD_FILE_ENTRY); if (err) { (void) printf(gettext("Tag error %s or bad file entry, " "tag=%d\n"), tagerrs[err], fp->fe_tag.tag_id); bp->b_flags &= ~B_INUSE; return (NULL); } return (bp); } void putfilentry(struct bufarea *bp) { struct file_entry *fp; /* LINTED */ fp = (struct file_entry *)bp->b_un.b_buf; maketag(&fp->fe_tag, &fp->fe_tag); } int32_t reply(char *question) { char line[80]; if (preen) pfatal(gettext("INTERNAL ERROR: GOT TO reply()")); (void) printf("\n%s? ", question); if (nflag || fswritefd < 0) { (void) printf(gettext(" no\n\n")); iscorrupt = 1; /* known to be corrupt */ return (0); } if (yflag) { (void) printf(gettext(" yes\n\n")); return (1); } if (getaline(stdin, line, sizeof (line)) == EOF) errexit("\n"); (void) printf("\n"); if (line[0] == 'y' || line[0] == 'Y') return (1); else { iscorrupt = 1; /* known to be corrupt */ return (0); } } int32_t getaline(FILE *fp, char *loc, int32_t maxlen) { int n; register char *p, *lastloc; p = loc; lastloc = &p[maxlen-1]; while ((n = getc(fp)) != '\n') { if (n == EOF) return (EOF); if (!isspace(n) && p < lastloc) *p++ = n; } *p = 0; return (p - loc); } /* * Malloc buffers and set up cache. */ void bufinit() { register struct bufarea *bp; long bufcnt, i; char *bufp; bufp = malloc((unsigned int)fsbsize); if (bufp == 0) errexit(gettext("cannot allocate buffer pool\n")); bufhead.b_next = bufhead.b_prev = &bufhead; bufcnt = MAXBUFSPACE / fsbsize; if (bufcnt < MINBUFS) bufcnt = MINBUFS; for (i = 0; i < bufcnt; i++) { bp = (struct bufarea *)malloc(sizeof (struct bufarea)); bufp = malloc((unsigned int)fsbsize); if (bp == NULL || bufp == NULL) { if (i >= MINBUFS) break; errexit(gettext("cannot allocate buffer pool\n")); } bp->b_un.b_buf = bufp; bp->b_prev = &bufhead; bp->b_next = bufhead.b_next; bufhead.b_next->b_prev = bp; bufhead.b_next = bp; initbarea(bp); } bufhead.b_size = i; /* save number of buffers */ } /* * Manage a cache of directory blocks. */ static struct bufarea * getdatablk(daddr_t blkno, long size) { register struct bufarea *bp; for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) if (bp->b_bno == fsbtodb(blkno)) goto foundit; for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) if ((bp->b_flags & B_INUSE) == 0) break; if (bp == &bufhead) errexit(gettext("deadlocked buffer pool\n")); (void) getblk(bp, blkno, size); /* fall through */ foundit: totalreads++; bp->b_prev->b_next = bp->b_next; bp->b_next->b_prev = bp->b_prev; bp->b_prev = &bufhead; bp->b_next = bufhead.b_next; bufhead.b_next->b_prev = bp; bufhead.b_next = bp; bp->b_flags |= B_INUSE; return (bp); } static struct bufarea * getblk(struct bufarea *bp, daddr_t blk, long size) { daddr_t dblk; dblk = fsbtodb(blk); if (bp->b_bno == dblk) return (bp); flush(fswritefd, bp); diskreads++; bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size); bp->b_bno = dblk; bp->b_size = size; return (bp); } void flush(int32_t fd, struct bufarea *bp) { if (!bp->b_dirty) return; if (bp->b_errs != 0) pfatal(gettext("WRITING ZERO'ED BLOCK %d TO DISK\n"), bp->b_bno); bp->b_dirty = 0; bp->b_errs = 0; bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); } static void rwerror(char *mesg, daddr_t blk) { if (preen == 0) (void) printf("\n"); pfatal(gettext("CANNOT %s: BLK %ld"), mesg, blk); if (reply(gettext("CONTINUE")) == 0) errexit(gettext("Program terminated\n")); } void ckfini() { struct bufarea *bp, *nbp; int cnt = 0; for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { cnt++; flush(fswritefd, bp); nbp = bp->b_prev; free(bp->b_un.b_buf); free((char *)bp); } if (bufhead.b_size != cnt) errexit(gettext("Panic: lost %d buffers\n"), bufhead.b_size - cnt); if (debug) (void) printf("cache missed %ld of %ld (%ld%%)\n", diskreads, totalreads, totalreads ? diskreads * 100 / totalreads : 0); (void) close(fsreadfd); (void) close(fswritefd); } int32_t bread(int fd, char *buf, daddr_t blk, long size) { char *cp; int i, errs; offset_t offset = ldbtob(blk); offset_t addr; if (llseek(fd, offset, 0) < 0) rwerror(gettext("SEEK"), blk); else if (read(fd, buf, (int)size) == size) return (0); rwerror(gettext("READ"), blk); if (llseek(fd, offset, 0) < 0) rwerror(gettext("SEEK"), blk); errs = 0; bzero(buf, (int)size); pwarn(gettext("THE FOLLOWING SECTORS COULD NOT BE READ:")); for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) { addr = ldbtob(blk + i); if (llseek(fd, addr, SEEK_CUR) < 0 || read(fd, cp, (int)secsize) < 0) { (void) printf(" %ld", blk + i); errs++; } } (void) printf("\n"); return (errs); } void bwrite(int fd, char *buf, daddr_t blk, long size) { int i, n; char *cp; offset_t offset = ldbtob(blk); offset_t addr; if (fd < 0) return; if (llseek(fd, offset, 0) < 0) rwerror(gettext("SEEK"), blk); else if (write(fd, buf, (int)size) == size) { fsmodified = 1; return; } rwerror(gettext("WRITE"), blk); if (llseek(fd, offset, 0) < 0) rwerror(gettext("SEEK"), blk); pwarn(gettext("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:")); for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) { n = 0; addr = ldbtob(blk + i); if (llseek(fd, addr, SEEK_CUR) < 0 || (n = write(fd, cp, DEV_BSIZE)) < 0) { (void) printf(" %ld", blk + i); } else if (n > 0) { fsmodified = 1; } } (void) printf("\n"); } void catch() { ckfini(); exit(37); } /* * When preening, allow a single quit to signal * a special exit after filesystem checks complete * so that reboot sequence may be interrupted. */ void catchquit() { extern int returntosingle; (void) printf(gettext("returning to single-user after filesystem " "check\n")); returntosingle = 1; (void) signal(SIGQUIT, SIG_DFL); } /* * determine whether an inode should be fixed. */ /* ARGSUSED1 */ int32_t dofix(struct inodesc *idesc, char *msg) { switch (idesc->id_fix) { case DONTKNOW: pwarn(msg); if (preen) { (void) printf(gettext(" (SALVAGED)\n")); idesc->id_fix = FIX; return (ALTERED); } if (reply(gettext("SALVAGE")) == 0) { idesc->id_fix = NOFIX; return (0); } idesc->id_fix = FIX; return (ALTERED); case FIX: return (ALTERED); case NOFIX: return (0); default: errexit(gettext("UNKNOWN INODESC FIX MODE %d\n"), idesc->id_fix); } /* NOTREACHED */ } /* * Check to see if unraw version of name is already mounted. * Since we do not believe /etc/mnttab, we stat the mount point * to see if it is really looks mounted. */ int mounted(char *name) { int found = 0; struct mnttab mnt; FILE *mnttab; struct stat device_stat, mount_stat; char *blkname, *unrawname(); int err; mnttab = fopen(MNTTAB, "r"); if (mnttab == NULL) { (void) printf(gettext("can't open %s\n"), MNTTAB); return (0); } blkname = unrawname(name); while ((getmntent(mnttab, &mnt)) == 0) { if (strcmp(mnt.mnt_fstype, MNTTYPE_UDFS) != 0) { continue; } if (strcmp(blkname, mnt.mnt_special) == 0) { err = stat(mnt.mnt_mountp, &mount_stat); err |= stat(mnt.mnt_special, &device_stat); if (err < 0) continue; if (device_stat.st_rdev == mount_stat.st_dev) { (void) strncpy(mnt.mnt_mountp, mountpoint, sizeof (mountpoint)); if (hasmntopt(&mnt, MNTOPT_RO) != 0) found = 2; /* mounted as RO */ else found = 1; /* mounted as R/W */ } break; } } (void) fclose(mnttab); return (found); } /* * Check to see if name corresponds to an entry in vfstab, and that the entry * does not have option ro. */ int writable(char *name) { int rw = 1; struct vfstab vfsbuf; FILE *vfstab; char *blkname, *unrawname(); vfstab = fopen(VFSTAB, "r"); if (vfstab == NULL) { (void) printf(gettext("can't open %s\n"), VFSTAB); return (1); } blkname = unrawname(name); if ((getvfsspec(vfstab, &vfsbuf, blkname) == 0) && (vfsbuf.vfs_fstype != NULL) && (strcmp(vfsbuf.vfs_fstype, MNTTYPE_UDFS) == 0) && (hasvfsopt(&vfsbuf, MNTOPT_RO))) { rw = 0; } (void) fclose(vfstab); return (rw); } /* * print out clean info */ void printclean() { char *s; switch (lvintp->lvid_int_type) { case LVI_CLOSE: s = gettext("clean"); break; case LVI_OPEN: s = gettext("active"); break; default: s = gettext("unknown"); } if (preen) pwarn(gettext("is %s.\n"), s); else (void) printf("** %s is %s.\n", devname, s); }