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