Patchwork [3/3] container quota tool: teach quota tools to detect and perfrom quota if desired.

login
register
mail settings
Submitter jeff.liu
Date May 30, 2012, 3:07 p.m.
Message ID <1338390478-13951-3-git-send-email-jeff.liu@oracle.com>
Download mbox | patch
Permalink /patch/162007/
State Not Applicable
Headers show

Comments

jeff.liu - May 30, 2012, 3:07 p.m.
Teach quota pre-checking stuff to detect and perform container quota if desired.

Signed-off-by: Jie Liu <jeff.liu@oracle.com>

---
 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(-)

Patch

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... */