Update to 4.01.
[profile/ivi/quota.git] / rquota_client.c
1 /*
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
8  *          group quota files.
9  *
10  *          This part does the rpc-communication with the rquotad.
11  *
12  * Author:  Marco van Wieringen <mvw@planets.elm.net>
13  *
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.
18  */
19
20 #include "config.h"
21
22 #include <rpc/rpc.h>
23 #include <sys/types.h>
24 #include <sys/param.h>
25 #include <sys/stat.h>
26 #include <sys/file.h>
27 #include <errno.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <ctype.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <signal.h>
34 #include <time.h>
35
36 #include "mntopt.h"
37 #include "rquota.h"
38 #include "common.h"
39 #include "quotaio.h"
40
41 #if defined(RPC)
42
43 /* Convert network format of quotas to utils one */
44 static inline void clinet2utildqblk(struct util_dqblk *u, struct rquota *n)
45 {
46         time_t now;
47         
48         /* Copy the quota */
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;
55         time(&now);
56         if (n->rq_btimeleft)
57                 u->dqb_btime = n->rq_btimeleft + now;
58         else
59                 u->dqb_btime = 0;
60         if (n->rq_ftimeleft)
61                 u->dqb_itime = n->rq_ftimeleft + now;
62         else
63                 u->dqb_itime = 0;
64 }
65
66 /* Convert utils format of quotas to network one */
67 static inline void cliutil2netdqblk(struct sq_dqblk *n, struct util_dqblk *u)
68 {
69         time_t now;
70
71         time(&now);
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;
78         if (u->dqb_btime)
79                 n->rq_btimeleft = u->dqb_btime - now;
80         else
81                 n->rq_btimeleft = 0;
82         if (u->dqb_itime)
83                 n->rq_ftimeleft = u->dqb_itime - now;
84         else
85                 n->rq_ftimeleft = 0;
86 }
87
88 /* Write appropriate error message */
89 static int rquota_err(int stat)
90 {
91         switch (stat) {
92                 case -1:
93                         return -ECONNREFUSED;
94                 case 0:
95                         return -ENOSYS;
96                 case Q_NOQUOTA:
97                         return -ENOENT;
98                 case Q_OK:
99                         return 0;
100                 case Q_EPERM:
101                         return -EPERM;
102                 default:
103                         return -EINVAL;
104         }
105 }
106
107 static int split_nfs_mount(char *devname, char **host, char **path)
108 {
109         char *pathname;
110
111         /* NFS server name contained in brackets? */
112         if (*devname == '[') {
113                 *host = devname + 1;
114                 pathname = strchr(devname, ']');
115                 if (!pathname || pathname[1] != ':')
116                         return 0;
117                 /* Autofs? */
118                 if (pathname[2] == '(')
119                         return 0;
120                 *pathname = 0;
121                 *path = pathname + 2;
122                 return 1;
123         }
124         *host = devname;
125         pathname = strchr(devname, ':');
126         if (!pathname)
127                 return 0;
128         /* Autofs? */
129         if (pathname[1] == '(')
130                 return 0;
131         *pathname = 0;
132         *path = pathname + 1;
133         return 1;
134 }
135
136 /*
137  * Collect the requested quota information from a remote host.
138  */
139 int rpc_rquota_get(struct dquot *dquot)
140 {
141         CLIENT *clnt;
142         getquota_rslt *result;
143         union {
144                 getquota_args arg;
145                 ext_getquota_args ext_arg;
146         } args;
147         char *fsname_tmp, *host, *pathname;
148         struct timeval timeout = { 2, 0 };
149
150         /*
151          * Initialize with NULL.
152          */
153         memset(&dquot->dq_dqb, 0, sizeof(dquot->dq_dqb));
154
155         /*
156          * Convert host:pathname to seperate host and pathname.
157          */
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)) {
161                 free(fsname_tmp);
162                 return -ENOENT;
163         }
164
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 == '/')
170                         pathname++;
171         }
172
173         /*
174          * First try EXT_RQUOTAPROG (Extended (LINUX) RPC quota program)
175          */
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;
179
180         /*
181          * Create a RPC client.
182          */
183         if ((clnt = clnt_create(host, RQUOTAPROG, EXT_RQUOTAVERS, "udp")) != NULL) {
184                 /*
185                  * Initialize unix authentication
186                  */
187                 clnt->cl_auth = authunix_create_default();
188
189                 /*
190                  * Setup protocol timeout.
191                  */
192                 clnt_control(clnt, CLSET_TIMEOUT, (caddr_t) & timeout);
193
194                 /*
195                  * Do RPC call and check result.
196                  */
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);
200
201                 /*
202                  * Destroy unix authentication and RPC client structure.
203                  */
204                 auth_destroy(clnt->cl_auth);
205                 clnt_destroy(clnt);
206         }
207         else
208                 result = NULL;
209
210         if (result == NULL || !result->status) {
211                 if (dquot->dq_h->qh_type == USRQUOTA) {
212                         /*
213                          * Try RQUOTAPROG because server doesn't seem to understand EXT_RQUOTAPROG. (NON-LINUX servers.)
214                          */
215                         args.arg.gqa_pathp = pathname;
216                         args.arg.gqa_uid = dquot->dq_id;
217
218                         /*
219                          * Create a RPC client.
220                          */
221                         if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) != NULL) {
222                                 /*
223                                  * Initialize unix authentication
224                                  */
225                                 clnt->cl_auth = authunix_create_default();
226
227                                 /*
228                                  * Setup protocol timeout.
229                                  */
230                                 clnt_control(clnt, CLSET_TIMEOUT, (caddr_t) & timeout);
231
232                                 /*
233                                  * Do RPC call and check result.
234                                  */
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);
239
240                                 /*
241                                  * Destroy unix authentication and RPC client structure.
242                                  */
243                                 auth_destroy(clnt->cl_auth);
244                                 clnt_destroy(clnt);
245                         }
246                 }
247         }
248         free(fsname_tmp);
249         return rquota_err(result?result->status:-1);
250 }
251
252 /*
253  * Set the requested quota information on a remote host.
254  */
255 int rpc_rquota_set(int qcmd, struct dquot *dquot)
256 {
257 #if defined(RPC_SETQUOTA)
258         CLIENT *clnt;
259         setquota_rslt *result;
260         union {
261                 setquota_args arg;
262                 ext_setquota_args ext_arg;
263         } args;
264         char *fsname_tmp, *host, *pathname;
265         struct timeval timeout = { 2, 0 };
266
267         /* RPC limits values to 32b variables. Prevent value wrapping. */
268         if (check_dquot_range(dquot) < 0)
269                 return -ERANGE;
270
271         /*
272          * Convert host:pathname to seperate host and pathname.
273          */
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)) {
277                 free(fsname_tmp);
278                 return -ENOENT;
279         }
280
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 == '/')
286                         pathname++;
287         }
288
289         /*
290          * First try EXT_RQUOTAPROG (Extended (LINUX) RPC quota program)
291          */
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);
297
298         if ((clnt = clnt_create(host, RQUOTAPROG, EXT_RQUOTAVERS, "udp")) != NULL) {
299                 /*
300                  * Initialize unix authentication
301                  */
302                 clnt->cl_auth = authunix_create_default();
303
304                 /*
305                  * Setup protocol timeout.
306                  */
307                 clnt_control(clnt, CLSET_TIMEOUT, (caddr_t) & timeout);
308
309                 /*
310                  * Do RPC call and check result.
311                  */
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);
315
316                 /*
317                  * Destroy unix authentication and RPC client structure.
318                  */
319                 auth_destroy(clnt->cl_auth);
320                 clnt_destroy(clnt);
321         }
322         else
323                 result = NULL;
324
325         if (result == NULL || !result->status) {
326                 if (dquot->dq_h->qh_type == USRQUOTA) {
327                         /*
328                          * Try RQUOTAPROG because server doesn't seem to understand EXT_RQUOTAPROG. (NON-LINUX servers.)
329                          */
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);
334
335                         /*
336                          * Create a RPC client.
337                          */
338                         if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) != NULL) {
339                                 /*
340                                  * Initialize unix authentication
341                                  */
342                                 clnt->cl_auth = authunix_create_default();
343
344                                 /*
345                                  * Setup protocol timeout.
346                                  */
347                                 clnt_control(clnt, CLSET_TIMEOUT, (caddr_t) & timeout);
348
349                                 /*
350                                  * Do RPC call and check result.
351                                  */
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);
356
357                                 /*
358                                  * Destroy unix authentication and RPC client structure.
359                                  */
360                                 auth_destroy(clnt->cl_auth);
361                                 clnt_destroy(clnt);
362                         }
363                 }
364         }
365         free(fsname_tmp);
366         return rquota_err(result?result->status:-1);
367 #endif
368         return -1;
369 }
370 #endif