Re: [PATCH] e2fsck: take flock(LOCK_EX) on whole-disk device during filesystem check

Andreas Dilger <[email protected]>
Newsgroups org.kernel.vger.linux-ext4
Message-ID <[email protected]>
On Aug 14, 2026, at 23:29, Nandakumar Raghavan <[email protected]> wrote:
> 
> Hi,
> 
> Gentle ping on this patch.
> 
> I would appreciate any feedback when time permits.
> 
> On Mon, Jul 20, 2026 at 02:32:46PM +0000, Nandakumar Raghavan wrote:
>> During journal replay, e2fsck writes the primary superblock back to disk
>> in multiple I/O operations. The payload lands before the checksum, leaving
>> a transient window where the on-disk superblock has a bad checksum.
>> 
>> If udevd processes a change uevent during this window, libblkid probes the
>> primary superblock, finds a checksum mismatch, and concludes the partition
>> has no recognisable filesystem. udev then fires a remove event, wiping all
>> symlinks in /dev/disk/by-label/ and /dev/disk/by-uuid/. Any mount unit
>> that depends on those symlinks will fail.
>> 
>> udevd already serialises its own partition probes against whole-disk device
>> access using flock(LOCK_SH|LOCK_NB); if EAGAIN is returned it requeues the
>> event. Take advantage of this protocol by acquiring flock(LOCK_EX) on the
>> whole-disk device before opening the filesystem. This forces udevd to defer
>> all probes on that disk until e2fsck exits and the lock is released, by
>> which point the filesystem is fully consistent.
>> 
>> The whole-disk device is resolved via sysfs (/sys/dev/block/MAJ:MIN/partition)
>> so that the lock covers the same device node that udevd locks. If sysfs
>> resolution fails, a warning is emitted and the lock falls back to the
>> partition device itself.
>> 
>> Signed-off-by: Nandakumar Raghavan <[email protected]>

This looks like it would unnecessarily emit an error message if run on any
non-Linux platform (e.g. MacOS, BSD), so it needs to be conditionally built.

>> ---
>> e2fsck/unix.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 105 insertions(+)
>> 
>> diff --git a/e2fsck/unix.c b/e2fsck/unix.c
>> index 335ca377..12a9f50a 100644
>> --- a/e2fsck/unix.c
>> +++ b/e2fsck/unix.c
>> @@ -36,6 +36,15 @@ extern int optind;

Presumably this code is only useful on Linux, so the conditional headers are not needed,
only a single `#ifdef __linux__`?  Not really critical either way.

>> #ifdef HAVE_SYS_IOCTL_H
>> #include <sys/ioctl.h>
>> #endif
>> +#ifdef HAVE_SYS_FILE_H
>> +#include <sys/file.h>
>> +#endif
>> +#ifdef HAVE_SYS_STAT_H
>> +#include <sys/stat.h>
>> +#endif
>> +#ifdef HAVE_SYS_SYSMACROS_H
>> +#include <sys/sysmacros.h>
>> +#endif
>> #ifdef HAVE_MALLOC_H
>> #include <malloc.h>
>> #endif
>> @@ -1397,6 +1406,92 @@ err:
>> +static int lock_whole_disk(e2fsck_t ctx, const char *dev_name)

This function should similarly be #ifdef'd out if not building on Linux.

