]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/quot.c
libfrog: move fsgeom.h to libfrog/
[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(
b46789e2 72 struct xfs_bstat *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) ?
22bc10ed 96 p->bs_gid : bstat_get_projid(p));
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
116 if (now - p->bs_atime.tv_sec > 30 * (60*60*24))
117 dp->blocks30 += size;
118 if (now - p->bs_atime.tv_sec > 60 * (60*60*24))
119 dp->blocks60 += size;
120 if (now - p->bs_atime.tv_sec > 90 * (60*60*24))
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;
b46789e2 132 struct xfs_bstat *buf;
f31b5e12
DW
133 uint64_t last = 0;
134 uint32_t count;
248af7cb 135 int i, sts, ret;
5aead01d
NS
136 du_t **dp;
137
138 /*
139 * Initialize tables between checks; because of the qsort
140 * in report() the hash tables must be rebuilt each time.
141 */
142 for (sts = 0; sts < TSIZE; sts++)
143 sizes[sts] = 0;
144 overflow = 0;
145 for (i = 0; i < 3; i++)
146 for (dp = duhash[i]; dp < &duhash[i][DUHASH]; dp++)
5e656dbb 147 *dp = NULL;
5aead01d
NS
148 ndu[0] = ndu[1] = ndu[2] = 0;
149
248af7cb
DW
150 ret = xfd_open(&fsxfd, fsdir, O_RDONLY);
151 if (ret) {
152 errno = ret;
5aead01d
NS
153 perror(fsdir);
154 return;
155 }
156
b46789e2 157 buf = (struct xfs_bstat *)calloc(NBSTAT, sizeof(struct xfs_bstat));
5aead01d
NS
158 if (!buf) {
159 perror("calloc");
f31b5e12 160 xfd_close(&fsxfd);
5aead01d
NS
161 return;
162 }
163
f31b5e12
DW
164 while ((sts = xfrog_bulkstat(&fsxfd, &last, NBSTAT, buf,
165 &count)) == 0) {
5aead01d
NS
166 if (count == 0)
167 break;
168 for (i = 0; i < count; i++)
169 quot_bulkstat_add(&buf[i], flags);
170 }
f31b5e12
DW
171 if (sts < 0) {
172 errno = sts;
173 perror("XFS_IOC_FSBULKSTAT");
174 }
5aead01d 175 free(buf);
f31b5e12 176 xfd_close(&fsxfd);
5aead01d
NS
177}
178
179static int
180qcompare(
181 du_t *p1,
182 du_t *p2)
183{
184 if (p1->blocks > p2->blocks)
185 return -1;
186 if (p1->blocks < p2->blocks)
187 return 1;
188 if (p1->id > p2->id)
189 return 1;
190 else if (p1->id < p2->id)
191 return -1;
192 return 0;
193}
194
14f8b681 195typedef char *(*idtoname_t)(uint32_t);
5aead01d
NS
196
197static void
198quot_report_mount_any_type(
199 FILE *fp,
200 du_t *dp,
201 int count,
202 idtoname_t names,
203 uint form,
204 uint type,
205 fs_path_t *mount,
206 uint flags)
207{
208 char *cp;
209
210 fprintf(fp, _("%s (%s) %s:\n"),
211 mount->fs_name, mount->fs_dir, type_to_string(type));
212 qsort(dp, count, sizeof(dp[0]),
213 (int (*)(const void *, const void *))qcompare);
214 for (; dp < &dp[count]; dp++) {
215 if (dp->blocks == 0)
216 return;
217 fprintf(fp, "%8llu ", (unsigned long long) dp->blocks);
218 if (form & XFS_INODE_QUOTA)
219 fprintf(fp, "%8llu ",
220 (unsigned long long) dp->nfiles);
1774874a
NS
221 if (!(flags & NO_LOOKUP_FLAG) &&
222 ((cp = (names)(dp->id)) != NULL))
5aead01d
NS
223 fprintf(fp, "%-8.8s", cp);
224 else
225 fprintf(fp, "#%-7d", dp->id);
226 if (flags & VERBOSE_FLAG)
227 fprintf(fp, " %8llu %8llu %8llu",
228 (unsigned long long) dp->blocks30,
229 (unsigned long long) dp->blocks60,
230 (unsigned long long) dp->blocks90);
231 fputc('\n', fp);
232 }
233}
234
235static void
236quot_report_mount(
237 FILE *fp,
238 uint form,
239 uint type,
240 fs_path_t *mount,
241 uint flags)
242{
243 switch (type) {
244 case XFS_GROUP_QUOTA:
245 quot_report_mount_any_type(fp, du[1], ndu[1], gid_to_name,
246 form, type, mount, flags);
247 break;
248 case XFS_PROJ_QUOTA:
249 quot_report_mount_any_type(fp, du[2], ndu[2], prid_to_name,
250 form, type, mount, flags);
251 break;
252 case XFS_USER_QUOTA:
253 quot_report_mount_any_type(fp, du[0], ndu[0], uid_to_name,
254 form, type, mount, flags);
255 }
256}
257
258static void
259quot_report(
260 FILE *fp,
261 uint form,
262 uint type,
263 char *dir,
264 uint flags)
265{
266 fs_cursor_t cursor;
267 fs_path_t *mount;
268
5e656dbb 269 now = time(NULL);
5aead01d
NS
270 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
271 while ((mount = fs_cursor_next_entry(&cursor))) {
272 quot_bulkstat_mount(mount->fs_dir, flags);
273 quot_report_mount(fp, form, type, mount, flags);
274 }
275}
276
277static void
278quot_histogram_mount(
279 FILE *fp,
280 fs_path_t *mount,
281 uint flags)
282{
14f8b681 283 uint64_t t = 0;
5aead01d
NS
284 int i;
285
286 fprintf(fp, _("%s (%s):\n"), mount->fs_name, mount->fs_dir);
287
288 for (i = 0; i < TSIZE - 1; i++)
289 if (sizes[i] > 0) {
290 t += sizes[i] * i;
291 fprintf(fp, _("%d\t%llu\t%llu\n"), i,
292 (unsigned long long) sizes[i],
293 (unsigned long long) t);
294 }
295 fprintf(fp, _("%d\t%llu\t%llu\n"), TSIZE - 1,
296 (unsigned long long) sizes[TSIZE - 1],
297 (unsigned long long) (overflow + t));
298}
299
300static void
301quot_histogram(
302 FILE *fp,
303 char *dir,
304 uint flags)
305{
306 fs_cursor_t cursor;
307 fs_path_t *mount;
308
309 fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
310 while ((mount = fs_cursor_next_entry(&cursor))) {
311 quot_bulkstat_mount(mount->fs_dir, flags);
312 quot_histogram_mount(fp, mount, flags);
313 }
314}
315
316static void
317quot_any_type(
318 FILE *fp,
319 uint form,
320 uint type,
321 char *dir,
322 uint flags)
323{
324 if (flags & HISTOGRAM_FLAG)
325 quot_histogram(fp, dir, flags);
326 else
327 quot_report(fp, form, type, dir, flags);
328}
329
330static int
331quot_f(
332 int argc,
333 char **argv)
334{
335 FILE *fp = NULL;
336 char *fname = NULL;
337 int c, flags = 0, type = 0, form = 0;
338
1774874a 339 while ((c = getopt(argc, argv, "abcf:ghinpruv")) != EOF) {
5aead01d
NS
340 switch (c) {
341 case 'f':
342 fname = optarg;
343 break;
344 case 'b':
345 form |= XFS_BLOCK_QUOTA;
346 break;
347 case 'i':
348 form |= XFS_INODE_QUOTA;
349 break;
350 case 'r':
351 form |= XFS_RTBLOCK_QUOTA;
352 break;
353 case 'g':
c614d3bf 354 type |= XFS_GROUP_QUOTA;
5aead01d
NS
355 break;
356 case 'p':
c614d3bf 357 type |= XFS_PROJ_QUOTA;
5aead01d
NS
358 break;
359 case 'u':
c614d3bf 360 type |= XFS_USER_QUOTA;
5aead01d
NS
361 break;
362 case 'a':
363 flags |= ALL_MOUNTS_FLAG;
364 break;
365 case 'c':
366 flags |= HISTOGRAM_FLAG;
367 break;
1774874a
NS
368 case 'n':
369 flags |= NO_LOOKUP_FLAG;
370 break;
5aead01d
NS
371 case 'v':
372 flags |= VERBOSE_FLAG;
373 break;
374 default:
375 return command_usage(&quot_cmd);
376 }
377 }
378
379 if (!form)
380 form = XFS_BLOCK_QUOTA;
381
c614d3bf 382 if (!type) {
5aead01d 383 type = XFS_USER_QUOTA;
c614d3bf
ZL
384 } else if (type != XFS_GROUP_QUOTA &&
385 type != XFS_PROJ_QUOTA &&
386 type != XFS_USER_QUOTA) {
387 return command_usage(&quot_cmd);
388 }
5aead01d
NS
389
390 if ((fp = fopen_write_secure(fname)) == NULL)
391 return 0;
392
fa13a00f
NS
393 if (argc == optind) {
394 if (flags & ALL_MOUNTS_FLAG)
395 quot_any_type(fp, form, type, NULL, flags);
396 else if (fs_path->fs_flags & FS_MOUNT_POINT)
397 quot_any_type(fp, form, type, fs_path->fs_dir, flags);
398 } else while (argc > optind) {
5aead01d 399 quot_any_type(fp, form, type, argv[optind++], flags);
fa13a00f 400 }
5aead01d
NS
401
402 if (fname)
403 fclose(fp);
404 return 0;
405}
406
407void
408quot_init(void)
409{
ad765595 410 quot_cmd.name = "quot";
5aead01d
NS
411 quot_cmd.cfunc = quot_f;
412 quot_cmd.argmin = 0;
413 quot_cmd.argmax = -1;
c614d3bf 414 quot_cmd.args = _("[-bir] [-g|-p|-u] [-acv] [-f file]");
5aead01d
NS
415 quot_cmd.oneline = _("summarize filesystem ownership");
416 quot_cmd.help = quot_help;
417
418 if (expert)
419 add_command(&quot_cmd);
420}