From 107a344bbb9f8dd60f59a0a7f657330963e3a46b Mon Sep 17 00:00:00 2001 From: sef Date: Thu, 5 Jul 2018 22:56:13 +0000 Subject: [PATCH] This exposes ZFS user and group quotas via the normal quatactl(2) mechanism. (Read-only at this point, however.) In particular, this is to allow rpc.rquotad query quotas for NFS mounts, allowing users to see their quotas on the hosts using the datasets. The changes specifically: * Add new RPC entry points for querying quotas. * Changes the library routines to allow non-UFS quotas. * Changes rquotad to check for quotas on mounted filesystems, rather than being limited to entries in /etc/fstab * Lastly, adds a VFS entry-point for ZFS to query quotas. Note that this makes one unavoidable behavioural change: if quotas are enabled, then they can be queried, as opposed to the current method of checking for quotas being specified in fstab. (With ZFS, if there are user or group quotas, they're used, always.) Reviewed by: delphij, mav Approved by: mav Sponsored by: iXsystems Inc Differential Revision: https://reviews.freebsd.org/D15886 --- include/rpcsvc/rquota.x | 89 +++++++- lib/libutil/quotafile.c | 9 +- libexec/rpc.rquotad/rquotad.c | 195 +++++++++++++----- .../uts/common/fs/zfs/zfs_vfsops.c | 157 ++++++++++++++ usr.bin/quota/quota.c | 42 ++-- 5 files changed, 417 insertions(+), 75 deletions(-) diff --git a/include/rpcsvc/rquota.x b/include/rpcsvc/rquota.x index 0f3ecfc54d2..8c8dfe452b5 100644 --- a/include/rpcsvc/rquota.x +++ b/include/rpcsvc/rquota.x @@ -1,22 +1,53 @@ +/* @(#)rquota.x 2.1 88/08/01 4.0 RPCSRC */ +/* @(#)rquota.x 1.2 87/09/20 Copyr 1987 Sun Micro */ + /* * Remote quota protocol * Requires unix authentication */ #ifndef RPC_HDR -%#ifndef lint -%/*static char sccsid[] = "from: @(#)rquota.x 1.2 87/09/20 Copyr 1987 Sun Micro";*/ -%/*static char sccsid[] = "from: @(#)rquota.x 2.1 88/08/01 4.0 RPCSRC";*/ -%#endif /* not lint */ %#include %__FBSDID("$FreeBSD$"); #endif const RQ_PATHLEN = 1024; +struct sq_dqblk { + unsigned int rq_bhardlimit; /* absolute limit on disk blks alloc */ + unsigned int rq_bsoftlimit; /* preferred limit on disk blks */ + unsigned int rq_curblocks; /* current block count */ + unsigned int rq_fhardlimit; /* absolute limit on allocated files */ + unsigned int rq_fsoftlimit; /* preferred file limit */ + unsigned int rq_curfiles; /* current # allocated files */ + unsigned int rq_btimeleft; /* time left for excessive disk use */ + unsigned int rq_ftimeleft; /* time left for excessive files */ +}; + struct getquota_args { string gqa_pathp; /* path to filesystem of interest */ - int gqa_uid; /* inquire about quota for uid */ + int gqa_uid; /* Inquire about quota for uid */ +}; + +struct setquota_args { + int sqa_qcmd; + string sqa_pathp; /* path to filesystem of interest */ + int sqa_id; /* Set quota for uid */ + sq_dqblk sqa_dqblk; +}; + +struct ext_getquota_args { + string gqa_pathp; /* path to filesystem of interest */ + int gqa_type; /* Type of quota info is needed about */ + int gqa_id; /* Inquire about quota for id */ +}; + +struct ext_setquota_args { + int sqa_qcmd; + string sqa_pathp; /* path to filesystem of interest */ + int sqa_id; /* Set quota for id */ + int sqa_type; /* Type of quota to set */ + sq_dqblk sqa_dqblk; }; /* @@ -37,7 +68,7 @@ struct rquota { enum gqr_status { Q_OK = 1, /* quota returned */ - Q_NOQUOTA = 2, /* noquota for uid */ + Q_NOQUOTA = 2, /* noquota for uid */ Q_EPERM = 3 /* no permission to access quota */ }; @@ -50,6 +81,15 @@ case Q_EPERM: void; }; +union setquota_rslt switch (gqr_status status) { +case Q_OK: + rquota sqr_rquota; /* valid if status == Q_OK */ +case Q_NOQUOTA: + void; +case Q_EPERM: + void; +}; + program RQUOTAPROG { version RQUOTAVERS { /* @@ -63,5 +103,42 @@ program RQUOTAPROG { */ getquota_rslt RQUOTAPROC_GETACTIVEQUOTA(getquota_args) = 2; + + /* + * Set all quotas + */ + setquota_rslt + RQUOTAPROC_SETQUOTA(setquota_args) = 3; + + /* + * Get active quotas only + */ + setquota_rslt + RQUOTAPROC_SETACTIVEQUOTA(setquota_args) = 4; } = 1; + version EXT_RQUOTAVERS { + /* + * Get all quotas + */ + getquota_rslt + RQUOTAPROC_GETQUOTA(ext_getquota_args) = 1; + + /* + * Get active quotas only + */ + getquota_rslt + RQUOTAPROC_GETACTIVEQUOTA(ext_getquota_args) = 2; + + /* + * Set all quotas + */ + setquota_rslt + RQUOTAPROC_SETQUOTA(ext_setquota_args) = 3; + + /* + * Set active quotas only + */ + setquota_rslt + RQUOTAPROC_SETACTIVEQUOTA(ext_setquota_args) = 4; + } = 2; } = 100011; diff --git a/lib/libutil/quotafile.c b/lib/libutil/quotafile.c index 2172a5b8e43..d5ef11267df 100644 --- a/lib/libutil/quotafile.c +++ b/lib/libutil/quotafile.c @@ -120,8 +120,6 @@ quota_open(struct fstab *fs, int quotatype, int openflags) struct stat st; int qcmd, serrno; - if (strcmp(fs->fs_vfstype, "ufs")) - return (NULL); if ((qf = calloc(1, sizeof(*qf))) == NULL) return (NULL); qf->fd = -1; @@ -130,10 +128,15 @@ quota_open(struct fstab *fs, int quotatype, int openflags) if (stat(qf->fsname, &st) != 0) goto error; qf->dev = st.st_dev; - serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname)); qcmd = QCMD(Q_GETQUOTASIZE, quotatype); if (quotactl(qf->fsname, qcmd, 0, &qf->wordsize) == 0) return (qf); + /* We only check the quota file for ufs */ + if (strcmp(fs->fs_vfstype, "ufs")) { + errno = 0; + goto error; + } + serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname)); if (serrno == 0) { errno = EOPNOTSUPP; goto error; diff --git a/libexec/rpc.rquotad/rquotad.c b/libexec/rpc.rquotad/rquotad.c index d9ce06aac82..cc78a9a71c3 100644 --- a/libexec/rpc.rquotad/rquotad.c +++ b/libexec/rpc.rquotad/rquotad.c @@ -28,18 +28,19 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include -static void rquota_service(struct svc_req *request, SVCXPRT *transp); +static void rquota_service_1(struct svc_req *request, SVCXPRT *transp); +static void rquota_service_2(struct svc_req *request, SVCXPRT *transp); static void sendquota(struct svc_req *request, SVCXPRT *transp); -static void initfs(void); -static int getfsquota(long id, char *path, struct dqblk *dqblk); +static void sendquota_extended(struct svc_req *request, SVCXPRT *transp); +static int getfsquota(int type, long id, char *path, struct dqblk *dqblk); -static struct quotafile **qfa; /* array of qfs */ -static int nqf, szqf; /* number of qfs and size of array */ static int from_inetd = 1; +static int debug = 0; static void cleanup(int sig) @@ -51,19 +52,32 @@ cleanup(int sig) } int -main(void) +main(int argc, char **argv) { SVCXPRT *transp; int ok; struct sockaddr_storage from; socklen_t fromlen; + int vers; + int ch; + + while ((ch = getopt(argc, argv, "d")) != -1) { + switch (ch) { + case 'd': + debug++; + break; + default: + break; + } + } fromlen = sizeof(from); if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) from_inetd = 0; if (!from_inetd) { - daemon(0, 0); + if (!debug) + daemon(0, 0); (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); (void)signal(SIGINT, cleanup); (void)signal(SIGTERM, cleanup); @@ -79,27 +93,60 @@ main(void) syslog(LOG_ERR, "couldn't create udp service."); exit(1); } + vers = RQUOTAVERS; ok = svc_reg(transp, RQUOTAPROG, RQUOTAVERS, - rquota_service, NULL); + rquota_service_1, NULL); + if (ok) { + vers = EXT_RQUOTAVERS; + ok = svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS, + rquota_service_2, NULL); + } } else { - ok = svc_create(rquota_service, + vers = RQUOTAVERS; + ok = svc_create(rquota_service_1, RQUOTAPROG, RQUOTAVERS, "udp"); + if (ok) { + vers = EXT_RQUOTAVERS; + ok = svc_create(rquota_service_2, + RQUOTAPROG, EXT_RQUOTAVERS, "udp"); + + } } if (!ok) { syslog(LOG_ERR, - "unable to register (RQUOTAPROG, RQUOTAVERS, %s)", - from_inetd ? "(inetd)" : "udp"); + "unable to register (RQUOTAPROG, %s, %s)", + vers == RQUOTAVERS ? "RQUOTAVERS" : "EXT_RQUOTAVERS", + from_inetd ? "(inetd)" : "udp"); exit(1); } - initfs(); svc_run(); syslog(LOG_ERR, "svc_run returned"); exit(1); } static void -rquota_service(struct svc_req *request, SVCXPRT *transp) +rquota_service_2(struct svc_req *request, SVCXPRT *transp) +{ + + switch (request->rq_proc) { + case NULLPROC: + (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL); + break; + case RQUOTAPROC_GETQUOTA: + case RQUOTAPROC_GETACTIVEQUOTA: + sendquota_extended(request, transp); + break; + default: + svcerr_noproc(transp); + break; + } + if (from_inetd) + exit(0); +} + +static void +rquota_service_1(struct svc_req *request, SVCXPRT *transp) { switch (request->rq_proc) { @@ -136,7 +183,7 @@ sendquota(struct svc_req *request, SVCXPRT *transp) if (request->rq_cred.oa_flavor != AUTH_UNIX) { /* bad auth */ getq_rslt.status = Q_EPERM; - } else if (!getfsquota(getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) { + } else if (!getfsquota(USRQUOTA, getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) { /* failed, return noquota */ getq_rslt.status = Q_NOQUOTA; } else { @@ -172,38 +219,55 @@ sendquota(struct svc_req *request, SVCXPRT *transp) } static void -initfs(void) +sendquota_extended(struct svc_req *request, SVCXPRT *transp) { - struct fstab *fs; - - setfsent(); - szqf = 8; - if ((qfa = malloc(szqf * sizeof *qfa)) == NULL) - goto enomem; - while ((fs = getfsent())) { - if (strcmp(fs->fs_vfstype, "ufs")) - continue; - if (nqf >= szqf) { - szqf *= 2; - if ((qfa = reallocf(qfa, szqf * sizeof *qfa)) == NULL) - goto enomem; - } - if ((qfa[nqf] = quota_open(fs, USRQUOTA, O_RDONLY)) == NULL) { - if (errno != EOPNOTSUPP) - goto fserr; - continue; - } - ++nqf; - /* XXX */ + struct ext_getquota_args getq_args; + struct getquota_rslt getq_rslt; + struct dqblk dqblk; + struct timeval timev; + int scale; + + bzero(&getq_args, sizeof(getq_args)); + if (!svc_getargs(transp, (xdrproc_t)xdr_ext_getquota_args, &getq_args)) { + svcerr_decode(transp); + return; + } + if (request->rq_cred.oa_flavor != AUTH_UNIX) { + /* bad auth */ + getq_rslt.status = Q_EPERM; + } else if (!getfsquota(getq_args.gqa_type, getq_args.gqa_id, getq_args.gqa_pathp, &dqblk)) { + /* failed, return noquota */ + getq_rslt.status = Q_NOQUOTA; + } else { + gettimeofday(&timev, NULL); + getq_rslt.status = Q_OK; + getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE; + scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32); + getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = + DEV_BSIZE * scale; + getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit = + dqblk.dqb_bhardlimit / scale; + getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit = + dqblk.dqb_bsoftlimit / scale; + getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks = + dqblk.dqb_curblocks / scale; + getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit = + dqblk.dqb_ihardlimit; + getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit = + dqblk.dqb_isoftlimit; + getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles = + dqblk.dqb_curinodes; + getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft = + dqblk.dqb_btime - timev.tv_sec; + getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft = + dqblk.dqb_itime - timev.tv_sec; + } + if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt)) + svcerr_systemerr(transp); + if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) { + syslog(LOG_ERR, "unable to free arguments"); + exit(1); } - endfsent(); - return; -enomem: - syslog(LOG_ERR, "out of memory"); - exit(1); -fserr: - syslog(LOG_ERR, "%s: %s", fs->fs_file, strerror(errno)); - exit(1); } /* @@ -211,12 +275,43 @@ initfs(void) * Return 0 if fail, 1 otherwise */ static int -getfsquota(long id, char *path, struct dqblk *dqblk) +getfsquota(int type, long id, char *path, struct dqblk *dqblk) { - int i; + struct quotafile *qf; + /* + * Remote quota checking is limited to mounted filesystems. + * Since UFS and ZFS support the quota system calls, we + * only need to make an fstab object that has the path, and + * a blank name for the filesystem type. + * This allows the quota_open() call to work the way we + * expect it to. + * + * The static char declaration is because compiler warnings + * don't allow passing a const char * to a char *. + */ + int rv; + static char blank[] = ""; + struct fstab fst; + + fst.fs_file = path; + fst.fs_mntops = blank; + fst.fs_vfstype = blank; + + if (type != USRQUOTA && type != GRPQUOTA) + return (0); + + qf = quota_open(&fst, type, O_RDONLY); + if (debug) + warnx("quota_open(<%s, %s>, %d) returned %p", + fst.fs_file, fst.fs_mntops, type, + qf); + if (qf == NULL) + return (0); - for (i = 0; i < nqf; ++i) - if (quota_check_path(qfa[i], path) == 1) - return (quota_read(qfa[i], dqblk, id) == 0); - return (0); + rv = quota_read(qf, dqblk, id) == 0; + quota_close(qf); + if (debug) + warnx("getfsquota(%d, %ld, %s, %p) -> %d", + type, id, path, dqblk, rv); + return (rv); } diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c index bba3c341210..980c32820c3 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c @@ -64,6 +64,8 @@ #include #include #include +#include + #include "zfs_comutil.h" struct mtx zfs_debug_mtx; @@ -90,6 +92,7 @@ static int zfs_version_zpl = ZPL_VERSION; SYSCTL_INT(_vfs_zfs_version, OID_AUTO, zpl, CTLFLAG_RD, &zfs_version_zpl, 0, "ZPL_VERSION"); +static int zfs_quotactl(vfs_t *vfsp, int cmds, uid_t id, void *arg); static int zfs_mount(vfs_t *vfsp); static int zfs_umount(vfs_t *vfsp, int fflag); static int zfs_root(vfs_t *vfsp, int flags, vnode_t **vpp); @@ -111,6 +114,7 @@ struct vfsops zfs_vfsops = { .vfs_sync = zfs_sync, .vfs_checkexp = zfs_checkexp, .vfs_fhtovp = zfs_fhtovp, + .vfs_quotactl = zfs_quotactl, }; VFS_SET(zfs_vfsops, zfs, VFCF_JAIL | VFCF_DELEGADMIN); @@ -122,6 +126,159 @@ VFS_SET(zfs_vfsops, zfs, VFCF_JAIL | VFCF_DELEGADMIN); */ static uint32_t zfs_active_fs_count = 0; +static int +zfs_getquota(zfsvfs_t *zfsvfs, uid_t id, int isgroup, struct dqblk64 *dqp) +{ + int error = 0; + char buf[32]; + int err; + uint64_t usedobj, quotaobj; + uint64_t quota, used = 0; + timespec_t now; + + usedobj = isgroup ? DMU_GROUPUSED_OBJECT : DMU_USERUSED_OBJECT; + quotaobj = isgroup ? zfsvfs->z_groupquota_obj : zfsvfs->z_userquota_obj; + + if (quotaobj == 0 || zfsvfs->z_replay) { + error = ENOENT; + goto done; + } + (void)sprintf(buf, "%llx", (longlong_t)id); + if ((error = zap_lookup(zfsvfs->z_os, quotaobj, + buf, sizeof(quota), 1, "a)) != 0) { + dprintf("%s(%d): quotaobj lookup failed\n", __FUNCTION__, __LINE__); + goto done; + } + /* + * quota(8) uses bsoftlimit as "quoota", and hardlimit as "limit". + * So we set them to be the same. + */ + dqp->dqb_bsoftlimit = dqp->dqb_bhardlimit = btodb(quota); + error = zap_lookup(zfsvfs->z_os, usedobj, buf, sizeof(used), 1, &used); + if (error && error != ENOENT) { + dprintf("%s(%d): usedobj failed; %d\n", __FUNCTION__, __LINE__, error); + goto done; + } + dqp->dqb_curblocks = btodb(used); + dqp->dqb_ihardlimit = dqp->dqb_isoftlimit = 0; + vfs_timestamp(&now); + /* + * Setting this to 0 causes FreeBSD quota(8) to print + * the number of days since the epoch, which isn't + * particularly useful. + */ + dqp->dqb_btime = dqp->dqb_itime = now.tv_sec; +done: + return (error); +} + +static int +zfs_quotactl(vfs_t *vfsp, int cmds, uid_t id, void *arg) +{ + zfsvfs_t *zfsvfs = vfsp->vfs_data; + struct thread *td; + int cmd, type, error = 0; + int bitsize; + uint64_t fuid; + zfs_userquota_prop_t quota_type; + struct dqblk64 dqblk = { 0 }; + + td = curthread; + cmd = cmds >> SUBCMDSHIFT; + type = cmds & SUBCMDMASK; + + ZFS_ENTER(zfsvfs); + if (id == -1) { + switch (type) { + case USRQUOTA: + id = td->td_ucred->cr_ruid; + break; + case GRPQUOTA: + id = td->td_ucred->cr_rgid; + break; + default: + error = EINVAL; + goto done; + } + } + /* + * Map BSD type to: + * ZFS_PROP_USERUSED, + * ZFS_PROP_USERQUOTA, + * ZFS_PROP_GROUPUSED, + * ZFS_PROP_GROUPQUOTA + */ + switch (cmd) { + case Q_SETQUOTA: + case Q_SETQUOTA32: + if (type == USRQUOTA) + quota_type = ZFS_PROP_USERQUOTA; + else if (type == GRPQUOTA) + quota_type = ZFS_PROP_GROUPQUOTA; + else + error = EINVAL; + break; + case Q_GETQUOTA: + case Q_GETQUOTA32: + if (type == USRQUOTA) + quota_type = ZFS_PROP_USERUSED; + else if (type == GRPQUOTA) + quota_type = ZFS_PROP_GROUPUSED; + else + error = EINVAL; + break; + } + + /* + * Depending on the cmd, we may need to get + * the ruid and domain (see fuidstr_to_sid?), + * the fuid (how?), or other information. + * Create fuid using zfs_fuid_create(zfsvfs, id, + * ZFS_OWNER or ZFS_GROUP, cr, &fuidp)? + * I think I can use just the id? + * + * Look at zfs_fuid_overquota() to look up a quota. + * zap_lookup(something, quotaobj, fuidstring, sizeof(long long), 1, "a) + * + * See zfs_set_userquota() to set a quota. + */ + if ((u_int)type >= MAXQUOTAS) { + error = EINVAL; + goto done; + } + + switch (cmd) { + case Q_GETQUOTASIZE: + bitsize = 64; + error = copyout(&bitsize, arg, sizeof(int)); + break; + case Q_QUOTAON: + // As far as I can tell, you can't turn quotas on or off on zfs + error = 0; + break; + case Q_QUOTAOFF: + error = ENOTSUP; + break; + case Q_SETQUOTA: + error = copyin(&dqblk, arg, sizeof(dqblk)); + if (error == 0) + error = zfs_set_userquota(zfsvfs, quota_type, + "", id, dbtob(dqblk.dqb_bhardlimit)); + break; + case Q_GETQUOTA: + error = zfs_getquota(zfsvfs, id, type == GRPQUOTA, &dqblk); + if (error == 0) + error = copyout(&dqblk, arg, sizeof(dqblk)); + break; + default: + error = EINVAL; + break; + } +done: + ZFS_EXIT(zfsvfs); + return (error); +} + /*ARGSUSED*/ static int zfs_sync(vfs_t *vfsp, int waitfor) diff --git a/usr.bin/quota/quota.c b/usr.bin/quota/quota.c index 8e4bb8a6f40..f4481fecece 100644 --- a/usr.bin/quota/quota.c +++ b/usr.bin/quota/quota.c @@ -98,7 +98,7 @@ static int getufsquota(struct fstab *fs, struct quotause *qup, long id, int quotatype); static int getnfsquota(struct statfs *fst, struct quotause *qup, long id, int quotatype); -static int callaurpc(char *host, int prognum, int versnum, int procnum, +static enum clnt_stat callaurpc(char *host, int prognum, int versnum, int procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out); static int alldigits(char *s); @@ -568,21 +568,17 @@ getufsquota(struct fstab *fs, struct quotause *qup, long id, int quotatype) static int getnfsquota(struct statfs *fst, struct quotause *qup, long id, int quotatype) { - struct getquota_args gq_args; + struct ext_getquota_args gq_args; + struct getquota_args old_gq_args; struct getquota_rslt gq_rslt; struct dqblk *dqp = &qup->dqblk; struct timeval tv; char *cp, host[NI_MAXHOST]; + enum clnt_stat call_stat; if (fst->f_flags & MNT_LOCAL) return (0); - /* - * rpc.rquotad does not support group quotas - */ - if (quotatype != USRQUOTA) - return (0); - /* * must be some form of "hostname:/path" */ @@ -604,11 +600,26 @@ getnfsquota(struct statfs *fst, struct quotause *qup, long id, int quotatype) return (0); gq_args.gqa_pathp = cp + 1; - gq_args.gqa_uid = id; - if (callaurpc(host, RQUOTAPROG, RQUOTAVERS, - RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_getquota_args, (char *)&gq_args, - (xdrproc_t)xdr_getquota_rslt, (char *)&gq_rslt) != 0) - return (0); + gq_args.gqa_id = id; + gq_args.gqa_type = quotatype; + + call_stat = callaurpc(host, RQUOTAPROG, EXT_RQUOTAVERS, + RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_ext_getquota_args, (char *)&gq_args, + (xdrproc_t)xdr_getquota_rslt, (char *)&gq_rslt); + if (call_stat == RPC_PROGVERSMISMATCH) { + if (quotatype == USRQUOTA) { + old_gq_args.gqa_pathp = cp + 1; + old_gq_args.gqa_uid = id; + call_stat = callaurpc(host, RQUOTAPROG, RQUOTAVERS, + RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_getquota_args, (char *)&old_gq_args, + (xdrproc_t)xdr_getquota_rslt, (char *)&gq_rslt); + } else { + /* Old rpc quota does not support group type */ + return (0); + } + } + if (call_stat != 0) + return (call_stat); switch (gq_rslt.status) { case Q_NOQUOTA: @@ -650,7 +661,7 @@ getnfsquota(struct statfs *fst, struct quotause *qup, long id, int quotatype) return (0); } -static int +static enum clnt_stat callaurpc(char *host, int prognum, int versnum, int procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out) { @@ -671,8 +682,7 @@ callaurpc(char *host, int prognum, int versnum, int procnum, tottimeout.tv_usec = 0; clnt_stat = clnt_call(client, procnum, inproc, in, outproc, out, tottimeout); - - return ((int) clnt_stat); + return (clnt_stat); } static int -- 2.45.0