]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - quota/quot.c
libfrog: convert fsgeom.c functions to negative error codes
[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/logging.h"
15 #include "libfrog/fsgeom.h"
16 #include "libfrog/bulkstat.h"
17
18 typedef struct du {
19 struct du *next;
20 uint64_t blocks;
21 uint64_t blocks30;
22 uint64_t blocks60;
23 uint64_t blocks90;
24 uint64_t nfiles;
25 uint32_t id;
26 } du_t;
27
28 #define TSIZE 500
29 static uint64_t sizes[TSIZE];
30 static uint64_t overflow;
31
32 #define NDU 60000
33 #define DUHASH 8209
34 static du_t du[3][NDU];
35 static du_t *duhash[3][DUHASH];
36 static int ndu[3]; /* #usr/grp/prj */
37
38 #define NBSTAT 4069
39
40 static time_t now;
41 static cmdinfo_t quot_cmd;
42
43 static void
44 quot_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"
63 " -n -- skip identifier-to-name translations, just report IDs\n"
64 " -N -- suppress the initial header\n"
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
71 static void
72 quot_bulkstat_add(
73 struct xfs_bulkstat *p,
74 uint flags)
75 {
76 du_t *dp;
77 du_t **hp;
78 uint64_t size;
79 uint32_t i, id;
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) ?
97 p->bs_gid : p->bs_projectid);
98 hp = &duhash[i][id % DUHASH];
99 for (dp = *hp; dp; dp = dp->next)
100 if (dp->id == id)
101 break;
102 if (dp == NULL) {
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
117 if (now - p->bs_atime > 30 * (60*60*24))
118 dp->blocks30 += size;
119 if (now - p->bs_atime > 60 * (60*60*24))
120 dp->blocks60 += size;
121 if (now - p->bs_atime > 90 * (60*60*24))
122 dp->blocks90 += size;
123 dp->nfiles++;
124 }
125 }
126
127 static void
128 quot_bulkstat_mount(
129 char *fsdir,
130 unsigned int flags)
131 {
132 struct xfs_fd fsxfd = XFS_FD_INIT_EMPTY;
133 struct xfs_bulkstat_req *breq;
134 int i, sts, ret;
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++)
146 *dp = NULL;
147 ndu[0] = ndu[1] = ndu[2] = 0;
148
149 ret = -xfd_open(&fsxfd, fsdir, O_RDONLY);
150 if (ret) {
151 xfrog_perror(ret, 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 xfrog_perror(sts, "XFS_IOC_FSBULKSTAT");
170 free(breq);
171 xfd_close(&fsxfd);
172 }
173
174 static int
175 qcompare(
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
190 typedef char *(*idtoname_t)(uint32_t);
191
192 static void
193 quot_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);
216 if (!(flags & NO_LOOKUP_FLAG) &&
217 ((cp = (names)(dp->id)) != NULL))
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
230 static void
231 quot_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
253 static void
254 quot_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
264 now = time(NULL);
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
272 static void
273 quot_histogram_mount(
274 FILE *fp,
275 fs_path_t *mount,
276 uint flags)
277 {
278 uint64_t t = 0;
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
295 static void
296 quot_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
311 static void
312 quot_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
325 static int
326 quot_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
334 while ((c = getopt(argc, argv, "abcf:ghinpruv")) != EOF) {
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':
349 type |= XFS_GROUP_QUOTA;
350 break;
351 case 'p':
352 type |= XFS_PROJ_QUOTA;
353 break;
354 case 'u':
355 type |= XFS_USER_QUOTA;
356 break;
357 case 'a':
358 flags |= ALL_MOUNTS_FLAG;
359 break;
360 case 'c':
361 flags |= HISTOGRAM_FLAG;
362 break;
363 case 'n':
364 flags |= NO_LOOKUP_FLAG;
365 break;
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
377 if (!type) {
378 type = XFS_USER_QUOTA;
379 } else if (type != XFS_GROUP_QUOTA &&
380 type != XFS_PROJ_QUOTA &&
381 type != XFS_USER_QUOTA) {
382 return command_usage(&quot_cmd);
383 }
384
385 if ((fp = fopen_write_secure(fname)) == NULL)
386 return 0;
387
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) {
394 quot_any_type(fp, form, type, argv[optind++], flags);
395 }
396
397 if (fname)
398 fclose(fp);
399 return 0;
400 }
401
402 void
403 quot_init(void)
404 {
405 quot_cmd.name = "quot";
406 quot_cmd.cfunc = quot_f;
407 quot_cmd.argmin = 0;
408 quot_cmd.argmax = -1;
409 quot_cmd.args = _("[-bir] [-g|-p|-u] [-acv] [-f file]");
410 quot_cmd.oneline = _("summarize filesystem ownership");
411 quot_cmd.help = quot_help;
412
413 if (expert)
414 add_command(&quot_cmd);
415 }