1 /* $NetBSD: quota_nfs.c,v 1.5 2016/01/30 16:31:28 bouyer Exp $ */ 2 /*- 3 * Copyright (c) 2011 Manuel Bouyer 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __RCSID("$NetBSD: quota_nfs.c,v 1.5 2016/01/30 16:31:28 bouyer Exp $"); 30 31 #include <sys/types.h> 32 #include <sys/param.h> /* XXX for DEV_BSIZE */ 33 #include <stdlib.h> 34 #include <string.h> 35 #include <limits.h> 36 #include <netdb.h> 37 #include <errno.h> 38 39 #include <rpc/rpc.h> 40 #include <rpc/pmap_prot.h> 41 #include <rpcsvc/rquota.h> 42 43 #include <quota.h> 44 #include "quotapvt.h" 45 46 static uint64_t 47 rq_scale(uint32_t rqblocks, uint32_t rqsize) 48 { 49 return ((uint64_t)rqblocks * rqsize) / DEV_BSIZE; 50 } 51 52 static uint64_t 53 rq_scalelimit(uint32_t rqval, uint32_t rqsize) 54 { 55 if (rqval == 0) { 56 return QUOTA_NOLIMIT; 57 } else { 58 return rq_scale(rqval - 1, rqsize); 59 } 60 } 61 62 static uint64_t 63 rq_plainlimit(uint32_t rqval) 64 { 65 if (rqval == 0) { 66 return QUOTA_NOLIMIT; 67 } else { 68 return rqval - 1; 69 } 70 } 71 72 static void 73 rquota_to_quotavals(const struct rquota *rq, 74 struct quotaval *blocks, struct quotaval *files) 75 { 76 struct timeval now; 77 78 gettimeofday(&now, NULL); 79 80 /* blocks*/ 81 blocks->qv_hardlimit = rq_scalelimit(rq->rq_bhardlimit, rq->rq_bsize); 82 blocks->qv_softlimit = rq_scalelimit(rq->rq_bsoftlimit, rq->rq_bsize); 83 blocks->qv_usage = rq_scale(rq->rq_curblocks, rq->rq_bsize); 84 blocks->qv_expiretime = rq->rq_btimeleft + now.tv_sec; 85 blocks->qv_grace = QUOTA_NOTIME; 86 87 /* inodes */ 88 files->qv_hardlimit = rq_plainlimit(rq->rq_fhardlimit); 89 files->qv_softlimit = rq_plainlimit(rq->rq_fsoftlimit); 90 files->qv_usage = rq->rq_curfiles; 91 files->qv_expiretime = rq->rq_ftimeleft + now.tv_sec; 92 files->qv_grace = QUOTA_NOTIME; 93 } 94 95 static int 96 callaurpc(const char *host, rpcprog_t prognum, rpcvers_t versnum, 97 rpcproc_t procnum, xdrproc_t inproc, void *in, xdrproc_t outproc, void *out) 98 { 99 struct sockaddr_in server_addr; 100 enum clnt_stat clnt_stat; 101 struct hostent *hp; 102 struct timeval timeout, tottimeout; 103 104 CLIENT *client = NULL; 105 int sock = RPC_ANYSOCK; 106 107 if ((hp = gethostbyname(host)) == NULL) 108 return (int) RPC_UNKNOWNHOST; 109 timeout.tv_usec = 0; 110 timeout.tv_sec = 6; 111 memmove(&server_addr.sin_addr, hp->h_addr, hp->h_length); 112 server_addr.sin_family = AF_INET; 113 server_addr.sin_port = 0; 114 115 if ((client = clntudp_create(&server_addr, prognum, 116 versnum, timeout, &sock)) == NULL) 117 return (int) rpc_createerr.cf_stat; 118 119 client->cl_auth = authunix_create_default(); 120 tottimeout.tv_sec = 25; 121 tottimeout.tv_usec = 0; 122 clnt_stat = clnt_call(client, procnum, inproc, in, 123 outproc, out, tottimeout); 124 125 return (int) clnt_stat; 126 } 127 128 int 129 __quota_nfs_get(struct quotahandle *qh, const struct quotakey *qk, 130 struct quotaval *qv) 131 { 132 struct getquota_args gq_args; 133 struct ext_getquota_args ext_gq_args; 134 struct getquota_rslt gq_rslt; 135 struct quotaval blocks, inodes; 136 char *host, *path; 137 int ret, rpcqtype; 138 int sverrno; 139 140 switch (qk->qk_idtype) { 141 case QUOTA_IDTYPE_USER: 142 rpcqtype = RQUOTA_USRQUOTA; 143 break; 144 case QUOTA_IDTYPE_GROUP: 145 rpcqtype = RQUOTA_GRPQUOTA; 146 break; 147 default: 148 errno = EINVAL; 149 return -1; 150 } 151 152 switch (qk->qk_objtype) { 153 case QUOTA_OBJTYPE_BLOCKS: 154 case QUOTA_OBJTYPE_FILES: 155 break; 156 default: 157 errno = EINVAL; 158 return -1; 159 } 160 161 /* 162 * must be some form of "hostname:/path" 163 */ 164 path = strdup(qh->qh_mountdevice); 165 if (path == NULL) { 166 errno = ENOMEM; 167 return -1; 168 } 169 host = strsep(&path, ":"); 170 if (path == NULL) { 171 free(host); 172 errno = EINVAL; 173 return -1; 174 } 175 176 ext_gq_args.gqa_pathp = path; 177 ext_gq_args.gqa_id = qk->qk_id; 178 ext_gq_args.gqa_type = rpcqtype; 179 ret = callaurpc(host, RQUOTAPROG, EXT_RQUOTAVERS, 180 RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_ext_getquota_args, 181 &ext_gq_args, (xdrproc_t)xdr_getquota_rslt, &gq_rslt); 182 if ((ret == RPC_PROGVERSMISMATCH || ret == RPC_PROGNOTREGISTERED) 183 && rpcqtype == RQUOTA_USRQUOTA) { 184 /* try RQUOTAVERS */ 185 gq_args.gqa_pathp = path; 186 gq_args.gqa_uid = qk->qk_id; 187 ret = callaurpc(host, RQUOTAPROG, RQUOTAVERS, 188 RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_getquota_args, 189 &gq_args, (xdrproc_t)xdr_getquota_rslt, &gq_rslt); 190 } 191 sverrno = errno; 192 free(host); 193 194 if (ret != RPC_SUCCESS) { 195 /* 196 * Remap some error codes for callers convenience: 197 * - if the file server does not support any quotas at all, 198 * return ENOENT 199 * - if the server can not be reached something is very 200 * wrong - or we are run inside a virtual rump network 201 * but querying an NFS mount from the host. Make sure 202 * to fail silently and return ENOENT as well. 203 */ 204 if (ret == RPC_SYSTEMERROR 205 && rpc_createerr.cf_error.re_errno == EHOSTUNREACH) 206 sverrno = ENOENT; 207 else if (sverrno == ENOTCONN) 208 sverrno = ENOENT; 209 errno = sverrno; 210 return -1; 211 } 212 213 switch (gq_rslt.status) { 214 case Q_NOQUOTA: 215 quotaval_clear(qv); 216 return 0; 217 case Q_EPERM: 218 errno = EACCES; 219 return -1; 220 case Q_OK: 221 rquota_to_quotavals(&gq_rslt.getquota_rslt_u.gqr_rquota, 222 &blocks, &inodes); 223 if (qk->qk_objtype == QUOTA_OBJTYPE_BLOCKS) { 224 *qv = blocks; 225 } else { 226 *qv = inodes; 227 } 228 return 0; 229 default: 230 break; 231 } 232 /* XXX not exactly a good errno */ 233 errno = ERANGE; 234 return -1; 235 } 236 237