summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2016-03-30 11:35:07 +0200
committerJan Kara <jack@suse.cz>2016-03-31 09:46:40 +0200
commit6206e37f8f478d0908bfb93045b5b303a09f8a9b (patch)
treefecf1a83a55b510c6fbe4400a986d3b6272c9a37
parentc4b56ee58b9b76d2598535cf6109a27b22e60abe (diff)
downloadquota-tools-6206e37f8f478d0908bfb93045b5b303a09f8a9b.tar.gz
Add support for project quota into generic code
The support for project quota in generic code is simple. We just need functions to convert project ID to project name and back (we follow what xfsprogs do in that regard), add detection whether project quota is enabled for the filesystem, and increase number of quota types. We also have to update various checks to count with project quotas. Signed-off-by: Li Xi <lixi@ddn.com> Signed-off-by: Jan Kara <jack@suse.cz>
-rw-r--r--quota.h3
-rw-r--r--quotaio_generic.c16
-rw-r--r--quotaio_xfs.c44
-rw-r--r--quotaio_xfs.h2
-rw-r--r--quotasys.c152
-rw-r--r--quotasys.h20
6 files changed, 224 insertions, 13 deletions
diff --git a/quota.h b/quota.h
index 0607e04..3628614 100644
--- a/quota.h
+++ b/quota.h
@@ -8,7 +8,7 @@ typedef u_int32_t qid_t; /* Type in which we store ids in memory */
typedef int64_t qsize_t; /* Type in which we store size limitations */
#define QSIZE_MAX INT64_MAX /* Maximum value storable in qsize_t */
-#define MAXQUOTAS 2
+#define MAXQUOTAS 3
#define USRQUOTA 0 /* element used for user quotas */
#define GRPQUOTA 1 /* element used for group quotas */
#define PRJQUOTA 2 /* element used for project quotas */
@@ -21,6 +21,7 @@ typedef int64_t qsize_t; /* Type in which we store size limitations */
#define INITQFNAMES { \
N_("user"), /* USRQUOTA */ \
N_("group"), /* GRPQUOTA */ \
+ N_("project"), /* PRJQUOTA */ \
N_("undefined"), \
}
diff --git a/quotaio_generic.c b/quotaio_generic.c
index 4bdf380..025d712 100644
--- a/quotaio_generic.c
+++ b/quotaio_generic.c
@@ -157,6 +157,22 @@ int generic_scan_dquots(struct quota_handle *h,
break;
}
endgrent();
+ } else if (h->qh_type == PRJQUOTA) {
+ struct fs_project *prj;
+
+ setprent();
+ while ((prj = getprent()) != NULL) {
+ dquot->dq_id = prj->pr_id;
+ ret = scan_one_dquot(dquot, get_dquot);
+ if (ret < 0)
+ break;
+ if (ret > 0)
+ continue;
+ ret = process_dquot(dquot, prj->pr_name);
+ if (ret < 0)
+ break;
+ }
+ endprent();
}
free(dquot);
return ret;
diff --git a/quotaio_xfs.c b/quotaio_xfs.c
index 9d90a3e..1374cf4 100644
--- a/quotaio_xfs.c
+++ b/quotaio_xfs.c
@@ -23,6 +23,8 @@
(h)->qh_info.u.xfs_mdqi.qs_flags & XFS_QUOTA_UDQ_ACCT)
#define XFS_GRPQUOTA(h) ((h)->qh_type == GRPQUOTA && \
(h)->qh_info.u.xfs_mdqi.qs_flags & XFS_QUOTA_GDQ_ACCT)
+#define XFS_PRJQUOTA(h) ((h)->qh_type == PRJQUOTA && \
+ (h)->qh_info.u.xfs_mdqi.qs_flags & XFS_QUOTA_PDQ_ACCT)
static int xfs_init_io(struct quota_handle *h);
static int xfs_write_info(struct quota_handle *h);
@@ -97,7 +99,7 @@ static int xfs_write_info(struct quota_handle *h)
struct xfs_kern_dqblk xdqblk;
int qcmd;
- if (!XFS_USRQUOTA(h) && !XFS_GRPQUOTA(h))
+ if (!XFS_USRQUOTA(h) && !XFS_GRPQUOTA(h) && !XFS_PRJQUOTA(h))
return 0;
memset(&xdqblk, 0, sizeof(struct xfs_kern_dqblk));
@@ -123,7 +125,7 @@ static struct dquot *xfs_read_dquot(struct quota_handle *h, qid_t id)
dquot->dq_id = id;
dquot->dq_h = h;
- if (!XFS_USRQUOTA(h) && !XFS_GRPQUOTA(h))
+ if (!XFS_USRQUOTA(h) && !XFS_GRPQUOTA(h) && !XFS_PRJQUOTA(h))
return dquot;
qcmd = QCMD(Q_XFS_GETQUOTA, h->qh_type);
@@ -146,11 +148,16 @@ static int xfs_commit_dquot(struct dquot *dquot, int flags)
qid_t id = dquot->dq_id;
int qcmd;
- if (!XFS_USRQUOTA(h) && !XFS_GRPQUOTA(h))
+ if (!XFS_USRQUOTA(h) && !XFS_GRPQUOTA(h) && !XFS_PRJQUOTA(h))
return 0;
xfs_util2kerndqblk(&xdqblk, &dquot->dq_dqb);
- xdqblk.d_flags |= XFS_USRQUOTA(h) ? XFS_USER_QUOTA : XFS_GROUP_QUOTA;
+ if (XFS_USRQUOTA(h))
+ xdqblk.d_flags |= XFS_USER_QUOTA;
+ else if (XFS_GRPQUOTA(h))
+ xdqblk.d_flags |= XFS_GROUP_QUOTA;
+ else if (XFS_PRJQUOTA(h))
+ xdqblk.d_flags |= XFS_PROJ_QUOTA;
xdqblk.d_id = id;
if (strcmp(h->qh_fstype, MNTTYPE_GFS2) == 0) {
if (flags & COMMIT_LIMITS) /* warn/limit */
@@ -231,7 +238,7 @@ static int xfs_scan_dquots(struct quota_handle *h, int (*process_dquot) (struct
ret = quotactl(QCMD(Q_XGETNEXTQUOTA, h->qh_type), h->qh_quotadev, 0,
(void *)&xdqblk);
if (ret < 0 && (errno == ENOSYS || errno == EINVAL)) {
- if (!XFS_USRQUOTA(h) && !XFS_GRPQUOTA(h))
+ if (!XFS_USRQUOTA(h) && !XFS_GRPQUOTA(h) && !XFS_PRJQUOTA(h))
return 0;
return generic_scan_dquots(h, process_dquot, xfs_get_dquot);
}
@@ -257,10 +264,14 @@ static int xfs_report(struct quota_handle *h, int verbose)
printf(_("Accounting: %s; Enforcement: %s\n"),
XQM_ON(XFS_QUOTA_UDQ_ACCT), XQM_ON(XFS_QUOTA_UDQ_ENFD));
}
- else { /* qh_type == USRQUOTA */
+ else if (h->qh_type == GRPQUOTA) {
printf(_("Accounting: %s; Enforcement: %s\n"),
XQM_ON(XFS_QUOTA_GDQ_ACCT), XQM_ON(XFS_QUOTA_GDQ_ENFD));
}
+ else if (h->qh_type == PRJQUOTA) {
+ printf(_("Accounting: %s; Enforcement: %s\n"),
+ XQM_ON(XFS_QUOTA_PDQ_ACCT), XQM_ON(XFS_QUOTA_PDQ_ENFD));
+ }
#undef XQM_ON
/*
@@ -273,10 +284,14 @@ static int xfs_report(struct quota_handle *h, int verbose)
printf(_("Accounting [ondisk]: %s; Enforcement [ondisk]: %s\n"),
XQM_ONDISK(XFS_QUOTA_UDQ_ACCT), XQM_ONDISK(XFS_QUOTA_UDQ_ENFD));
}
- else { /* qh_type == USRQUOTA */
+ else if (h->qh_type == GRPQUOTA) {
printf(_("Accounting [ondisk]: %s; Enforcement [ondisk]: %s\n"),
XQM_ONDISK(XFS_QUOTA_GDQ_ACCT), XQM_ONDISK(XFS_QUOTA_GDQ_ENFD));
}
+ else if (h->qh_type == PRJQUOTA) {
+ printf(_("Accounting [ondisk]: %s; Enforcement [ondisk]: %s\n"),
+ XQM_ONDISK(XFS_QUOTA_PDQ_ACCT), XQM_ONDISK(XFS_QUOTA_PDQ_ENFD));
+ }
#undef XQM_ONDISK
}
@@ -290,7 +305,20 @@ static int xfs_report(struct quota_handle *h, int verbose)
(unsigned long long)info->qs_uquota.qfs_nblks,
info->qs_uquota.qfs_nextents);
}
- else { /* qh_type == GRPQUOTA */
+ else if (h->qh_type == GRPQUOTA) {
+ if (info->qs_gquota.qfs_ino == -1)
+ printf(_("Inode: none\n"));
+ else
+ printf(_("Inode: #%llu (%llu blocks, %u extents)\n"),
+ (unsigned long long)info->qs_gquota.qfs_ino,
+ (unsigned long long)info->qs_gquota.qfs_nblks,
+ info->qs_gquota.qfs_nextents);
+ }
+ else if (h->qh_type == PRJQUOTA) {
+ /*
+ * FIXME: Older XFS use group files for project quotas, newer
+ * have dedicated file and we should detect that.
+ */
if (info->qs_gquota.qfs_ino == -1)
printf(_("Inode: none\n"));
else
diff --git a/quotaio_xfs.h b/quotaio_xfs.h
index 2236da4..be7f86f 100644
--- a/quotaio_xfs.h
+++ b/quotaio_xfs.h
@@ -121,6 +121,8 @@ typedef struct fs_disk_quota {
#define XFS_QUOTA_UDQ_ENFD (1<<1) /* user quota limits enforcement */
#define XFS_QUOTA_GDQ_ACCT (1<<2) /* group quota accounting */
#define XFS_QUOTA_GDQ_ENFD (1<<3) /* group quota limits enforcement */
+#define XFS_QUOTA_PDQ_ACCT (1<<4) /* group quota accounting */
+#define XFS_QUOTA_PDQ_ENFD (1<<5) /* group quota limits enforcement */
#define XFS_USER_QUOTA (1<<0) /* user quota type */
#define XFS_PROJ_QUOTA (1<<1) /* (IRIX) project quota type */
diff --git a/quotasys.c b/quotasys.c
index 4b49e0e..c78e02c 100644
--- a/quotasys.c
+++ b/quotasys.c
@@ -80,6 +80,93 @@ char *type2name(int type)
}
/*
+ * Project quota handling
+ */
+#define PROJECT_FILE "/etc/projid"
+#define MAX_LINE_LEN 1024
+
+static FILE *project_file;
+
+/* Rewind /etc/projid to the beginning */
+void setprent(void)
+{
+ if (project_file)
+ fclose(project_file);
+ project_file = fopen(PROJECT_FILE, "r");
+}
+
+/* Close /etc/projid file */
+void endprent(void)
+{
+ fclose(project_file);
+ project_file = NULL;
+}
+
+/* Get next entry in /etc/projid */
+struct fs_project *getprent(void)
+{
+ static struct fs_project p;
+ static char linebuf[MAX_LINE_LEN];
+ char *idstart, *idend;
+
+ if (!project_file)
+ return NULL;
+ while (fgets(linebuf, MAX_LINE_LEN, project_file)) {
+ /* Line too long? */
+ if (linebuf[strlen(linebuf) - 1] != '\n')
+ continue;
+ /* Skip comments */
+ if (linebuf[0] == '#')
+ continue;
+ idstart = strchr(linebuf, ':');
+ /* Skip invalid lines... We follow what xfs_quota does */
+ if (!idstart)
+ continue;
+ *idstart = 0;
+ idstart++;
+ /*
+ * Colon can separate name from something else - follow what
+ * xfs_quota does
+ */
+ idend = strchr(idstart, ':');
+ if (idend)
+ *idend = 0;
+ p.pr_name = linebuf;
+ p.pr_id = strtoul(idstart, NULL, 10);
+ return &p;
+ }
+ return NULL;
+}
+
+static struct fs_project *get_project_by_name(char *name)
+{
+ struct fs_project *p;
+
+ setprent();
+ while ((p = getprent())) {
+ if (!strcmp(name, p->pr_name))
+ break;
+ }
+ endprent();
+
+ return p;
+}
+
+static struct fs_project *get_project_by_id(qid_t id)
+{
+ struct fs_project *p;
+
+ setprent();
+ while ((p = getprent())) {
+ if (p->pr_id == id)
+ break;
+ }
+ endprent();
+
+ return p;
+}
+
+/*
* Convert name to uid
*/
uid_t user2uid(char *name, int flag, int *err)
@@ -138,14 +225,46 @@ gid_t group2gid(char *name, int flag, int *err)
}
/*
+ * Convert project name to project id
+ */
+qid_t project2pid(char *name, int flag, int *err)
+{
+ qid_t ret;
+ char *errch;
+ struct fs_project *p;
+
+ if (err)
+ *err = 0;
+ if (!flag) {
+ ret = strtoul(name, &errch, 0);
+ if (!*errch) /* Is name number - we got directly pid? */
+ return ret;
+ }
+ p = get_project_by_name(name);
+ if (!p) {
+ if (!err) {
+ errstr(_("project %s does not exist.\n"), name);
+ exit(1);
+ }
+ else {
+ *err = -1;
+ return 0;
+ }
+ }
+ return p->pr_id;
+}
+
+/*
* Convert name to id
*/
int name2id(char *name, int qtype, int flag, int *err)
{
if (qtype == USRQUOTA)
return user2uid(name, flag, err);
- else
+ else if (qtype == GRPQUOTA)
return group2gid(name, flag, err);
+ else
+ return project2pid(name, flag, err);
}
/*
@@ -181,14 +300,32 @@ int gid2group(gid_t id, char *buf)
}
/*
+ * Convert project id to name
+ */
+int pid2project(qid_t id, char *buf)
+{
+ struct fs_project *p;
+
+ if (!(p = get_project_by_id(id))) {
+ snprintf(buf, MAXNAMELEN, "#%u", (uint) id);
+ return 1;
+ }
+ else
+ sstrncpy(buf, p->pr_name, MAXNAMELEN);
+ return 0;
+}
+
+/*
* Convert id to user/groupname
*/
int id2name(int id, int qtype, char *buf)
{
if (qtype == USRQUOTA)
return uid2user(id, buf);
- else
+ else if (qtype == GRPQUOTA)
return gid2group(id, buf);
+ else
+ return pid2project(id, buf);
}
/*
@@ -531,6 +668,8 @@ static int hasxfsquota(const char *dev, struct mntent *mnt, int type, int flags)
return QF_XFS;
else if (type == GRPQUOTA && (info.qs_flags & XFS_QUOTA_GDQ_ACCT))
return QF_XFS;
+ else if (type == PRJQUOTA && (info.qs_flags & XFS_QUOTA_PDQ_ACCT))
+ return QF_XFS;
#ifdef XFS_ROOTHACK
/*
* Old XFS filesystems (up to XFS 1.2 / Linux 2.5.47) had a
@@ -543,6 +682,8 @@ static int hasxfsquota(const char *dev, struct mntent *mnt, int type, int flags)
return QF_XFS;
else if (type == GRPQUOTA && (sbflags & XFS_QUOTA_GDQ_ACCT))
return QF_XFS;
+ else if (type == PRJQUOTA && (sbflags & XFS_QUOTA_PDQ_ACCT))
+ return QF_XFS;
#endif /* XFS_ROOTHACK */
}
@@ -981,6 +1122,8 @@ static int xfs_kern_quota_on(const char *dev, int type)
return 1;
if (type == GRPQUOTA && (info.qs_flags & XFS_QUOTA_GDQ_ACCT))
return 1;
+ if (type == PRJQUOTA && (info.qs_flags & XFS_QUOTA_PDQ_ACCT))
+ return 1;
}
return 0;
}
@@ -1137,12 +1280,13 @@ alloc:
free((char *)devname);
devname = sstrdup(loopdev);
}
-
/* Further we are not interested in mountpoints without quotas and
we don't want to touch them */
qfmt[USRQUOTA] = hasquota(devname, mnt, USRQUOTA, flags);
qfmt[GRPQUOTA] = hasquota(devname, mnt, GRPQUOTA, flags);
- if (qfmt[USRQUOTA] < 0 && qfmt[GRPQUOTA] < 0) {
+ qfmt[PRJQUOTA] = hasquota(devname, mnt, PRJQUOTA, flags);
+ if (qfmt[USRQUOTA] < 0 && qfmt[GRPQUOTA] < 0 &&
+ qfmt[PRJQUOTA] < 0) {
free((char *)devname);
continue;
}
diff --git a/quotasys.h b/quotasys.h
index 5f188a4..f19b00e 100644
--- a/quotasys.h
+++ b/quotasys.h
@@ -46,6 +46,11 @@ struct mount_entry {
int me_qfmt[MAXQUOTAS]; /* Detected quota formats */
};
+struct fs_project {
+ qid_t pr_id;
+ char *pr_name;
+};
+
/*
* Exported functions
*/
@@ -57,12 +62,24 @@ int meta_qf_fstype(char *type);
/* Convert quota type to written form */
char *type2name(int);
+/* Rewind /etc/projid to the beginning */
+void setprent(void);
+
+/* Close /etc/projid file */
+void endprent(void);
+
+/* Get next entry in /etc/projid */
+struct fs_project *getprent(void);
+
/* Convert username to uid */
uid_t user2uid(char *, int flag, int *err);
/* Convert groupname to gid */
gid_t group2gid(char *, int flag, int *err);
+/* Convert project name to project id */
+qid_t project2pid(char *name, int flag, int *err);
+
/* Convert user/groupname to id */
int name2id(char *name, int qtype, int flag, int *err);
@@ -72,6 +89,9 @@ int uid2user(uid_t, char *);
/* Convert gid to groupname */
int gid2group(gid_t, char *);
+/* Convert project id to name */
+int pid2project(qid_t, char *);
+
/* Convert id to user/group name */
int id2name(int id, int qtype, char *buf);