cluster: STABLE3 - fsck.gfs2 fails on root fs: Device X is busy.

Bob Peterson rpeterso at fedoraproject.org
Thu Jan 28 21:02:49 UTC 2010


Gitweb:        http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=a1222d2f5923cefbb70990ae89e1cd63aed7d2ca
Commit:        a1222d2f5923cefbb70990ae89e1cd63aed7d2ca
Parent:        aeaf791266b40f75fa7434064244a80db2a3aa17
Author:        Bob Peterson <rpeterso at redhat.com>
AuthorDate:    Thu Jan 28 14:58:19 2010 -0600
Committer:     Bob Peterson <rpeterso at redhat.com>
CommitterDate: Thu Jan 28 14:58:19 2010 -0600

fsck.gfs2 fails on root fs: Device X is busy.

In a previous commit, we added an O_EXCL when opening the
device.  This was done to prevent other nodes from interacting
with the file system while it was being checked.  However, this
turns out to be a problem when the root file system is GFS2.
In that case, it's proper to allow the fsck to complete normally
and if errors are found, tell vfs to flush its cache after
corrections are made.

rhbz#557128
---
 gfs2/fsck/initialize.c |   73 ++++++++++++++++++++++++++++++++++++++----------
 gfs2/libgfs2/libgfs2.h |    1 +
 gfs2/libgfs2/misc.c    |   28 +++++++++++++-----
 3 files changed, 79 insertions(+), 23 deletions(-)

diff --git a/gfs2/fsck/initialize.c b/gfs2/fsck/initialize.c
index 00d4ed2..2910326 100644
--- a/gfs2/fsck/initialize.c
+++ b/gfs2/fsck/initialize.c
@@ -25,6 +25,8 @@
 		x = NULL; \
 	}
 
+static int was_mounted_ro = 0;
+
 /**
  * block_mounters
  *
@@ -453,27 +455,55 @@ static int fill_super_block(struct gfs2_sbd *sdp)
 int initialize(struct gfs2_sbd *sbp, int force_check, int preen,
 	       int *all_clean)
 {
-	int clean_journals = 0;
+	int clean_journals = 0, open_flag;
 
 	*all_clean = 0;
 
-	if(opts.no) {
-		if ((sbp->device_fd = open(opts.device, O_RDONLY)) < 0) {
-			log_crit( _("Unable to open device: %s\n"), opts.device);
-			return FSCK_USAGE;
-		}
-	} else {
-		/* read in sb from disk */
-		if ((sbp->device_fd = open(opts.device, O_RDWR | O_EXCL)) < 0){
-			if (errno == EBUSY)
-				log_crit( _("Device %s is busy.\n"),
-					 opts.device);
-			else
-				log_crit( _("Unable to open device: %s\n"),
-					  opts.device);
+	if(opts.no)
+		open_flag = O_RDONLY;
+	else
+		open_flag = O_RDWR | O_EXCL;
+
+	sbp->device_fd = open(opts.device, open_flag);
+	if (sbp->device_fd < 0) {
+		int is_mounted, ro;
+
+		if (open_flag == O_RDONLY || errno != EBUSY) {
+			log_crit( _("Unable to open device: %s\n"),
+				  opts.device);
 			return FSCK_USAGE;
 		}
+		/* We can't open it EXCL.  It may be already open rw (in which
+		   case we want to deny them access) or it may be mounted as
+		   the root file system at boot time (in which case we need to
+		   allow it.)  We use is_pathname_mounted here even though
+		   we're specifying a device name, not a path name.  The
+		   function checks for device as well. */
+		strncpy(sbp->device_name, opts.device,
+			sizeof(sbp->device_name));
+		sbp->path_name = sbp->device_name; /* This gets overwritten */
+		is_mounted = is_pathname_mounted(sbp, &ro);
+		/* If the device is busy, but not because it's mounted, fail.
+		   This protects against cases where the file system is LVM
+		   and perhaps mounted on a different node. */
+		if (!is_mounted)
+			goto mount_fail;
+		/* If the device is mounted, but not mounted RO, fail.  This
+		   protects them against cases where the file system is
+		   mounted RW, but still allows us to check our own root
+		   file system. */
+		if (!ro)
+			goto mount_fail;
+		/* The device is mounted RO, so it's likely our own root
+		   file system.  We can only do so much to protect the users
+		   from themselves.  Try opening without O_EXCL. */
+		if ((sbp->device_fd = open(opts.device, O_RDWR)) < 0)
+			goto mount_fail;
+
+		was_mounted_ro = 1;
 	}
+
+	/* read in sb from disk */
 	if (fill_super_block(sbp)) {
 		stack;
 		return FSCK_ERROR;
@@ -509,6 +539,10 @@ int initialize(struct gfs2_sbd *sbp, int force_check, int preen,
 		return FSCK_ERROR;
 
 	return FSCK_OK;
+
+mount_fail:
+	log_crit( _("Device %s is busy.\n"), opts.device);
+	return FSCK_USAGE;
 }
 
 static void destroy_sbp(struct gfs2_sbd *sbp)
@@ -523,6 +557,15 @@ static void destroy_sbp(struct gfs2_sbd *sbp)
 	}
 	empty_super_block(sbp);
 	close(sbp->device_fd);
+	if (was_mounted_ro && errors_corrected) {
+		sbp->device_fd = open("/proc/sys/vm/drop_caches", O_WRONLY);
+		if (sbp->device_fd >= 0) {
+			write(sbp->device_fd, "2", 1);
+			close(sbp->device_fd);
+		} else
+			log_err( _("fsck.gfs2: Non-fatal error dropping "
+				   "caches.\n"));
+	}
 }
 
 void destroy(struct gfs2_sbd *sbp)
diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h
index c4675f7..d89194c 100644
--- a/gfs2/libgfs2/libgfs2.h
+++ b/gfs2/libgfs2/libgfs2.h
@@ -598,6 +598,7 @@ extern int gfs2_query(int *setonabort, struct gfs2_options *opts,
 extern int compute_heightsize(struct gfs2_sbd *sdp, uint64_t *heightsize,
 		uint32_t *maxheight, uint32_t bsize1, int diptrs, int inptrs);
 extern int compute_constants(struct gfs2_sbd *sdp);
+extern int is_pathname_mounted(struct gfs2_sbd *sdp, int *ro_mount);
 extern int find_gfs2_meta(struct gfs2_sbd *sdp);
 extern int dir_exists(const char *dir);
 extern int check_for_gfs2(struct gfs2_sbd *sdp);
diff --git a/gfs2/libgfs2/misc.c b/gfs2/libgfs2/misc.c
index 541db02..5ccdab4 100644
--- a/gfs2/libgfs2/misc.c
+++ b/gfs2/libgfs2/misc.c
@@ -96,7 +96,7 @@ int compute_constants(struct gfs2_sbd *sdp)
 	return 0;
 }
 
-int check_for_gfs2(struct gfs2_sbd *sdp)
+int is_pathname_mounted(struct gfs2_sbd *sdp, int *ro_mount)
 {
 	FILE *fp;
 	char buffer[PATH_MAX];
@@ -106,14 +106,14 @@ int check_for_gfs2(struct gfs2_sbd *sdp)
 	char fsoptions[PATH_MAX];
 	char *realname;
 
+	*ro_mount = 0;
 	realname = realpath(sdp->path_name, NULL);
-	if (!realname) {
-		return -1;
-	}
+	if (!realname)
+		return 0;
 	fp = fopen("/proc/mounts", "r");
 	if (fp == NULL) {
 		free(realname);
-		return -1;
+		return 0;
 	}
 	while ((fgets(buffer, PATH_MAX - 1, fp)) != NULL) {
 		buffer[PATH_MAX - 1] = 0;
@@ -135,19 +135,31 @@ int check_for_gfs2(struct gfs2_sbd *sdp)
 		else if (strcmp(fspath, realname) != 0)
 			continue;
 
+		if (strncmp(fsoptions, "ro,", 3) == 0 ||
+		    strcmp(fsoptions, "ro") == 0)
+			*ro_mount = 1;
 		fclose(fp);
 		free(realname);
 		if (strncmp(sdp->device_name, "/dev/loop", 9) == 0) {
 			errno = EINVAL;
-			return -1;
+			return 0;
 		}
 
-		return 0;
+		return 1;
 	}
 	free(realname);
 	fclose(fp);
 	errno = EINVAL;
-	return -1;
+	return 0;
+}
+
+int check_for_gfs2(struct gfs2_sbd *sdp)
+{
+	int ro;
+
+	if (!is_pathname_mounted(sdp, &ro))
+		return -1;
+	return 0;
 }
 
 static int lock_for_admin(struct gfs2_sbd *sdp)


More information about the cluster-commits mailing list