/* * 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); }