CVSROOT:        /cvs/cluster
Module name:    cluster
Branch:         RHEL5
Changes by:     [EMAIL PROTECTED]       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;

Reply via email to