summaryrefslogtreecommitdiff
path: root/convertquota.c
diff options
context:
space:
mode:
authorYang Lin <lin.a.yang@intel.com>2012-05-30 19:43:21 +0800
committerYang Lin <lin.a.yang@intel.com>2012-05-30 19:43:21 +0800
commit8c82de96cd23e4823a2d29eb2de2295c0866b0c9 (patch)
tree4c255e87442eb17f710f1674e13303ff56be0c7a /convertquota.c
downloadquota-8c82de96cd23e4823a2d29eb2de2295c0866b0c9.tar.gz
quota-8c82de96cd23e4823a2d29eb2de2295c0866b0c9.tar.bz2
quota-8c82de96cd23e4823a2d29eb2de2295c0866b0c9.zip
Initial commit to Gerrittizen/20120530.11.0_branch1.0
Diffstat (limited to 'convertquota.c')
-rw-r--r--convertquota.c409
1 files changed, 409 insertions, 0 deletions
diff --git a/convertquota.c b/convertquota.c
new file mode 100644
index 0000000..ab16193
--- /dev/null
+++ b/convertquota.c
@@ -0,0 +1,409 @@
+/*
+ *
+ * Utility for converting quota file from old to new format
+ *
+ * Sponsored by SuSE CR
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include <asm/byteorder.h>
+
+#include "pot.h"
+#include "common.h"
+#include "quotaio.h"
+#include "quotasys.h"
+#include "quota.h"
+#include "bylabel.h"
+#include "quotaio_v2.h"
+#include "dqblk_v2.h"
+
+#define ACT_FORMAT 1 /* Convert format from old to new */
+#define ACT_ENDIAN 2 /* Convert endianity */
+
+char *mntpoint;
+char *progname;
+int ucv, gcv;
+struct quota_handle *qn; /* Handle of new file */
+int action; /* Action to be performed */
+int infmt, outfmt;
+
+static void usage(void)
+{
+ errstr(_("Utility for converting quota files.\nUsage:\n\t%s [options] mountpoint\n\n\
+-u, --user convert user quota file\n\
+-g, --group convert group quota file\n\
+-e, --convert-endian convert quota file to correct endianity\n\
+-f, --convert-format oldfmt,newfmt convert from old to VFSv0 quota format\n\
+-h, --help show this help text and exit\n\
+-V, --version output version information and exit\n\n"), progname);
+ errstr(_("Bugs to %s\n"), MY_EMAIL);
+ exit(1);
+}
+
+static inline unsigned int min(unsigned a, unsigned b)
+{
+ if (a < b)
+ return a;
+ return b;
+}
+
+#define MAX_FMTNAME_LEN 32
+
+static void parse_options(int argcnt, char **argstr)
+{
+ int ret;
+ struct option long_opts[] = {
+ { "help", 0, NULL, 'h'},
+ { "version", 0, NULL, 'V'},
+ { "user", 0, NULL, 'u'},
+ { "group", 0, NULL, 'g'},
+ { "convert-endian", 0, NULL, 'e'},
+ { "convert-format", 1, NULL, 'f'},
+ { NULL, 0, NULL, 0}
+ };
+ char *comma;
+ char fmtbuf[MAX_FMTNAME_LEN];
+
+ while ((ret = getopt_long(argcnt, argstr, "Vugef:h", long_opts, NULL)) != -1) {
+ switch (ret) {
+ case '?':
+ case 'h':
+ usage();
+ case 'V':
+ version();
+ exit(0);
+ case 'u':
+ ucv = 1;
+ break;
+ case 'g':
+ gcv = 1;
+ break;
+ case 'e':
+ action = ACT_ENDIAN;
+ break;
+ case 'f':
+ action = ACT_FORMAT;
+ comma = strchr(optarg, ',');
+ if (!comma) {
+ errstr(_("You have to specify source and target format of conversion.\n"));
+ usage();
+ }
+ sstrncpy(fmtbuf, optarg, min(comma - optarg + 1, MAX_FMTNAME_LEN));
+ infmt = name2fmt(fmtbuf);
+ if (infmt == QF_ERROR)
+ usage();
+ outfmt = name2fmt(comma + 1);
+ if (outfmt == QF_ERROR)
+ usage();
+ break;
+ }
+ }
+
+ if (optind + 1 != argcnt) {
+ errstr(_("Bad number of arguments.\n"));
+ usage();
+ }
+
+ if (!(ucv | gcv))
+ ucv = 1;
+ if (!action) {
+ errstr(_("You have to specify action to perform.\n"));
+ usage();
+ }
+
+ mntpoint = argstr[optind];
+}
+
+/*
+ * Implementation of endian conversion
+ */
+
+typedef char *dqbuf_t;
+
+#define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7)))
+#define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7)))
+
+#define getdqbuf() smalloc(QT_BLKSIZE)
+#define freedqbuf(buf) free(buf)
+
+static inline void endian_disk2memdqblk(struct util_dqblk *m, struct v2r0_disk_dqblk *d)
+{
+ m->dqb_ihardlimit = __be32_to_cpu(d->dqb_ihardlimit);
+ m->dqb_isoftlimit = __be32_to_cpu(d->dqb_isoftlimit);
+ m->dqb_bhardlimit = __be32_to_cpu(d->dqb_bhardlimit);
+ m->dqb_bsoftlimit = __be32_to_cpu(d->dqb_bsoftlimit);
+ m->dqb_curinodes = __be32_to_cpu(d->dqb_curinodes);
+ m->dqb_curspace = __be64_to_cpu(d->dqb_curspace);
+ m->dqb_itime = __be64_to_cpu(d->dqb_itime);
+ m->dqb_btime = __be64_to_cpu(d->dqb_btime);
+}
+
+/* Is given dquot empty? */
+static int endian_empty_dquot(struct v2r0_disk_dqblk *d)
+{
+ static struct v2r0_disk_dqblk fakedquot;
+
+ return !memcmp(d, &fakedquot, sizeof(fakedquot));
+}
+
+/* Read given block */
+static void read_blk(int fd, uint blk, dqbuf_t buf)
+{
+ int err;
+
+ lseek(fd, blk << QT_BLKSIZE_BITS, SEEK_SET);
+ err = read(fd, buf, QT_BLKSIZE);
+ if (err < 0)
+ die(2, _("Cannot read block %u: %s\n"), blk, strerror(errno));
+ else if (err != QT_BLKSIZE)
+ memset(buf + err, 0, QT_BLKSIZE - err);
+}
+
+static void endian_report_block(int fd, uint blk, char *bitmap)
+{
+ dqbuf_t buf = getdqbuf();
+ struct qt_disk_dqdbheader *dh;
+ struct v2r0_disk_dqblk *ddata;
+ struct dquot dquot;
+ struct qtree_mem_dqinfo *info = &qn->qh_info.u.v2_mdqi.dqi_qtree;
+ int i;
+
+ set_bit(bitmap, blk);
+ read_blk(fd, blk, buf);
+ dh = (struct qt_disk_dqdbheader *)buf;
+ ddata = (struct v2r0_disk_dqblk *)(dh + 1);
+ for (i = 0; i < qtree_dqstr_in_blk(info); i++)
+ if (!endian_empty_dquot(ddata + i)) {
+ memset(&dquot, 0, sizeof(dquot));
+ dquot.dq_h = qn;
+ endian_disk2memdqblk(&dquot.dq_dqb, ddata + i);
+ dquot.dq_id = __be32_to_cpu(ddata[i].dqb_id);
+ if (qn->qh_ops->commit_dquot(&dquot, COMMIT_ALL) < 0)
+ errstr(_("Cannot commit dquot for id %u: %s\n"),
+ (uint)dquot.dq_id, strerror(errno));
+ }
+ freedqbuf(buf);
+}
+
+static void endian_report_tree(int fd, uint blk, int depth, char *bitmap)
+{
+ int i;
+ dqbuf_t buf = getdqbuf();
+ u_int32_t *ref = (u_int32_t *) buf;
+
+ read_blk(fd, blk, buf);
+ if (depth == QT_TREEDEPTH - 1) {
+ for (i = 0; i < QT_BLKSIZE >> 2; i++) {
+ blk = __be32_to_cpu(ref[i]);
+ if (blk && !get_bit(bitmap, blk))
+ endian_report_block(fd, blk, bitmap);
+ }
+ }
+ else {
+ for (i = 0; i < QT_BLKSIZE >> 2; i++)
+ if ((blk = __be32_to_cpu(ref[i])))
+ endian_report_tree(fd, blk, depth + 1, bitmap);
+ }
+ freedqbuf(buf);
+}
+
+static int endian_scan_structures(int fd, int type)
+{
+ char *bitmap;
+ loff_t blocks = (lseek(fd, 0, SEEK_END) + QT_BLKSIZE - 1) >> QT_BLKSIZE_BITS;
+
+ bitmap = smalloc((blocks + 7) >> 3);
+ memset(bitmap, 0, (blocks + 7) >> 3);
+ endian_report_tree(fd, QT_TREEOFF, 0, bitmap);
+ free(bitmap);
+ return 0;
+}
+
+static int endian_check_header(int fd, int type)
+{
+ struct v2_disk_dqheader head;
+ u_int32_t file_magics[] = INITQMAGICS;
+ u_int32_t known_versions[] = INIT_V2_VERSIONS;
+
+ lseek(fd, 0, SEEK_SET);
+ if (read(fd, &head, sizeof(head)) != sizeof(head)) {
+ errstr(_("Cannot read header of old quotafile.\n"));
+ return -1;
+ }
+ if (__be32_to_cpu(head.dqh_magic) != file_magics[type] || __be32_to_cpu(head.dqh_version) > known_versions[type]) {
+ errstr(_("Bad file magic or version (probably not quotafile with bad endianity).\n"));
+ return -1;
+ }
+ return 0;
+}
+
+static int endian_load_info(int fd, int type)
+{
+ struct v2_disk_dqinfo dinfo;
+
+ if (read(fd, &dinfo, sizeof(dinfo)) != sizeof(dinfo)) {
+ errstr(_("Cannot read information about old quotafile.\n"));
+ return -1;
+ }
+ qn->qh_info.u.v2_mdqi.dqi_flags = __be32_to_cpu(dinfo.dqi_flags);
+ qn->qh_info.dqi_bgrace = __be32_to_cpu(dinfo.dqi_bgrace);
+ qn->qh_info.dqi_igrace = __be32_to_cpu(dinfo.dqi_igrace);
+ return 0;
+}
+
+/*
+ * End of endian conversion
+ */
+
+static int convert_dquot(struct dquot *dquot, char *name)
+{
+ struct dquot newdquot;
+
+ memset(&newdquot, 0, sizeof(newdquot));
+ newdquot.dq_id = dquot->dq_id;
+ newdquot.dq_h = qn;
+ newdquot.dq_dqb.dqb_ihardlimit = dquot->dq_dqb.dqb_ihardlimit;
+ newdquot.dq_dqb.dqb_isoftlimit = dquot->dq_dqb.dqb_isoftlimit;
+ newdquot.dq_dqb.dqb_curinodes = dquot->dq_dqb.dqb_curinodes;
+ newdquot.dq_dqb.dqb_bhardlimit = dquot->dq_dqb.dqb_bhardlimit;
+ newdquot.dq_dqb.dqb_bsoftlimit = dquot->dq_dqb.dqb_bsoftlimit;
+ newdquot.dq_dqb.dqb_curspace = dquot->dq_dqb.dqb_curspace;
+ newdquot.dq_dqb.dqb_btime = dquot->dq_dqb.dqb_btime;
+ newdquot.dq_dqb.dqb_itime = dquot->dq_dqb.dqb_itime;
+ if (qn->qh_ops->commit_dquot(&newdquot, COMMIT_ALL) < 0) {
+ errstr(_("Cannot commit dquot for id %u: %s\n"),
+ (uint)dquot->dq_id, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int rename_file(int type, int fmt, struct mntent *mnt)
+{
+ char *qfname, namebuf[PATH_MAX];
+ int ret = 0;
+
+ if (get_qf_name(mnt, type, fmt, 0, &qfname) < 0) {
+ errstr(_("Cannot get name of new quotafile.\n"));
+ return -1;
+ }
+ strcpy(namebuf, qfname);
+ sstrncat(namebuf, ".new", sizeof(namebuf));
+ if (rename(namebuf, qfname) < 0) {
+ errstr(_("Cannot rename new quotafile %s to name %s: %s\n"),
+ namebuf, qfname, strerror(errno));
+ ret = -1;
+ }
+ free(qfname);
+ return ret;
+}
+
+static int convert_format(int type, struct mntent *mnt)
+{
+ struct quota_handle *qo;
+ int ret = 0;
+
+ if (!(qo = init_io(mnt, type, infmt, IOI_OPENFILE))) {
+ errstr(_("Cannot open old format file for %ss on %s\n"),
+ type2name(type), mnt->mnt_dir);
+ return -1;
+ }
+ if (!(qn = new_io(mnt, type, outfmt))) {
+ errstr(_("Cannot create file for %ss for new format on %s: %s\n"),
+ type2name(type), mnt->mnt_dir, strerror(errno));
+ end_io(qo);
+ return -1;
+ }
+ if (qo->qh_ops->scan_dquots(qo, convert_dquot) >= 0) /* Conversion succeeded? */
+ ret = rename_file(type, outfmt, mnt);
+ else
+ ret = -1;
+ end_io(qo);
+ end_io(qn);
+ return ret;
+}
+
+static int convert_endian(int type, struct mntent *mnt)
+{
+ int ret = 0;
+ int ofd;
+ char *qfname;
+
+ if (get_qf_name(mnt, type, QF_VFSV0, NF_EXIST, &qfname) < 0)
+ return -1;
+ if ((ofd = open(qfname, O_RDONLY)) < 0) {
+ errstr(_("Cannot open old quota file on %s: %s\n"), mnt->mnt_dir, strerror(errno));
+ free(qfname);
+ return -1;
+ }
+ free(qfname);
+ if (endian_check_header(ofd, type) < 0) {
+ close(ofd);
+ return -1;
+ }
+ if (!(qn = new_io(mnt, type, QF_VFSV0))) {
+ errstr(_("Cannot create file for %ss for new format on %s: %s\n"),
+ type2name(type), mnt->mnt_dir, strerror(errno));
+ close(ofd);
+ return -1;
+ }
+ if (endian_load_info(ofd, type) < 0) {
+ end_io(qn);
+ close(ofd);
+ return -1;
+ }
+ ret = endian_scan_structures(ofd, type);
+ end_io(qn);
+ if (ret < 0)
+ return ret;
+
+ return rename_file(type, QF_VFSV0, mnt);
+}
+
+static int convert_file(int type, struct mntent *mnt)
+{
+ switch (action) {
+ case ACT_FORMAT:
+ return convert_format(type, mnt);
+ case ACT_ENDIAN:
+ return convert_endian(type, mnt);
+ }
+ errstr(_("Unknown action should be performed.\n"));
+ return -1;
+}
+
+int main(int argc, char **argv)
+{
+ struct mntent *mnt;
+ int ret = 0;
+
+ gettexton();
+ progname = basename(argv[0]);
+
+ parse_options(argc, argv);
+ init_kernel_interface();
+ if (init_mounts_scan(1, &mntpoint, 0) < 0)
+ return 1;
+ if (!(mnt = get_next_mount())) {
+ end_mounts_scan();
+ return 1;
+ }
+ if (ucv)
+ ret |= convert_file(USRQUOTA, mnt);
+ if (gcv)
+ ret |= convert_file(GRPQUOTA, mnt);
+ end_mounts_scan();
+
+ if (ret)
+ return 1;
+ return 0;
+}