Sophie

Sophie

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

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

commit 3431e564c5873134bea32dadc488bed728805d10
Author: Abhijith Das <adas@redhat.com>
Date:   Mon Dec 14 16:27:28 2009 -0600

    gfs2_quota: Bug 536902 - quota file size not a multiple of struct gfs2_quota
    
    This is the userland portion of a patch-set that rips out the
    linked list implementation in the quota file. It makes all
    the quota linked list related warnings go away.
    An accompanying kernel patch (tracked in bz 546455) is the
    other half of this patch-set. This userland patch should
    work harmoniously with an unpatched kernel, but it is
    recommended that the kernel patch be applied as well.
    Patched kernel and unpatched userland gfs2_quota is not
    tested and is likely to crash.
    
    It is also recommended that the quotas be reset on the
    filesystem using "gfs2_quota reset -f /mnt/gfs2". This is to
    avoid a possible corruption introduced by the linked-list
    code.

    Resolves: rhbz#536902

diff --git a/gfs2/libgfs2/ondisk.c b/gfs2/libgfs2/ondisk.c
index 60a0bd8..896ec73 100644
--- a/gfs2/libgfs2/ondisk.c
+++ b/gfs2/libgfs2/ondisk.c
@@ -267,8 +267,7 @@ void gfs2_quota_in(struct gfs2_quota *qu, char *buf)
 	CPIN_64(qu, str, qu_limit);
 	CPIN_64(qu, str, qu_warn);
 	CPIN_64(qu, str, qu_value);
-	CPIN_32(qu, str, qu_ll_next);
-	CPIN_08(qu, str, qu_reserved, 60);
+	CPIN_08(qu, str, qu_reserved, sizeof(qu->qu_reserved));
 }
 
 void gfs2_quota_out(struct gfs2_quota *qu, char *buf)
@@ -278,8 +277,7 @@ void gfs2_quota_out(struct gfs2_quota *qu, char *buf)
 	CPOUT_64(qu, str, qu_limit);
 	CPOUT_64(qu, str, qu_warn);
 	CPOUT_64(qu, str, qu_value);
-	CPOUT_32(qu, str, qu_ll_next);
-	CPOUT_08(qu, str, qu_reserved, 60);
+	memset(qu->qu_reserved, 0, sizeof(qu->qu_reserved));
 }
 
 void gfs2_quota_print(struct gfs2_quota *qu)
diff --git a/gfs2/quota/check.c b/gfs2/quota/check.c
index ae09cd0..f33aa17 100644
--- a/gfs2/quota/check.c
+++ b/gfs2/quota/check.c
@@ -25,12 +25,16 @@
 #include <limits.h>
 #include <stdint.h>
 #include <inttypes.h>
+#include <linux/types.h>
+#include <linux/fiemap.h>
 
 #define __user
 #include "osi_list.h"
 
 #include "gfs2_quota.h"
 
