2012-05-30 15:07:58

by Jeff Liu

[permalink] [raw]
Subject: [PATCH 3/3] container quota tool: teach quota tools to detect and perfrom quota if desired.

Teach quota pre-checking stuff to detect and perform container quota if desired.

Signed-off-by: Jie Liu <[email protected]>

---
Makefile.in | 4 +-
config.h.in | 6 ++--
mntopt.h | 1 +
quotacheck.c | 40 +++++++++++++++++++++++--
quotacheck.h | 1 +
quotaio.c | 25 ++++++++++++++++
quotaio.h | 7 ++++-
quotasys.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
8 files changed, 162 insertions(+), 13 deletions(-)

diff --git a/Makefile.in b/Makefile.in
index c81d7a9..04582bc 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,5 +1,5 @@
PROGS = quotacheck quotaon quota quot repquota warnquota quotastats xqmstats edquota setquota convertquota rpc.rquotad quotasync @QUOTA_NETLINK_PROG@
-SOURCES = bylabel.c common.c convertquota.c edquota.c pot.c quot.c quota.c quotacheck.c quotacheck_v1.c quotacheck_v2.c quotaio.c quotaio_rpc.c quotaio_v1.c quotaio_v2.c quotaio_tree.c quotaio_xfs.c quotaio_meta.c quotaio_generic.c quotaon.c quotaon_xfs.c quotaops.c quotastats.c quotasys.c repquota.c rquota_client.c rquota_server.c rquota_svc.c setquota.c warnquota.c xqmstats.c svc_socket.c quotasync.c
+SOURCES = bylabel.c common.c convertquota.c edquota.c pot.c quot.c quota.c quotacheck.c quotacheck_v1.c quotacheck_v2.c quotaio.c quotaio_rpc.c quotaio_v1.c quotaio_v2.c quotaio_tree.c quotaio_xfs.c quotaio_meta.c quotaio_lxc.c quotaio_generic.c quotaon.c quotaon_xfs.c quotaops.c quotastats.c quotasys.c repquota.c rquota_client.c rquota_server.c rquota_svc.c setquota.c warnquota.c xqmstats.c svc_socket.c quotasync.c
CFLAGS = @CFLAGS@ -D_GNU_SOURCE -Wall -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
CPPFLAGS = @CPPFLAGS@
EXT2LIBS = @EXT2LIBS@
@@ -34,7 +34,7 @@ sysconfdir = @sysconfdir@
datarootdir = @datarootdir@

RPCCLNTOBJS = rquota_xdr.o rquota_client.o rquota_clnt.o
-IOOBJS = quotaio.o quotaio_v1.o quotaio_v2.o quotaio_tree.o quotaio_rpc.o quotaio_xfs.o quotaio_meta.o quotaio_generic.o
+IOOBJS = quotaio.o quotaio_v1.o quotaio_v2.o quotaio_tree.o quotaio_rpc.o quotaio_xfs.o quotaio_meta.o quotaio_lxc.o quotaio_generic.o
IOOBJS += $(RPCCLNTOBJS)
LIBOBJS = bylabel.o common.o quotasys.o pot.o $(IOOBJS)
LIBOBJS += @LIBMALLOC@
diff --git a/config.h.in b/config.h.in
index 432e3b5..97e23f1 100644
--- a/config.h.in
+++ b/config.h.in
@@ -1,8 +1,5 @@
/* config.h.in. Generated from configure.in by autoheader. */

-/* Alternative file format of edquota */
-#undef ALT_FORMAT
-
/* File with mounted filesystems */
#undef ALT_MTAB

@@ -66,6 +63,9 @@
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME

+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
/* Version of quota tools */
#undef PACKAGE_VERSION

diff --git a/mntopt.h b/mntopt.h
index 63905bd..de6cd62 100644
--- a/mntopt.h
+++ b/mntopt.h
@@ -20,6 +20,7 @@
#define MNTTYPE_MPFS "mpfs" /* EMC Celerra MPFS filesystem */
#define MNTTYPE_OCFS2 "ocfs2" /* Oracle Cluster filesystem */
#define MNTTYPE_GFS2 "gfs2" /* Red Hat Global filesystem 2 */
+#define MNTTYPE_LXC "rootfs" /* Container rootfs mount type */

