]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame_incremental - quota/quot.c
xfs_scrub: reduce fsmap activity for media errors
[thirdparty/xfsprogs-dev.git] / quota / quot.c
... / ...
CommitLineData
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5 */
6
7#include <stdbool.h>
8#include "command.h"
9#include <ctype.h>
10#include <pwd.h>
11#include <grp.h>
12#include "init.h"
13#include "quota.h"
14#include "libfrog/fsgeom.h"
15#include "libfrog/bulkstat.h"
16
17typedef struct du {
18 struct du *next;
19 uint64_t blocks;
20 uint64_t blocks30;
21 uint64_t blocks60;
22 uint64_t blocks90;
23 uint64_t nfiles;
24 uint32_t id;
25} du_t;
26
27#define TSIZE 500
28static uint64_t sizes[TSIZE];
29static uint64_t overflow;
30
31#define NDU 60000
32#define DUHASH 8209
33static du_t du[3][NDU];
34static du_t *duhash[3][DUHASH];
35static int ndu[3]; /* #usr/grp/prj */
36
37#define NBSTAT 4069
38
39static time_t now;
40static cmdinfo_t quot_cmd;
41
42static void
43quot_help(void)
44{
45 printf(_(
46"\n"
47" display a summary of filesystem ownership\n"
48"\n"
49" -a -- summarise for all local XFS filesystem mount points\n"
50" -c -- display three columns giving file size in kilobytes, number of files\n"
51" of that size, and cumulative total of kilobytes in that size or\n"
52" smaller file. The last row is used as an overflow bucket and is the\n"
53" total of all files greater than 500 kilobytes.\n"
54" -v -- display three columns containing the number of kilobytes not\n"
55" accessed in the last 30, 60, and 90 days.\n"
56" -g -- display group summary\n"
57" -p -- display project summary\n"
58" -u -- display user summary\n"
59" -b -- display number of blocks used\n"
60" -i -- display number of inodes used\n"
61" -r -- display number of realtime blocks used\n"
62" -n -- skip identifier-to-name translations, just report IDs\n"
63" -N -- suppress the initial header\n"
64" -f -- send output to a file\n"
65" The (optional) user/group/project can be specified either by name or by\n"
66" number (i.e. uid/gid/projid).\n"
67"\n"));
68}
69
70static void
71quot_bulkstat_add(
72 struct xfs_bulkstat *p,
73 uint flags)
74{
75 du_t *dp;
76 du_t **hp;
77 uint64_t size;
78 uint32_t i, id;
79
80 if ((p->bs_mode & S_IFMT) == 0)
81 return;
82 size = howmany((p->bs_blocks * p->bs_blksize), 0x400ULL);
83
84 if (flags & HISTOGRAM_FLAG) {
85 if (!(S_ISDIR(p->bs_mode) || S_ISREG(p->bs_mode)))
86 return;
87 if (size >= TSIZE) {
88 overflow += size;
89 size = TSIZE - 1;
90 }
91 sizes[(int)size]++;
92 return;
93 }
94 for (i = 0; i < 3; i++) {
95 id = (i == 0) ? p->bs_uid : ((i == 1) ?
96 p->bs_gid : p->bs_projectid);
97 hp = &duhash[i][id % DUHASH];
98 for (dp = *hp; dp; dp = dp->next)
99 if (dp->id == id)
100 break;
101 if (dp == NULL) {
102 if (ndu[i] >= NDU)
103 return;
104 dp = &du[i][(ndu[i]++)];
105 dp->next = *hp;
106 *hp = dp;
107 dp->id = id;
108 dp->nfiles = 0;
109 dp->blocks = 0;
110 dp->blocks30 = 0;
111 dp->blocks60 = 0;
112 dp->blocks90 = 0;
113 }
114 dp->blocks += size;
115
116 if (now - p->bs_atime > 30 * (60*60*24))
117 dp->blocks30 += size;
118 if (now - p->bs_atime > 60 * (60*60*24))
119 dp->blocks60 += size;
120 if (now - p->bs_atime > 90 * (60*60*24))
121 dp->blocks90 += size;
122 dp->nfiles++;
123 }
124}
125
126static void
127quot_bulkstat_mount(
128 char *fsdir,
129 unsigned int flags)
130{
131 struct xfs_fd fsxfd = XFS_FD_INIT_EMPTY;
132 struct xfs_bulkstat_req *breq;
133 int i, sts, ret;
134 du_t **dp;
135
136 /*
137 * Initialize tables between checks; because of the qsort
138 * in report() the hash tables must be rebuilt each time.
139 */
140 for (sts = 0; sts < TSIZE; sts++)
141 sizes[sts] = 0;
142 overflow = 0;
143 for (i = 0; i < 3; i++)
144 for (dp = duhash[i]; dp < &duhash[i][DUHASH]; dp++)
145 *dp = NULL;
146 ndu[0] = ndu[1] = ndu[2] = 0;
147
148 ret = xfd_open(&fsxfd, fsdir, O_RDONLY);
149 if (ret) {
150 errno = ret;
151 perror(fsdir);
152 return;
153 }
154
155 breq = xfrog_bulkstat_alloc_req(NBSTAT, 0);
156 if (!breq) {
157 perror("calloc");
158 xfd_close(&fsxfd);
159 return;
160 }
161
162 while ((sts = xfrog_bulkstat(&fsxfd, breq)) == 0) {
163 if (breq->hdr.ocount == 0)
164 break;
165 for (i = 0; i < breq->hdr.ocount; i++)
166 quot_bulkstat_add(&breq->bulkstat[i], flags);
167 }
168 if (sts < 0) {
169 errno = sts;
170 perror("XFS_IOC_FSBULKSTAT");
171 }
172 free(breq);
173 xfd_close(&fsxfd);
174}
175
176static int
177qcompare(
178 du_t *p1,
179 du_t *p2)
180{
181 if (p1->blocks > p2->blocks)
182 return -1;
183 if (p1->blocks < p2->blocks)
184 return 1;
185 if (p1->id > p2->id)
186 return 1;
187 else if (p1->id < p2->id)
188 return -1;
189 return 0;
190}
191
192typedef char *(*idtoname_t)(uint32_t);
193
194static void
195quot_report_mount_any_type(
196 FILE *fp,
197 du_t *dp,
198 int count,
199 idtoname_t names,
200 uint form,
201 uint type,
202 fs_path_t *mount,
203 uint flags)
204{
205 char *cp;
206
207 fprintf(fp, _("%s (%s) %s:\n"),
208 mount->fs_name, mount->fs_dir, type_to_string(type));
209 qsort(dp, count, sizeof(dp[0]),
210 (int (*)(const void *, const void *))qcompare);
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);
218 if (!(flags & NO_LOOKUP_FLAG) &&
219 ((cp = (names)(dp->id)) != NULL))
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
266 now = time(NULL);
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{
280 uint64_t t = 0;
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
336 while ((c = getopt(argc, argv, "abcf:ghinpruv")) != EOF) {
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':
351 type |= XFS_GROUP_QUOTA;
352 break;
353 case 'p':
354 type |= XFS_PROJ_QUOTA;
355 break;
356 case 'u':
357 type |= XFS_USER_QUOTA;
358 break;
359 case 'a':
360 flags |= ALL_MOUNTS_FLAG;
361 break;
362 case 'c':
363 flags |= HISTOGRAM_FLAG;
364 break;
365 case 'n':
366 flags |= NO_LOOKUP_FLAG;
367 break;
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
379 if (!type) {
380 type = XFS_USER_QUOTA;
381 } else if (type != XFS_GROUP_QUOTA &&
382 type != XFS_PROJ_QUOTA &&
383 type != XFS_USER_QUOTA) {
384 return command_usage(&quot_cmd);
385 }
386
387 if ((fp = fopen_write_secure(fname)) == NULL)
388 return 0;
389
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) {
396 quot_any_type(fp, form, type, argv[optind++], flags);
397 }
398
399 if (fname)
400 fclose(fp);
401 return 0;
402}
403
404void
405quot_init(void)
406{
407 quot_cmd.name = "quot";
408 quot_cmd.cfunc = quot_f;
409 quot_cmd.argmin = 0;
410 quot_cmd.argmax = -1;
411 quot_cmd.args = _("[-bir] [-g|-p|-u] [-acv] [-f file]");
412 quot_cmd.oneline = _("summarize filesystem ownership");
413 quot_cmd.help = quot_help;
414
415 if (expert)
416 add_command(&quot_cmd);
417}