]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/quot.c
xfs_scrub: reduce fsmap activity for media errors
[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"
fee68490 14#include "libfrog/fsgeom.h"
f31b5e12 15#include "libfrog/bulkstat.h"
5aead01d
NS
16
17typedef struct du {
18 struct du *next;
14f8b681
DW
19 uint64_t blocks;
20 uint64_t blocks30;
21 uint64_t blocks60;
22 uint64_t blocks90;
23 uint64_t nfiles;
24 uint32_t id;
5aead01d
NS
25} du_t;
26
27#define TSIZE 500
14f8b681
DW
28static uint64_t sizes[TSIZE];
29static uint64_t overflow;
5aead01d
NS
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"
1774874a
NS
62" -n -- skip identifier-to-name translations, just report IDs\n"
63" -N -- suppress the initial header\n"
5aead01d
NS
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(
4cca629d 72 struct xfs_bulkstat *p,
5aead01d
NS
73 uint flags)
74{
75 du_t *dp;
76 du_t **hp;
14f8b681
DW
77 uint64_t size;
78 uint32_t i, id;
5aead01d
NS
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) ?
4cca629d 96 p->bs_gid : p->bs_projectid);
5aead01d
NS
97 hp = &duhash[i][id % DUHASH];
98 for (dp = *hp; dp; dp = dp->next)
99 if (dp->id == id)
100 break;
5e656dbb 101 if (dp == NULL) {
5aead01d
NS
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
4cca629d 116 if (now - p->bs_atime > 30 * (60*60*24))
5aead01d 117 dp->blocks30 += size;
4cca629d 118 if (now - p->bs_atime > 60 * (60*60*24))
5aead01d 119 dp->blocks60 += size;
4cca629d 120 if (now - p->bs_atime > 90 * (60*60*24))
5aead01d
NS
121 dp->blocks90 += size;
122 dp->nfiles++;
123 }
124}
125
126static void
127quot_bulkstat_mount(
128 char *fsdir,
f31b5e12 129 unsigned int flags)
5aead01d 130{
f31b5e12 131 struct xfs_fd fsxfd = XFS_FD_INIT_EMPTY;
4cca629d 132 struct xfs_bulkstat_req *breq;
248af7cb 133 int i, sts, ret;
5aead01d
NS
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++)
5e656dbb 145 *dp = NULL;
5aead01d
NS
146 ndu[0] = ndu[1] = ndu[2] = 0;
147
248af7cb
DW
148 ret = xfd_open(&fsxfd, fsdir, O_RDONLY);
149 if (ret) {
150 errno = ret;
5aead01d
NS
151 perror(fsdir);
152 return;
153 }
154
4cca629d
DW
155 breq = xfrog_bulkstat_alloc_req(NBSTAT, 0);
156 if (!breq) {
5aead01d 157 perror("calloc");
f31b5e12 158 xfd_close(&fsxfd);
5aead01d
NS
159 return;
160 }
161
4cca629d
DW
162 while ((sts = xfrog_bulkstat(&fsxfd, breq)) == 0) {
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 }
f31b5e12
DW
168 if (sts < 0) {
169 errno = sts;
170 perror("XFS_IOC_FSBULKSTAT");
171 }
4cca629d 172 free(breq);
f31b5e12 173 xfd_close(&fsxfd);
5aead01d
NS
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
14f8b681 192typedef char *(*idtoname_t)(uint32_t);
5aead01d
NS
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);
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}