Home | History | Annotate | Line # | Download | only in libquota
      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
     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 || 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