]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/quot.c
xfsprogs: Release v6.7.0
[thirdparty/xfsprogs-dev.git] / quota / quot.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"
9fc3ef62 14#include "libfrog/logging.h"
fee68490 15#include "libfrog/fsgeom.h"
f31b5e12 16#include "libfrog/bulkstat.h"
5aead01d
NS
17
18typedef struct du {
19 struct du *next;
14f8b681
DW
20 uint64_t blocks;
21 uint64_t blocks30;
22 uint64_t blocks60;
23 uint64_t blocks90;
24 uint64_t nfiles;
25 uint32_t id;
5aead01d
NS
26} du_t;
27
28#define TSIZE 500
14f8b681
DW
29static uint64_t sizes[TSIZE];
30static uint64_t overflow;
5aead01d
NS
31
32#define NDU 60000
33#define DUHASH 8209
34static du_t du[3][NDU];
35static du_t *duhash[3][DUHASH];
36static int ndu[3]; /* #usr/grp/prj */
37
38#define NBSTAT 4069
39
40static time_t now;
41static cmdinfo_t quot_cmd;
42
43static void
44quot_help(void)
45{
46 printf(_(
47"\n"
48" display a summary of filesystem ownership\n"
49"\n"
50" -a -- summarise for all local XFS filesystem mount points\n"
51" -c -- display three columns giving file size in kilobytes, number of files\n"
52" of that size, and cumulative total of kilobytes in that size or\n"
53" smaller file. The last row is used as an overflow bucket and is the\n"
54" total of all files greater than 500 kilobytes.\n"
55" -v -- display three columns containing the number of kilobytes not\n"
56" accessed in the last 30, 60, and 90 days.\n"
57" -g -- display group summary\n"
58" -p -- display project summary\n"
59" -u -- display user summary\n"
60" -b -- display number of blocks used\n"
61" -i -- display number of inodes used\n"
62" -r -- display number of realtime blocks used\n"
1774874a
NS
63" -n -- skip identifier-to-name translations, just report IDs\n"
64" -N -- suppress the initial header\n"
5aead01d
NS
65" -f -- send output to a file\n"
66" The (optional) user/group/project can be specified either by name or by\n"
67" number (i.e. uid/gid/projid).\n"
68"\n"));
69}
70
71static void
72quot_bulkstat_add(
4cca629d 73 struct xfs_bulkstat *p,
5aead01d
NS
74 uint flags)
75{
76 du_t *dp;
77 du_t **hp;
14f8b681
DW
78 uint64_t size;
79 uint32_t i, id;
5aead01d
NS
80
81 if ((p->bs_mode & S_IFMT) == 0)
82 return;
83 size = howmany((p->bs_blocks * p->bs_blksize), 0x400ULL);
84
85 if (flags & HISTOGRAM_FLAG) {
86 if (!(S_ISDIR(p->bs_mode) || S_ISREG(p->bs_mode)))
87 return;
88 if (size >= TSIZE) {
89 overflow += size;
90 size = TSIZE - 1;
91 }
92 sizes[(int)size]++;
93 return;
94 }
95 for (i = 0; i < 3; i++) {
96 id = (i == 0) ? p->bs_uid : ((i == 1) ?
4cca629d 97 p->bs_gid : p->bs_projectid);
5aead01d
NS
98 hp = &duhash[i][id % DUHASH];
99 for (dp = *hp; dp; dp = dp->next)
100 if (dp->id == id)
101 break;
5e656dbb 102 if (dp == NULL) {
5aead01d
NS
103 if (ndu[i] >= NDU)
104 return;
105 dp = &du[i][(ndu[i]++)];
106 dp->next = *hp;
107 *hp = dp;
108 dp->id = id;
109 dp->nfiles = 0;
110 dp->blocks = 0;
111 dp->blocks30 = 0;
112 dp->blocks60 = 0;
113 dp->blocks90 = 0;
114 }
115 dp->blocks += size;
116
4cca629d 117 if (now - p->bs_atime > 30 * (60*60*24))
5aead01d 118 dp->blocks30 += size;
4cca629d 119 if (now - p->bs_atime > 60 * (60*60*24))
5aead01d 120 dp->blocks60 += size;
4cca629d 121 if (now - p->bs_atime > 90 * (60*60*24))
5aead01d
NS
122 dp->blocks90 += size;
123 dp->nfiles++;
124 }
125}
126
127static void
128quot_bulkstat_mount(
129 char *fsdir,
f31b5e12 130 unsigned int flags)
5aead01d 131{
f31b5e12 132 struct xfs_fd fsxfd = XFS_FD_INIT_EMPTY;
4cca629d 133 struct xfs_bulkstat_req *breq;
248af7cb 134 int i, sts, ret;
5aead01d
NS
135 du_t **dp;
136
137 /*
138 * Initialize tables between checks; because of the qsort
139 * in report() the hash tables must be rebuilt each time.
140 */
141 for (sts = 0; sts < TSIZE; sts++)
142 sizes[sts] = 0;
143 overflow = 0;
144 for (i = 0; i < 3; i++)
145 for (dp = duhash[i]; dp < &duhash[i][DUHASH]; dp++)
5e656dbb 146 *dp = NULL;
5aead01d
NS
147 ndu[0] = ndu[1] = ndu[2] = 0;
148
03d96c64 149 ret = -xfd_open(&fsxfd, fsdir, O_RDONLY);
248af7cb 150 if (ret) {
9fc3ef62 151 xfrog_perror(ret, fsdir);
5aead01d
NS
152 return;
153 }
154
e6542132
DW
155 ret = -xfrog_bulkstat_alloc_req(NBSTAT, 0, &breq);
156 if (ret) {
157 xfrog_perror(ret, "calloc");
f31b5e12 158 xfd_close(&fsxfd);
5aead01d
NS
159 return;
160 }
161
e6542132 162 while ((sts = -xfrog_bulkstat(&fsxfd, breq)) == 0) {
4cca629d 163 if (breq->hdr.ocount == 0)
5aead01d 164 break;
4cca629d
DW
165 for (i = 0; i < breq->hdr.ocount; i++)
166 quot_bulkstat_add(&breq->bulkstat[i], flags);
5aead01d 167 }
9fc3ef62
DW
168 if (sts < 0)
169 xfrog_perror(sts, "XFS_IOC_FSBULKSTAT");
4cca629d 170 free(breq);
f31b5e12 171 xfd_close(&fsxfd);
5aead01d
NS
172}
173
174static int
175qcompare(
559b58fa
ES
176 const void *p1,
177 const void *p2)
5aead01d 178{
559b58fa
ES
179 du_t *d1 = (struct du *)p1;
180 du_t *d2 = (struct du *)p2;
181
182 if (d1->blocks > d2->blocks)
5aead01d 183 return -1;
559b58fa 184 if (d1->blocks < d2->blocks)
5aead01d 185 return 1;
559b58fa 186 if (d1->id > d2->id)
5aead01d 187 return 1;
559b58fa 188 else if (d1->id < d2->id)
5aead01d
NS
189 return -1;
190 return 0;
191}
192
14f8b681 193typedef char *(*idtoname_t)(uint32_t);
5aead01d
NS
194
195static void
196quot_report_mount_any_type(
197 FILE *fp,
198 du_t *dp,
199 int count,
200 idtoname_t names,
201 uint form,
202 uint type,
203 fs_path_t *mount,
204 uint flags)
205{
206 char *cp;
207
208 fprintf(fp, _("%s (%s) %s:\n"),
209 mount->fs_name, mount->fs_dir, type_to_string(type));
559b58fa 210 qsort(dp, count, sizeof(dp[0]), qcompare);
5aead01d
NS
211 for (; dp < &dp[count]; dp++) {
212 if (dp->blocks == 0)
213 return;
214 fprintf(fp, "%8llu ", (unsigned long long) dp->blocks);
215 if (form & XFS_INODE_QUOTA)
216 fprintf(fp, "%8llu ",
217 (unsigned long long) dp->nfiles);
1774874a
NS
218 if (!(flags & NO_LOOKUP_FLAG) &&
219 ((cp = (names)(dp->id)) != NULL))
5aead01d
NS
220 fprintf(fp, "%-8.8s", cp);
221 else
222 fprintf(fp, "#%-7d", dp->id);
223 if (flags & VERBOSE_FLAG)
224 fprintf(fp, " %8llu %8llu %8llu",
225 (unsigned long long) dp->blocks30,
226 (unsigned long long) dp->blocks60,
227 (unsigned long long) dp->blocks90);
228 fputc('\n', fp);
229 }
230}
231
232static void
233quot_report_mount(
234 FILE *fp,
235 uint form,
236 uint type,
237 fs_path_t *mount,
238 uint flags)
239{
240 switch (type) {
241 case XFS_GROUP_QUOTA:
242 quot_report_mount_any_type(fp, du[1], ndu[1], gid_to_name,
243 form, type, mount, flags);
244 break;
245 case XFS_PROJ_QUOTA:
246 quot_report_mount_any_type(fp, du[2], ndu[2], prid_to_name,
247 form, type, mount, flags);
248 break;
249 case XFS_USER_QUOTA:
250 quot_report_mount_any_type(fp, du[0], ndu[0], uid_to_name,
251 form, type, mount, flags);
252 }
253}
254
255static void
256quot_report(
257 FILE *fp,
258 uint form,
259 uint type,
260 char *dir,
261 uint flags)
262{
263 fs_cursor_t cursor;
264 fs_path_t *mount;
265
5e656dbb 266 now = time(NULL);
5aead01d
NS
267 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
268 while ((mount = fs_cursor_next_entry(&cursor))) {
269 quot_bulkstat_mount(mount->fs_dir, flags);
270 quot_report_mount(fp, form, type, mount, flags);
271 }
272}
273
274static void
275quot_histogram_mount(
276 FILE *fp,
277 fs_path_t *mount,
278 uint flags)
279{
14f8b681 280 uint64_t t = 0;
5aead01d
NS
281 int i;
282
283 fprintf(fp, _("%s (%s):\n"), mount->fs_name, mount->fs_dir);
284
285 for (i = 0; i < TSIZE - 1; i++)
286 if (sizes[i] > 0) {
287 t += sizes[i] * i;
288 fprintf(fp, _("%d\t%llu\t%llu\n"), i,
289 (unsigned long long) sizes[i],
290 (unsigned long long) t);
291 }
292 fprintf(fp, _("%d\t%llu\t%llu\n"), TSIZE - 1,
293 (unsigned long long) sizes[TSIZE - 1],
294 (unsigned long long) (overflow + t));
295}
296
297static void
298quot_histogram(
299 FILE *fp,
300 char *dir,
301 uint flags)
302{
303 fs_cursor_t cursor;
304 fs_path_t *mount;
305
306 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
307 while ((mount = fs_cursor_next_entry(&cursor))) {
308 quot_bulkstat_mount(mount->fs_dir, flags);
309 quot_histogram_mount(fp, mount, flags);
310 }
311}
312
313static void
314quot_any_type(
315 FILE *fp,
316 uint form,
317 uint type,
318 char *dir,
319 uint flags)
320{
321 if (flags & HISTOGRAM_FLAG)
322 quot_histogram(fp, dir, flags);
323 else
324 quot_report(fp, form, type, dir, flags);
325}
326
327static int
328quot_f(
329 int argc,
330 char **argv)
331{
332 FILE *fp = NULL;
333 char *fname = NULL;
334 int c, flags = 0, type = 0, form = 0;
335
1774874a 336 while ((c = getopt(argc, argv, "abcf:ghinpruv")) != EOF) {
5aead01d
NS
337 switch (c) {
338 case 'f':
339 fname = optarg;
340 break;
341 case 'b':
342 form |= XFS_BLOCK_QUOTA;
343 break;
344 case 'i':
345 form |= XFS_INODE_QUOTA;
346 break;
347 case 'r':
348 form |= XFS_RTBLOCK_QUOTA;
349 break;
350 case 'g':
c614d3bf 351 type |= XFS_GROUP_QUOTA;
5aead01d
NS
352 break;
353 case 'p':
c614d3bf 354 type |= XFS_PROJ_QUOTA;
5aead01d
NS
355 break;
356 case 'u':
c614d3bf 357 type |= XFS_USER_QUOTA;
5aead01d
NS
358 break;
359 case 'a':
360 flags |= ALL_MOUNTS_FLAG;
361 break;
362 case 'c':
363 flags |= HISTOGRAM_FLAG;
364 break;
1774874a
NS
365 case 'n':
366 flags |= NO_LOOKUP_FLAG;
367 break;
5aead01d
NS
368 case 'v':
369 flags |= VERBOSE_FLAG;
370 break;
371 default:
372 return command_usage(&quot_cmd);
373 }
374 }
375
376 if (!form)
377 form = XFS_BLOCK_QUOTA;
378
c614d3bf 379 if (!type) {
5aead01d 380 type = XFS_USER_QUOTA;
c614d3bf
ZL
381 } else if (type != XFS_GROUP_QUOTA &&
382 type != XFS_PROJ_QUOTA &&
383 type != XFS_USER_QUOTA) {
384 return command_usage(&quot_cmd);
385 }
5aead01d
NS
386
387 if ((fp = fopen_write_secure(fname)) == NULL)
388 return 0;
389
fa13a00f
NS
390 if (argc == optind) {
391 if (flags & ALL_MOUNTS_FLAG)
392 quot_any_type(fp, form, type, NULL, flags);
393 else if (fs_path->fs_flags & FS_MOUNT_POINT)
394 quot_any_type(fp, form, type, fs_path->fs_dir, flags);
395 } else while (argc > optind) {
5aead01d 396 quot_any_type(fp, form, type, argv[optind++], flags);
fa13a00f 397 }
5aead01d
NS
398
399 if (fname)
400 fclose(fp);
401 return 0;
402}
403
404void
405quot_init(void)
406{
ad765595 407 quot_cmd.name = "quot";
5aead01d
NS
408 quot_cmd.cfunc = quot_f;
409 quot_cmd.argmin = 0;
410 quot_cmd.argmax = -1;
c614d3bf 411 quot_cmd.args = _("[-bir] [-g|-p|-u] [-acv] [-f file]");
5aead01d
NS
412 quot_cmd.oneline = _("summarize filesystem ownership");
413 quot_cmd.help = quot_help;
414
415 if (expert)
416 add_command(&quot_cmd);
417}