From mboxrd@z Thu Jan 1 00:00:00 1970 From: adas@sourceware.org Date: 24 Aug 2007 06:08:22 -0000 Subject: [Cluster-devel] cluster/gfs2 libgfs2/ondisk.c quota/check.c qu ... Message-ID: <20070824060822.14488.qmail@sourceware.org> List-Id: To: cluster-devel.redhat.com MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit 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@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;