diff options
-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); |