diff options
-rw-r--r-- | Changelog | 6 | ||||
-rw-r--r-- | convertquota.8 | 10 | ||||
-rw-r--r-- | convertquota.c | 242 | ||||
-rw-r--r-- | dqblk_v2.h | 2 | ||||
-rw-r--r-- | quotaio.c | 16 | ||||
-rw-r--r-- | quotaio_v1.c | 7 | ||||
-rw-r--r-- | quotaio_v2.c | 27 | ||||
-rw-r--r-- | quotaio_v2.h | 2 | ||||
-rw-r--r-- | quotastats.c | 7 | ||||
-rw-r--r-- | quotasys.c | 25 |
10 files changed, 301 insertions, 43 deletions
@@ -1,6 +1,12 @@ Changes in quota-tools from 3.01 to ???? * added -n option (don't resolve names) to repquota (Jan Kara) +* quota tools now correctly handle zero grace times in old format (Jan Kara) +* edquota, setquota commit just grace times and flags - fixed possible races + when used on live filesystem (Jan Kara) +* another fix to quotastats (Jan Kara) +* added check to format detection for bad endianity (Jan Kara) +* implemented conversion of files with bad endianity (Jan Kara) Changes in quota-tools from 3.01-final to 3.01 diff --git a/convertquota.8 b/convertquota.8 index 34db1b5..ea5b803 100644 --- a/convertquota.8 +++ b/convertquota.8 @@ -6,6 +6,10 @@ convertquota \- convert quota from old file format to new one .B convertquota [ .B -ug +] [ +.B -e +| +.B -f ] .I filesystem .SH DESCRIPTION @@ -33,6 +37,12 @@ convert user quota file. This is the default. .B -g convert group quota file. .TP +.B -f +convert from old file format to new one. This is the default. +.TP +.B -e +convert new file format from big endian to little endian. +.TP .B -V print version information. .SH FILES diff --git a/convertquota.c b/convertquota.c index f82a6b8..69a14cf 100644 --- a/convertquota.c +++ b/convertquota.c @@ -20,15 +20,21 @@ #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 */ static void usage(void) { - errstr(_("Utility for converting quota files.\nUsage:\n\t%s [-u] [-g] mountpoint\n"), progname); + errstr(_("Utility for converting quota files.\nUsage:\n\t%s [-u] [-g] [-e|-f] mountpoint\n"), progname); errstr(_("Bugs to %s\n"), MY_EMAIL); exit(1); } @@ -43,8 +49,9 @@ static void parse_options(int argcnt, char **argstr) else slash++; + action = ACT_FORMAT; sstrncpy(cmdname, slash, sizeof(cmdname)); - while ((ret = getopt(argcnt, argstr, "Vugh:")) != -1) { + while ((ret = getopt(argcnt, argstr, "Vugefh:")) != -1) { switch (ret) { case '?': case 'h': @@ -58,6 +65,12 @@ static void parse_options(int argcnt, char **argstr) case 'g': gcv = 1; break; + case 'e': + action = ACT_ENDIAN; + break; + case 'f': + action = ACT_FORMAT; + break; } } @@ -72,6 +85,146 @@ static void parse_options(int argcnt, char **argstr) 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(V2_DQBLKSIZE) +#define freedqbuf(buf) free(buf) + +static inline void endian_disk2memdqblk(struct util_dqblk *m, struct v2_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 v2_disk_dqblk *d) +{ + static struct v2_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 << V2_DQBLKSIZE_BITS, SEEK_SET); + err = read(fd, buf, V2_DQBLKSIZE); + if (err < 0) + die(2, _("Can't read block %u: %s\n"), blk, strerror(errno)); + else if (err != V2_DQBLKSIZE) + memset(buf + err, 0, V2_DQBLKSIZE - err); +} + +static void endian_report_block(int fd, uint blk, char *bitmap) +{ + dqbuf_t buf = getdqbuf(); + struct v2_disk_dqdbheader *dh; + struct v2_disk_dqblk *ddata; + struct dquot dquot; + int i; + + set_bit(bitmap, blk); + read_blk(fd, blk, buf); + dh = (struct v2_disk_dqdbheader *)buf; + ddata = V2_GETENTRIES(buf); + for (i = 0; i < V2_DQSTRINBLK; 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(_("Can't 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 == V2_DQTREEDEPTH - 1) { + for (i = 0; i < V2_DQBLKSIZE >> 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 < V2_DQBLKSIZE >> 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) + V2_DQBLKSIZE - 1) >> V2_DQBLKSIZE_BITS; + + bitmap = smalloc((blocks + 7) >> 3); + memset(bitmap, 0, (blocks + 7) >> 3); + endian_report_tree(fd, V2_DQTREEOFF, 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(_("Can't 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(_("Can't 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; @@ -88,18 +241,34 @@ static int convert_dquot(struct dquot *dquot, char *name) 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(_("Can't commit dquot for id %u (%s): %s\n"), - (uint)dquot->dq_id, name, strerror(errno)); + errstr(_("Can't commit dquot for id %u: %s\n"), + (uint)dquot->dq_id, strerror(errno)); return -1; } return 0; } -static int convert_file(int type, struct mntent *mnt) +static int rename_file(int type, struct mntent *mnt) { - struct quota_handle *qo; char *qfname, namebuf[PATH_MAX]; int ret = 0; + + qfname = get_qf_name(mnt, type, QF_VFSV0); + strcpy(namebuf, qfname); + sstrncat(namebuf, ".new", sizeof(namebuf)); + if (rename(namebuf, qfname) < 0) { + errstr(_("Can't 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, QF_VFSOLD, 1))) { errstr(_("Can't open old format file for %ss on %s\n"), @@ -112,21 +281,64 @@ static int convert_file(int type, struct mntent *mnt) end_io(qo); return -1; } - if (qo->qh_ops->scan_dquots(qo, convert_dquot) >= 0) { /* Conversion succeeded? */ - qfname = get_qf_name(mnt, type, QF_VFSV0); - strcpy(namebuf, qfname); - sstrncat(namebuf, ".new", sizeof(namebuf)); - if (rename(namebuf, qfname) < 0) - errstr(_("Can't rename new quotafile %s to name %s: %s\n"), - namebuf, qfname, strerror(errno)); - free(qfname); + if (qo->qh_ops->scan_dquots(qo, convert_dquot) >= 0) /* Conversion succeeded? */ + ret = rename_file(type, 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 (!(qfname = get_qf_name(mnt, type, QF_VFSV0))) + return -1; + if ((ofd = open(qfname, O_RDONLY)) < 0) { + errstr(_("Can't 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(_("Can't 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, 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; @@ -15,6 +15,8 @@ #define Q_V2_SETQLIM 0x0700 /* Set only limits */ #define Q_V2_GETINFO 0x0900 /* Get information about quota */ #define Q_V2_SETINFO 0x0A00 /* Set information about quota */ +#define Q_V2_SETGRACE 0x0B00 /* Set just grace times in quotafile information */ +#define Q_V2_SETFLAGS 0x0C00 /* Set just flags in quotafile information */ #define Q_V2_GETSTATS 0x1100 /* get collected stats (before proc was used) */ /* Structure for format specific information */ @@ -13,6 +13,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <sys/file.h> +#include <asm/byteorder.h> #include "pot.h" #include "bylabel.h" @@ -44,9 +45,14 @@ int detect_qf_format(int fd, int type) if ((ret = read(fd, &head, sizeof(head))) < 0) die(2, _("Error while reading from quotafile: %s\n"), strerror(errno)); - if (ret != sizeof(head) || head.dqh_magic != file_magics[type]) /* Short file? Probably old format */ + if (ret != sizeof(head)) /* Short file? Probably old format */ return QF_VFSOLD; - if (head.dqh_version > known_versions[type]) /* Too new format? */ + if (__le32_to_cpu(head.dqh_magic) != file_magics[type]) + if (__be32_to_cpu(head.dqh_magic) == file_magics[type]) + die(3, _("Your quota file is stored in wrong endianity. Please use convertquota to convert it.\n")); + else + return QF_VFSOLD; + if (__le32_to_cpu(head.dqh_version) > known_versions[type]) /* Too new format? */ return QF_TOONEW; return QF_VFSV0; } @@ -227,8 +233,10 @@ int end_io(struct quota_handle *h) } if (h->qh_ops->end_io && h->qh_ops->end_io(h) < 0) return -1; - flock(h->qh_fd, LOCK_UN); - close(h->qh_fd); + if (h->qh_fd != -1) { + flock(h->qh_fd, LOCK_UN); + close(h->qh_fd); + } free(h); return 0; } diff --git a/quotaio_v1.c b/quotaio_v1.c index 948d244..195534c 100644 --- a/quotaio_v1.c +++ b/quotaio_v1.c @@ -34,7 +34,7 @@ #ident "$Copyright: (c) 1980, 1990 Regents of the University of California. $" #ident "$Copyright: All rights reserved. $" -#ident "$Id: quotaio_v1.c,v 1.10 2001/09/27 21:34:58 jkar8572 Exp $" +#ident "$Id: quotaio_v1.c,v 1.11 2001/11/08 23:56:11 jkar8572 Exp $" #include <unistd.h> #include <errno.h> @@ -149,6 +149,11 @@ static int v1_init_io(struct quota_handle *h) h->qh_info.dqi_bgrace = ddqblk.dqb_btime; h->qh_info.dqi_igrace = ddqblk.dqb_itime; } + if (!h->qh_info.dqi_bgrace) + h->qh_info.dqi_bgrace = MAX_DQ_TIME; + if (!h->qh_info.dqi_igrace) + h->qh_info.dqi_igrace = MAX_IQ_TIME; + return 0; } diff --git a/quotaio_v2.c b/quotaio_v2.c index 449c751..2bbf699 100644 --- a/quotaio_v2.c +++ b/quotaio_v2.c @@ -42,6 +42,12 @@ report: v2_report #define getdqbuf() smalloc(V2_DQBLKSIZE) #define freedqbuf(buf) free(buf) +extern inline void mark_quotafile_metainfo_dirty(struct quota_handle *h) +{ + h->qh_info.u.v2_mdqi.dqi_flags |= V2_IOFL_METAINFO_DIRTY; + mark_quotafile_info_dirty(h); +} + /* * Copy dquot from disk to memory */ @@ -213,8 +219,15 @@ static int v2_write_info(struct quota_handle *h) kdqinfo.dqi_blocks = h->qh_info.u.v2_mdqi.dqi_blocks; kdqinfo.dqi_free_blk = h->qh_info.u.v2_mdqi.dqi_free_blk; kdqinfo.dqi_free_entry = h->qh_info.u.v2_mdqi.dqi_free_entry; - if (quotactl(QCMD(Q_V2_SETINFO, h->qh_type), h->qh_quotadev, 0, (void *)&kdqinfo) < 0) - return -1; + if (h->qh_info.u.v2_mdqi.dqi_flags & V2_IOFL_METAINFO_DIRTY) { + if (quotactl(QCMD(Q_V2_SETINFO, h->qh_type), h->qh_quotadev, 0, (void *)&kdqinfo) < 0) + return -1; + } + else { + if (quotactl(QCMD(Q_V2_SETGRACE, h->qh_type), h->qh_quotadev, 0, (void *)&kdqinfo) < 0 || + quotactl(QCMD(Q_V2_SETFLAGS, h->qh_type), h->qh_quotadev, 0, (void *)&kdqinfo) < 0) + return -1; + } } else { struct v2_disk_dqinfo ddqinfo; @@ -276,7 +289,7 @@ static int get_free_dqblk(struct quota_handle *h) } blk = info->dqi_blocks++; } - mark_quotafile_info_dirty(h); + mark_quotafile_metainfo_dirty(h); freedqbuf(buf); return blk; } @@ -291,7 +304,7 @@ static void put_free_dqblk(struct quota_handle *h, dqbuf_t buf, uint blk) dh->dqdh_prev_free = __cpu_to_le32(0); dh->dqdh_entries = __cpu_to_le16(0); info->dqi_free_blk = blk; - mark_quotafile_info_dirty(h); + mark_quotafile_metainfo_dirty(h); write_blk(h, blk, buf); } @@ -316,7 +329,7 @@ static void remove_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk) } else { h->qh_info.u.v2_mdqi.dqi_free_entry = nextblk; - mark_quotafile_info_dirty(h); + mark_quotafile_metainfo_dirty(h); } freedqbuf(tmpbuf); dh->dqdh_next_free = dh->dqdh_prev_free = __cpu_to_le32(0); @@ -340,7 +353,7 @@ static void insert_free_dqentry(struct quota_handle *h, dqbuf_t buf, uint blk) } freedqbuf(tmpbuf); info->dqi_free_entry = blk; - mark_quotafile_info_dirty(h); + mark_quotafile_metainfo_dirty(h); } /* Find space for dquot */ @@ -369,7 +382,7 @@ static uint find_free_dqentry(struct quota_handle *h, struct dquot *dquot, int * } memset(buf, 0, V2_DQBLKSIZE); info->dqi_free_entry = blk; - mark_quotafile_info_dirty(h); + mark_quotafile_metainfo_dirty(h); } if (__le16_to_cpu(dh->dqdh_entries) + 1 >= V2_DQSTRINBLK) /* Block will be full? */ remove_free_dqentry(h, buf, blk); diff --git a/quotaio_v2.h b/quotaio_v2.h index 2482156..bb3a000 100644 --- a/quotaio_v2.h +++ b/quotaio_v2.h @@ -20,6 +20,8 @@ #define V2_GETENTRIES(buf) ((struct v2_disk_dqblk *)(((char *)(buf)) + sizeof(struct v2_disk_dqdbheader))) #define INIT_V2_VERSIONS { 0, 0} +#define V2_IOFL_METAINFO_DIRTY 0x100 /* Is dirty also metadata information in info? */ + struct v2_disk_dqheader { u_int32_t dqh_magic; /* Magic number identifying file */ u_int32_t dqh_version; /* File version */ diff --git a/quotastats.c b/quotastats.c index 9e412be..1879b6d 100644 --- a/quotastats.c +++ b/quotastats.c @@ -10,7 +10,7 @@ * * Author: Marco van Wieringen <mvw@planets.elm.net> * - * Version: $Id: quotastats.c,v 1.6 2001/10/12 13:38:56 jkar8572 Exp $ + * Version: $Id: quotastats.c,v 1.7 2001/11/08 23:56:11 jkar8572 Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -59,13 +59,14 @@ static inline int get_stats(struct util_dqstats *dqstats) goto out; } } - else if (quotactl(QCMD(Q_V1_GETSTATS, 0), NULL, 0, (caddr_t)&old_dqstats) >= 0) { + else if (quotactl(QCMD(Q_V1_GETSTATS, 0), "/dev/null", 0, (caddr_t)&old_dqstats) >= 0) { /* Structures are currently the same */ memcpy(dqstats, &old_dqstats, sizeof(old_dqstats)); dqstats->version = 0; } else { - if (errno != EINVAL) { + /* Sadly these all are possible to get from kernel :( */ + if (errno != EINVAL && errno != ENOTBLK && errno != EPERM) { errstr(_("Error while getting quota statistics from kernel: %s\n"), strerror(errno)); goto out; } @@ -292,8 +292,6 @@ static int hasxfsquota(struct mntent *mnt, int type) */ int hasquota(struct mntent *mnt, int type) { - char *option; - if (!CORRECT_FSTYPE(mnt->mnt_type)) return 0; @@ -302,11 +300,11 @@ int hasquota(struct mntent *mnt, int type) if (!strcmp(mnt->mnt_type, MNTTYPE_NFS)) /* NFS always has quota or better there is no good way how to detect it */ return 1; - if ((type == USRQUOTA) && (option = hasmntopt(mnt, MNTOPT_USRQUOTA))) + if ((type == USRQUOTA) && hasmntopt(mnt, MNTOPT_USRQUOTA)) return 1; - if ((type == GRPQUOTA) && (option = hasmntopt(mnt, MNTOPT_GRPQUOTA))) + if ((type == GRPQUOTA) && hasmntopt(mnt, MNTOPT_GRPQUOTA)) return 1; - if ((type == USRQUOTA) && (option = hasmntopt(mnt, MNTOPT_QUOTA))) + if ((type == USRQUOTA) && hasmntopt(mnt, MNTOPT_QUOTA)) return 1; return 0; } @@ -757,15 +755,16 @@ static int find_next_entry_all(int *pos) { struct mntent mnt; -restart: - if (++act_checked == mnt_entries_cnt) + while (++act_checked < mnt_entries_cnt) { + mnt.mnt_fsname = (char *)mnt_entries[act_checked].me_devname; + mnt.mnt_type = mnt_entries[act_checked].me_type; + mnt.mnt_opts = mnt_entries[act_checked].me_opts; + mnt.mnt_dir = (char *)mnt_entries[act_checked].me_dir; + if (!hasmntopt(&mnt, MNTOPT_NOAUTO)) + break; + } + if (act_checked >= mnt_entries_cnt) return 0; - mnt.mnt_fsname = (char *)mnt_entries[act_checked].me_devname; - mnt.mnt_type = mnt_entries[act_checked].me_type; - mnt.mnt_opts = mnt_entries[act_checked].me_opts; - mnt.mnt_dir = (char *)mnt_entries[act_checked].me_dir; - if (hasmntopt(&mnt, MNTOPT_NOAUTO)) - goto restart; *pos = act_checked; return 1; } |