2 * QUOTA An implementation of the diskquota system for the LINUX
3 * operating system. QUOTA is implemented using the BSD systemcall
4 * interface as the means of communication with the user level.
5 * Should work for all filesystems because of integration into the
6 * VFS layer of the operating system.
7 * This is based on the Melbourne quota system wich uses both user and
10 * This part does the rpc-communication with the rquotad.
12 * Author: Marco van Wieringen <mvw@planets.elm.net>
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version
17 * 2 of the License, or (at your option) any later version.
23 #include <sys/types.h>
24 #include <sys/param.h>
43 /* Convert network format of quotas to utils one */
44 static inline void clinet2utildqblk(struct util_dqblk *u, struct rquota *n)
49 u->dqb_bhardlimit = toqb(((qsize_t)n->rq_bhardlimit) * n->rq_bsize);
50 u->dqb_bsoftlimit = toqb(((qsize_t)n->rq_bsoftlimit) * n->rq_bsize);
51 u->dqb_ihardlimit = n->rq_fhardlimit;
52 u->dqb_isoftlimit = n->rq_fsoftlimit;
53 u->dqb_curinodes = n->rq_curfiles;
54 u->dqb_curspace = ((qsize_t)n->rq_curblocks) * n->rq_bsize;
57 u->dqb_btime = n->rq_btimeleft + now;
61 u->dqb_itime = n->rq_ftimeleft + now;
66 /* Convert utils format of quotas to network one */
67 static inline void cliutil2netdqblk(struct sq_dqblk *n, struct util_dqblk *u)
72 n->rq_bhardlimit = u->dqb_bhardlimit;
73 n->rq_bsoftlimit = u->dqb_bsoftlimit;
74 n->rq_fhardlimit = u->dqb_ihardlimit;
75 n->rq_fsoftlimit = u->dqb_isoftlimit;
76 n->rq_curblocks = toqb(u->dqb_curspace);
77 n->rq_curfiles = u->dqb_curinodes;
79 n->rq_btimeleft = u->dqb_btime - now;
83 n->rq_ftimeleft = u->dqb_itime - now;
88 /* Write appropriate error message */
89 static int rquota_err(int stat)
107 static int split_nfs_mount(char *devname, char **host, char **path)
111 /* NFS server name contained in brackets? */
112 if (*devname == '[') {
114 pathname = strchr(devname, ']');
115 if (!pathname || pathname[1] != ':')
118 if (pathname[2] == '(')
121 *path = pathname + 2;
125 pathname = strchr(devname, ':');
129 if (pathname[1] == '(')
132 *path = pathname + 1;
137 * Collect the requested quota information from a remote host.
139 int rpc_rquota_get(struct dquot *dquot)
142 getquota_rslt *result;
145 ext_getquota_args ext_arg;
147 char *fsname_tmp, *host, *pathname;
148 struct timeval timeout = { 2, 0 };
151 * Initialize with NULL.
153 memset(&dquot->dq_dqb, 0, sizeof(dquot->dq_dqb));
156 * Convert host:pathname to seperate host and pathname.
158 fsname_tmp = (char *)smalloc(strlen(dquot->dq_h->qh_quotadev) + 1);
159 strcpy(fsname_tmp, dquot->dq_h->qh_quotadev);
160 if (!split_nfs_mount(fsname_tmp, &host, &pathname)) {
165 /* For NFSv4, we send the filesystem path without initial /. Server prepends proper
166 * NFS pseudoroot automatically and uses this for detection of NFSv4 mounts. */
167 if ((dquot->dq_h->qh_io_flags & IOFL_NFS_MIXED_PATHS) &&
168 !strcmp(dquot->dq_h->qh_fstype, MNTTYPE_NFS4)) {
169 while (*pathname == '/')
174 * First try EXT_RQUOTAPROG (Extended (LINUX) RPC quota program)
176 args.ext_arg.gqa_pathp = pathname;
177 args.ext_arg.gqa_id = dquot->dq_id;
178 args.ext_arg.gqa_type = dquot->dq_h->qh_type;
181 * Create a RPC client.
183 if ((clnt = clnt_create(host, RQUOTAPROG, EXT_RQUOTAVERS, "udp")) != NULL) {
185 * Initialize unix authentication
187 clnt->cl_auth = authunix_create_default();
190 * Setup protocol timeout.
192 clnt_control(clnt, CLSET_TIMEOUT, (caddr_t) & timeout);
195 * Do RPC call and check result.
197 result = rquotaproc_getquota_2(&args.ext_arg, clnt);
198 if (result != NULL && result->status == Q_OK)
199 clinet2utildqblk(&dquot->dq_dqb, &result->getquota_rslt_u.gqr_rquota);
202 * Destroy unix authentication and RPC client structure.
204 auth_destroy(clnt->cl_auth);
210 if (result == NULL || !result->status) {
211 if (dquot->dq_h->qh_type == USRQUOTA) {
213 * Try RQUOTAPROG because server doesn't seem to understand EXT_RQUOTAPROG. (NON-LINUX servers.)
215 args.arg.gqa_pathp = pathname;
216 args.arg.gqa_uid = dquot->dq_id;
219 * Create a RPC client.
221 if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) != NULL) {
223 * Initialize unix authentication
225 clnt->cl_auth = authunix_create_default();
228 * Setup protocol timeout.
230 clnt_control(clnt, CLSET_TIMEOUT, (caddr_t) & timeout);
233 * Do RPC call and check result.
235 result = rquotaproc_getquota_1(&args.arg, clnt);
236 if (result != NULL && result->status == Q_OK)
237 clinet2utildqblk(&dquot->dq_dqb,
238 &result->getquota_rslt_u.gqr_rquota);
241 * Destroy unix authentication and RPC client structure.
243 auth_destroy(clnt->cl_auth);
249 return rquota_err(result?result->status:-1);
253 * Set the requested quota information on a remote host.
255 int rpc_rquota_set(int qcmd, struct dquot *dquot)
257 #if defined(RPC_SETQUOTA)
259 setquota_rslt *result;
262 ext_setquota_args ext_arg;
264 char *fsname_tmp, *host, *pathname;
265 struct timeval timeout = { 2, 0 };
267 /* RPC limits values to 32b variables. Prevent value wrapping. */
268 if (check_dquot_range(dquot) < 0)
272 * Convert host:pathname to seperate host and pathname.
274 fsname_tmp = (char *)smalloc(strlen(dquot->dq_h->qh_quotadev) + 1);
275 strcpy(fsname_tmp, dquot->dq_h->qh_quotadev);
276 if (!split_nfs_mount(fsname_tmp, &host, &pathname)) {
281 /* For NFSv4, we send the filesystem path without initial /. Server prepends proper
282 * NFS pseudoroot automatically and uses this for detection of NFSv4 mounts. */
283 if ((dquot->dq_h->qh_io_flags & IOFL_NFS_MIXED_PATHS) &&
284 !strcmp(dquot->dq_h->qh_fstype, MNTTYPE_NFS4)) {
285 while (*pathname == '/')
290 * First try EXT_RQUOTAPROG (Extended (LINUX) RPC quota program)
292 args.ext_arg.sqa_qcmd = qcmd;
293 args.ext_arg.sqa_pathp = pathname;
294 args.ext_arg.sqa_id = dquot->dq_id;
295 args.ext_arg.sqa_type = dquot->dq_h->qh_type;
296 cliutil2netdqblk(&args.ext_arg.sqa_dqblk, &dquot->dq_dqb);
298 if ((clnt = clnt_create(host, RQUOTAPROG, EXT_RQUOTAVERS, "udp")) != NULL) {
300 * Initialize unix authentication
302 clnt->cl_auth = authunix_create_default();
305 * Setup protocol timeout.
307 clnt_control(clnt, CLSET_TIMEOUT, (caddr_t) & timeout);
310 * Do RPC call and check result.
312 result = rquotaproc_setquota_2(&args.ext_arg, clnt);
313 if (result != NULL && result->status == Q_OK)
314 clinet2utildqblk(&dquot->dq_dqb, &result->setquota_rslt_u.sqr_rquota);
317 * Destroy unix authentication and RPC client structure.
319 auth_destroy(clnt->cl_auth);
325 if (result == NULL || !result->status) {
326 if (dquot->dq_h->qh_type == USRQUOTA) {
328 * Try RQUOTAPROG because server doesn't seem to understand EXT_RQUOTAPROG. (NON-LINUX servers.)
330 args.arg.sqa_qcmd = qcmd;
331 args.arg.sqa_pathp = pathname;
332 args.arg.sqa_id = dquot->dq_id;
333 cliutil2netdqblk(&args.arg.sqa_dqblk, &dquot->dq_dqb);
336 * Create a RPC client.
338 if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) != NULL) {
340 * Initialize unix authentication
342 clnt->cl_auth = authunix_create_default();
345 * Setup protocol timeout.
347 clnt_control(clnt, CLSET_TIMEOUT, (caddr_t) & timeout);
350 * Do RPC call and check result.
352 result = rquotaproc_setquota_1(&args.arg, clnt);
353 if (result != NULL && result->status == Q_OK)
354 clinet2utildqblk(&dquot->dq_dqb,
355 &result->setquota_rslt_u.sqr_rquota);
358 * Destroy unix authentication and RPC client structure.
360 auth_destroy(clnt->cl_auth);
366 return rquota_err(result?result->status:-1);