08c4f8c8c9eb750069829c879e96e449589226ea
[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 #include "config.h"
22
23 #include <rpc/rpc.h>
24 #include "rquota.h"
25 #include <sys/file.h>
26 #include <sys/stat.h>
27 #include <sys/param.h>
28 #include <sys/quota.h>
29 #include <dirent.h>
30 #include <paths.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include "mntent.h"
35 #include "xmalloc.h"
36
37 #define TYPE_EXTENDED   0x01
38 #define ACTIVE          0x02
39
40 #ifdef ELM
41 #define _PATH_DEV_DSK   "/dev/dsk/"
42 #else
43 #define _PATH_DEV_DSK   "/dev/"
44 #endif
45
46 /*
47  * Global unix authentication credentials.
48  */
49 extern struct authunix_parms *unix_cred;
50
51 char *nfsmount_to_devname(char *pathname, int *blksize)
52 {
53    DIR *dp;
54    dev_t device;
55    struct stat st;
56    struct dirent *de;
57    static char *devicename = NULL;
58    static int devicelen = 0;
59
60    if (stat(pathname, &st) == -1)
61       return (char *)0;
62
63    device = st.st_dev;
64    *blksize = st.st_blksize;
65
66    /*
67     * search for devicename in _PATH_DEV_DSK dir.
68     */
69    if ((dp = opendir(_PATH_DEV_DSK)) == (DIR *)0)
70       return (char *)0;
71
72    while ((de = readdir(dp)) != (struct dirent *)0) {
73       if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
74          continue;
75
76       if (devicelen == 0) {
77          devicelen = sizeof (_PATH_DEV_DSK) + strlen (de->d_name) + 1;
78          devicename = (char *) xmalloc (devicelen);
79       }
80       else {
81           int newlen = sizeof (_PATH_DEV_DSK) + strlen (de->d_name) + 1;
82           if (newlen > devicelen) {
83               devicelen = newlen;
84               devicename = (char *) xrealloc (devicename, devicelen);
85           }
86       }
87
88       strcpy(devicename, _PATH_DEV_DSK);
89       strcat(devicename, de->d_name);
90       if (stat(devicename, &st) == -1)
91          continue;
92
93       if (!S_ISBLK(st.st_mode))
94          continue;
95
96       if ((device == st.st_rdev) && S_ISBLK(st.st_mode))
97          break;
98    }
99    closedir(dp);
100
101    if (de != (struct dirent *)0) {
102       return devicename;
103    } else
104       return (char *)0;
105 }
106
107 int in_group (gid_t *gids, u_int len, gid_t gid)
108 {
109    int cnt = 0;
110
111    while (cnt < len) {
112       if (gids[cnt] == gid)
113          return 1;
114       cnt++;
115    }
116    return 0;
117 }
118
119 getquota_rslt *getquotainfo(int flags, caddr_t *argp, struct svc_req *rqstp)
120 {
121    static getquota_rslt result;
122    union {
123       getquota_args *args;
124       ext_getquota_args *ext_args;
125    } arguments;
126    FILE *fp;
127    struct dqblk dq_dqb;
128    struct mntent *mnt;
129    char *pathname, *devicename, *qfpathname;
130    int fd, err, id, type;
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    /*
164     * Convert a nfs_mountpoint to a local devicename.
165     */
166    if ((devicename = nfsmount_to_devname(pathname,
167         &result.getquota_rslt_u.gqr_rquota.rq_bsize)) == (char *)0) {
168       result.status = Q_NOQUOTA;   
169       return(&result);
170    }
171
172    fp = setmntent(MNTTAB, "r");
173    while ((mnt = getmntent(fp)) != (struct mntent *)0) {
174       if (strcmp(devicename, mnt->mnt_fsname))
175          continue;
176
177       if (hasquota(mnt, type, &qfpathname)) {
178          if ((err = quotactl(QCMD(Q_GETQUOTA, type), devicename, id,
179             (caddr_t)&dq_dqb)) == -1 && !(flags & ACTIVE)) {
180             if ((fd = open(qfpathname, O_RDONLY)) < 0)
181             {
182                free(qfpathname);
183                continue;
184             }
185             free(qfpathname);
186             lseek(fd, (long) dqoff(id), L_SET);
187             switch (read(fd, &dq_dqb, sizeof(struct dqblk))) {
188                case 0:/* EOF */
189                   /*
190                    * Convert implicit 0 quota (EOF) into an
191                    * explicit one (zero'ed dqblk)
192                    */
193                   memset((caddr_t)&dq_dqb, 0, sizeof(struct dqblk));
194                   break;
195                case sizeof(struct dqblk):   /* OK */
196                   break;
197                default:   /* ERROR */
198                   close(fd);
199                   continue;
200             }
201             close(fd);
202          }
203          endmntent(fp);
204
205          if (err && (flags & ACTIVE)) {
206             result.status = Q_NOQUOTA;   
207             return(&result);
208          }
209
210          result.status = Q_OK;   
211          result.getquota_rslt_u.gqr_rquota.rq_active = (err == 0) ? TRUE : FALSE;
212          /*
213           * Make a copy of the info into the last part of the remote quota
214           * struct which is exactly the same.
215           */
216          memcpy((caddr_t *)&result.getquota_rslt_u.gqr_rquota.rq_bhardlimit,
217                 (caddr_t *)&dq_dqb, sizeof(struct dqblk));
218
219          return(&result);
220       }
221    }
222    endmntent(fp);
223
224    result.status = Q_NOQUOTA;   
225    return(&result);
226 }
227
228 getquota_rslt *rquotaproc_getquota_1(getquota_args *argp, struct svc_req *rqstp)
229 {
230    return(getquotainfo(0, (caddr_t *)argp, rqstp));
231 }
232
233 getquota_rslt *rquotaproc_getactivequota_1(getquota_args *argp, struct svc_req *rqstp)
234 {
235    return(getquotainfo(ACTIVE, (caddr_t *)argp, rqstp));
236 }
237
238 getquota_rslt *rquotaproc_getquota_2(ext_getquota_args *argp, struct svc_req *rqstp)
239 {
240    return(getquotainfo(TYPE_EXTENDED, (caddr_t *)argp, rqstp));
241 }
242
243 getquota_rslt *rquotaproc_getactivequota_2(ext_getquota_args *argp, struct svc_req *rqstp)
244 {
245    return(getquotainfo(TYPE_EXTENDED | ACTIVE, (caddr_t *)argp, rqstp));
246 }