static BLOCK *find_free_slot(FS *fs) { int attempts = 0, max = 3; while (attempts < max) { BLOCK *ptr = fs->cache_tail; attempts++; while (ptr != NULL) { if (!(ptr->flags & F_CACHED) || (!(ptr->flags & F_DIRTY) && ptr->pins == 0)) { flush_block(fs, ptr); remove_block_from_hash(fs, ptr); ptr->flags = 0; set_flag(ptr, F_CACHED); ptr->pins = 0; return ptr; } ptr = ptr->prev; } flush_fs(fs); } error("No more free slots in cache -- increase size or find bug"); return NULL; }
caddr_t setup(caddr_t dev) { int corefs; static char devstr[MAXPATHLEN + 1]; havesb = 0; devname = devstr; derive_devstr(dev, devstr, sizeof (devstr)); errorlocked = is_errorlocked(devstr); corefs = check_mount_state(devstr, sizeof (devstr)); sblock_init(); if (open_and_intro(devstr, corefs) == -1) goto cleanup; /* * Check log state */ if (!logsetup(devstr)) goto cleanup; /* * Flush fs if we're going to do anything other than a sanity check. * Note, if logging then the fs was already flushed in logsetup(). */ if (!islog && !mflag) flush_fs(); if (find_superblock(devstr) == -1) goto cleanup; fixup_superblock(); if (errorlocked && (initial_error_state_adjust() == -1)) goto cleanup; /* * asblk could be dirty because we found a mismatch between * the primary superblock and one of its backups in checksb(). */ if (asblk.b_dirty && !bflag) { (void) memmove(&altsblock, &sblock, (size_t)sblock.fs_sbsize); flush(fswritefd, &asblk); } getsummaryinfo(); /* * if not error-locked, using the standard superblock, * not bad log, not forced, preening, and is clean; * stop checking */ if (!errorlocked && (bflag == 0) && ((!islog || islogok) && (fflag == 0) && preen && (FSOKAY == (sblock.fs_state + sblock.fs_time)) && ((sblock.fs_clean == FSLOG && islog) || ((sblock.fs_clean == FSCLEAN) || (sblock.fs_clean == FSSTABLE))))) { iscorrupt = 0; printclean(); goto cleanup; } if (create_and_init_maps() == -1) goto nomaps; bufinit(); return (devstr); nomaps: ckfini(); exitstat = EXERRFATAL; /* FALLTHROUGH */ cleanup: unbufinit(); uncreate_maps(); ungetsummaryinfo(); /* * Can't get rid of the superblock buffer, because our * caller references it to generate the summary statistics. */ return (NULL); }
/* * Roll the embedded log, if any, and set up the global variables * islog and islogok. */ static int logsetup(caddr_t devstr) { void *buf; extent_block_t *ebp; ml_unit_t *ul; ml_odunit_t *ud; void *ud_buf; int badlog; islog = islogok = 0; if (bflag != 0) return (1); /* can't roll log while alternate sb specified */ /* * Roll the log, if any. A bad sb implies we'll be using * an alternate sb as far as logging goes, so just fail back * to the caller if we can't read the default sb. Suppress * complaints, because the caller will be reading the same * superblock again and running full verification on it, so * whatever is bad will be reported then. */ sblock.fs_logbno = 0; badlog = 0; if (!read_super_block(0)) return (1); /* * Roll the log in 3 cases: * 1. If it's unmounted (mount_point == NULL) and it's not marked * as fully rolled (sblock.fs_rolled != FS_ALL_ROLLED) * 2. If it's mounted and anything other than a sanity * check fsck (mflag) is being done, as we have the current * super block. Note, only a sanity check is done for * root/usr at boot. If a roll were done then the expensive * ufs_flush() gets called, leading to a slower boot. * 3. If anything other then a sanity check (mflag) is being done * to a mounted filesystem while it is in read-only state * (e.g. root during early boot stages) we have to detect this * and have to roll the log as well. NB. the read-only mount * will flip fs_clean from FSLOG to FSSTABLE and marks the * log as FS_NEED_ROLL. */ if (sblock.fs_logbno && (((mount_point == NULL) && (sblock.fs_rolled != FS_ALL_ROLLED)) || ((mount_point != NULL) && !mflag))) { int roll_log_err = 0; if (sblock.fs_ronly && (sblock.fs_clean == FSSTABLE) && (sblock.fs_state + sblock.fs_time == FSOKAY)) { /* * roll the log without a mount */ flush_fs(); } if (sblock.fs_clean == FSLOG && (sblock.fs_state + sblock.fs_time == FSOKAY)) { if (rl_roll_log(devstr) != RL_SUCCESS) roll_log_err = 1; } if (roll_log_err) { (void) printf("Can't roll the log for %s.\n", devstr); /* * There are two cases where we want to set * an error code and return: * - We're preening * - We're not on a live root and the user * chose *not* to ignore the log * Otherwise, we want to mark the log as bad * and continue to check the filesystem. This * has the side effect of destroying the log. */ if (preen || (!hotroot && reply( "DISCARDING THE LOG MAY DISCARD PENDING TRANSACTIONS.\n" "DISCARD THE LOG AND CONTINUE") == 0)) { exitstat = EXERRFATAL; return (0); } ++badlog; } } /* Logging UFS may be enabled */ if (sblock.fs_logbno) { ++islog; /* log is not okay; check the fs */ if (FSOKAY != (sblock.fs_state + sblock.fs_time)) return (1); /* * If logging or (stable and mounted) then continue */ if (!((sblock.fs_clean == FSLOG) || (sblock.fs_clean == FSSTABLE) && (mount_point != NULL))) return (1); /* get the log allocation block */ buf = malloc(dev_bsize); if (buf == NULL) { return (1); } ud_buf = malloc(dev_bsize); if (ud_buf == NULL) { free(buf); return (1); } (void) fsck_bread(fsreadfd, buf, logbtodb(&sblock, sblock.fs_logbno), dev_bsize); ebp = (extent_block_t *)buf; /* log allocation block is not okay; check the fs */ if (ebp->type != LUFS_EXTENTS) { free(buf); free(ud_buf); return (1); } /* get the log state block(s) */ if (fsck_bread(fsreadfd, ud_buf, (logbtodb(&sblock, ebp->extents[0].pbno)), dev_bsize)) { (void) fsck_bread(fsreadfd, ud_buf, (logbtodb(&sblock, ebp->extents[0].pbno)) + 1, dev_bsize); } ud = (ml_odunit_t *)ud_buf; ul = (ml_unit_t *)malloc(sizeof (*ul)); if (ul == NULL) { free(buf); free(ud_buf); return (1); } ul->un_ondisk = *ud; /* log state is okay; don't need to check the fs */ if ((ul->un_chksum == ul->un_head_ident + ul->un_tail_ident) && (ul->un_version == LUFS_VERSION_LATEST) && (ul->un_badlog == 0) && (!badlog)) { ++islogok; } free(ud_buf); free(buf); free(ul); } return (1); }