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