From: Karel Zak Date: Tue, 26 Oct 2010 10:58:32 +0000 (+0200) Subject: fsck: add support for whole-disk locking (-l option) X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dd0bd943f94392d165c5903e271c966afb0d7b75;p=util-linux fsck: add support for whole-disk locking (-l option) This feature allows to call multiple independent fsck instances rather than use only one "fsck -A" process. The lock uses LOCK_EX flock(2). The lock request is ignored if the whole-disk is non-rotating disk. The verbose mode (-V) provides information about disk locking. Note that "fsck -l" does not care if the device is stacked, for example if you want to call "fsck -l /dev/md0" and "fsck -l /dev/md1" then the underlying devices will not be locked. The traditional "fsck -A" does not run in parallel for stacked devices. Requested-by: Lennart Poettering Signed-off-by: Karel Zak --- diff --git a/fsck/fsck.8 b/fsck/fsck.8 index 9cf80785..3d60ac08 100644 --- a/fsck/fsck.8 +++ b/fsck/fsck.8 @@ -7,7 +7,7 @@ fsck \- check and repair a Linux file system .SH SYNOPSIS .B fsck -.RB [ \-sAVRTMNP ] +.RB [ \-lsAVRTMNP ] .RB [ \-C .RI [ fd ]] .RB [ \-t @@ -80,6 +80,17 @@ variable. Please see the file system-specific checker manual pages for further details. .SH OPTIONS .TP +.B \-l +Lock whole-disk device by exclusive +.BR flock (2). +This option can be used with one device only (e.g. -A and -l are mutually +exclusive). This option is recommended when more +.B fsck (8) +instances are executed in the same time. The option is ignored when used for +multiple devices or for non-rotating disk. The fsck does not lock underlying +devices if executed to check stacked devices (e.g. MD or DM) -- this feature is +not implemented yet. +.TP .B \-s Serialize .B fsck diff --git a/fsck/fsck.c b/fsck/fsck.c index 7e1608f9..6a58d7bf 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #include @@ -87,6 +89,7 @@ char *devices[MAX_DEVICES]; char *args[MAX_ARGS]; int num_devices, num_args; +int lockdisk = 0; int verbose = 0; int doall = 0; int noexecute = 0; @@ -216,8 +219,96 @@ static void parse_escape(char *word) *q = 0; } +static dev_t get_disk(const char *device) +{ + struct stat st; + dev_t disk; + + if (!stat(device, &st) && + !blkid_devno_to_wholedisk(st.st_rdev, NULL, 0, &disk)) + return disk; + + return 0; +} + +static int is_irrotational_disk(dev_t disk) +{ + char path[PATH_MAX]; + FILE *f; + int rc, x; + + rc = snprintf(path, sizeof(path), + "/sys/dev/block/%d:%d/queue/rotational", + major(disk), minor(disk)); + + if (rc < 0 || rc + 1 > sizeof(path)) + return 0; + + f = fopen(path, "r"); + if (!f) + return 0; + + rc = fscanf(f, "%u", &x); + fclose(f); + + return rc == 1 ? !x : 0; +} + +static void lock_disk(struct fsck_instance *inst) +{ + dev_t disk = inst->fs->disk ? : get_disk(inst->fs->device); + char *diskname; + + if (!disk || is_irrotational_disk(disk)) + return; + + diskname = blkid_devno_to_devname(disk); + if (!diskname) + return; + + if (verbose) + printf(_("Locking disk %s ... "), diskname); + + inst->lock = open(diskname, O_CLOEXEC | O_RDONLY); + if (inst->lock >= 0) { + int rc = -1; + + /* inform users that we're waiting on the lock */ + if (verbose && + (rc = flock(inst->lock, LOCK_EX | LOCK_NB)) != 0 && + errno == EWOULDBLOCK) + printf(_("(waiting) ")); + + if (rc != 0 && flock(inst->lock, LOCK_EX) != 0) { + close(inst->lock); /* failed */ + inst->lock = -1; + } + } + + if (verbose) + printf("%s.\n", inst->lock >= 0 ? _("success") : _("failed")); + + free(diskname); + return; +} + +static void unlock_disk(struct fsck_instance *inst) +{ + if (inst->lock >= 0) { + /* explicitly unlock, don't rely on close(), maybe some library + * (e.g. liblkid) has still open the device. + */ + flock(inst->lock, LOCK_UN); + close(inst->lock); + } +} + + + static void free_instance(struct fsck_instance *i) { + if (lockdisk) + unlock_disk(i); free(i->prog); free(i); return; @@ -471,6 +562,13 @@ static int execute(const char *type, struct fs_info *fs, int interactive) printf("\n"); } + + inst->fs = fs; + inst->lock = -1; + + if (lockdisk) + lock_disk(inst); + /* Fork and execute the correct program. */ if (noexecute) pid = -1; @@ -494,7 +592,6 @@ static int execute(const char *type, struct fs_info *fs, int interactive) inst->prog = string_copy(prog); inst->type = string_copy(type); inst->start_time = time(0); - inst->fs = fs; inst->next = NULL; /* @@ -977,14 +1074,9 @@ static int disk_already_active(struct fs_info *fs) return 1; if (!fs->disk) { - struct stat st; - dev_t disk; - - if (!stat(fs->device, &st) && - !blkid_devno_to_wholedisk(st.st_rdev, NULL, 0, &disk)) { - fs->disk = disk; - fs->stacked = count_slaves(disk); - } + fs->disk = get_disk(fs->device); + if (fs->disk) + fs->stacked = count_slaves(fs->disk); } /* @@ -1230,6 +1322,9 @@ static void PRS(int argc, char *argv[]) } } break; + case 'l': + lockdisk++; + break; case 'V': verbose++; break; @@ -1340,6 +1435,12 @@ int main(int argc, char *argv[]) if ((num_devices == 1) || (serialize)) interactive = 1; + if (lockdisk && (doall || num_devices > 1)) { + fprintf(stderr, _("%s: the -l option can be used with one " + "device only -- ignore\n"), progname); + lockdisk = 0; + } + /* If -A was specified ("check all"), do that! */ if (doall) return check_all(); diff --git a/fsck/fsck.h b/fsck/fsck.h index d212622f..5612b218 100644 --- a/fsck/fsck.h +++ b/fsck/fsck.h @@ -58,6 +58,7 @@ struct fs_info { struct fsck_instance { int pid; int flags; + int lock; /* flock()ed whole disk file descriptor or -1 */ int exit_status; time_t start_time; char * prog;