+#define FS_IOC_FIEMAP                   _IOWR('f', 11, struct fiemap)
+
 struct values {
 	osi_list_t v_list;
 
@@ -187,9 +191,12 @@ read_quota_file(struct gfs2_sbd *sdp, commandline_t *comline,
 	char buf[sizeof(struct gfs2_quota)];
 	struct gfs2_quota q;
 	uint64_t offset = 0;
-	uint32_t id, prev, maxid;
-	int error, pass, id_type;
+	uint32_t id, startid;
+	int error = 0;
 	char quota_file[BUF_SIZE];
+	uint64_t quota_file_size = 0;
+	struct fiemap fmap = { 0, }, *fmap2;
+	struct stat statbuf;
 	
 	strcpy(sdp->path_name, comline->filesystem);
 	check_for_gfs2(sdp);
@@ -206,62 +213,73 @@ read_quota_file(struct gfs2_sbd *sdp, commandline_t *comline,
 		die("can't open file %s: %s\n", comline->filesystem,
 		    strerror(errno));
 	}
-
-	if (!is_valid_quota_list(fd)) {
-		print_quota_list_warning();
-		goto do_old_school;
+	if (fstat(fd, &statbuf) < 0) {
+		close(fd);
+		close(sdp->metafs_fd);
+		cleanup_metafs(sdp);
+		die("can't stat file %s: %s\n", quota_file,
+		    strerror(errno));
 	}
-	get_last_quota_id(fd, &maxid);
-	
-	for (pass=0; pass<2; pass++) {
-		id = 0;
-		id_type = pass ? GQ_ID_GROUP : GQ_ID_USER;
-		
-		do {
-			read_quota_internal(fd, id, id_type, &q);
-			prev = id;
-			q.qu_value <<= sdp->sd_sb.sb_bsize_shift - 9;
-			
-			if (q.qu_value) {
-				if (pass)
-					add_value(gid, id, q.qu_value);
-				else
-					add_value(uid, id, q.qu_value);
-			}
-			id = q.qu_ll_next;
-		} while(id && id > prev && id <= maxid);
+	quota_file_size = statbuf.st_size;
+	/* First find the number of extents in the quota file */
+	fmap.fm_start = 0;
+	fmap.fm_length = (~0ULL);
+	error = ioctl(fd, FS_IOC_FIEMAP, &fmap);
+	if (error == -1) {
+		fprintf(stderr, "fiemap error (%d): %s\n", errno, strerror(errno));
+		goto out;
 	}
-	goto out;
-	
-do_old_school:
-	do {
-		
-		memset(buf, 0, sizeof(struct gfs2_quota));
-		/* read hidden quota file here */
-		lseek(fd, offset, SEEK_SET);
-		error = read(fd, buf, sizeof(struct gfs2_quota));
-		if (error < 0) {
-			close(fd);
-			close(sdp->metafs_fd);
-			cleanup_metafs(sdp);
-			die("can't read quota file (%d): %s\n",
-			    error, strerror(errno));
-		}
-		gfs2_quota_in(&q, buf);
-
-		id = (offset / sizeof(struct gfs2_quota)) >> 1;
-		q.qu_value <<= sdp->sd_sb.sb_bsize_shift - 9;
-
-		if (q.qu_value) {
-			if (id * sizeof(struct gfs2_quota) * 2 == offset)
-				add_value(uid, id, q.qu_value);
-			else
-				add_value(gid, id, q.qu_value);
+	fmap2 = malloc(sizeof(struct fiemap) +
+		       fmap.fm_mapped_extents * sizeof(struct fiemap_extent));
+	if (fmap2 == NULL) {
+		fprintf(stderr, "malloc error (%d): %s\n", errno, strerror(errno));
+		goto out;
+	}
+	fmap2->fm_start = 0;
+	fmap2->fm_length = (~0ULL);
+	fmap2->fm_extent_count = fmap.fm_mapped_extents;
+	error = ioctl(fd, FS_IOC_FIEMAP, fmap2);
+	if (error == -1) {
+		fprintf(stderr, "fiemap error (%d): %s\n", errno, strerror(errno));
+		goto fmap2_free;
+	}
+	if (fmap2->fm_mapped_extents) {
+		int i;
+		for (i=0; i<fmap2->fm_mapped_extents; i++) {
+			struct fiemap_extent *fe = &fmap2->fm_extents[i];
+			uint64_t end = fe->fe_logical + fe->fe_length;
+
+			end = end > quota_file_size ? quota_file_size : end;
+			startid = DIV_RU(fe->fe_logical, sizeof(struct gfs2_quota));
+			offset = startid * sizeof(struct gfs2_quota);
+			do {
+				memset(buf, 0, sizeof(struct gfs2_quota));
+				/* read hidden quota file here */
+				lseek(fd, offset, SEEK_SET);
+				error = read(fd, buf, sizeof(struct gfs2_quota));
+				if (error < 0) {
+					fprintf(stderr, "read error (%d): %s\n", 
+						errno, strerror(errno));
+					goto fmap2_free;
+				}
+				gfs2_quota_in(&q, buf);
+				id = (offset / sizeof(struct gfs2_quota)) >> 1;
+				q.qu_value <<= sdp->sd_sb.sb_bsize_shift - 9;
+
+				if (q.qu_value) {
+					if (id * sizeof(struct gfs2_quota) * 2 == offset)
+						add_value(uid, id, q.qu_value);
+					else
+						add_value(gid, id, q.qu_value);
+				}
+
+				offset += sizeof(struct gfs2_quota);
+			} while ((offset + sizeof(struct gfs2_quota)) < 
+				 end);
 		}
-
-		offset += sizeof(struct gfs2_quota);
-	} while (error == sizeof(struct gfs2_quota));
-
+	}
+fmap2_free:
+	free(fmap2);
 out:
 	close(fd);
 	close(sdp->metafs_fd);
diff --git a/gfs2/quota/gfs2_quota.h b/gfs2/quota/gfs2_quota.h
index 6c7c87f..a929dfa 100644
--- a/gfs2/quota/gfs2_quota.h
+++ b/gfs2/quota/gfs2_quota.h
@@ -81,13 +81,10 @@ void do_get_super(int fd, struct gfs2_sb *sb);
 void do_sync(struct gfs2_sbd *sdp, commandline_t *comline);
 void cleanup();
 void read_superblock(struct gfs2_sb *sb, struct gfs2_sbd *sdp);
-void get_last_quota_id(int fd, uint32_t *max_id);
-int is_valid_quota_list(int fd);
 inline void read_quota_internal(int fd, unsigned int id, int id_type, 
 				struct gfs2_quota *q);
 inline void write_quota_internal(int fd, unsigned int id, int id_type, 
 				 struct gfs2_quota *q);
-void print_quota_list_warning();
 
 /*  check.c  */
 
diff --git a/gfs2/quota/main.c b/gfs2/quota/main.c
index 3b26a11..e8b0fa2 100644
--- a/gfs2/quota/main.c
+++ b/gfs2/quota/main.c
@@ -30,6 +30,7 @@
 #include <inttypes.h>
 
 #include <linux/types.h>
+#include <linux/fiemap.h>
 #include "gfs2_quota.h"
 
 #define __user
@@ -40,6 +41,7 @@
 /*  Constants  */
 
 #define OPTION_STRING ("bdf:g:hkl:mnsu:V")
+#define FS_IOC_FIEMAP                   _IOWR('f', 11, struct fiemap)
 
 char *prog_name;
 
@@ -319,7 +321,7 @@ read_quota_internal(int fd, uint32_t id, int id_type, struct gfs2_quota *q)
 	if (error < 0)
 		die("failed to read from quota file: %s\n", strerror(errno));
 	if (error != sizeof(struct gfs2_quota))
-		die("Couldn't read %lu bytes from quota file at offset %llu\n",
+		die("Couldn't read %u bytes from quota file at offset %llu\n",
 		    sizeof(struct gfs2_quota), (unsigned long long)offset);
 	gfs2_quota_in(q, buf);
 }
@@ -344,139 +346,6 @@ write_quota_internal(int fd, uint32_t id, int id_type, struct gfs2_quota *q)
 }
 
 /**
- * get_last_quota_id - Get the last quota in the quota file
- * @fd: an open file descriptor of the quota file
- * @id_type: GQ_ID_USER or GQ_ID_GROUP
- * @max_id: return the maximum id obtained
- */
-void 
-get_last_quota_id(int fd, uint32_t *max_id)
-{
-	/* stat(2) the quota file to find how big it is. This will give us a
-	 * a rough idea of what the last valid quota uid/gid is. It is possible 
-	 * that the last quota in the file belongs to a group with gid:x and 
-	 * the corresponding user quota with uid:x doesn't exist. In such cases
-	 * we still return x as max_id. This max_id is ONLY A HINT. If used 
-	 * as a terminating condition of a loop, another condition should also
-	 * be specified.
-	 */
-	struct stat st;
-	uint32_t qsize = sizeof(struct gfs2_quota);
-	uint64_t size;
-	if (fstat(fd, &st))
-		die("failed to stat the quota file: %s\n", strerror(errno));
-	size = st.st_size;
-	if (!size)
-		die("error: quota file is truncated to zero!\n");
-	if (size % qsize) {
-		printf("warning: quota file size not a multiple of "
-		       "struct gfs2_quota\n");
-		size = qsize * (size / qsize);
-	}
-	*max_id = (size - 1) / (2 * qsize);
-}
-
-/**
- * is_valid_quota_list - Check if we have a valid quota list
- * @fd: an open file descriptor of the quota file
- * Returns 0 or 1.
- */
-int 
-is_valid_quota_list(int fd)
-{
-	/* This is a slow test to determine if the quotas are in a 
-	 * linked list. We should come up with something better
-	 * Quota linked list format is identified by the following.
-	 * step1: Get the maximum groupid and userid having valid
-	 *        quotas in the quota file.
-	 * step2: Obtain the size of the quota file. The size of the 
-	 *        quota file (position of the last valid quota) 
-	 *        determines the last user/group id.
-	 * step3: If we can obtain the last valid quota through the 
-	 *        lists, then our lists are good. Else, the lists are 
-	 *        either corrupt or an older quota file format is in use
-	 */
-	int id_type = GQ_ID_GROUP;
-	uint32_t id = 0, prev, ulast = 0, glast = 0, max;
-	struct gfs2_quota q;
-
-	get_last_quota_id(fd, &max);
-again:
-	do {
-		read_quota_internal(fd, id, id_type, &q);
-		prev = id;
-		id = q.qu_ll_next;
-		if (id > max)
-			return 0;
-	} while (id && id > prev);
-
-	if (id && id <= prev)
-		return 0;
-
-	if (id_type == GQ_ID_GROUP)
-		glast = prev;
-	else
-		ulast = prev;
-
-	if (id_type == GQ_ID_GROUP) {
-		id_type = GQ_ID_USER;
-		id = 0;
-		goto again;
-	}
-
-	if (glast != max && ulast != max)
-		return 0;
-	
-	return 1;
-}
-
-void 
-print_quota_list_warning()
-{
-	printf("\nWarning: This filesystem doesn't seem to have the new quota "
-	       "list format or the quota list is corrupt. list, check and init "
-	       "operation performance will suffer due to this. It is recommended "
-	       "that you run the 'gfs2_quota reset' operation to reset the quota "
-	       "file. All current quota information will be lost and you will "
-	       "have to reassign all quota limits and warnings\n\n"); 
-}
-
-/**
- * adjust_quota_list - Adjust the quota linked list
- * @fd: The quota file descriptor
- * @comline: the struct containing the parsed command line arguments
- */
-static void
-adjust_quota_list(int fd, commandline_t *comline)
-{
-	uint32_t prev = 0, next = 0, id = comline->id;
-	struct gfs2_quota tmpq, q;
-	int id_type = comline->id_type;
-	
-	if (id == 0) /* root quota, don't do anything */
-		goto out;
-	/* We just wrote the quota for id in do_set(). Get it */
-	next = 0;
-	do {
-		read_quota_internal(fd, next, id_type, &q);
-		prev = next;
-		next = q.qu_ll_next;
-		if (prev == id) /* no duplicates, bail */
-			goto out;
-		if (prev < id && id < next) /* gotcha! */
-			break;
-	} while(next && next > prev);
-	read_quota_internal(fd, id, id_type, &tmpq);
-	tmpq.qu_ll_next = next;
-	q.qu_ll_next = id;
-	write_quota_internal(fd, id, id_type, &tmpq);
-	write_quota_internal(fd, prev, id_type, &q);
-
-out:
-	return;
-}
-
-/**
  * do_reset - Reset all the quota data for a filesystem
  * @comline: the struct containing the parsed command line arguments
  */
@@ -515,11 +384,9 @@ do_reset(struct gfs2_sbd *sdp, commandline_t *comline)
 	}
 
 	read_quota_internal(fd, 0, GQ_ID_USER, &q);
-	q.qu_ll_next = 0;
 	write_quota_internal(fd, 0, GQ_ID_USER, &q);
 
 	read_quota_internal(fd, 0, GQ_ID_GROUP, &q);
-	q.qu_ll_next = 0;
 	write_quota_internal(fd, 0, GQ_ID_GROUP, &q);
 
 	/* truncate the quota file such that only the first
@@ -544,13 +411,14 @@ do_list(struct gfs2_sbd *sdp, commandline_t *comline)
 {
 	int fd;
 	struct gfs2_quota q;
-	char buf[sizeof(struct gfs2_quota)];
-	uint64_t offset;
-	uint32_t id, prev, maxid;
+	uint32_t startid, endid;
+	int id_type;
 	int pass = 0;
 	int error = 0;
 	char quota_file[BUF_SIZE];
-	int id_type = comline->id_type;
+	uint64_t quota_file_size = 0;
+	struct fiemap fmap = { 0, }, *fmap2;
+	struct stat statbuf;
 	
 	if (!*comline->filesystem)
 		die("need a filesystem to work on\n");
@@ -570,54 +438,68 @@ do_list(struct gfs2_sbd *sdp, commandline_t *comline)
 		die("can't open file %s: %s\n", quota_file,
 		    strerror(errno));
 	}
-	
-	if (!is_valid_quota_list(fd)) {
-		print_quota_list_warning();
-		goto do_old_school;
+	if (fstat(fd, &statbuf) < 0) {
+		close(fd);
+		close(sdp->metafs_fd);
+		cleanup_metafs(sdp);
+		die("can't stat file %s: %s\n", quota_file,
+		    strerror(errno));
+	}
+	quota_file_size = statbuf.st_size;
+	/* First find the number of extents in the quota file */
+	fmap.fm_start = 0;
+	fmap.fm_length = (~0ULL);
+	error = ioctl(fd, FS_IOC_FIEMAP, &fmap);
+	if (error == -1) {
+		fprintf(stderr, "fiemap error (%d): %s\n", errno, strerror(errno));
+		goto out;
+	}
+	fmap2 = malloc(sizeof(struct fiemap) + 
+		       fmap.fm_mapped_extents * sizeof(struct fiemap_extent));
+	if (fmap2 == NULL) {
+		fprintf(stderr, "malloc error (%d): %s\n", errno, strerror(errno));
+		goto out;
 	}
-	get_last_quota_id(fd, &maxid);
+	fmap2->fm_start = 0;
+	fmap2->fm_length = (~0ULL);
+	fmap2->fm_extent_count = fmap.fm_mapped_extents;
 	
-	for (pass=0; pass<2; pass++) {
-		id = 0;
-		id_type = pass ? GQ_ID_GROUP : GQ_ID_USER;
-		
-		do {
-			read_quota_internal(fd, id, id_type, &q);
-			prev = id;
-			if (q.qu_limit || q.qu_warn || q.qu_value)
-				print_quota(comline, 
-					    id_type == GQ_ID_USER ? TRUE : FALSE, 
-					    id, &q, &sdp->sd_sb);
-			id = q.qu_ll_next;
-		} while(id && id > prev && id <= maxid);
+	error = ioctl(fd, FS_IOC_FIEMAP, fmap2);
+	if (error == -1) {
+		fprintf(stderr, "fiemap error (%d): %s\n", errno, strerror(errno));
+		goto fmap2_free;
 	}
-	goto out;
-
-do_old_school:
-	for (pass=0; pass<2; pass++) {
-		if (!pass)
-			offset = 0;
-		else
-			offset = sizeof(struct gfs2_quota);
-
-		do {
-			memset(buf, 0, sizeof(struct gfs2_quota));
-
-			/* read hidden quota file here */
-			lseek(fd, offset, SEEK_SET);
-			error = read(fd, buf, sizeof(struct gfs2_quota));
-
-			gfs2_quota_in(&q, buf);
-
-			id = (offset / sizeof(struct gfs2_quota)) >> 1;
-
-			if (q.qu_limit || q.qu_warn || q.qu_value)
-				print_quota(comline, (pass) ? FALSE : TRUE, id,
-					    &q, &sdp->sd_sb);
-
-			offset += 2 * sizeof(struct gfs2_quota);
-		} while (error == sizeof(struct gfs2_quota));
+	if (fmap2->fm_mapped_extents) {
+		int i;
+	again:
+		for (i=0; i<fmap2->fm_mapped_extents; i++) {
+			struct fiemap_extent *fe = &fmap2->fm_extents[i];
+			uint64_t end = fe->fe_logical + fe->fe_length;
+
+			end = end > quota_file_size ? quota_file_size : end;
+			startid = DIV_RU(fe->fe_logical, sizeof(struct gfs2_quota));
+			endid = end /(2 * sizeof(struct gfs2_quota));
+			if (startid % 2 != pass)
+				startid++;
+
+			startid = DIV_RU(startid, 2);
+			id_type = pass == 0 ? GQ_ID_USER : GQ_ID_GROUP;
+			do {
+				read_quota_internal(fd, startid, id_type, &q);
+				if (q.qu_limit || q.qu_warn || q.qu_value)
+					print_quota(comline, (pass) ? FALSE : TRUE, startid,
+						    &q, &sdp->sd_sb);
+				startid++;
+			} while (startid < endid);
+		}
+		if (!pass) {
+			pass = 1;
+			goto again;
+		}
 	}
+
+fmap2_free:
+	free(fmap2);
 out:
 	close(fd);
 	close(sdp->metafs_fd);
@@ -639,7 +521,6 @@ do_get_one(struct gfs2_sbd *sdp, commandline_t *comline, char *filesystem)
 	struct gfs2_quota q;
 	uint64_t offset;
 	int error;
-	uint32_t maxid;
 	char quota_file[BUF_SIZE];
 
 	strcpy(sdp->path_name, filesystem);
@@ -665,10 +546,6 @@ do_get_one(struct gfs2_sbd *sdp, commandline_t *comline, char *filesystem)
 
 	memset(&q, 0, sizeof(struct gfs2_quota));
 	
-	get_last_quota_id(fd, &maxid);
-	if (comline->id > maxid)
-		goto print_empty_quota;
-
 	lseek(fd, offset, SEEK_SET);
 	error = read(fd, buf, sizeof(struct gfs2_quota));
 	if (error < 0) {
@@ -680,9 +557,6 @@ do_get_one(struct gfs2_sbd *sdp, commandline_t *comline, char *filesystem)
 	}
 
 	gfs2_quota_in(&q, buf);
-
-
-print_empty_quota:
 	print_quota(comline,
 		    (comline->id_type == GQ_ID_USER), comline->id,
 		    &q, &sdp->sd_sb);
@@ -792,7 +666,7 @@ do_set(struct gfs2_sbd *sdp, commandline_t *comline)
 	int fd;
 	uint64_t offset;
 	uint64_t new_value;
-	int error, adj_flag = 0;;
+	int error;
 	char quota_file[BUF_SIZE];
 	char id_str[16];
 	struct stat stat_buf;
@@ -811,7 +685,7 @@ do_set(struct gfs2_sbd *sdp, commandline_t *comline)
 	strcpy(quota_file, sdp->metafs_path);
 	strcat(quota_file, "/quota");
 
-	fd = open(quota_file, O_RDWR);
+	fd = open(quota_file, O_WRONLY);
 	if (fd < 0) {
 		close(sdp->metafs_fd);
 		cleanup_metafs(sdp);
@@ -819,11 +693,6 @@ do_set(struct gfs2_sbd *sdp, commandline_t *comline)
 		    strerror(errno));
 	}
 	
-	if (is_valid_quota_list(fd))
-		adj_flag = 1;
-	else
-		print_quota_list_warning();
-
 	switch (comline->id_type) {
 	case GQ_ID_USER:
 		offset = (2 * (uint64_t)comline->id) * sizeof(struct gfs2_quota);
@@ -941,8 +810,6 @@ do_set(struct gfs2_sbd *sdp, commandline_t *comline)
 	set_sysfs(fs, comline->id_type == GQ_ID_USER ?
 		  "quota_refresh_user" : "quota_refresh_group", id_str);
 	
-	if (adj_flag)
-		adjust_quota_list(fd, comline);
 out:
 	close(fd);
 	close(sdp->metafs_fd);