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
rq_scale(uint32_t rqblocks,uint32_t rqsize)47 rq_scale(uint32_t rqblocks, uint32_t rqsize)
48 {
49 return ((uint64_t)rqblocks * rqsize) / DEV_BSIZE;
50 }
51
52 static uint64_t
rq_scalelimit(uint32_t rqval,uint32_t rqsize)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
rq_plainlimit(uint32_t rqval)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
rquota_to_quotavals(const struct rquota * rq,struct quotaval * blocks,struct quotaval * files)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
callaurpc(const char * host,rpcprog_t prognum,rpcvers_t versnum,rpcproc_t procnum,xdrproc_t inproc,void * in,xdrproc_t outproc,void * out)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
__quota_nfs_get(struct quotahandle * qh,const struct quotakey * qk,struct quotaval * qv)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