[Cluster-devel] cluster/gfs2 libgfs2/ondisk.c quota/check.c qu ...

adas at sourceware.org adas at sourceware.org
Fri Aug 24 06:08:22 UTC 2007


CVSROOT:	/cvs/cluster
Module name:	cluster
Branch: 	RHEL5
Changes by:	adas at sourceware.org	2007-08-24 06:08:21

Modified files:
	gfs2/libgfs2   : ondisk.c 
	gfs2/quota     : check.c gfs2_quota.h main.c 

Log message:
	fix for bz253016: userland fixes for gfs2 quota linked list

Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs2/libgfs2/ondisk.c.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.5.2.1&r2=1.5.2.2
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs2/quota/check.c.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.2.2.3&r2=1.2.2.4
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs2/quota/gfs2_quota.h.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.1.2.3&r2=1.1.2.4
http://sourceware.org/cgi-bin/cvsweb.cgi/cluster/gfs2/quota/main.c.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.2.2.4&r2=1.2.2.5

--- cluster/gfs2/libgfs2/ondisk.c	2007/06/18 21:50:00	1.5.2.1
+++ cluster/gfs2/libgfs2/ondisk.c	2007/08/24 06:08:21	1.5.2.2
@@ -252,6 +252,8 @@
 	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);
 }
 
 void gfs2_quota_out(struct gfs2_quota *qu, char *buf)
@@ -261,7 +263,8 @@
 	CPOUT_64(qu, str, qu_limit);
 	CPOUT_64(qu, str, qu_warn);
 	CPOUT_64(qu, str, qu_value);
-	memset(qu->qu_reserved, 0, sizeof(qu->qu_reserved));
+	CPOUT_32(qu, str, qu_ll_next);
+	CPOUT_08(qu, str, qu_reserved, 60);
 }
 
 void gfs2_quota_print(struct gfs2_quota *qu)
--- cluster/gfs2/quota/check.c	2007/05/31 22:39:15	1.2.2.3
+++ cluster/gfs2/quota/check.c	2007/08/24 06:08:21	1.2.2.4
@@ -187,8 +187,8 @@
 	char buf[sizeof(struct gfs2_quota)];
 	struct gfs2_quota q;
 	uint64_t offset = 0;
-	uint32_t id;
-	int error;
+	uint32_t id, prev, maxid;
+	int error, pass, id_type;
 	char quota_file[BUF_SIZE];
 	
 	strcpy(sdp->path_name, comline->filesystem);
@@ -209,6 +209,33 @@
 		    strerror(errno));
 	}
 
+	if (!is_valid_quota_list(fd)) {
+		print_quota_list_warning();
+		goto do_old_school;
+	}
+	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);
+	}
+	goto out;
+	
+do_old_school:
 	do {
 		
 		memset(buf, 0, sizeof(struct gfs2_quota));
@@ -237,6 +264,7 @@
 		offset += sizeof(struct gfs2_quota);
 	} while (error == sizeof(struct gfs2_quota));
 
+out:
 	close(fd);
 	close(sdp->metafs_fd);
 	cleanup_metafs(sdp);
--- cluster/gfs2/quota/gfs2_quota.h	2007/05/31 22:39:15	1.1.2.3
+++ cluster/gfs2/quota/gfs2_quota.h	2007/08/24 06:08:21	1.1.2.4
@@ -58,6 +58,7 @@
 #define GQ_OP_WARN           (16)
 #define GQ_OP_CHECK          (17)
 #define GQ_OP_INIT           (18)
+#define GQ_OP_RESET           (19)
 
 #define GQ_ID_USER           (23)
 #define GQ_ID_GROUP          (24)
@@ -97,6 +98,13 @@
 void mount_gfs2_meta();
 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  */
 
--- cluster/gfs2/quota/main.c	2007/05/31 22:39:15	1.2.2.4
+++ cluster/gfs2/quota/main.c	2007/08/24 06:08:21	1.2.2.5
@@ -77,6 +77,7 @@
 	printf("  warn             set a quota warning value for an ID\n");
 	printf("  check            check the quota file\n");
 	printf("  init             initialize the quota file\n");
+	printf("  reset            reset the quota file\n");
 	printf("\n");
 	printf("Options:\n");
 	printf("  -b               sizes are in FS blocks\n");
@@ -214,6 +215,10 @@
 			if (comline->operation)
 				die("can't specify two operations\n");
 			comline->operation = GQ_OP_INIT;
+		} else if (strcmp(argv[optind], "reset") == 0) {
+			if (comline->operation)
+				die("can't specify two operations\n");
+			comline->operation = GQ_OP_RESET;
 		} else
 			die("unknown option %s\n", argv[optind]);
 
@@ -297,6 +302,239 @@
 	close(fd);
 }
 
