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