From be598569bbb9dec5cb8035441ef7411ebed29991 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Fri, 6 Aug 2010 11:58:46 +0200 Subject: s3:smbd: add nfs quota support to the linux-non-sysquota code This is based on the implementation for solaris and FreeBSD. It makes rpc calls out to the nfs server to retrieve quota information. --- source3/smbd/quotas.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) (limited to 'source3/smbd/quotas.c') diff --git a/source3/smbd/quotas.c b/source3/smbd/quotas.c index 1ad2b937b1..ad3973f754 100644 --- a/source3/smbd/quotas.c +++ b/source3/smbd/quotas.c @@ -73,6 +73,197 @@ typedef struct _LINUX_SMB_DISK_QUOTA { } LINUX_SMB_DISK_QUOTA; +/* + * nfs quota support + * (essentially taken from FreeBSD / SUNOS5 section) + */ +#include +#include +#include +#ifdef HAVE_RPC_NETTYPE_H +#include +#endif +#include + +static int my_xdr_getquota_args(XDR *xdrsp, struct getquota_args *args) +{ + if (!xdr_string(xdrsp, &args->gqa_pathp, RQ_PATHLEN )) + return(0); + if (!xdr_int(xdrsp, &args->gqa_uid)) + return(0); + return (1); +} + +static int my_xdr_getquota_rslt(XDR *xdrsp, struct getquota_rslt *gqr) +{ + int quotastat; + + if (!xdr_int(xdrsp, "astat)) { + DEBUG(6,("nfs_quotas: Status bad or zero\n")); + return 0; + } + gqr->status = quotastat; + + if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsize)) { + DEBUG(6,("nfs_quotas: Block size bad or zero\n")); + return 0; + } + if (!xdr_bool(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_active)) { + DEBUG(6,("nfs_quotas: Active bad or zero\n")); + return 0; + } + if (!xdr_int(xdrsp, (int *)&gqr->getquota_rslt_u.gqr_rquota.rq_bhardlimit)) { + DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n")); + return 0; + } + if (!xdr_int(xdrsp, (int *)&gqr->getquota_rslt_u.gqr_rquota.rq_bsoftlimit)) { + DEBUG(6,("nfs_quotas: Softlimit bad or zero\n")); + return 0; + } + if (!xdr_int(xdrsp, (int *)&gqr->getquota_rslt_u.gqr_rquota.rq_curblocks)) { + DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n")); + return 0; + } + return 1; +} + +static bool nfs_quotas(char *nfspath, uid_t euser_id, uint64_t *bsize, + uint64_t *dfree, uint64_t *dsize) +{ + uid_t uid = euser_id; + LINUX_SMB_DISK_QUOTA D; + char *mnttype = nfspath; + CLIENT *clnt; + struct getquota_rslt gqr; + struct getquota_args args; + char *cutstr, *pathname, *host, *testpath; + int len; + static struct timeval timeout = {2,0}; + enum clnt_stat clnt_stat; + bool ret = True; + + *bsize = *dfree = *dsize = (uint64_t)0; + + len=strcspn(mnttype, ":"); + pathname=strstr(mnttype, ":"); + cutstr = (char *) SMB_MALLOC(len+1); + if (!cutstr) + return False; + + memset(cutstr, '\0', len+1); + host = strncat(cutstr,mnttype, sizeof(char) * len ); + DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr)); + DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype)); + testpath=strchr_m(mnttype, ':'); + args.gqa_pathp = testpath+1; + args.gqa_uid = uid; + + DEBUG(5, ("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers " + "\"%i\" network \"%s\"\n", host, RQUOTAPROG, RQUOTAVERS, + "udp")); + + if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) == NULL) { + ret = False; + goto out; + } + + clnt->cl_auth = authunix_create_default(); + DEBUG(9,("nfs_quotas: auth_success\n")); + + clnt_stat=clnt_call(clnt, + RQUOTAPROC_GETQUOTA, + (const xdrproc_t)my_xdr_getquota_args, + (caddr_t)&args, + (const xdrproc_t)my_xdr_getquota_rslt, + (caddr_t)&gqr, timeout); + + if (clnt_stat != RPC_SUCCESS) { + DEBUG(9,("nfs_quotas: clnt_call fail\n")); + ret = False; + goto out; + } + + /* + * gqr.status returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is + * no quota set, and 3 if no permission to get the quota. If 0 or 3 return + * something sensible. + */ + + switch (gqr.status) { + case 0: + DEBUG(9, ("nfs_quotas: Remote Quotas Failed! Error \"%i\" \n", + gqr.status)); + ret = False; + goto out; + + case 1: + DEBUG(9,("nfs_quotas: Good quota data\n")); + D.softlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit; + D.hardlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit; + D.curblocks = gqr.getquota_rslt_u.gqr_rquota.rq_curblocks; + break; + + case 2: + case 3: + D.softlimit = 1; + D.curblocks = 1; + DEBUG(9, ("nfs_quotas: Remote Quotas returned \"%i\" \n", + gqr.status)); + break; + + default: + DEBUG(9, ("nfs_quotas: Remote Quotas Questionable! " + "Error \"%i\" \n", gqr.status)); + break; + } + + DEBUG(10, ("nfs_quotas: Let`s look at D a bit closer... " + "status \"%i\" bsize \"%i\" active? \"%i\" bhard " + "\"%i\" bsoft \"%i\" curb \"%i\" \n", + gqr.status, + gqr.getquota_rslt_u.gqr_rquota.rq_bsize, + gqr.getquota_rslt_u.gqr_rquota.rq_active, + gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit, + gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit, + gqr.getquota_rslt_u.gqr_rquota.rq_curblocks)); + + if (D.softlimit == 0) + D.softlimit = D.hardlimit; + if (D.softlimit == 0) + return False; + + *bsize = gqr.getquota_rslt_u.gqr_rquota.rq_bsize; + *dsize = D.softlimit; + + if (D.curblocks == 1) + *bsize = DEV_BSIZE; + + if (D.curblocks > D.softlimit) { + *dfree = 0; + *dsize = D.curblocks; + } else + *dfree = D.softlimit - D.curblocks; + + out: + + if (clnt) { + if (clnt->cl_auth) + auth_destroy(clnt->cl_auth); + clnt_destroy(clnt); + } + + DEBUG(5, ("nfs_quotas: For path \"%s\" returning " + "bsize %.0f, dfree %.0f, dsize %.0f\n", + args.gqa_pathp, (double)*bsize, (double)*dfree, + (double)*dsize)); + + SAFE_FREE(cutstr); + DEBUG(10,("nfs_quotas: End of nfs_quotas\n" )); + return ret; +} + +/* end of nfs quota section */ + #ifdef HAVE_LINUX_DQBLK_XFS_H #include @@ -251,6 +442,13 @@ bool disk_quotas(const char *path, uint64_t *bsize, uint64_t *dfree, uint64_t *d become_root(); + if (strcmp(mnt->mnt_type, "nfs") == 0) { + bool retval; + retval = nfs_quotas(mnt->mnt_fsname , euser_id, bsize, dfree, dsize); + unbecome_root(); + return retval; + } + if (strcmp(mnt->mnt_type, "xfs")==0) { r=get_smb_linux_xfs_quota(mnt->mnt_fsname, euser_id, egrp_id, &D); } else { -- cgit