Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 9383e745e23602bc45f9c92184feea59 > files > 104

gfs2-utils-0.1.62-28.el5.src.rpm

commit 112a4979a64dc0e8cc74e5e63ae55b5788cf334e
Author: Bob Peterson <rpeterso@redhat.com>
Date:   Thu Jan 28 13:50:53 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

diff --git a/gfs2/fsck/initialize.c b/gfs2/fsck/initialize.c
index 10c8ecc..93fef78 100644
--- a/gfs2/fsck/initialize.c
+++ b/gfs2/fsck/initialize.c
@@ -35,6 +35,8 @@
 		x = NULL; \
 	}
 
+static int was_mounted_ro = 0;
+
 /**
  * block_mounters
  *
@@ -330,26 +332,54 @@ 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) {
+	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;
 		}
-	} 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);
-			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;
@@ -385,6 +415,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)
@@ -399,6 +433,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 7a505a1..9958980 100644
--- a/gfs2/libgfs2/libgfs2.h
+++ b/gfs2/libgfs2/libgfs2.h
@@ -663,6 +663,7 @@ int gfs2_query(int *setonabort, struct gfs2_options *opts,
 uint32_t compute_heightsize(struct gfs2_sbd *sdp, uint64_t *heightsize,
 			    uint32_t bsize1, int diptrs, int inptrs);
 void compute_constants(struct gfs2_sbd *sdp);
+int is_pathname_mounted(struct gfs2_sbd *sdp, int *ro_mount);
 void check_for_gfs2(struct gfs2_sbd *sdp);
 void mount_gfs2_meta(struct gfs2_sbd *sdp);
 void cleanup_metafs(struct gfs2_sbd *sdp);
diff --git a/gfs2/libgfs2/misc.c b/gfs2/libgfs2/misc.c
index 60e807a..bcccba9 100644
--- a/gfs2/libgfs2/misc.c
+++ b/gfs2/libgfs2/misc.c
@@ -105,8 +105,7 @@ compute_constants(struct gfs2_sbd *sdp)
 						 sdp->sd_inptrs);
 }
 
-void
-check_for_gfs2(struct gfs2_sbd *sdp)
+int is_pathname_mounted(struct gfs2_sbd *sdp, int *ro_mount)
 {
 	FILE *fp = fopen("/proc/mounts", "r");
 	char buffer[PATH_MAX];
@@ -116,10 +115,11 @@ check_for_gfs2(struct gfs2_sbd *sdp)
 	char fsoptions[PATH_MAX];
 	char *realname;
 
+	*ro_mount = 0;
 	realname = realpath(sdp->path_name, NULL);
 	if (!realname) {
 		perror(sdp->path_name);
-		return;
+		return 0;
 	}
 	if (fp == NULL) {
 		perror("open: /proc/mounts");
@@ -145,16 +145,28 @@ 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);
 		if (strncmp(sdp->device_name, "/dev/loop", 9) == 0)
 			die("Cannot perform this operation on a loopback GFS2 mount.\n");
 
 		free(realname);
-		return;
+		return 1; /* mounted */
 	}
-	free(realname);
 	fclose(fp);
-	die("gfs2 Filesystem %s is not mounted.\n", sdp->path_name);
+	free(realname);
+	return 0; /* not mounted */
+}
+
+void
+check_for_gfs2(struct gfs2_sbd *sdp)
+{
+	int ro;
+
+	if (!is_pathname_mounted(sdp, &ro))
+		die("gfs2 Filesystem %s is not mounted.\n", sdp->path_name);
 }
 
 static void