1 |
|
|
/* $OpenBSD: quota.c,v 1.38 2016/03/16 15:41:11 krw Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 1980, 1990, 1993 |
5 |
|
|
* The Regents of the University of California. All rights reserved. |
6 |
|
|
* |
7 |
|
|
* This code is derived from software contributed to Berkeley by |
8 |
|
|
* Robert Elz at The University of Melbourne. |
9 |
|
|
* |
10 |
|
|
* Redistribution and use in source and binary forms, with or without |
11 |
|
|
* modification, are permitted provided that the following conditions |
12 |
|
|
* are met: |
13 |
|
|
* 1. Redistributions of source code must retain the above copyright |
14 |
|
|
* notice, this list of conditions and the following disclaimer. |
15 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
16 |
|
|
* notice, this list of conditions and the following disclaimer in the |
17 |
|
|
* documentation and/or other materials provided with the distribution. |
18 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
19 |
|
|
* may be used to endorse or promote products derived from this software |
20 |
|
|
* without specific prior written permission. |
21 |
|
|
* |
22 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
23 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
26 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
28 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
29 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
31 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 |
|
|
* SUCH DAMAGE. |
33 |
|
|
*/ |
34 |
|
|
|
35 |
|
|
/* |
36 |
|
|
* Disk quota reporting program. |
37 |
|
|
*/ |
38 |
|
|
#include <sys/param.h> /* DEV_BSIZE dbtob */ |
39 |
|
|
#include <sys/types.h> |
40 |
|
|
#include <sys/file.h> |
41 |
|
|
#include <sys/stat.h> |
42 |
|
|
#include <sys/mount.h> |
43 |
|
|
#include <sys/socket.h> |
44 |
|
|
|
45 |
|
|
#include <ufs/ufs/quota.h> |
46 |
|
|
#include <ctype.h> |
47 |
|
|
#include <err.h> |
48 |
|
|
#include <errno.h> |
49 |
|
|
#include <fstab.h> |
50 |
|
|
#include <grp.h> |
51 |
|
|
#include <netdb.h> |
52 |
|
|
#include <pwd.h> |
53 |
|
|
#include <stdio.h> |
54 |
|
|
#include <stdlib.h> |
55 |
|
|
#include <string.h> |
56 |
|
|
#include <time.h> |
57 |
|
|
#include <unistd.h> |
58 |
|
|
|
59 |
|
|
#include <rpc/rpc.h> |
60 |
|
|
#include <rpc/pmap_prot.h> |
61 |
|
|
#include <rpcsvc/rquota.h> |
62 |
|
|
|
63 |
|
|
char *qfname = QUOTAFILENAME; |
64 |
|
|
char *qfextension[] = INITQFNAMES; |
65 |
|
|
|
66 |
|
|
struct quotause { |
67 |
|
|
struct quotause *next; |
68 |
|
|
long flags; |
69 |
|
|
struct dqblk dqblk; |
70 |
|
|
char fsname[PATH_MAX + 1]; |
71 |
|
|
}; |
72 |
|
|
#define FOUND 0x01 |
73 |
|
|
|
74 |
|
|
int alldigits(char *); |
75 |
|
|
int callaurpc(char *, int, int, int, xdrproc_t, void *, xdrproc_t, void *); |
76 |
|
|
int getnfsquota(struct statfs *, struct fstab *, struct quotause *, |
77 |
|
|
long, int); |
78 |
|
|
struct quotause |
79 |
|
|
*getprivs(long id, int quotatype); |
80 |
|
|
int getufsquota(struct statfs *, struct fstab *, struct quotause *, |
81 |
|
|
long, int); |
82 |
|
|
void heading(int, u_long, const char *, const char *); |
83 |
|
|
void showgid(gid_t); |
84 |
|
|
void showgrpname(const char *); |
85 |
|
|
void showquotas(int, u_long, const char *); |
86 |
|
|
void showuid(uid_t); |
87 |
|
|
void showusrname(const char *); |
88 |
|
|
char *timeprt(time_t seconds); |
89 |
|
|
int ufshasquota(struct fstab *, int, char **); |
90 |
|
|
void usage(void); |
91 |
|
|
|
92 |
|
|
int qflag; |
93 |
|
|
int vflag; |
94 |
|
|
|
95 |
|
|
int |
96 |
|
|
main(int argc, char *argv[]) |
97 |
|
|
{ |
98 |
|
|
int ngroups; |
99 |
|
|
gid_t mygid, gidset[NGROUPS_MAX]; |
100 |
|
|
int i, gflag = 0, uflag = 0; |
101 |
|
|
int ch; |
102 |
|
|
extern char *optarg; |
103 |
|
|
extern int optind; |
104 |
|
|
|
105 |
|
|
while ((ch = getopt(argc, argv, "ugvq")) != -1) { |
106 |
|
|
switch(ch) { |
107 |
|
|
case 'g': |
108 |
|
|
gflag = 1; |
109 |
|
|
break; |
110 |
|
|
case 'u': |
111 |
|
|
uflag = 1; |
112 |
|
|
break; |
113 |
|
|
case 'v': |
114 |
|
|
vflag = 1; |
115 |
|
|
break; |
116 |
|
|
case 'q': |
117 |
|
|
qflag = 1; |
118 |
|
|
break; |
119 |
|
|
default: |
120 |
|
|
usage(); |
121 |
|
|
} |
122 |
|
|
} |
123 |
|
|
argc -= optind; |
124 |
|
|
argv += optind; |
125 |
|
|
if (!uflag && !gflag) |
126 |
|
|
uflag = 1; |
127 |
|
|
if (argc == 0) { |
128 |
|
|
if (uflag) |
129 |
|
|
showuid(getuid()); |
130 |
|
|
if (gflag) { |
131 |
|
|
mygid = getgid(); |
132 |
|
|
ngroups = getgroups(NGROUPS_MAX, gidset); |
133 |
|
|
if (ngroups < 0) |
134 |
|
|
err(1, "getgroups"); |
135 |
|
|
showgid(mygid); |
136 |
|
|
for (i = 0; i < ngroups; i++) |
137 |
|
|
if (gidset[i] != mygid) |
138 |
|
|
showgid(gidset[i]); |
139 |
|
|
} |
140 |
|
|
exit(0); |
141 |
|
|
} |
142 |
|
|
if (uflag && gflag) |
143 |
|
|
usage(); |
144 |
|
|
if (uflag) { |
145 |
|
|
for (; argc > 0; argc--, argv++) { |
146 |
|
|
if (alldigits(*argv)) |
147 |
|
|
showuid(atoi(*argv)); |
148 |
|
|
else |
149 |
|
|
showusrname(*argv); |
150 |
|
|
} |
151 |
|
|
exit(0); |
152 |
|
|
} |
153 |
|
|
if (gflag) { |
154 |
|
|
for (; argc > 0; argc--, argv++) { |
155 |
|
|
if (alldigits(*argv)) |
156 |
|
|
showgid(atoi(*argv)); |
157 |
|
|
else |
158 |
|
|
showgrpname(*argv); |
159 |
|
|
} |
160 |
|
|
exit(0); |
161 |
|
|
} |
162 |
|
|
/* NOTREACHED */ |
163 |
|
|
|
164 |
|
|
exit(1); |
165 |
|
|
} |
166 |
|
|
|
167 |
|
|
void |
168 |
|
|
usage(void) |
169 |
|
|
{ |
170 |
|
|
fprintf(stderr, "%s\n%s\n%s\n", |
171 |
|
|
"usage: quota [-q | -v] [-gu]", |
172 |
|
|
" quota [-q | -v] -g group ...", |
173 |
|
|
" quota [-q | -v] -u user ..."); |
174 |
|
|
exit(1); |
175 |
|
|
} |
176 |
|
|
|
177 |
|
|
/* |
178 |
|
|
* Print out quotas for a specified user identifier. |
179 |
|
|
*/ |
180 |
|
|
void |
181 |
|
|
showuid(uid_t uid) |
182 |
|
|
{ |
183 |
|
|
struct passwd *pwd = getpwuid(uid); |
184 |
|
|
uid_t myuid; |
185 |
|
|
const char *name; |
186 |
|
|
|
187 |
|
|
if (pwd == NULL) |
188 |
|
|
name = "(no account)"; |
189 |
|
|
else |
190 |
|
|
name = pwd->pw_name; |
191 |
|
|
myuid = getuid(); |
192 |
|
|
if (uid != myuid && myuid != 0) { |
193 |
|
|
warnx("%s (uid %u): permission denied", name, uid); |
194 |
|
|
return; |
195 |
|
|
} |
196 |
|
|
showquotas(USRQUOTA, uid, name); |
197 |
|
|
} |
198 |
|
|
|
199 |
|
|
/* |
200 |
|
|
* Print out quotas for a specified user name. |
201 |
|
|
*/ |
202 |
|
|
void |
203 |
|
|
showusrname(const char *name) |
204 |
|
|
{ |
205 |
|
|
struct passwd *pwd = getpwnam(name); |
206 |
|
|
uid_t myuid; |
207 |
|
|
|
208 |
|
|
if (pwd == NULL) { |
209 |
|
|
warnx("%s: unknown user", name); |
210 |
|
|
return; |
211 |
|
|
} |
212 |
|
|
myuid = getuid(); |
213 |
|
|
if (pwd->pw_uid != myuid && myuid != 0) { |
214 |
|
|
warnx("%s (uid %u): permission denied", pwd->pw_name, |
215 |
|
|
pwd->pw_uid); |
216 |
|
|
return; |
217 |
|
|
} |
218 |
|
|
showquotas(USRQUOTA, pwd->pw_uid, pwd->pw_name); |
219 |
|
|
} |
220 |
|
|
|
221 |
|
|
/* |
222 |
|
|
* Print out quotas for a specified group identifier. |
223 |
|
|
*/ |
224 |
|
|
void |
225 |
|
|
showgid(gid_t gid) |
226 |
|
|
{ |
227 |
|
|
struct group *grp = getgrgid(gid); |
228 |
|
|
int ngroups; |
229 |
|
|
gid_t mygid, gidset[NGROUPS_MAX]; |
230 |
|
|
int i; |
231 |
|
|
const char *name; |
232 |
|
|
|
233 |
|
|
if (grp == NULL) |
234 |
|
|
name = "(no entry)"; |
235 |
|
|
else |
236 |
|
|
name = grp->gr_name; |
237 |
|
|
mygid = getgid(); |
238 |
|
|
ngroups = getgroups(NGROUPS_MAX, gidset); |
239 |
|
|
if (ngroups < 0) { |
240 |
|
|
warn("getgroups"); |
241 |
|
|
return; |
242 |
|
|
} |
243 |
|
|
if (gid != mygid) { |
244 |
|
|
for (i = 0; i < ngroups; i++) |
245 |
|
|
if (gid == gidset[i]) |
246 |
|
|
break; |
247 |
|
|
if (i >= ngroups && getuid() != 0) { |
248 |
|
|
warnx("%s (gid %u): permission denied", name, gid); |
249 |
|
|
return; |
250 |
|
|
} |
251 |
|
|
} |
252 |
|
|
showquotas(GRPQUOTA, gid, name); |
253 |
|
|
} |
254 |
|
|
|
255 |
|
|
/* |
256 |
|
|
* Print out quotas for a specified group name. |
257 |
|
|
*/ |
258 |
|
|
void |
259 |
|
|
showgrpname(const char *name) |
260 |
|
|
{ |
261 |
|
|
struct group *grp = getgrnam(name); |
262 |
|
|
int ngroups; |
263 |
|
|
gid_t mygid, gidset[NGROUPS_MAX]; |
264 |
|
|
int i; |
265 |
|
|
|
266 |
|
|
if (grp == NULL) { |
267 |
|
|
warnx("%s: unknown group", name); |
268 |
|
|
return; |
269 |
|
|
} |
270 |
|
|
mygid = getgid(); |
271 |
|
|
ngroups = getgroups(NGROUPS_MAX, gidset); |
272 |
|
|
if (ngroups < 0) { |
273 |
|
|
warn("getgroups"); |
274 |
|
|
return; |
275 |
|
|
} |
276 |
|
|
if (grp->gr_gid != mygid) { |
277 |
|
|
for (i = 0; i < ngroups; i++) |
278 |
|
|
if (grp->gr_gid == gidset[i]) |
279 |
|
|
break; |
280 |
|
|
if (i >= ngroups && getuid() != 0) { |
281 |
|
|
warnx("%s (gid %u): permission denied", |
282 |
|
|
grp->gr_name, grp->gr_gid); |
283 |
|
|
return; |
284 |
|
|
} |
285 |
|
|
} |
286 |
|
|
showquotas(GRPQUOTA, grp->gr_gid, grp->gr_name); |
287 |
|
|
} |
288 |
|
|
|
289 |
|
|
void |
290 |
|
|
showquotas(int type, u_long id, const char *name) |
291 |
|
|
{ |
292 |
|
|
struct quotause *qup; |
293 |
|
|
struct quotause *quplist; |
294 |
|
|
char *msgi, *msgb, *nam; |
295 |
|
|
uid_t lines = 0; |
296 |
|
|
static time_t now; |
297 |
|
|
|
298 |
|
|
if (now == 0) |
299 |
|
|
time(&now); |
300 |
|
|
quplist = getprivs(id, type); |
301 |
|
|
for (qup = quplist; qup; qup = qup->next) { |
302 |
|
|
if (!vflag && |
303 |
|
|
qup->dqblk.dqb_isoftlimit == 0 && |
304 |
|
|
qup->dqblk.dqb_ihardlimit == 0 && |
305 |
|
|
qup->dqblk.dqb_bsoftlimit == 0 && |
306 |
|
|
qup->dqblk.dqb_bhardlimit == 0) |
307 |
|
|
continue; |
308 |
|
|
msgi = NULL; |
309 |
|
|
if (qup->dqblk.dqb_ihardlimit && |
310 |
|
|
qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_ihardlimit) |
311 |
|
|
msgi = "File limit reached on"; |
312 |
|
|
else if (qup->dqblk.dqb_isoftlimit && |
313 |
|
|
qup->dqblk.dqb_curinodes >= qup->dqblk.dqb_isoftlimit) { |
314 |
|
|
if (qup->dqblk.dqb_itime > now) |
315 |
|
|
msgi = "In file grace period on"; |
316 |
|
|
else |
317 |
|
|
msgi = "Over file quota on"; |
318 |
|
|
} |
319 |
|
|
msgb = NULL; |
320 |
|
|
if (qup->dqblk.dqb_bhardlimit && |
321 |
|
|
qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bhardlimit) |
322 |
|
|
msgb = "Block limit reached on"; |
323 |
|
|
else if (qup->dqblk.dqb_bsoftlimit && |
324 |
|
|
qup->dqblk.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit) { |
325 |
|
|
if (qup->dqblk.dqb_btime > now) |
326 |
|
|
msgb = "In block grace period on"; |
327 |
|
|
else |
328 |
|
|
msgb = "Over block quota on"; |
329 |
|
|
} |
330 |
|
|
if (qflag) { |
331 |
|
|
if ((msgi != NULL || msgb != NULL) && |
332 |
|
|
lines++ == 0) |
333 |
|
|
heading(type, id, name, ""); |
334 |
|
|
if (msgi != NULL) |
335 |
|
|
printf("\t%s %s\n", msgi, qup->fsname); |
336 |
|
|
if (msgb != NULL) |
337 |
|
|
printf("\t%s %s\n", msgb, qup->fsname); |
338 |
|
|
continue; |
339 |
|
|
} |
340 |
|
|
if (vflag || |
341 |
|
|
qup->dqblk.dqb_curblocks || |
342 |
|
|
qup->dqblk.dqb_curinodes) { |
343 |
|
|
if (lines++ == 0) |
344 |
|
|
heading(type, id, name, ""); |
345 |
|
|
nam = qup->fsname; |
346 |
|
|
if (strlen(qup->fsname) > 15) { |
347 |
|
|
printf("%s\n", qup->fsname); |
348 |
|
|
nam = ""; |
349 |
|
|
} |
350 |
|
|
printf("%12s %7d%c %7d %7d %7s", |
351 |
|
|
nam, |
352 |
|
|
(int)(dbtob((u_quad_t)qup->dqblk.dqb_curblocks) |
353 |
|
|
/ 1024), |
354 |
|
|
(msgb == NULL) ? ' ' : '*', |
355 |
|
|
(int)(dbtob((u_quad_t)qup->dqblk.dqb_bsoftlimit) |
356 |
|
|
/ 1024), |
357 |
|
|
(int)(dbtob((u_quad_t)qup->dqblk.dqb_bhardlimit) |
358 |
|
|
/ 1024), |
359 |
|
|
(msgb == NULL) ? "" |
360 |
|
|
: timeprt(qup->dqblk.dqb_btime)); |
361 |
|
|
printf(" %7d%c %7d %7d %7s\n", |
362 |
|
|
qup->dqblk.dqb_curinodes, |
363 |
|
|
(msgi == NULL) ? ' ' : '*', |
364 |
|
|
qup->dqblk.dqb_isoftlimit, |
365 |
|
|
qup->dqblk.dqb_ihardlimit, |
366 |
|
|
(msgi == NULL) ? "" |
367 |
|
|
: timeprt(qup->dqblk.dqb_itime) |
368 |
|
|
); |
369 |
|
|
continue; |
370 |
|
|
} |
371 |
|
|
} |
372 |
|
|
if (!qflag && lines == 0) |
373 |
|
|
heading(type, id, name, "none"); |
374 |
|
|
} |
375 |
|
|
|
376 |
|
|
void |
377 |
|
|
heading(int type, u_long id, const char *name, const char *tag) |
378 |
|
|
{ |
379 |
|
|
|
380 |
|
|
printf("Disk quotas for %s %s (%cid %ld): %s\n", qfextension[type], |
381 |
|
|
name, *qfextension[type], id, tag); |
382 |
|
|
if (!qflag && tag[0] == '\0') { |
383 |
|
|
printf("%12s%8s%9s%8s%8s%9s%8s%8s%8s\n", |
384 |
|
|
"Filesystem", |
385 |
|
|
"KBytes", |
386 |
|
|
"quota", |
387 |
|
|
"limit", |
388 |
|
|
"grace", |
389 |
|
|
"files", |
390 |
|
|
"quota", |
391 |
|
|
"limit", |
392 |
|
|
"grace"); |
393 |
|
|
} |
394 |
|
|
} |
395 |
|
|
|
396 |
|
|
/* |
397 |
|
|
* Calculate the grace period and return a printable string for it. |
398 |
|
|
*/ |
399 |
|
|
char * |
400 |
|
|
timeprt(time_t seconds) |
401 |
|
|
{ |
402 |
|
|
time_t hours, minutes; |
403 |
|
|
static char buf[20]; |
404 |
|
|
static time_t now; |
405 |
|
|
|
406 |
|
|
if (now == 0) |
407 |
|
|
time(&now); |
408 |
|
|
if (now > seconds) |
409 |
|
|
return ("none"); |
410 |
|
|
seconds -= now; |
411 |
|
|
minutes = (seconds + 30) / 60; |
412 |
|
|
hours = (minutes + 30) / 60; |
413 |
|
|
if (hours >= 36) { |
414 |
|
|
(void)snprintf(buf, sizeof buf, "%ddays", |
415 |
|
|
(int)((hours + 12) / 24)); |
416 |
|
|
return (buf); |
417 |
|
|
} |
418 |
|
|
if (minutes >= 60) { |
419 |
|
|
(void)snprintf(buf, sizeof buf, "%2d:%d", |
420 |
|
|
(int)(minutes / 60), (int)(minutes % 60)); |
421 |
|
|
return (buf); |
422 |
|
|
} |
423 |
|
|
(void)snprintf(buf, sizeof buf, "%2d", (int)minutes); |
424 |
|
|
return (buf); |
425 |
|
|
} |
426 |
|
|
|
427 |
|
|
/* |
428 |
|
|
* Collect the requested quota information. |
429 |
|
|
*/ |
430 |
|
|
struct quotause * |
431 |
|
|
getprivs(long id, int quotatype) |
432 |
|
|
{ |
433 |
|
|
struct quotause *qup, *quptail; |
434 |
|
|
struct fstab *fs; |
435 |
|
|
struct quotause *quphead; |
436 |
|
|
struct statfs *fst; |
437 |
|
|
int nfst, i; |
438 |
|
|
|
439 |
|
|
qup = quphead = NULL; |
440 |
|
|
|
441 |
|
|
nfst = getmntinfo(&fst, MNT_WAIT); |
442 |
|
|
if (nfst == 0) |
443 |
|
|
errx(2, "no filesystems mounted!"); |
444 |
|
|
setfsent(); |
445 |
|
|
for (i = 0; i < nfst; i++) { |
446 |
|
|
if (qup == NULL) { |
447 |
|
|
if ((qup = malloc(sizeof *qup)) == NULL) |
448 |
|
|
errx(2, "out of memory"); |
449 |
|
|
} |
450 |
|
|
if (strncmp(fst[i].f_fstypename, "nfs", MFSNAMELEN) == 0) { |
451 |
|
|
if (getnfsquota(&fst[i], NULL, qup, id, quotatype) == 0) |
452 |
|
|
continue; |
453 |
|
|
} else if (!strncmp(fst[i].f_fstypename, "ffs", MFSNAMELEN) || |
454 |
|
|
!strncmp(fst[i].f_fstypename, "ufs", MFSNAMELEN) || |
455 |
|
|
!strncmp(fst[i].f_fstypename, "mfs", MFSNAMELEN)) { |
456 |
|
|
/* |
457 |
|
|
* XXX |
458 |
|
|
* UFS filesystems must be in /etc/fstab, and must |
459 |
|
|
* indicate that they have quotas on (?!) This is quite |
460 |
|
|
* unlike SunOS where quotas can be enabled/disabled |
461 |
|
|
* on a filesystem independent of /etc/fstab, and it |
462 |
|
|
* will still print quotas for them. |
463 |
|
|
*/ |
464 |
|
|
if ((fs = getfsspec(fst[i].f_mntfromspec)) == NULL) |
465 |
|
|
continue; |
466 |
|
|
if (getufsquota(&fst[i], fs, qup, id, quotatype) == 0) |
467 |
|
|
continue; |
468 |
|
|
} else |
469 |
|
|
continue; |
470 |
|
|
strncpy(qup->fsname, fst[i].f_mntonname, sizeof qup->fsname-1); |
471 |
|
|
qup->fsname[sizeof qup->fsname-1] = '\0'; |
472 |
|
|
if (quphead == NULL) |
473 |
|
|
quphead = qup; |
474 |
|
|
else |
475 |
|
|
quptail->next = qup; |
476 |
|
|
quptail = qup; |
477 |
|
|
quptail->next = 0; |
478 |
|
|
qup = NULL; |
479 |
|
|
} |
480 |
|
|
free(qup); |
481 |
|
|
endfsent(); |
482 |
|
|
return (quphead); |
483 |
|
|
} |
484 |
|
|
|
485 |
|
|
/* |
486 |
|
|
* Check to see if a particular quota is to be enabled. |
487 |
|
|
*/ |
488 |
|
|
int |
489 |
|
|
ufshasquota(struct fstab *fs, int type, char **qfnamep) |
490 |
|
|
{ |
491 |
|
|
static char initname, usrname[100], grpname[100]; |
492 |
|
|
static char buf[BUFSIZ]; |
493 |
|
|
char *opt, *cp; |
494 |
|
|
|
495 |
|
|
cp = NULL; |
496 |
|
|
if (!initname) { |
497 |
|
|
(void)snprintf(usrname, sizeof usrname, "%s%s", |
498 |
|
|
qfextension[USRQUOTA], qfname); |
499 |
|
|
(void)snprintf(grpname, sizeof grpname, "%s%s", |
500 |
|
|
qfextension[GRPQUOTA], qfname); |
501 |
|
|
initname = 1; |
502 |
|
|
} |
503 |
|
|
strncpy(buf, fs->fs_mntops, sizeof buf); |
504 |
|
|
buf[sizeof(buf) - 1] = '\0'; |
505 |
|
|
for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { |
506 |
|
|
if ((cp = strchr(opt, '='))) |
507 |
|
|
*cp++ = '\0'; |
508 |
|
|
if (type == USRQUOTA && strcmp(opt, usrname) == 0) |
509 |
|
|
break; |
510 |
|
|
if (type == GRPQUOTA && strcmp(opt, grpname) == 0) |
511 |
|
|
break; |
512 |
|
|
} |
513 |
|
|
if (!opt) |
514 |
|
|
return (0); |
515 |
|
|
if (cp) { |
516 |
|
|
*qfnamep = cp; |
517 |
|
|
return (1); |
518 |
|
|
} |
519 |
|
|
(void)snprintf(buf, sizeof buf, "%s/%s.%s", |
520 |
|
|
fs->fs_file, qfname, qfextension[type]); |
521 |
|
|
*qfnamep = buf; |
522 |
|
|
return (1); |
523 |
|
|
} |
524 |
|
|
|
525 |
|
|
int |
526 |
|
|
getufsquota(struct statfs *fst, struct fstab *fs, struct quotause *qup, |
527 |
|
|
long id, int quotatype) |
528 |
|
|
{ |
529 |
|
|
char *qfpathname; |
530 |
|
|
int fd, qcmd; |
531 |
|
|
|
532 |
|
|
qcmd = QCMD(Q_GETQUOTA, quotatype); |
533 |
|
|
if (!ufshasquota(fs, quotatype, &qfpathname)) |
534 |
|
|
return (0); |
535 |
|
|
|
536 |
|
|
if (quotactl(fs->fs_file, qcmd, id, (char *)&qup->dqblk) != 0) { |
537 |
|
|
if ((fd = open(qfpathname, O_RDONLY)) < 0) { |
538 |
|
|
warn("%s", qfpathname); |
539 |
|
|
return (0); |
540 |
|
|
} |
541 |
|
|
(void)lseek(fd, (off_t)(id * sizeof(struct dqblk)), SEEK_SET); |
542 |
|
|
switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) { |
543 |
|
|
case 0: /* EOF */ |
544 |
|
|
/* |
545 |
|
|
* Convert implicit 0 quota (EOF) |
546 |
|
|
* into an explicit one (zero'ed dqblk) |
547 |
|
|
*/ |
548 |
|
|
memset((caddr_t)&qup->dqblk, 0, sizeof(struct dqblk)); |
549 |
|
|
break; |
550 |
|
|
case sizeof(struct dqblk): /* OK */ |
551 |
|
|
break; |
552 |
|
|
default: /* ERROR */ |
553 |
|
|
warn("%s", qfpathname); |
554 |
|
|
close(fd); |
555 |
|
|
return (0); |
556 |
|
|
} |
557 |
|
|
close(fd); |
558 |
|
|
} |
559 |
|
|
return (1); |
560 |
|
|
} |
561 |
|
|
|
562 |
|
|
int |
563 |
|
|
getnfsquota(struct statfs *fst, struct fstab *fs, struct quotause *qup, |
564 |
|
|
long id, int quotatype) |
565 |
|
|
{ |
566 |
|
|
struct getquota_args gq_args; |
567 |
|
|
struct getquota_rslt gq_rslt; |
568 |
|
|
struct dqblk *dqp = &qup->dqblk; |
569 |
|
|
struct timeval tv; |
570 |
|
|
char *cp; |
571 |
|
|
|
572 |
|
|
if (fst->f_flags & MNT_LOCAL) |
573 |
|
|
return (0); |
574 |
|
|
|
575 |
|
|
/* |
576 |
|
|
* rpc.rquotad does not support group quotas |
577 |
|
|
*/ |
578 |
|
|
if (quotatype != USRQUOTA) |
579 |
|
|
return (0); |
580 |
|
|
|
581 |
|
|
/* |
582 |
|
|
* must be some form of "hostname:/path" |
583 |
|
|
*/ |
584 |
|
|
cp = strchr(fst->f_mntfromname, ':'); |
585 |
|
|
if (cp == NULL) { |
586 |
|
|
warnx("cannot find hostname for %s", fst->f_mntfromname); |
587 |
|
|
return (0); |
588 |
|
|
} |
589 |
|
|
|
590 |
|
|
*cp = '\0'; |
591 |
|
|
if (cp[1] != '/') { |
592 |
|
|
*cp = ':'; |
593 |
|
|
return (0); |
594 |
|
|
} |
595 |
|
|
|
596 |
|
|
gq_args.gqa_pathp = &cp[1]; |
597 |
|
|
gq_args.gqa_uid = id; |
598 |
|
|
if (callaurpc(fst->f_mntfromname, RQUOTAPROG, RQUOTAVERS, |
599 |
|
|
RQUOTAPROC_GETQUOTA, xdr_getquota_args, &gq_args, |
600 |
|
|
xdr_getquota_rslt, &gq_rslt) != 0) { |
601 |
|
|
*cp = ':'; |
602 |
|
|
return (0); |
603 |
|
|
} |
604 |
|
|
|
605 |
|
|
switch (gq_rslt.status) { |
606 |
|
|
case Q_NOQUOTA: |
607 |
|
|
break; |
608 |
|
|
case Q_EPERM: |
609 |
|
|
warnx("permission error, host: %s", fst->f_mntfromname); |
610 |
|
|
break; |
611 |
|
|
case Q_OK: |
612 |
|
|
gettimeofday(&tv, NULL); |
613 |
|
|
/* blocks*/ |
614 |
|
|
dqp->dqb_bhardlimit = |
615 |
|
|
gq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit * |
616 |
|
|
(gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE); |
617 |
|
|
dqp->dqb_bsoftlimit = |
618 |
|
|
gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit * |
619 |
|
|
(gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE); |
620 |
|
|
dqp->dqb_curblocks = |
621 |
|
|
gq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks * |
622 |
|
|
(gq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize / DEV_BSIZE); |
623 |
|
|
/* inodes */ |
624 |
|
|
dqp->dqb_ihardlimit = |
625 |
|
|
gq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit; |
626 |
|
|
dqp->dqb_isoftlimit = |
627 |
|
|
gq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit; |
628 |
|
|
dqp->dqb_curinodes = |
629 |
|
|
gq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles; |
630 |
|
|
/* grace times */ |
631 |
|
|
dqp->dqb_btime = |
632 |
|
|
tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft; |
633 |
|
|
dqp->dqb_itime = |
634 |
|
|
tv.tv_sec + gq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft; |
635 |
|
|
*cp = ':'; |
636 |
|
|
return (1); |
637 |
|
|
default: |
638 |
|
|
warnx("bad rpc result, host: %s", fst->f_mntfromname); |
639 |
|
|
break; |
640 |
|
|
} |
641 |
|
|
*cp = ':'; |
642 |
|
|
return (0); |
643 |
|
|
} |
644 |
|
|
|
645 |
|
|
int |
646 |
|
|
callaurpc(char *host, int prognum, int versnum, int procnum, |
647 |
|
|
xdrproc_t inproc, void *in, xdrproc_t outproc, void *out) |
648 |
|
|
{ |
649 |
|
|
struct sockaddr_in server_addr; |
650 |
|
|
enum clnt_stat clnt_stat; |
651 |
|
|
struct hostent *hp; |
652 |
|
|
struct timeval timeout, tottimeout; |
653 |
|
|
|
654 |
|
|
CLIENT *client = NULL; |
655 |
|
|
int socket = RPC_ANYSOCK; |
656 |
|
|
|
657 |
|
|
if ((hp = gethostbyname(host)) == NULL) |
658 |
|
|
return ((int) RPC_UNKNOWNHOST); |
659 |
|
|
timeout.tv_usec = 0; |
660 |
|
|
timeout.tv_sec = 6; |
661 |
|
|
|
662 |
|
|
memset(&server_addr, 0, sizeof server_addr); |
663 |
|
|
memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length); |
664 |
|
|
server_addr.sin_family = AF_INET; |
665 |
|
|
server_addr.sin_port = 0; |
666 |
|
|
|
667 |
|
|
if ((client = clntudp_create(&server_addr, prognum, |
668 |
|
|
versnum, timeout, &socket)) == NULL) |
669 |
|
|
return ((int) rpc_createerr.cf_stat); |
670 |
|
|
|
671 |
|
|
client->cl_auth = authunix_create_default(); |
672 |
|
|
tottimeout.tv_sec = 25; |
673 |
|
|
tottimeout.tv_usec = 0; |
674 |
|
|
clnt_stat = clnt_call(client, procnum, inproc, in, |
675 |
|
|
outproc, out, tottimeout); |
676 |
|
|
|
677 |
|
|
return ((int) clnt_stat); |
678 |
|
|
} |
679 |
|
|
|
680 |
|
|
int |
681 |
|
|
alldigits(char *s) |
682 |
|
|
{ |
683 |
|
|
int c; |
684 |
|
|
|
685 |
|
|
c = (unsigned char)*s++; |
686 |
|
|
do { |
687 |
|
|
if (!isdigit(c)) |
688 |
|
|
return (0); |
689 |
|
|
} while ((c = (unsigned char)*s++)); |
690 |
|
|
return (1); |
691 |
|
|
} |