Handle new-style quotactl.
[nfs-utils.git] / utils / rquotad / rquota_server.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 lookup of the info.
11  *
12  * Version: $Id: rquota_server.c,v 2.9 1996/11/17 16:59:46 mvw Exp mvw $
13  *
14  * Author:  Marco van Wieringen <mvw@planets.elm.net>
15  *
16  *          This program is free software; you can redistribute it and/or
17  *          modify it under the terms of the GNU General Public License
18  *          as published by the Free Software Foundation; either version
19  *          2 of the License, or (at your option) any later version.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <rpc/rpc.h>
27 #include "rquota.h"
28 #include <sys/file.h>
29 #include <sys/stat.h>
30 #include <sys/param.h>
31 /* Unfortunately we cannot trust sys/quota.h to have
32  * what we need, either the old interface could be missing
33  * (SLES9) or the new (SLES8 and others).
34  * So we will just put it explicitly below
35  */
36 #if 0
37 #include <sys/quota.h>
38 #endif
39 #include <sys/mount.h>
40 #include <dirent.h>
41 #include <paths.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include "mntent.h"
46 #include "xmalloc.h"
47
48 #define TYPE_EXTENDED   0x01
49 #define ACTIVE          0x02
50
51 #ifndef MNTTYPE_AUTOFS
52 #define MNTTYPE_AUTOFS  "autofs"
53 #endif
54
55 #ifndef BLOCK_SIZE
56 #define BLOCK_SIZE 1024
57 #endif
58
59 #define MAXQUOTAS 2
60 #define USRQUOTA  0             /* element used for user quotas */
61 #define GRPQUOTA  1             /* element used for group quotas */
62
63 struct dqblk {
64         u_int32_t dqb_bhardlimit;   /* absolute limit on disk blks alloc */
65         u_int32_t dqb_bsoftlimit;   /* preferred limit on disk blks */
66         u_int32_t dqb_curblocks;    /* current block count */
67         u_int32_t dqb_ihardlimit;   /* maximum # allocated inodes */
68         u_int32_t dqb_isoftlimit;   /* preferred inode limit */
69         u_int32_t dqb_curinodes;    /* current # allocated inodes */
70         time_t dqb_btime;           /* time limit for excessive disk use */
71         time_t dqb_itime;           /* time limit for excessive files */
72 };
73
74 struct if_dqblk {
75         u_int64_t dqb_bhardlimit;
76         u_int64_t dqb_bsoftlimit;
77         u_int64_t dqb_curspace;
78         u_int64_t dqb_ihardlimit;
79         u_int64_t dqb_isoftlimit;
80         u_int64_t dqb_curinodes;
81         u_int64_t dqb_btime;
82         u_int64_t dqb_itime;
83         u_int32_t dqb_valid;
84 };
85
86 #define SUBCMDMASK  0x00ff
87 #define SUBCMDSHIFT 8
88 #define QCMD(cmd, type)  (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK))
89
90 #define Q_GETQUOTA 0x0300       /* get limits and usage */
91 #define Q_SETQUOTA 0x0400       /* set limits and usage */
92
93 #define Q_GETFMT   0x800004     /* get quota format used on given filesystem */
94 #define Q_GETQUOTA_NEW 0x800007 /* get user quota structure */
95 #define Q_SETQUOTA_NEW 0x800008 /* set user quota structure */
96 #define dqoff(UID)      ((loff_t)((UID) * sizeof (struct dqblk)))
97
98 extern int quotactl (int __cmd, const char *__special, int __id,
99                      caddr_t __addr) __THROW;
100 /*
101  * Global unix authentication credentials.
102  */
103 extern struct authunix_parms *unix_cred;
104
105 int in_group (gid_t *gids, u_int len, gid_t gid)
106 {
107    int cnt = 0;
108
109    while (cnt < len) {
110       if (gids[cnt] == gid)
111          return 1;
112       cnt++;
113    }
114    return 0;
115 }
116
117 getquota_rslt *getquotainfo(int flags, caddr_t *argp, struct svc_req *rqstp)
118 {
119    static getquota_rslt result;
120    union {
121       getquota_args *args;
122       ext_getquota_args *ext_args;
123    } arguments;
124    FILE *fp;
125    struct mntent *mnt;
126    char *pathname, *qfpathname;
127    int fd, err, id, type;
128    struct stat stm, stn;
129    struct rquota *rquota;
130    struct if_dqblk dqb;
131
132    /*
133     * First check authentication.
134     */
135    if (flags & TYPE_EXTENDED) {
136       arguments.ext_args = (ext_getquota_args *)argp;
137       id = arguments.ext_args->gqa_id;
138       type = arguments.ext_args->gqa_type;
139       pathname = arguments.ext_args->gqa_pathp;
140
141       if (type == USRQUOTA && unix_cred->aup_uid && unix_cred->aup_uid != id) {
142          result.status = Q_EPERM;
143          return(&result);
144       }
145
146       if (type == GRPQUOTA && unix_cred->aup_uid && unix_cred->aup_gid != id &&
147           !in_group((gid_t *)unix_cred->aup_gids, unix_cred->aup_len, id)) {
148          result.status = Q_EPERM;
149          return(&result);
150       }
151    } else {
152       arguments.args = (getquota_args *)argp;
153       id = arguments.args->gqa_uid;
154       type = USRQUOTA;
155       pathname = arguments.args->gqa_pathp;
156
157       if (unix_cred->aup_uid && unix_cred->aup_uid != id) {
158          result.status = Q_EPERM;
159          return(&result);
160       }
161    }
162
163    fp = setmntent(MNTTAB, "r");
164    while ((mnt = getmntent(fp)) != (struct mntent *)0) {
165       if (stat(mnt->mnt_dir, &stm) == -1)
166           continue;
167
168       if (stat(pathname, &stn) == -1)
169           break;
170       else if (stm.st_dev != stn.st_dev)
171           continue;
172
173       if (mnt->mnt_fsname [0] != '/'
174           || strcasecmp (mnt->mnt_type, MNTTYPE_NFS) == 0
175           || strcasecmp (mnt->mnt_type, MNTTYPE_AUTOFS) == 0
176           || strcasecmp (mnt->mnt_type, MNTTYPE_SWAP) == 0
177           || strcasecmp (mnt->mnt_type, MNTTYPE_IGNORE) == 0)
178          break;
179
180       /* All blocks reported are in BLOCK_SIZE. */
181       result.getquota_rslt_u.gqr_rquota.rq_bsize = BLOCK_SIZE;
182
183       if (hasquota(mnt, type, &qfpathname)) {
184               int fmt;
185               if (quotactl(QCMD(Q_GETFMT, type), mnt->mnt_fsname, 0, (caddr_t)&fmt)==0) {
186                       /* new style interface
187                        * Don't bother trying to read from the file
188                        */
189                       err = quotactl(QCMD(Q_GETQUOTA_NEW, type),
190                                      mnt->mnt_fsname, id, (caddr_t)&dqb);
191                       if (err) memset(&dqb, 0, sizeof(dqb));
192               } else {
193                       /* old style */
194                       struct dqblk dq_dqb;
195
196                       if ((err = quotactl(QCMD(Q_GETQUOTA, type), mnt->mnt_fsname,
197                                           id, (caddr_t)&dq_dqb)) == -1
198                           && !(flags & ACTIVE)) {
199                               if ((fd = open(qfpathname, O_RDONLY)) < 0)
200                               {
201                                       free(qfpathname);
202                                       continue;
203                               }
204                               free(qfpathname);
205                               lseek(fd, (long) dqoff(id), L_SET);
206                               switch (read(fd, &dq_dqb, sizeof(struct dqblk))) {
207                               case 0:/* EOF */
208                                       /*
209                                        * Convert implicit 0 quota (EOF) into an
210                                        * explicit one (zero'ed dqblk)
211                                        */
212                                       memset((caddr_t)&dq_dqb, 0, sizeof(struct dqblk));
213                                       break;
214                               case sizeof(struct dqblk):   /* OK */
215                                       break;
216                               default:   /* ERROR */
217                                       close(fd);
218                                       continue;
219                               }
220                               close(fd);
221                       }
222                       dqb.dqb_bhardlimit = dq_dqb.dqb_bhardlimit;
223                       dqb.dqb_bsoftlimit = dq_dqb.dqb_bsoftlimit;
224                       dqb.dqb_curspace = dq_dqb.dqb_curblocks * 1024;
225                       dqb.dqb_ihardlimit = dq_dqb.dqb_ihardlimit;
226                       dqb.dqb_isoftlimit = dq_dqb.dqb_isoftlimit;
227                       dqb.dqb_curinodes = dq_dqb.dqb_curinodes;
228                       dqb.dqb_btime = dq_dqb.dqb_btime;
229                       dqb.dqb_itime = dq_dqb.dqb_itime;
230               }
231          endmntent(fp);
232
233          if (err && (flags & ACTIVE)) {
234             result.status = Q_NOQUOTA;   
235             return(&result);
236          }
237
238          result.status = Q_OK;   
239          result.getquota_rslt_u.gqr_rquota.rq_active = (err == 0) ? TRUE : FALSE;
240          /*
241           * Make a copy of the info into the last part of the remote quota
242           * struct might not be exactly the same on all architectures...
243           */
244
245          rquota = &result.getquota_rslt_u.gqr_rquota;
246          rquota->rq_bhardlimit = dqb.dqb_bhardlimit;
247          rquota->rq_bsoftlimit = dqb.dqb_bsoftlimit;;
248          rquota->rq_curblocks = dqb.dqb_curspace/1024;
249          rquota->rq_fhardlimit = dqb.dqb_ihardlimit;
250          rquota->rq_fsoftlimit = dqb.dqb_isoftlimit;
251          rquota->rq_curfiles = dqb.dqb_curinodes;
252          rquota->rq_btimeleft = dqb.dqb_btime;
253          rquota->rq_ftimeleft = dqb.dqb_itime;
254
255          return(&result);
256       }
257    }
258    endmntent(fp);
259
260    result.status = Q_NOQUOTA;   
261    return(&result);
262 }
263
264 getquota_rslt *rquotaproc_getquota_1(getquota_args *argp, struct svc_req *rqstp)
265 {
266    return(getquotainfo(0, (caddr_t *)argp, rqstp));
267 }
268
269 getquota_rslt *rquotaproc_getactivequota_1(getquota_args *argp, struct svc_req *rqstp)
270 {
271    return(getquotainfo(ACTIVE, (caddr_t *)argp, rqstp));
272 }
273
274 getquota_rslt *rquotaproc_getquota_2(ext_getquota_args *argp, struct svc_req *rqstp)
275 {
276    return(getquotainfo(TYPE_EXTENDED, (caddr_t *)argp, rqstp));
277 }
278
279 getquota_rslt *rquotaproc_getactivequota_2(ext_getquota_args *argp, struct svc_req *rqstp)
280 {
281    return(getquotainfo(TYPE_EXTENDED | ACTIVE, (caddr_t *)argp, rqstp));
282 }