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