/* mount options */
#define MNTOPT_NOQUOTA "noquota" /* don't enforce quota */
diff --git a/quotacheck.c b/quotacheck.c
index 0d0d4b2..a59ae0f 100644
--- a/quotacheck.c
+++ b/quotacheck.c
@@ -410,6 +410,9 @@ static void parse_options(int argcnt, char **argstr)
mntpoint = argstr[optind];
else
mntpoint = NULL;
+
+ if (fmt == QF_LXC)
+ flags |= FL_LXC;
}

#if defined(EXT2_DIRECT)
@@ -795,6 +798,12 @@ static int dump_to_file(struct mount_entry *mnt, int type)
strerror(errno));
return -1;
}
+ } else if (cfmt == QF_LXC) {
+ if (!(h = init_lxc_io(mnt, type))) {
+ errstr(_("Cannot initialize IO on lxc %s\n"),
+ strerror(errno));
+ return -1;
+ }
} else {
if (!(h = new_io(mnt, type, cfmt))) {
errstr(_("Cannot initialize IO on new quotafile: %s\n"),
@@ -828,6 +837,11 @@ static int dump_to_file(struct mount_entry *mnt, int type)
return -1;
}
debug(FL_DEBUG, _("Data dumped.\n"));
+
+ /* For LXC quota, don't need to do left checking stuff. */
+ if (cfmt == QF_LXC)
+ return 0;
+
if (kern_quota_on(mnt, type, cfmt) >= 0) { /* Quota turned on? */
char *filename;

@@ -914,12 +928,12 @@ static int check_dir(struct mount_entry *mnt)
cur_dev = st.st_dev;
files_done = dirs_done = 0;
/*
- * For gfs2, we scan the fs first and then tell the kernel about the new usage.
+ * For gfs2/lxc, we scan the fs first and then tell the kernel about the new usage.
* So, there's no need to load any information. We also don't remount the
* filesystem read-only because for a clustering filesystem it won't stop
* modifications from other nodes anyway.
*/
- if (cfmt == QF_XFS)
+ if (cfmt == QF_XFS || cfmt == QF_LXC)
goto start_scan;
if (ucheck)
if (process_file(mnt, USRQUOTA) < 0)
@@ -1133,11 +1147,30 @@ static int check_all(void)
static int warned;
int failed = 0;

- if (init_mounts_scan((flags & FL_ALL) ? 0 : 1, &mntpoint, 0) < 0)
+ /*
+ * Call init_mounts_scan() with flags to ensure LXC quotacheck
+ * can be detected properly.
+ */
+ if (init_mounts_scan((flags & FL_ALL) ? 0 : 1, &mntpoint, flags) < 0)
die(2, _("Cannot initialize mountpoint scan.\n"));
while ((mnt = get_next_mount())) {
if (flags & FL_ALL && flags & FL_NOROOT && !strcmp(mnt->me_dir, "/"))
continue;
+
+ /*
+ * Deal with LXC format separately, since we have no general
+ * quota mount options.
+ * FIXME:
+ * Need to tweak up if we have to implements container quota
+ * with those quota mount strings.
+ */
+ if (fmt == QF_LXC && flags & FL_LXC) {
+ cfmt = fmt;
+ ucheck = uwant ? 1 : 0;
+ gcheck = gwant ? 1 : 0;
+ goto start_check_dir;
+ }
+
if (!compatible_fs_qfmt(mnt->me_type, fmt)) {
debug(FL_DEBUG | FL_VERBOSE, _("Skipping %s [%s]\n"), mnt->me_devname, mnt->me_dir);
continue;
@@ -1177,6 +1210,7 @@ static int check_all(void)
warn_if_jquota_supported();
}

+start_check_dir:
checked++;
failed |= check_dir(mnt);
}
diff --git a/quotacheck.h b/quotacheck.h
index 0abdaaa..27c4d3e 100644
--- a/quotacheck.h
+++ b/quotacheck.h
@@ -26,6 +26,7 @@
#define FL_NOROOT 512 /* Scan all mountpoints except root */
#define FL_BACKUPS 1024 /* Create backup of old quota file? */
#define FL_VERYVERBOSE 2048 /* Print directory names when checking */
+#define FL_LXC 4096 /* Perform container quotacheck */

extern int flags; /* Options from command line */
extern struct util_dqinfo old_info[MAXQUOTAS]; /* Loaded info from file */
diff --git a/quotaio.c b/quotaio.c
index d3c7cb6..95a180c 100644
--- a/quotaio.c
+++ b/quotaio.c
@@ -27,6 +27,7 @@
#include "dqblk_v2.h"
#include "dqblk_rpc.h"
#include "dqblk_xfs.h"
+#include "dqblk_lxc.h"

/* Header in all newer quotafiles */
struct disk_dqheader {
@@ -186,6 +187,30 @@ out_handle:
return NULL;
}

+/* Initialize LXC quota IO */
+struct quota_handle *init_lxc_io(struct mount_entry *mnt, int type)
+{
+ struct quota_handle *h = smalloc(sizeof(struct quota_handle));
+
+ /* FIXME: maybe we don't need to stat(2) here */
+ if (stat(mnt->me_devname, &h->qh_stat) < 0)
+ memset(&h->qh_stat, 0, sizeof(struct stat));
+
+ h->qh_io_flags = 0;
+ h->qh_type = type;
+ sstrncpy(h->qh_quotadev, mnt->me_devname, sizeof(h->qh_quotadev));
+ sstrncpy(h->qh_fstype, mnt->me_type, MAX_FSTYPE_LEN);
+ sstrncpy(h->qh_dir, mnt->me_dir, PATH_MAX);
+
+ h->qh_fd = -1;
+ h->qh_fmt = QF_LXC;
+ h->qh_ops = &quotafile_ops_lxc;
+ memset(&h->qh_info, 0, sizeof(h->qh_info));
+ h->qh_ops->init_io(h);
+
+ return h;
+}
+
/*
* Create new quotafile of specified format on given filesystem
*/
diff --git a/quotaio.h b/quotaio.h
index 2c373b2..1f90178 100644
--- a/quotaio.h
+++ b/quotaio.h
@@ -19,7 +19,8 @@
#include "dqblk_rpc.h"
#include "dqblk_xfs.h"

-#define QUOTAFORMATS 6
+/* With LXC quota, now we support 8 kind of quota formats */
+#define QUOTAFORMATS 8

#define INITQFBASENAMES {\
"quota",\
@@ -41,6 +42,7 @@
#define QF_XFS 4 /* XFS quota format */
#define QF_META 5 /* Quota files are hidden, we don't care about the format */
#define QF_VFSUNKNOWN 6 /* Some VFS quotas, we didn't detect particular format yet */
+#define QF_LXC 7 /* Container quota format */

static inline int is_tree_qfmt(int fmt)
{
@@ -173,6 +175,9 @@ struct quota_handle *init_io(struct mount_entry *mnt, int type, int fmt, int fla
/* Create new quotafile of specified format on given filesystem */
struct quota_handle *new_io(struct mount_entry *mnt, int type, int fmt);

+/* Check quota format used on LXC and initialize it */
+struct quota_handle *init_lxc_io(struct mount_entry *mnt, int type);
+
/* Close quotafile */
int end_io(struct quota_handle *h);

diff --git a/quotasys.c b/quotasys.c
index 73a0799..7365cb4 100644
--- a/quotasys.c
+++ b/quotasys.c
@@ -34,9 +34,16 @@
#include "dqblk_xfs.h"
#include "quotaio_v2.h"

+#include "dqblk_lxc.h" /* export lxc quota opertions */
+#include "quotacheck.h" /* FL_LXC */
+
#define min(x,y) (((x) < (y)) ? (x) : (y))

-#define QFMT_NAMES 5
+/*
+ * Index container disk quota format.
+ * FIXME: fix up in a proper way.
+ */
+#define QFMT_NAMES 8

static char extensions[MAXQUOTAS + 2][20] = INITQFNAMES;
static char *basenames[] = INITQFBASENAMES;
@@ -45,9 +52,20 @@ static char *fmtnames[] = { "vfsold",
"vfsv1",
"rpc",
"xfs",
+ "pad", /* Two pading string to index lxc */
+ "pad",
+ "lxc",
};

/*
+ * check for container rootfs
+ */
+int lxc_fstype(char *type)
+{
+ return !strcmp(type, MNTTYPE_LXC);
+}
+
+/*
* Check for various kinds of NFS filesystem
*/
int nfs_fstype(char *type)
@@ -227,7 +245,8 @@ int name2fmt(char *str)
vfsv0 - standard quota format\n\
vfsv1 - quota format with 64-bit limits\n\
rpc - use RPC calls\n\
- xfs - XFS quota format\n"), str);
+ xfs - XFS quota format\n\
+ lxc - LXC quota format\n"), str);
return QF_ERROR;
}

@@ -253,6 +272,8 @@ static int kern2utilfmt(int kernfmt)
return QF_VFSV1;
case QFMT_OCFS2:
return QF_META;
+ case QFMT_NS:
+ return QF_LXC;
}
return -1;
}
@@ -495,6 +516,14 @@ static int hasquota(const char *dev, struct mntent *mnt, int type, int flags)
return hasxfsquota(dev, mnt, type, flags);
if (!strcmp(mnt->mnt_type, MNTTYPE_OCFS2))
return hasvfsmetaquota(dev, mnt, type, flags);
+
+ /*
+ * For LXC, no quota mount options at all. Just return the LXC
+ * quota format identifier is ok.
+ */
+ if (lxc_fstype(mnt->mnt_type) || flags & FL_LXC)
+ return QF_LXC;
+
/*
* For ext4 we check whether it has quota in system files and if not,
* we fall back on checking standard quotas. Furthermore we cannot use
@@ -645,7 +674,11 @@ struct quota_handle **create_handle_list(int count, char **mntpoints, int type,
if (nfs_fstype(mnt->mnt_type))
continue;
#endif
- if (fmt == -1 || count) {
+ if (fmt == QF_LXC) {
+ if (!(hlist[gotmnt] = init_lxc_io(mnt, type)))
+ continue;
+ gotmnt++;
+ } else if (fmt == -1 || count) {
add_entry:
if (gotmnt+1 >= hlist_allocated) {
hlist_allocated += START_MNT_POINTS;
@@ -772,6 +805,7 @@ void init_kernel_interface(void)
kernel_qfmt[kernel_qfmt_num++] = QF_VFSOLD;
kernel_qfmt[kernel_qfmt_num++] = QF_VFSV0;
kernel_qfmt[kernel_qfmt_num++] = QF_VFSV1;
+ kernel_qfmt[kernel_qfmt_num++] = QF_LXC;
}
else {
struct v2_dqstats v2_stats;
@@ -924,6 +958,41 @@ static struct mount_entry *mnt_entries; /* Cached mounted filesystems */
static int check_dirs_cnt, act_checked; /* Number of dirs to check; Actual checked dir/(mountpoint in case of -a) */
static struct searched_dir *check_dirs; /* Directories to check */

+static int get_rootfs_device(dev_t *dev)
+{
+ FILE *fp = NULL;
+ struct stat st;
+ char fstype[256];
+ char device[256];
+ char mp[256];
+
+ memset(fstype, 0, sizeof(fstype));
+ memset(device, 0, sizeof(device));
+ memset(mp, 0, sizeof(mp));
+
+ if (!(fp = fopen("/proc/mounts", "r"))) {
+ perror("fopen");
+ return -1;
+ }
+
+ while (fscanf(fp, "%256s %256s %256s %*s %*d %*d\n",
+ device, mp, fstype) == 3) {
+ if (strcmp(mp, "/") == 0 && strcmp(device, "rootfs") != 0)
+ break;
+ }
+
+ if (!device[0])
+ return -1;
+
+ if (stat(device, &st) < 0) {
+ perror("stat");
+ return -1;
+ }
+
+ *dev = st.st_rdev;
+ return 0;
+}
+
/* Cache mtab/fstab */
static int cache_mnt_table(int flags)
{
@@ -1030,7 +1099,7 @@ alloc:
continue;
}

- if (!nfs_fstype(mnt->mnt_type)) {
+ if (!nfs_fstype(mnt->mnt_type) && !lxc_fstype(mnt->mnt_type)) {
if (stat(devname, &st) < 0) { /* Can't stat mounted device? */
errstr(_("Cannot stat() mounted device %s: %s\n"), devname, strerror(errno));
free((char *)devname);
@@ -1044,6 +1113,20 @@ alloc:
dev = st.st_rdev;
for (i = 0; i < mnt_entries_cnt && mnt_entries[i].me_dev != dev; i++);
}
+ /*
+ * FIXME: There is no relevant DEV for rootfs inside LXC guest
+ * by default. I have to create it through `mknod /dev/sdaX x
+ * x` to make the current code logic works. Maybe we don't
+ * need to fetch the corresponding dev_t at all.
+ */
+ if (lxc_fstype(mnt->mnt_type)) {
+ if (get_rootfs_device(&dev) < 0) {
+ errstr(_("Cannot find device for rootfs\n"));
+ continue;
+ }
+ for (i = 0; i < mnt_entries_cnt && mnt_entries[i].me_dev != dev; i++);
+ }
+
/* Cope with network filesystems or new mountpoint */
if (nfs_fstype(mnt->mnt_type) || i == mnt_entries_cnt) {
if (stat(mnt->mnt_dir, &st) < 0) { /* Can't stat mountpoint? We have better ignore it... */
--
1.7.9