>> +{
>> + 	struct stat st;
>> + 	char partition_attr[256];  /* sysfs 'partition' attribute path */
>> + 	char parent_dev_path[256]; /* sysfs '../dev' of the parent disk */
>> + 	char parent_devnum[32];
>> + 	FILE *f;
>> + 	unsigned int maj, min;
>> + 	int parent_resolved = 0;
>> + 	char lock_path[256];
>> + 	const char *opened_path;
>> + 	int fd;
>> +
>> + 	if (stat(dev_name, &st) < 0)
>> + 		return -1;
>> +
>> + 	if (!S_ISBLK(st.st_mode))
>> + 		return -1;
>> +
>> + 	maj = major(st.st_rdev);
>> + 	min = minor(st.st_rdev);
>> +
>> + 	/*
>> +	 * If dev_name is a partition (sysfs 'partition' attribute exists),
>> +	 * resolve the parent whole-disk device so the flock covers the same
>> +	 * device node that udevd locks before probing any partition on it.
>> +	 */
>> + 	snprintf(partition_attr, sizeof(partition_attr),
>> + 		 "/sys/dev/block/%u:%u/partition", maj, min);
>> +
>> + 	if (access(partition_attr, F_OK) == 0) {
>> + 		snprintf(parent_dev_path, sizeof(parent_dev_path),
>> + 			 "/sys/dev/block/%u:%u/../dev", maj, min);

(minor) this could reuse `partition_attr` here?

>> +		f = fopen(parent_dev_path, "r");
>> +		if (f) {
>> + 			if (fscanf(f, "%31s", parent_devnum) == 1) {
>> +				 unsigned int pmaj, pmin;
>> +				 if (sscanf(parent_devnum, "%u:%u",
>> +					    &pmaj, &pmin) == 2) {
>> + 					maj = pmaj;
>> +					min = pmin;
>> + 					parent_resolved = 1;
>> + 				}
>> +			}
>> + 			fclose(f);
>> + 		}
>> +		if (!parent_resolved)
>> +			 log_err(ctx, _("Warning: could not resolve whole-disk "
>> +				        "device for %s; lock may not prevent "
>> +				        "udev races\n"), dev_name);
>> +	}
>> +
>> + 	snprintf(lock_path, sizeof(lock_path),
>> + 		 "/dev/block/%u:%u", maj, min);

(style) could fit on a single line?

(style) could re-use `partition_attr` here (maybe renamed to `dev_path` or similar)
to avoid having 3 single-use pathnames on the stack.

>> +
>> +	opened_path = lock_path;
>> + 	fd = open(lock_path, O_RDONLY | O_CLOEXEC, 0);
>> + 	if (fd < 0) {
>> + 		fd = open(dev_name, O_RDONLY | O_CLOEXEC, 0);
>> + 		if (fd < 0)
>> + 			return -1;
>> + 		opened_path = dev_name;
>> + 		if (parent_resolved)
>> + 			log_err(ctx, _("Warning: %s not found; locking %s "
>> +				       "instead, lock may not prevent "
>> +				       "udev races\n"), lock_path, dev_name);

(style) shouldn't split error messages across lines, even if > 80 columns

>> +	}
>> +
>> + 	while (flock(fd, LOCK_EX) != 0) {
>> + 		if (errno == EINTR) {
>> + 			if (ctx->flags & E2F_FLAG_CANCEL) {
>> + 				close(fd);
>> + 				return -1;
>> + 			}
>> + 			continue;

Does this loop/hang forever if you try to kill it with CTRL-C?

>> + 		}
>> + 		com_err(ctx->program_name, errno,
>> + 			_("while trying to lock %s"), opened_path);
>> + 		close(fd);
>> +		return -1;
>> +	}
>> +
>> + 	return fd;
>> +}
>> +
>> int main (int argc, char *argv[])
>> {
>> errcode_t retval = 0, retval2 = 0, orig_retval = 0;
>> @@ -1413,6 +1508,7 @@ int main (int argc, char *argv[])
>> int journal_size;
>> int sysval, sys_page_size = 4096;
>> int old_bitmaps;
>> + int lock_fd = -1;
>> __u32 features[3];
>> char *cp;
>> enum quota_type qtype;
>> @@ -1488,6 +1584,12 @@ int main (int argc, char *argv[])
>> 
>> check_mount(ctx);
>> 
>> + lock_fd = lock_whole_disk(ctx, ctx->filesystem_name);
>> + if (lock_fd < 0)
>> + log_err(ctx, _("Warning: could not lock %s; "
>> +       "proceeding without block device lock\n"),
>> + ctx->filesystem_name);
>> +
>> if (!(ctx->options & E2F_OPT_PREEN) &&
>>    !(ctx->options & E2F_OPT_NO) &&
>>    !(ctx->options & E2F_OPT_YES)) {
>> @@ -2169,6 +2271,9 @@ skip_write:
>> ext2fs_close_free(&ctx->fs);
>> free(ctx->journal_name);
>> 
>> + if (lock_fd >= 0)
>> + close(lock_fd);
>> +
>> if (ctx->logf)
>> fprintf(ctx->logf, "Exit status: %d\n", exit_value);
>> e2fsck_free_context(ctx);
>> -- 
>> 2.54.0
> 


Cheers, Andreas
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.