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