+inline void 
+read_quota_internal(int fd, uint32_t id, int id_type, struct gfs2_quota *q)
+{
+	/* seek to the appropriate offset in the quota file and read the 
+	   quota info */
+	uint64_t offset;
+	char buf[256];
+	int error;
+	if (id_type == GQ_ID_USER)
+		offset = (2 * (uint64_t)id) * sizeof(struct gfs2_quota);
+	else
+		offset = (2 * (uint64_t)id + 1) * sizeof(struct gfs2_quota);
+	lseek(fd, offset, SEEK_SET);
+	error = read(fd, buf, sizeof(struct gfs2_quota));
+	if (error < 0)
+		die("failed to read from quota file: %s\n", strerror(errno));
+	if (error != sizeof(struct gfs2_quota))
+		die("Couldn't read %d bytes from quota file at offset %llu\n",
+		    sizeof(struct gfs2_quota), offset);
+	gfs2_quota_in(q, buf);
+}
+
+inline void 
+write_quota_internal(int fd, uint32_t id, int id_type, struct gfs2_quota *q)
+{
+	/* seek to the appropriate offset in the quota file and read the
+	   quota info */
+	uint64_t offset;
+	char buf[256];
+	int error;
+	if (id_type == GQ_ID_USER)
+		offset = (2 * (uint64_t)id) * sizeof(struct gfs2_quota);
+	else
+		offset = (2 * (uint64_t)id + 1) * sizeof(struct gfs2_quota);
+	lseek(fd, offset, SEEK_SET);
+	gfs2_quota_out(q, buf);
+	error = write(fd, buf, sizeof(struct gfs2_quota));
+	if (error != sizeof(struct gfs2_quota))
+		die("failed to write to quota file: %s\n", strerror(errno));
+}
+
+/**
+ * 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
+ */
+
+static void
+do_reset(struct gfs2_sbd *sdp, commandline_t *comline)
+{
+	int fd;
+	char quota_file[BUF_SIZE], c;
+	struct gfs2_quota q;
+
+	if (!*comline->filesystem)
+		die("need a filesystem to work on\n");
+
+	printf("This operation will permanently erase all quota information. "
+	       "You will have to re-assign all quota limit/warn values. "
+	       "Proceed [y/N]? ");
+	c = getchar();
+	if (c != 'y' && c != 'Y')
+		return;
+
+	strcpy(sdp->path_name, comline->filesystem);
+	check_for_gfs2(sdp);
+	read_superblock(&sdp->sd_sb, sdp);
+	if (!find_gfs2_meta(sdp))
+		mount_gfs2_meta(sdp);
+	lock_for_admin(sdp);
+	
+	strcpy(quota_file, sdp->metafs_path);
+	strcat(quota_file, "/quota");
+
+	fd = open(quota_file, O_RDWR);
+	if (fd < 0) {
+		close(sdp->metafs_fd);
+		cleanup_metafs(sdp);
+		die("can't open file %s: %s\n", quota_file,
+		    strerror(errno));
+	}
+
+	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
+	 * two quotas(uid=0 and gid=0) remain.
+	 */
+	if (ftruncate(fd, (sizeof(struct gfs2_quota)) * 2))
+		die("couldn't truncate quota file %s\n", strerror(errno));
+	
+	close(fd);
+	close(sdp->metafs_fd);
+	cleanup_metafs(sdp);
+}
+
 /**
  * do_list - List all the quota data for a filesystem
  * @comline: the struct containing the parsed command line arguments
@@ -310,10 +548,11 @@
 	struct gfs2_quota q;
 	char buf[sizeof(struct gfs2_quota)];
 	uint64_t offset;
-	uint32_t id;
+	uint32_t id, prev, maxid;
 	int pass = 0;
 	int error = 0;
 	char quota_file[BUF_SIZE];
+	int id_type = comline->id_type;
 	
 	if (!*comline->filesystem)
 		die("need a filesystem to work on\n");
@@ -335,6 +574,30 @@
 		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;
+	}
+	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;
+			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);
+	}
+	goto out;
+
+do_old_school:
 	for (pass=0; pass<2; pass++) {
 		if (!pass)
 			offset = 0;
@@ -359,7 +622,7 @@
 			offset += 2 * sizeof(struct gfs2_quota);
 		} while (error == sizeof(struct gfs2_quota));
 	}
-
+out:
 	close(fd);
 	close(sdp->metafs_fd);
 	cleanup_metafs(sdp);
@@ -380,6 +643,7 @@
 	struct gfs2_quota q;
 	uint64_t offset;
 	int error;
+	uint32_t maxid;
 	char quota_file[BUF_SIZE];
 
 	strcpy(sdp->path_name, filesystem);
@@ -407,6 +671,10 @@
 
 	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) {
@@ -420,6 +688,7 @@
 	gfs2_quota_in(&q, buf);
 
 
+print_empty_quota:
 	print_quota(comline,
 		    (comline->id_type == GQ_ID_USER), comline->id,
 		    &q, &sdp->sd_sb);
@@ -543,7 +812,7 @@
 	int fd, fd1;
 	uint64_t offset;
 	uint64_t new_value;
-	int error;
+	int error, adj_flag = 0;;
 	char quota_file[BUF_SIZE];
 	char sys_q_refresh[BUF_SIZE];
 	char id_str[16];
@@ -564,7 +833,7 @@
 	strcpy(quota_file, sdp->metafs_path);
 	strcat(quota_file, "/quota");
 
-	fd = open(quota_file, O_WRONLY);
+	fd = open(quota_file, O_RDWR);
 	if (fd < 0) {
 		close(sdp->metafs_fd);
 		cleanup_metafs(sdp);
@@ -572,6 +841,11 @@
 		    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);
@@ -706,6 +980,8 @@
 		goto out;
 	}
 	close(fd1);
+	if (adj_flag)
+		adjust_quota_list(fd, comline);
 out:
 	close(fd);
 	close(sdp->metafs_fd);
@@ -765,6 +1041,9 @@
 		do_quota_init(sdp, &comline);
 		break;
 
+	case GQ_OP_RESET:
+		do_reset(sdp, &comline);
+		break;
 	default:
 		if (!comline.id_type) {
 			comline.id_type = GQ_ID_USER;




More information about the Cluster-devel mailing list