You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
435 lines
12 KiB
435 lines
12 KiB
/* fsck.c - check and repair a Linux filesystem
|
|
*
|
|
* Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com>
|
|
* Copyright 2013 Kyungwan Han <asura321@gmail.com>
|
|
|
|
USE_FSCK(NEWTOY(fsck, "?t:ANPRTVsC#", TOYFLAG_USR|TOYFLAG_BIN))
|
|
|
|
config FSCK
|
|
bool "fsck"
|
|
default n
|
|
help
|
|
usage: fsck [-ANPRTV] [-C FD] [-t FSTYPE] [FS_OPTS] [BLOCKDEV]...
|
|
|
|
Check and repair filesystems
|
|
|
|
-A Walk /etc/fstab and check all filesystems
|
|
-N Don't execute, just show what would be done
|
|
-P With -A, check filesystems in parallel
|
|
-R With -A, skip the root filesystem
|
|
-T Don't show title on startup
|
|
-V Verbose
|
|
-C n Write status information to specified file descriptor
|
|
-t TYPE List of filesystem types to check
|
|
|
|
*/
|
|
|
|
#define FOR_fsck
|
|
#include "toys.h"
|
|
#include <mntent.h>
|
|
|
|
#define FLAG_WITHOUT_NO_PRFX 1
|
|
#define FLAG_WITH_NO_PRFX 2
|
|
#define FLAG_DONE 1
|
|
|
|
GLOBALS(
|
|
int fd_num;
|
|
char *t_list;
|
|
|
|
struct double_list *devices;
|
|
char *arr_flag;
|
|
char **arr_type;
|
|
int negate;
|
|
int sum_status;
|
|
int nr_run;
|
|
int sig_num;
|
|
long max_nr_run;
|
|
)
|
|
|
|
struct f_sys_info {
|
|
char *device, *mountpt, *type, *opts;
|
|
int passno, flag;
|
|
struct f_sys_info *next;
|
|
};
|
|
|
|
struct child_list {
|
|
struct child_list *next;
|
|
pid_t pid;
|
|
char *prog_name, *dev_name;
|
|
};
|
|
|
|
static struct f_sys_info *filesys_info = NULL; //fstab entry list
|
|
static struct child_list *c_list = NULL; //fsck.type child list.
|
|
|
|
static void kill_all(void)
|
|
{
|
|
struct child_list *child;
|
|
|
|
for (child = c_list; child; child = child->next)
|
|
kill(child->pid, SIGTERM);
|
|
_exit(0);
|
|
}
|
|
|
|
static long strtol_range(char *str, int min, int max)
|
|
{
|
|
char *endptr = NULL;
|
|
errno = 0;
|
|
long ret_value = strtol(str, &endptr, 10);
|
|
|
|
if(errno) perror_exit("Invalid num %s", str);
|
|
else if(endptr && (*endptr != '\0' || endptr == str))
|
|
perror_exit("Not a valid num %s", str);
|
|
if(ret_value >= min && ret_value <= max) return ret_value;
|
|
else perror_exit("Number %s is not in valid [%d-%d] Range", str, min, max);
|
|
}
|
|
|
|
//create fstab entries list.
|
|
static struct f_sys_info* create_db(struct mntent *f_info)
|
|
{
|
|
struct f_sys_info *temp = filesys_info;
|
|
if (temp) {
|
|
while (temp->next) temp = temp->next;
|
|
temp->next = xzalloc(sizeof(struct f_sys_info));
|
|
temp = temp->next;
|
|
} else filesys_info = temp = xzalloc(sizeof(struct f_sys_info));
|
|
|
|
temp->device = xstrdup(f_info->mnt_fsname);
|
|
temp->mountpt = xstrdup(f_info->mnt_dir);
|
|
if (strchr(f_info->mnt_type, ',')) temp->type = xstrdup("auto");
|
|
else temp->type = xstrdup(f_info->mnt_type);
|
|
temp->opts = xstrdup(f_info->mnt_opts);
|
|
temp->passno = f_info->mnt_passno;
|
|
return temp;
|
|
}
|
|
|
|
//is we have 'no' or ! before type.
|
|
static int is_no_prefix(char **p)
|
|
{
|
|
int no = 0;
|
|
|
|
if ((*p[0] == 'n' && *(*p + 1) == 'o')) no = 2;
|
|
else if (*p[0] == '!') no = 1;
|
|
*p += no;
|
|
return ((no) ? 1 :0);
|
|
}
|
|
|
|
static void fix_tlist(void)
|
|
{
|
|
char *p, *s = TT.t_list;
|
|
int n = 1, no;
|
|
|
|
while ((s = strchr(s, ','))) {
|
|
s++;
|
|
n++;
|
|
}
|
|
|
|
TT.arr_flag = xzalloc(n + 1);
|
|
TT.arr_type = xzalloc((n + 1) * sizeof(char *));
|
|
s = TT.t_list;
|
|
n = 0;
|
|
while ((p = strsep(&s, ","))) {
|
|
no = is_no_prefix(&p);
|
|
if (!strcmp(p, "loop")) {
|
|
TT.arr_flag[n] = no ? FLAG_WITH_NO_PRFX :FLAG_WITHOUT_NO_PRFX;
|
|
TT.negate = no;
|
|
} else if (!strncmp(p, "opts=", 5)) {
|
|
p+=5;
|
|
TT.arr_flag[n] = is_no_prefix(&p) ?FLAG_WITH_NO_PRFX :FLAG_WITHOUT_NO_PRFX;
|
|
TT.negate = no;
|
|
} else {
|
|
if (!n) TT.negate = no;
|
|
if (n && TT.negate != no) error_exit("either all or none of the filesystem"
|
|
" types passed to -t must be prefixed with 'no' or '!'");
|
|
}
|
|
TT.arr_type[n++] = p;
|
|
}
|
|
}
|
|
|
|
//ignore these types...
|
|
static int ignore_type(char *type)
|
|
{
|
|
int i = 0;
|
|
char *str;
|
|
char *ignored_types[] = {
|
|
"ignore","iso9660", "nfs","proc",
|
|
"sw","swap", "tmpfs","devpts",NULL
|
|
};
|
|
while ((str = ignored_types[i++])) {
|
|
if (!strcmp(str, type)) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// return true if has to ignore the filesystem.
|
|
static int to_be_ignored(struct f_sys_info *finfo)
|
|
{
|
|
int i, ret = 0, type_present = 0;
|
|
|
|
if (!finfo->passno) return 1; //Ignore with pass num = 0
|
|
if (TT.arr_type) {
|
|
for (i = 0; TT.arr_type[i]; i++) {
|
|
if (!TT.arr_flag[i]) { //it is type of filesys.
|
|
type_present = 2;
|
|
if (!strcmp(TT.arr_type[i], finfo->type)) ret = 0;
|
|
else ret = 1;
|
|
} else if (TT.arr_flag[i] == FLAG_WITH_NO_PRFX) { //it is option of filesys
|
|
if (hasmntopt((const struct mntent *)finfo, TT.arr_type[i])) return 1;
|
|
} else { //FLAG_WITHOUT_NO_PRFX
|
|
if (!hasmntopt((const struct mntent *)finfo, TT.arr_type[i])) return 1;
|
|
}
|
|
}
|
|
}
|
|
if (ignore_type(finfo->type)) return 1;
|
|
if (TT.arr_type && type_present != 2) return 0;
|
|
return ((TT.negate) ? !ret : ret);
|
|
}
|
|
|
|
// find type and execute corresponding fsck.type prog.
|
|
static void do_fsck(struct f_sys_info *finfo)
|
|
{
|
|
struct child_list *child;
|
|
char **args;
|
|
char *type;
|
|
pid_t pid;
|
|
int i = 1, j = 0;
|
|
|
|
if (strcmp(finfo->type, "auto")) type = finfo->type;
|
|
else if (TT.t_list && (TT.t_list[0] != 'n' || TT.t_list[1] != 'o' || TT.t_list[0] != '!')
|
|
&& strncmp(TT.t_list, "opts=", 5) && strncmp(TT.t_list , "loop", 4)
|
|
&& !TT.arr_type[1]) type = TT.t_list; //one file sys at cmdline
|
|
else type = "auto";
|
|
|
|
args = xzalloc((toys.optc + 2 + 1 + 1) * sizeof(char*)); //+1, for NULL, +1 if -C
|
|
args[0] = xmprintf("fsck.%s", type);
|
|
|
|
if(toys.optflags & FLAG_C) args[i++] = xmprintf("%s %d","-C", TT.fd_num);
|
|
while(toys.optargs[j]) {
|
|
if(*toys.optargs[j]) args[i++] = xstrdup(toys.optargs[j]);
|
|
j++;
|
|
}
|
|
args[i] = finfo->device;
|
|
|
|
TT.nr_run++;
|
|
if ((toys.optflags & FLAG_V) || (toys.optflags & FLAG_N)) {
|
|
printf("[%s (%d) -- %s]", args[0], TT.nr_run,
|
|
finfo->mountpt ? finfo->mountpt : finfo->device);
|
|
for (i = 0; args[i]; i++) xprintf(" %s", args[i]);
|
|
xputc('\n');
|
|
}
|
|
|
|
if (toys.optflags & FLAG_N) {
|
|
for (j=0;j<i;j++) free(args[i]);
|
|
free(args);
|
|
return;
|
|
} else {
|
|
if ((pid = fork()) < 0) {
|
|
perror_msg_raw(*args);
|
|
for (j=0;j<i;j++) free(args[i]);
|
|
free(args);
|
|
return;
|
|
}
|
|
if (!pid) xexec(args); //child, executes fsck.type
|
|
}
|
|
|
|
child = xzalloc(sizeof(struct child_list)); //Parent, add to child list.
|
|
child->dev_name = xstrdup(finfo->device);
|
|
child->prog_name = args[0];
|
|
child->pid = pid;
|
|
|
|
if (c_list) {
|
|
child->next = c_list;
|
|
c_list = child;
|
|
} else {
|
|
c_list = child;
|
|
child->next =NULL;
|
|
}
|
|
}
|
|
|
|
// for_all = 1; wait for all child to exit
|
|
// for_all = 0; wait for any one to exit
|
|
static int wait_for(int for_all)
|
|
{
|
|
pid_t pid;
|
|
int status = 0, child_exited;
|
|
struct child_list *prev, *temp;
|
|
|
|
errno = 0;
|
|
if (!c_list) return 0;
|
|
while ((pid = wait(&status))) {
|
|
temp = c_list;
|
|
prev = temp;
|
|
if (TT.sig_num) kill_all();
|
|
child_exited = 0;
|
|
if (pid < 0) {
|
|
if (errno == EINTR) continue;
|
|
else if (errno == ECHILD) break; //No child to wait, break and return status.
|
|
else perror_exit("option arg Invalid\n"); //paranoid.
|
|
}
|
|
while (temp) {
|
|
if (temp->pid == pid) {
|
|
child_exited = 1;
|
|
break;
|
|
}
|
|
prev = temp;
|
|
temp = temp->next;
|
|
}
|
|
if (child_exited) {
|
|
if (WIFEXITED(status)) TT.sum_status |= WEXITSTATUS(status);
|
|
else if (WIFSIGNALED(status)) {
|
|
TT.sum_status |= 4; //Uncorrected.
|
|
if (WTERMSIG(status) != SIGINT)
|
|
perror_msg("child Term. by sig: %d\n",(WTERMSIG(status)));
|
|
TT.sum_status |= 8; //Operatinal error
|
|
} else {
|
|
TT.sum_status |= 4; //Uncorrected.
|
|
perror_msg("%s %s: status is %x, should never happen\n",
|
|
temp->prog_name, temp->dev_name, status);
|
|
}
|
|
TT.nr_run--;
|
|
if (prev == temp) c_list = c_list->next; //first node
|
|
else prev->next = temp->next;
|
|
free(temp->prog_name);
|
|
free(temp->dev_name);
|
|
free(temp);
|
|
if (!for_all) break;
|
|
}
|
|
}
|
|
return TT.sum_status;
|
|
}
|
|
|
|
//scan all the fstab entries or -t matches with fstab.
|
|
static int scan_all(void)
|
|
{
|
|
struct f_sys_info *finfo = filesys_info;
|
|
int ret = 0, passno;
|
|
|
|
if (toys.optflags & FLAG_V) xprintf("Checking all filesystem\n");
|
|
while (finfo) {
|
|
if (to_be_ignored(finfo)) finfo->flag |= FLAG_DONE;
|
|
finfo = finfo->next;
|
|
}
|
|
finfo = filesys_info;
|
|
|
|
if (!(toys.optflags & FLAG_P)) {
|
|
while (finfo) {
|
|
if (!strcmp(finfo->mountpt, "/")) { // man says: check / in parallel with others if -P is absent.
|
|
if ((toys.optflags & FLAG_R) || to_be_ignored(finfo)) {
|
|
finfo->flag |= FLAG_DONE;
|
|
break;
|
|
} else {
|
|
do_fsck(finfo);
|
|
finfo->flag |= FLAG_DONE;
|
|
if (TT.sig_num) kill_all();
|
|
if ((ret |= wait_for(1)) > 4) return ret; //destruction in filesys.
|
|
break;
|
|
}
|
|
}
|
|
finfo = finfo->next;
|
|
}
|
|
}
|
|
if (toys.optflags & FLAG_R) { // with -PR we choose to skip root.
|
|
for (finfo = filesys_info; finfo; finfo = finfo->next) {
|
|
if(!strcmp(finfo->mountpt, "/")) finfo->flag |= FLAG_DONE;
|
|
}
|
|
}
|
|
passno = 1;
|
|
while (1) {
|
|
for (finfo = filesys_info; finfo; finfo = finfo->next)
|
|
if (!finfo->flag) break;
|
|
if (!finfo) break;
|
|
|
|
for (finfo = filesys_info; finfo; finfo = finfo->next) {
|
|
if (finfo->flag) continue;
|
|
if (finfo->passno == passno) {
|
|
do_fsck(finfo);
|
|
finfo->flag |= FLAG_DONE;
|
|
if ((toys.optflags & FLAG_s) || (TT.nr_run
|
|
&& (TT.nr_run >= TT.max_nr_run))) ret |= wait_for(0);
|
|
}
|
|
}
|
|
if (TT.sig_num) kill_all();
|
|
ret |= wait_for(1);
|
|
passno++;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void record_sig_num(int sig)
|
|
{
|
|
TT.sig_num = sig;
|
|
}
|
|
|
|
void fsck_main(void)
|
|
{
|
|
struct mntent mt;
|
|
struct double_list *dev;
|
|
struct f_sys_info *finfo;
|
|
FILE *fp;
|
|
char *tmp, **arg = toys.optargs;
|
|
|
|
sigatexit(record_sig_num);
|
|
while (*arg) {
|
|
if ((**arg == '/') || strchr(*arg, '=')) {
|
|
dlist_add(&TT.devices, xstrdup(*arg));
|
|
**arg = '\0';
|
|
}
|
|
arg++;
|
|
}
|
|
if (toys.optflags & FLAG_t) fix_tlist();
|
|
if (!(tmp = getenv("FSTAB_FILE"))) tmp = "/etc/fstab";
|
|
if (!(fp = setmntent(tmp, "r"))) perror_exit("setmntent failed:");
|
|
while (getmntent_r(fp, &mt, toybuf, 4096)) create_db(&mt);
|
|
endmntent(fp);
|
|
|
|
if (!(toys.optflags & FLAG_T)) xprintf("fsck ----- (Toybox)\n");
|
|
|
|
if ((tmp = getenv("FSCK_MAX_INST")))
|
|
TT.max_nr_run = strtol_range(tmp, 0, INT_MAX);
|
|
if (!TT.devices || (toys.optflags & FLAG_A)) {
|
|
toys.exitval = scan_all();
|
|
if (CFG_TOYBOX_FREE) goto free_all;
|
|
return; //if CFG_TOYBOX_FREE is not set, exit.
|
|
}
|
|
|
|
dev = TT.devices;
|
|
dev->prev->next = NULL; //break double list to traverse.
|
|
for (; dev; dev = dev->next) {
|
|
for (finfo = filesys_info; finfo; finfo = finfo->next)
|
|
if (!strcmp(finfo->device, dev->data)
|
|
|| !strcmp(finfo->mountpt, dev->data)) break;
|
|
if (!finfo) { //if not present, fill def values.
|
|
mt.mnt_fsname = dev->data;
|
|
mt.mnt_dir = "";
|
|
mt.mnt_type = "auto";
|
|
mt.mnt_opts = "";
|
|
mt.mnt_passno = -1;
|
|
finfo = create_db(&mt);
|
|
}
|
|
do_fsck(finfo);
|
|
finfo->flag |= FLAG_DONE;
|
|
if ((toys.optflags & FLAG_s) || (TT.nr_run && (TT.nr_run >= TT.max_nr_run)))
|
|
toys.exitval |= wait_for(0);
|
|
}
|
|
if (TT.sig_num) kill_all();
|
|
toys.exitval |= wait_for(1);
|
|
finfo = filesys_info;
|
|
|
|
free_all:
|
|
if (CFG_TOYBOX_FREE) {
|
|
struct f_sys_info *finfo, *temp;
|
|
|
|
llist_traverse(TT.devices, llist_free_double);
|
|
free(TT.arr_type);
|
|
free(TT.arr_flag);
|
|
for (finfo = filesys_info; finfo;) {
|
|
temp = finfo->next;
|
|
free(finfo->device);
|
|
free(finfo->mountpt);
|
|
free(finfo->type);
|
|
free(finfo->opts);
|
|
free(finfo);
|
|
finfo = temp;
|
|
}
|
|
}
|
|
}
|