]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/quota.c
xfs_quota: convert time_to_string to use time64_t
[thirdparty/xfsprogs-dev.git] / quota / quota.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0
5aead01d 2/*
da23017d
NS
3 * Copyright (c) 2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5aead01d
NS
5 */
6
29647c8d 7#include <stdbool.h>
6b803e5a 8#include "command.h"
5aead01d
NS
9#include <ctype.h>
10#include <pwd.h>
11#include <grp.h>
12#include "init.h"
13#include "quota.h"
14
15static cmdinfo_t quota_cmd;
16
17static void
18quota_help(void)
19{
20 printf(_(
21"\n"
22" display usage and quota information\n"
23"\n"
24" -g -- display group quota information\n"
25" -p -- display project quota information\n"
26" -u -- display user quota information\n"
27" -b -- display number of blocks used\n"
28" -i -- display number of inodes used\n"
29" -r -- display number of realtime blocks used\n"
30" -h -- report in a human-readable format\n"
1774874a
NS
31" -n -- skip identifier-to-name translations, just report IDs\n"
32" -N -- suppress the initial header\n"
5aead01d
NS
33" -v -- increase verbosity in reporting (also dumps zero values)\n"
34" -f -- send output to a file\n"
35" The (optional) user/group/project can be specified either by name or by\n"
36" number (i.e. uid/gid/projid).\n"
37"\n"));
38}
39
40static int
41quota_mount(
42 FILE *fp,
14f8b681 43 uint32_t id,
5aead01d
NS
44 char *name,
45 uint form,
46 uint type,
47 fs_path_t *mount,
48 uint flags)
49{
50 fs_disk_quota_t d;
219285ad 51 time64_t timer;
5aead01d
NS
52 char *dev = mount->fs_name;
53 char c[8], h[8], s[8];
54 uint qflags;
55 int count;
56
546bedf4 57 xfsquotactl(XFS_QSYNC, dev, type, 0, NULL);
5aead01d
NS
58 if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0)
59 return 0;
60
61 if (!(flags & VERBOSE_FLAG)) {
62 count = 0;
63 if ((form & XFS_BLOCK_QUOTA) && d.d_bcount)
64 count++;
65 if ((form & XFS_INODE_QUOTA) && d.d_icount)
66 count++;
67 if ((form & XFS_RTBLOCK_QUOTA) && d.d_rtbcount)
68 count++;
69 if (!count)
70 return 0;
71 }
72
73 if (!(flags & NO_HEADER_FLAG)) {
74 fprintf(fp,
75 _("Disk quotas for %s %s (%u)\nFilesystem%s"),
76 type_to_string(type), name, id,
77 (flags & HUMAN_FLAG) ? " " : " ");
78 if (form & XFS_BLOCK_QUOTA)
79 fprintf(fp, (flags & HUMAN_FLAG) ?
80 _(" Blocks Quota Limit Warn/Time ") :
81 _(" Blocks Quota Limit Warn/Time "));
82 if (form & XFS_INODE_QUOTA)
83 fprintf(fp, (flags & HUMAN_FLAG) ?
84 _(" Files Quota Limit Warn/Time ") :
85 _(" Files Quota Limit Warn/Time "));
86 if (form & XFS_RTBLOCK_QUOTA)
87 fprintf(fp, (flags & HUMAN_FLAG) ?
88 _("Realtime Quota Limit Warn/Time ") :
89 _(" Realtime Quota Limit Warn/Time "));
90 fputs("Mounted on\n", fp);
91 }
92
93 if (flags & HUMAN_FLAG) {
94 count = fprintf(fp, "%-12s", dev);
95 if (count > 13)
96 fprintf(fp, "\n%12s", " ");
97 } else {
98 count = fprintf(fp, "%-19s", dev);
99 if (count > 20)
100 fprintf(fp, "\n%19s", " ");
101 }
102
103 if (form & XFS_BLOCK_QUOTA) {
219285ad 104 timer = d.d_btimer;
5aead01d
NS
105 qflags = (flags & HUMAN_FLAG);
106 if (d.d_blk_hardlimit && d.d_bcount > d.d_blk_hardlimit)
107 qflags |= LIMIT_FLAG;
108 if (d.d_blk_softlimit && d.d_bcount > d.d_blk_softlimit)
109 qflags |= QUOTA_FLAG;
110 if (flags & HUMAN_FLAG)
111 fprintf(fp, " %6s %6s %6s %02d %8s ",
112 bbs_to_string(d.d_bcount, c, sizeof(c)),
113 bbs_to_string(d.d_blk_softlimit, s, sizeof(s)),
114 bbs_to_string(d.d_blk_hardlimit, h, sizeof(h)),
115 d.d_bwarns,
219285ad 116 time_to_string(timer, qflags));
5aead01d
NS
117 else
118 fprintf(fp, " %10llu %10llu %10llu %02d %9s ",
119 (unsigned long long)d.d_bcount >> 1,
120 (unsigned long long)d.d_blk_softlimit >> 1,
121 (unsigned long long)d.d_blk_hardlimit >> 1,
122 d.d_bwarns,
219285ad 123 time_to_string(timer, qflags));
5aead01d
NS
124 }
125 if (form & XFS_INODE_QUOTA) {
219285ad 126 timer = d.d_itimer;
5aead01d
NS
127 qflags = (flags & HUMAN_FLAG);
128 if (d.d_ino_hardlimit && d.d_icount > d.d_ino_hardlimit)
129 qflags |= LIMIT_FLAG;
130 if (d.d_ino_softlimit && d.d_icount > d.d_ino_softlimit)
131 qflags |= QUOTA_FLAG;
132 if (flags & HUMAN_FLAG)
133 fprintf(fp, " %6s %6s %6s %02d %8s ",
134 num_to_string(d.d_icount, c, sizeof(c)),
135 num_to_string(d.d_ino_softlimit, s, sizeof(s)),
136 num_to_string(d.d_ino_hardlimit, h, sizeof(h)),
137 d.d_iwarns,
219285ad 138 time_to_string(timer, qflags));
5aead01d
NS
139 else
140 fprintf(fp, " %10llu %10llu %10llu %02d %9s ",
141 (unsigned long long)d.d_icount,
142 (unsigned long long)d.d_ino_softlimit,
143 (unsigned long long)d.d_ino_hardlimit,
144 d.d_iwarns,
219285ad 145 time_to_string(timer, qflags));
5aead01d
NS
146 }
147 if (form & XFS_RTBLOCK_QUOTA) {
219285ad 148 timer = d.d_rtbtimer;
5aead01d
NS
149 qflags = (flags & HUMAN_FLAG);
150 if (d.d_rtb_hardlimit && d.d_rtbcount > d.d_rtb_hardlimit)
151 qflags |= LIMIT_FLAG;
152 if (d.d_rtb_softlimit && d.d_rtbcount > d.d_rtb_softlimit)
153 qflags |= QUOTA_FLAG;
154 if (flags & HUMAN_FLAG)
155 fprintf(fp, " %6s %6s %6s %02d %8s ",
156 bbs_to_string(d.d_rtbcount, c, sizeof(c)),
157 bbs_to_string(d.d_rtb_softlimit, s, sizeof(s)),
158 bbs_to_string(d.d_rtb_hardlimit, h, sizeof(h)),
159 d.d_rtbwarns,
219285ad 160 time_to_string(timer, qflags));
5aead01d
NS
161 else
162 fprintf(fp, " %10llu %10llu %10llu %02d %9s ",
163 (unsigned long long)d.d_rtbcount >> 1,
164 (unsigned long long)d.d_rtb_softlimit >> 1,
165 (unsigned long long)d.d_rtb_hardlimit >> 1,
166 d.d_rtbwarns,
219285ad 167 time_to_string(timer, qflags));
5aead01d
NS
168 }
169 fprintf(fp, "%s\n", mount->fs_dir);
170 return 1;
171}
172
173static void
174quota(
175 FILE *fp,
14f8b681 176 uint32_t id,
5aead01d
NS
177 char *name,
178 uint form,
179 uint type,
180 uint flags)
181{
182 fs_cursor_t cursor;
183 fs_path_t *path;
184
185 fs_cursor_initialise(NULL, FS_MOUNT_POINT, &cursor);
186 while ((path = fs_cursor_next_entry(&cursor))) {
187 if (quota_mount(fp, id, name, form, type, path, flags))
188 flags |= NO_HEADER_FLAG;
189 }
190}
191
192static char *
193getusername(
1774874a
NS
194 uid_t uid,
195 int numeric)
5aead01d
NS
196{
197 static char buffer[32];
5aead01d 198
6672a605
BN
199 if (!numeric) {
200 struct passwd *u = getpwuid(uid);
201 if (u)
202 return u->pw_name;
203 }
5aead01d
NS
204 snprintf(buffer, sizeof(buffer), "#%u", uid);
205 return &buffer[0];
206}
207
208static void
209quota_user_type(
210 FILE *fp,
211 char *name,
212 uint form,
213 uint type,
214 uint flags)
215{
216 struct passwd *u;
217 uid_t id;
218
219 if (name) {
fd537fc5 220 if (isdigits_only(name)) {
5aead01d 221 id = atoi(name);
1774874a 222 name = getusername(id, flags & NO_LOOKUP_FLAG);
5aead01d
NS
223 } else if ((u = getpwnam(name))) {
224 id = u->pw_uid;
225 name = u->pw_name;
226 } else {
e3210fd8 227 exitcode = 1;
5aead01d
NS
228 fprintf(stderr, _("%s: cannot find user %s\n"),
229 progname, name);
230 return;
231 }
232 } else {
233 id = getuid();
1774874a 234 name = getusername(id, flags & NO_LOOKUP_FLAG);
5aead01d
NS
235 }
236
237 quota(fp, id, name, form, type, flags);
238}
239
240static char *
241getgroupname(
1774874a
NS
242 gid_t gid,
243 int numeric)
5aead01d
NS
244{
245 static char buffer[32];
5aead01d 246
6672a605
BN
247 if (!numeric) {
248 struct group *g = getgrgid(gid);
249 if (g)
250 return g->gr_name;
251 }
5aead01d
NS
252 snprintf(buffer, sizeof(buffer), "#%u", gid);
253 return &buffer[0];
254}
255
256static void
257quota_group_type(
258 FILE *fp,
259 char *name,
260 uint form,
261 uint type,
262 uint flags)
263{
264 struct group *g;
265 gid_t gid, *gids = NULL;
266 int i, ngroups, dofree = 0;
267
268 if (name) {
fd537fc5 269 if (isdigits_only(name)) {
5aead01d 270 gid = atoi(name);
1774874a 271 name = getgroupname(gid, flags & NO_LOOKUP_FLAG);
5aead01d
NS
272 } else {
273 if ((g = getgrnam(name))) {
274 gid = g->gr_gid;
275 name = g->gr_name;
276 } else {
e3210fd8 277 exitcode = 1;
5aead01d
NS
278 fprintf(stderr, _("%s: cannot find group %s\n"),
279 progname, name);
280 return;
281 }
282 }
283 gids = &gid;
284 ngroups = 1;
5aead01d 285 } else {
f6a8b88b
ES
286 if ( ((ngroups = sysconf(_SC_NGROUPS_MAX)) < 0) ||
287 ((gids = malloc(ngroups * sizeof(gid_t))) == NULL) ||
288 ((ngroups = getgroups(ngroups, gids)) < 0)) {
289 /* something failed. Fall back to 1 group */
290 free(gids);
291 gid = getgid();
292 gids = &gid;
293 ngroups = 1;
294 } else {
295 /* It all worked, and we allocated memory */
296 dofree = 1;
297 }
5aead01d
NS
298 }
299
300 for (i = 0; i < ngroups; i++, name = NULL) {
301 if (!name)
1774874a 302 name = getgroupname(gids[i], flags & NO_LOOKUP_FLAG);
5aead01d
NS
303 quota(fp, gids[i], name, form, type, flags);
304 }
305
306 if (dofree)
307 free(gids);
308}
309
310static char *
311getprojectname(
1774874a
NS
312 prid_t prid,
313 int numeric)
5aead01d
NS
314{
315 static char buffer[32];
5aead01d 316
6672a605
BN
317 if (!numeric) {
318 fs_project_t *p = getprprid(prid);
319 if (p)
320 return p->pr_name;
321 }
2a1888c5 322 snprintf(buffer, sizeof(buffer), "#%u", (unsigned int)prid);
5aead01d
NS
323 return &buffer[0];
324}
325
326static void
327quota_proj_type(
328 FILE *fp,
329 char *name,
330 uint form,
331 uint type,
332 uint flags)
333{
334 fs_project_t *p;
335 prid_t id;
336
337 if (!name) {
e3210fd8 338 exitcode = 1;
5aead01d
NS
339 fprintf(stderr, _("%s: must specify a project name/ID\n"),
340 progname);
341 return;
342 }
343
fd537fc5 344 if (isdigits_only(name)) {
5aead01d 345 id = atoi(name);
1774874a 346 name = getprojectname(id, flags & NO_LOOKUP_FLAG);
5aead01d
NS
347 } else if ((p = getprnam(name))) {
348 id = p->pr_prid;
349 name = p->pr_name;
350 } else {
e3210fd8 351 exitcode = 1;
5aead01d
NS
352 fprintf(stderr, _("%s: cannot find project %s\n"),
353 progname, name);
354 return;
355 }
356
357 quota(fp, id, name, form, type, flags);
358}
359
360static void
361quota_any_type(
362 FILE *fp,
363 char *name,
364 uint form,
365 uint type,
366 uint flags)
367{
368 switch (type) {
369 case XFS_USER_QUOTA:
370 quota_user_type(fp, name, form, type, flags);
371 break;
372 case XFS_GROUP_QUOTA:
373 quota_group_type(fp, name, form, type, flags);
374 break;
375 case XFS_PROJ_QUOTA:
376 quota_proj_type(fp, name, form, type, flags);
377 break;
378 }
379}
380
381static int
382quota_f(
383 int argc,
384 char **argv)
385{
386 FILE *fp = NULL;
387 char *fname = NULL;
388 int c, flags = 0, type = 0, form = 0;
389
1774874a 390 while ((c = getopt(argc, argv, "bf:ghnNipruv")) != EOF) {
5aead01d
NS
391 switch (c) {
392 case 'f':
393 fname = optarg;
394 break;
395 case 'b':
396 form |= XFS_BLOCK_QUOTA;
397 break;
398 case 'i':
399 form |= XFS_INODE_QUOTA;
400 break;
401 case 'r':
402 form |= XFS_RTBLOCK_QUOTA;
403 break;
404 case 'g':
c614d3bf 405 type |= XFS_GROUP_QUOTA;
5aead01d
NS
406 break;
407 case 'p':
c614d3bf 408 type |= XFS_PROJ_QUOTA;
5aead01d
NS
409 break;
410 case 'u':
c614d3bf 411 type |= XFS_USER_QUOTA;
5aead01d
NS
412 break;
413 case 'h':
414 flags |= HUMAN_FLAG;
415 break;
416 case 'n':
1774874a
NS
417 flags |= NO_LOOKUP_FLAG;
418 break;
419 case 'N':
5aead01d
NS
420 flags |= NO_HEADER_FLAG;
421 break;
422 case 'v':
423 flags |= VERBOSE_FLAG;
424 break;
425 default:
426 return command_usage(&quota_cmd);
427 }
428 }
429
430 if (!form)
431 form = XFS_BLOCK_QUOTA;
432
c614d3bf 433 if (!type) {
5aead01d 434 type = XFS_USER_QUOTA;
c614d3bf
ZL
435 } else if (type != XFS_GROUP_QUOTA &&
436 type != XFS_PROJ_QUOTA &&
437 type != XFS_USER_QUOTA) {
438 return command_usage(&quota_cmd);
439 }
5aead01d
NS
440
441 if ((fp = fopen_write_secure(fname)) == NULL)
442 return 0;
443
444 if (argc == optind)
445 quota_any_type(fp, NULL, form, type, flags);
446 else while (argc > optind)
447 quota_any_type(fp, argv[optind++], form, type, flags);
448
449 if (fname)
450 fclose(fp);
451 return 0;
452}
453
454void
455quota_init(void)
456{
ad765595
AM
457 quota_cmd.name = "quota";
458 quota_cmd.altname = "l";
5aead01d
NS
459 quota_cmd.cfunc = quota_f;
460 quota_cmd.argmin = 0;
461 quota_cmd.argmax = -1;
c614d3bf 462 quota_cmd.args = _("[-bir] [-g|-p|-u] [-hnNv] [-f file] [id|name]...");
5aead01d
NS
463 quota_cmd.oneline = _("show usage and limits");
464 quota_cmd.help = quota_help;
29647c8d 465 quota_cmd.flags = CMD_FLAG_FOREIGN_OK;
5aead01d
NS
466
467 add_command(&quota_cmd);
468}