1 /* $NetBSD: quota_nfs.c,v 1.4 2014/06/11 08:43:01 martin 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.4 2014/06/11 08:43:01 martin 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 && rpcqtype == RQUOTA_USRQUOTA) { 183 /* try RQUOTAVERS */ 184 gq_args.gqa_pathp = path; 185 gq_args.gqa_uid = qk->qk_id; 186 ret = callaurpc(host, RQUOTAPROG, RQUOTAVERS, 187 RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_getquota_args, 188 &gq_args, (xdrproc_t)xdr_getquota_rslt, &gq_rslt); 189 } 190 sverrno = errno; 191 free(host); 192 193 if (ret != RPC_SUCCESS) { 194 /* 195 * Remap some error codes for callers convenience: 196 * - if the file server does not support any quotas at all, 197 * return ENOENT 198 * - if the server can not be reached something is very 199 * wrong - or we are run inside a virtual rump network 200 * but querying an NFS mount from the host. Make sure 201 * to fail silently and return ENOENT as well. 202 */ 203 if (ret == RPC_SYSTEMERROR 204 && rpc_createerr.cf_error.re_errno == EHOSTUNREACH) 205 sverrno = ENOENT; 206 else if (sverrno == ENOTCONN) 207 sverrno = ENOENT; 208 errno = sverrno; 209 return -1; 210 } 211 212 switch (gq_rslt.status) { 213 case Q_NOQUOTA: 214 quotaval_clear(qv); 215 return 0; 216 case Q_EPERM: 217 errno = EACCES; 218 return -1; 219 case Q_OK: 220 rquota_to_quotavals(&gq_rslt.getquota_rslt_u.gqr_rquota, 221 &blocks, &inodes); 222 if (qk->qk_objtype == QUOTA_OBJTYPE_BLOCKS) { 223 *qv = blocks; 224 } else { 225 *qv = inodes; 226 } 227 return 0; 228 default: 229 break; 230 } 231 /* XXX not exactly a good errno */ 232 errno = ERANGE; 233 return -1; 234 } 235 236