1 // written in the D programming language
2
3 module diquota;
4
5 import std.stdio;
6 import std.string;
7 private import std.process : geteuid, getegid;
8 private import core.stdc.errno;
9
10 import config;
11 import options;
12 import diskpart;
13
14 struct diQuota_t {
15 string special;
16 string name;
17 string fsType;
18 Uid_t uid;
19 Gid_t gid;
20 real limit;
21 real used;
22 real ilimit;
23 real iused;
24 }
25
26
27 static if (_cdefine_BLOCK_SIZE) {
28 alias BLOCK_SIZE DI_QUOT_BLOCK_SIZE; /* linux */
29 } else static if (_cdefine_DQBSIZE) {
30 alias DQBSIZE DI_QUOT_BLOCK_SIZE; /* aix */
31 } else static if (_cdefine_DEV_BSIZE) {
32 alias DEV_BSIZE DI_QUOT_BLOCK_SIZE; /* tru64, et. al. */
33 } else {
34 enum size_t DI_QUOT_BLOCK_SIZE = 512;
35 }
36
37 /* rename certain structure members for portability */
38 static if (_cmem_dqblk_dqb_fsoftlimit) {
39 alias dqb_fsoftlimit dqb_isoftlimit;
40 }
41 static if (_cmem_dqblk_dqb_fhardlimit) {
42 alias dqb_fhardlimit dqb_ihardlimit;
43 }
44 static if (_cmem_dqblk_dqb_curfiles) {
45 alias dqb_curfiles dqb_curinodes;
46 }
47
48 void
checkDiskQuotas(ref DiskPartitions dps,Options opts)49 checkDiskQuotas (ref DiskPartitions dps, Options opts)
50 {
51 int i;
52 Uid_t uid;
53 Gid_t gid;
54 diQuota_t diqinfo;
55 real tsize;
56
57 uid = 0;
58 gid = 0;
59 static if (_has_std_quotas) {
60 uid = geteuid ();
61 gid = getegid ();
62 }
63
64 foreach (ref dp; dps.diskPartitions)
65 {
66 // if (! dp.doPrint) {
67 // continue;
68 // }
69
70 diqinfo.uid = uid;
71 diqinfo.gid = gid;
72 diqinfo.name = dp.name;
73 diqinfo.special = dp.special;
74 diqinfo.fsType = dp.fsType;
75
76 diquota (&diqinfo, opts);
77
78 if (opts.debugLevel > 2) {
79 writefln ("quota: %s limit: %f", dp.name, diqinfo.limit);
80 writefln ("quota: tot: %f", dp.totalBlocks);
81 writefln ("quota: %s used: %f", dp.name, diqinfo.used);
82 writefln ("quota: avail: %f", dp.availBlocks);
83 }
84 if (diqinfo.limit != 0 && diqinfo.limit < dp.totalBlocks) {
85 dp.totalBlocks = diqinfo.limit;
86 tsize = diqinfo.limit - diqinfo.used;
87 if (tsize < 0) {
88 tsize = 0;
89 }
90 if (tsize < dp.availBlocks) {
91 dp.availBlocks = tsize;
92 dp.freeBlocks = tsize;
93 if (opts.debugLevel > 2) {
94 writefln ("quota: using quota for: total free avail");
95 }
96 } else if (tsize > dp.availBlocks && tsize < dp.freeBlocks) {
97 dp.freeBlocks = tsize;
98 if (opts.debugLevel > 2) {
99 writefln ("quota: using quota for: total free");
100 }
101 } else {
102 if (opts.debugLevel > 2) {
103 writefln ("quota: using quota for: total");
104 }
105 }
106 }
107
108 if (diqinfo.ilimit != 0 && diqinfo.ilimit < dp.totalInodes) {
109 dp.totalInodes = diqinfo.ilimit;
110 tsize = diqinfo.ilimit - diqinfo.iused;
111 if (tsize < 0) {
112 tsize = 0;
113 }
114 if (tsize < dp.availInodes) {
115 dp.availInodes = tsize;
116 dp.freeInodes = tsize;
117 if (opts.debugLevel > 2) {
118 writefln ("quota: using quota for inodes: total free avail");
119 }
120 } else if (tsize > dp.availInodes && tsize < dp.freeInodes) {
121 dp.freeInodes = tsize;
122 if (opts.debugLevel > 2) {
123 writefln ("quota: using quota for inodes: total free");
124 }
125 } else {
126 if (opts.debugLevel > 2) {
127 writefln ("quota: using quota for inodes: total");
128 }
129 }
130 }
131 }
132 return;
133 }
134
135 void
diquota(diQuota_t * diqinfo,Options opts)136 diquota (diQuota_t *diqinfo, Options opts)
137 {
138 int rc;
139 int xfsflag;
140 static if (_cstruct_dqblk) {
141 C_ST_dqblk qinfo;
142 char *qiptr;
143 }
144 static if (_cstruct_fs_disk_quota) {
145 C_ST_fs_disk_quota xfsqinfo;
146 }
147 static if (_has_std_quotas) {
148 int ucmd;
149 int gcmd;
150 }
151
152 if (opts.debugLevel > 5) {
153 writefln ("quota: diquota %s %s ", diqinfo.name, diqinfo.fsType);
154 }
155 rc = -1;
156 xfsflag = FALSE;
157
158 diqinfo.limit = 0;
159 diqinfo.used = 0;
160 diqinfo.ilimit = 0;
161 diqinfo.iused = 0;
162
163 if (diqinfo.fsType.length >= 3 && diqinfo.fsType[0..3] == "nfs" &&
164 diqinfo.fsType != "nfsd") {
165 if (opts.debugLevel > 5) {
166 writefln ("quota: diquota: nfs");
167 }
168 static if (_has_std_nfs_quotas) {
169 diquota_nfs (diqinfo, opts);
170 }
171 return;
172 }
173 if (diqinfo.fsType == "xfs") {
174 if (opts.debugLevel > 5) {
175 writefln ("quota: diquota: xfs");
176 }
177 static if (_hdr_linux_dqblk_xfs) {
178 ucmd = C_MACRO_QCMD (Q_XGETQUOTA, USRQUOTA);
179 gcmd = C_MACRO_QCMD (Q_XGETQUOTA, GRPQUOTA);
180 qiptr = cast(char *) &xfsqinfo;
181 xfsflag = TRUE;
182 }
183 ;
184 } else {
185 static if (_has_std_quotas) {
186 /* hp-ux doesn't have QCMD */
187 ucmd = Q_GETQUOTA;
188 gcmd = Q_GETQUOTA;
189 }
190 static if (_cmacro_QCMD) {
191 ucmd = C_MACRO_QCMD (Q_GETQUOTA, USRQUOTA);
192 gcmd = C_MACRO_QCMD (Q_GETQUOTA, GRPQUOTA);
193 }
194 static if (_cstruct_dqblk) {
195 qiptr = cast(char *) &qinfo;
196 }
197 }
198
199 static if (_cdefine___FreeBSD__ && _t_FreeBSD__ == 5) {
200 /* quotactl on devfs fs panics the system (FreeBSD 5.1) */
201 if (strcmp (diqinfo.fsType, "ufs") != 0) {
202 return;
203 }
204 }
205
206 if (opts.debugLevel > 5) {
207 writefln ("quota: quotactl on %s (%s)", diqinfo.name, _c_arg_1_quotactl);
208 }
209 static if (_clib_quotactl && _c_arg_1_quotactl == "char *") {
210 rc = quotactl (toStringz(diqinfo.name), ucmd,
211 cast(int) diqinfo.uid, cast(caddr_t) qiptr);
212 }
213 static if (_clib_quotactl && _c_arg_2_quotactl == "char *") {
214 /* AIX has linux compatibility routine, but still need name */
215 static if (_cdefine__AIX) {
216 rc = quotactl (ucmd, toStringz(diqinfo.name),
217 cast(int) diqinfo.uid, cast(caddr_t) qiptr);
218 } else {
219 rc = quotactl (ucmd, toStringz(diqinfo.special),
220 cast(int) diqinfo.uid, cast(caddr_t) qiptr);
221 }
222 }
223 static if (_sys_fs_ufs_quota) { /* Solaris */
224 {
225 int fd;
226 C_ST_quotctl qop;
227 char tname [DI_NAME_LEN];
228
229 qop.op = Q_GETQUOTA;
230 qop.uid = diqinfo.uid;
231 qop.addr = cast(caddr_t) qiptr;
232 strcpy (tname, diqinfo.name);
233 strcat (tname, "/quotas");
234 fd = open (tname, O_RDONLY | O_NOCTTY);
235 if (fd >= 0) {
236 rc = ioctl (fd, Q_QUOTACTL, &qop);
237 close (fd);
238 } else {
239 rc = fd;
240 }
241 }
242 } /* _sys_fs_ufs_quota */
243
244 if (opts.debugLevel > 5) {
245 writefln ("quota: quotactl on %s : rc %d : errno %d", diqinfo.name, rc, errno);
246 }
247 static if (_has_std_quotas) {
248 di_process_quotas ("usr", diqinfo, rc, xfsflag, qiptr, opts);
249
250 static if (_cdefine_GRPQUOTA) {
251 if (rc == 0 || errno != ESRCH) {
252 static if (_clib_quotactl && _c_arg_1_quotactl == "char *") {
253 rc = quotactl (toStringz(diqinfo.name), gcmd,
254 cast(int) diqinfo.gid, cast(caddr_t) qiptr);
255 }
256 static if (_clib_quotactl && _c_arg_1_quotactl != "char *") {
257 rc = quotactl (gcmd, toStringz(diqinfo.special),
258 cast(int) diqinfo.gid, cast(caddr_t) qiptr);
259 }
260
261 di_process_quotas ("grp", diqinfo, rc, xfsflag, qiptr, opts);
262 }
263 } /* _cdefine_GRPQUOTA */
264 } /* _has_std_quotas */
265 }
266
267 static if (_has_std_nfs_quotas) {
268
269 static if (_cdefine_RQ_PATHLEN) {
270 alias RQ_PATHLEN DI_RQ_PATHLEN;
271 } else {
272 enum int DI_RQ_PATHLEN = 1024;
273 }
274
275 extern (C) {
276
277 static C_TYP_bool_t
xdr_quota_get(XDR * xp,C_ST_getquota_args * args)278 xdr_quota_get (XDR *xp, C_ST_getquota_args *args)
279 {
280 if (! xdr_string (xp, &args.gqa_pathp, DI_RQ_PATHLEN)) {
281 return 0;
282 }
283 if (! xdr_gqa_uid (xp, &args.gqa_uid)) {
284 return 0;
285 }
286 return 1;
287 }
288
289 static C_TYP_bool_t
xdr_quota_rslt(XDR * xp,C_ST_getquota_rslt * rslt)290 xdr_quota_rslt (XDR *xp, C_ST_getquota_rslt *rslt)
291 {
292 C_ENUM_gqr_status quotastat;
293 int intquotastat;
294 C_ST_rquota *rptr;
295
296 if (! xdr_int (xp, &intquotastat)) {
297 return 0;
298 }
299 quotastat = cast(C_ENUM_gqr_status) intquotastat;
300 static if (_cmem_getquota_rslt_gqr_status) {
301 rslt.gqr_status = quotastat;
302 } else {
303 rslt.status = quotastat;
304 }
305 rptr = &rslt.gqr_rquota;
306
307 if (! xdr_int (xp, &rptr.rq_bsize)) {
308 return 0;
309 }
310 if (! xdr_bool (xp, &rptr.rq_active)) {
311 return 0;
312 }
313 if (! xdr_rq_bhardlimit (xp, &rptr.rq_bhardlimit)) {
314 return 0;
315 }
316 if (! xdr_rq_bsoftlimit (xp, &rptr.rq_bsoftlimit)) {
317 return 0;
318 }
319 if (! xdr_rq_curblocks (xp, &rptr.rq_curblocks)) {
320 return 0;
321 }
322 if (! xdr_rq_fhardlimit (xp, &rptr.rq_fhardlimit)) {
323 return 0;
324 }
325 if (! xdr_rq_fsoftlimit (xp, &rptr.rq_fsoftlimit)) {
326 return 0;
327 }
328 if (! xdr_rq_curfiles (xp, &rptr.rq_curfiles)) {
329 return 0;
330 }
331 return (1);
332 }
333 } /* extern (C) */
334
335 static void
diquota_nfs(diQuota_t * diqinfo,Options opts)336 diquota_nfs (diQuota_t *diqinfo, Options opts)
337 {
338 C_ST_CLIENT *rqclnt;
339 C_ENUM_clnt_stat clnt_stat;
340 C_ST_timeval timeout;
341 string host;
342 string path;
343 char[] hostz;
344 char[] pathz;
345 C_ST_getquota_args args;
346 C_ST_getquota_rslt result;
347 C_ST_rquota *rptr;
348 int quotastat;
349 real tsize;
350
351 if (opts.debugLevel > 5) {
352 writefln ("quota: diquota_nfs");
353 }
354 timeout.tv_sec = 2;
355 timeout.tv_usec = 0;
356
357 host = diqinfo.special;
358 path = host;
359 hostz = host.dup;
360 auto idx = indexOf (host, ':');
361 if (idx != -1) {
362 hostz[idx] = '\0';
363 ++idx;
364 path = host[idx..$];
365 }
366 pathz = path.dup;
367 pathz ~= '\0';
368 if (opts.debugLevel > 5) {
369 writefln ("quota: nfs: host: %s path: %s", host, path);
370 writefln ("quota: nfs: hostz: %s pathz: %s", hostz, pathz);
371 }
372 args.gqa_pathp = pathz.ptr;
373 args.gqa_uid = cast(C_TYP_gqa_uid) diqinfo.uid;
374
375 rqclnt = clnt_create (hostz.ptr, cast(C_TYP_u_long) RQUOTAPROG,
376 cast(C_TYP_u_long) RQUOTAVERS, toStringz("udp"));
377 if (rqclnt == cast(CLIENT *) null) {
378 if (opts.debugLevel > 2) {
379 writefln ("quota: nfs: create failed %d", errno);
380 }
381 return;
382 }
383 rqclnt.cl_auth = authunix_create_default();
384 clnt_stat = C_MACRO_clnt_call (rqclnt, cast(C_TYP_u_long) RQUOTAPROC_GETQUOTA,
385 cast(xdrproc_t) &xdr_quota_get, cast(caddr_t) &args,
386 cast(xdrproc_t) &xdr_quota_rslt, cast(caddr_t) &result, timeout);
387 if (clnt_stat != C_ENUM_clnt_stat.RPC_SUCCESS) {
388 if (opts.debugLevel > 2) {
389 writefln ("quota: nfs: not success: %d", cast(int) clnt_stat);
390 }
391 if (rqclnt.cl_auth) {
392 C_MACRO_auth_destroy (rqclnt.cl_auth);
393 }
394 C_MACRO_clnt_destroy (rqclnt);
395 return;
396 }
397
398 static if (_cmem_getquota_rslt_gqr_status) {
399 quotastat = result.gqr_status;
400 } else {
401 quotastat = result.status;
402 }
403 if (quotastat == 1) {
404 rptr = &result.gqr_rquota;
405
406 if (opts.debugLevel > 2) {
407 writefln ("quota: nfs: status 1");
408 writefln ("quota: nfs: rq_bsize: %d", rptr.rq_bsize);
409 writefln ("quota: nfs: rq_active: %d", rptr.rq_active);
410 }
411
412 diqinfo.limit = rptr.rq_bhardlimit * rptr.rq_bsize;
413 tsize = rptr.rq_bsoftlimit * rptr.rq_bsize;
414 if (tsize != 0 && tsize < diqinfo.limit) {
415 diqinfo.limit = tsize;
416 }
417 if (diqinfo.limit != 0) {
418 diqinfo.used = rptr.rq_curblocks * rptr.rq_bsize;
419 }
420
421 diqinfo.ilimit = rptr.rq_fhardlimit;
422 tsize = rptr.rq_fsoftlimit;
423 if (tsize != 0 && tsize < diqinfo.ilimit) {
424 diqinfo.ilimit = tsize;
425 }
426 if (diqinfo.ilimit != 0) {
427 diqinfo.iused = rptr.rq_curfiles;
428 }
429 }
430
431 if (rqclnt.cl_auth) {
432 C_MACRO_auth_destroy (rqclnt.cl_auth);
433 }
434 C_MACRO_clnt_destroy (rqclnt);
435 }
436 } /* have rpc headers */
437
438 static if (_has_std_quotas) {
439
440 static void
di_process_quotas(string tag,diQuota_t * diqinfo,int rc,int xfsflag,char * cqinfo,Options opts)441 di_process_quotas (string tag, diQuota_t *diqinfo,
442 int rc, int xfsflag, char *cqinfo, Options opts)
443 {
444 real quotBlockSize = DI_QUOT_BLOCK_SIZE;
445 real tsize;
446 real tlimit;
447 C_ST_dqblk *qinfo;
448 static if (_cstruct_fs_disk_quota) {
449 C_ST_fs_disk_quota *xfsqinfo;
450 }
451
452 if (opts.debugLevel > 5) {
453 writefln ("quota: di_process_quotas");
454 }
455 qinfo = cast(C_ST_dqblk *) cqinfo;
456 if (xfsflag) {
457 static if (_cstruct_fs_disk_quota) {
458 xfsqinfo = cast(C_ST_fs_disk_quota *) cqinfo;
459 }
460 quotBlockSize = 512;
461 }
462
463 if (rc == 0) {
464 tlimit = 0;
465 if (xfsflag) {
466 static if (_cstruct_fs_disk_quota) {
467 tsize = xfsqinfo.d_blk_hardlimit;
468 }
469 ;
470 } else {
471 tsize = qinfo.dqb_bhardlimit;
472 }
473 if (opts.debugLevel > 2) {
474 writefln ("quota: %s %s orig hard: %f", tag, diqinfo.name, tsize);
475 }
476 // OLD: tsize = tsize * quotBlockSize / diqinfo.blockSize;
477 if (tsize != 0 && (tsize < diqinfo.limit || diqinfo.limit == 0)) {
478 diqinfo.limit = tsize;
479 tlimit = tsize;
480 }
481
482 if (xfsflag) {
483 static if (_cstruct_fs_disk_quota) {
484 tsize = xfsqinfo.d_blk_softlimit;
485 }
486 ;
487 } else {
488 tsize = qinfo.dqb_bsoftlimit;
489 }
490 if (opts.debugLevel > 2) {
491 writefln ("quota: %s %s orig soft: %f", tag, diqinfo.name, tsize);
492 }
493 // OLD: tsize = tsize * quotBlockSize / diqinfo.blockSize;
494 if (tsize != 0 && (tsize < diqinfo.limit || diqinfo.limit == 0)) {
495 if (opts.debugLevel > 2) {
496 writefln ("quota: using soft: %f", tsize);
497 }
498 diqinfo.limit = tsize;
499 tlimit = tsize;
500 }
501
502 /* any quota set? */
503 if (tlimit == 0) {
504 if (opts.debugLevel > 2) {
505 writefln ("quota: %s %s no quota", tag, diqinfo.name);
506 }
507 return;
508 }
509
510 static if (_cmem_dqblk_dqb_curspace) {
511 // OLD: tsize = qinfo.dqb_curspace / diqinfo.blockSize;
512 if (tsize > diqinfo.used || diqinfo.used == 0) {
513 diqinfo.used = tsize;
514 }
515 }
516 static if (_cmem_dqblk_dqb_curblocks) {
517 if (xfsflag) {
518 static if (_cstruct_fs_disk_quota) {
519 tsize = xfsqinfo.d_bcount;
520 }
521 ;
522 } else {
523 tsize = qinfo.dqb_curblocks;
524 }
525 // OLD: tsize = tsize * quotBlockSize / diqinfo.blockSize;
526 if (tsize > diqinfo.used || diqinfo.used == 0) {
527 diqinfo.used = tsize;
528 }
529 }
530
531 if (opts.debugLevel > 2) {
532 writefln ("quota: %s %s used: %f limit: %f", tag, diqinfo.name,
533 diqinfo.used, diqinfo.limit);
534 }
535
536 if (xfsflag) {
537 static if (_cstruct_fs_disk_quota) {
538 tsize = xfsqinfo.d_ino_hardlimit;
539 }
540 ;
541 } else {
542 tsize = qinfo.dqb_ihardlimit;
543 }
544 if (tsize != 0 && (tsize < diqinfo.ilimit || diqinfo.ilimit == 0)) {
545 diqinfo.ilimit = tsize;
546 tlimit = tsize;
547 }
548 if (opts.debugLevel > 2) {
549 writefln ("quota: %s %s i hard: %f", tag, diqinfo.name, tsize);
550 }
551
552 if (xfsflag) {
553 static if (_cstruct_fs_disk_quota) {
554 tsize = xfsqinfo.d_ino_softlimit;
555 }
556 ;
557 } else {
558 tsize = qinfo.dqb_isoftlimit;
559 }
560 if (tsize != 0 && (tsize < diqinfo.ilimit || diqinfo.ilimit == 0)) {
561 diqinfo.ilimit = tsize;
562 tlimit = tsize;
563 }
564 if (opts.debugLevel > 2) {
565 writefln ("quota: %s %s i soft: %f", tag, diqinfo.name, tsize);
566 }
567
568 /* any quota set? */
569 if (tlimit == 0) {
570 if (opts.debugLevel > 2) {
571 writefln ("quota: %s %s no inode quota", tag, diqinfo.name);
572 }
573 return;
574 }
575
576 if (xfsflag) {
577 static if (_cstruct_fs_disk_quota) {
578 tsize = xfsqinfo.d_icount;
579 }
580 ;
581 } else {
582 tsize = qinfo.dqb_curinodes;
583 }
584 if (tsize > diqinfo.iused || diqinfo.iused == 0) {
585 diqinfo.iused = tsize;
586 }
587 if (opts.debugLevel > 2) {
588 writefln ("quota: %s %s i used: %f", tag, diqinfo.name, tsize);
589 }
590 } else {
591 if (opts.debugLevel > 2) {
592 writefln ("quota: %s %s errno %d", tag, diqinfo.name, errno);
593 }
594 }
595 }
596 } /* _has_std_quotas */
597