diff options
author | Jan Kara <jack@suse.cz> | 2016-03-30 11:35:07 +0200 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2016-03-31 09:46:40 +0200 |
commit | 6206e37f8f478d0908bfb93045b5b303a09f8a9b (patch) | |
tree | fecf1a83a55b510c6fbe4400a986d3b6272c9a37 | |
parent | c4b56ee58b9b76d2598535cf6109a27b22e60abe (diff) | |
download | quota-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.h | 3 | ||||
-rw-r--r-- | quotaio_generic.c | 16 | ||||
-rw-r--r-- | quotaio_xfs.c | 44 | ||||
-rw-r--r-- | quotaio_xfs.h | 2 | ||||
-rw-r--r-- | quotasys.c | 152 | ||||
-rw-r--r-- | quotasys.h | 20 |
6 files changed, 224 insertions, 13 deletions
@@ -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 */ @@ -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; } @@ -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); |