xref: /netbsd/lib/libquota/quota_nfs.c (revision 46546978)
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