]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - quota/quot.c
xfs_scrub: remove moveon from progress report helpers
[thirdparty/xfsprogs-dev.git] / quota / quot.c
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
17 typedef 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
28 static uint64_t sizes[TSIZE];
29 static uint64_t overflow;
30
31 #define NDU 60000
32 #define DUHASH 8209
33 static du_t du[3][NDU];
34 static du_t *duhash[3][DUHASH];
35 static int ndu[3]; /* #usr/grp/prj */
36
37 #define NBSTAT 4069
38
39 static time_t now;
40 static cmdinfo_t quot_cmd;
41
42 static void
43 quot_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
70 static void
71 quot_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
126 static void
127 quot_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
176 static int
177 qcompare(
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
192 typedef char *(*idtoname_t)(uint32_t);
193
194 static void
195 quot_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
232 static void
233 quot_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
255 static void
256 quot_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
274 static void
275 quot_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
297 static void
298 quot_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
313 static void
314 quot_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
327 static int
328 quot_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
404 void
405 quot_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 }