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