]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - quota/quot.c
libxcmd: don't check generic library commands
[thirdparty/xfsprogs-dev.git] / quota / quot.c
1 /*
2 * Copyright (c) 2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
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
7 * published by the Free Software Foundation.
8 *
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.
13 *
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
17 */
18
19 #include <stdbool.h>
20 #include "command.h"
21 #include <ctype.h>
22 #include <pwd.h>
23 #include <grp.h>
24 #include "init.h"
25 #include "quota.h"
26
27 typedef 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
38 static __uint64_t sizes[TSIZE];
39 static __uint64_t overflow;
40
41 #define NDU 60000
42 #define DUHASH 8209
43 static du_t du[3][NDU];
44 static du_t *duhash[3][DUHASH];
45 static int ndu[3]; /* #usr/grp/prj */
46
47 #define NBSTAT 4069
48
49 static time_t now;
50 static cmdinfo_t quot_cmd;
51
52 static void
53 quot_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"
72 " -n -- skip identifier-to-name translations, just report IDs\n"
73 " -N -- suppress the initial header\n"
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
80 static void
81 quot_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) ?
106 p->bs_gid : bstat_get_projid(p));
107 hp = &duhash[i][id % DUHASH];
108 for (dp = *hp; dp; dp = dp->next)
109 if (dp->id == id)
110 break;
111 if (dp == NULL) {
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
136 static void
137 quot_bulkstat_mount(
138 char *fsdir,
139 uint flags)
140 {
141 xfs_fsop_bulkreq_t bulkreq;
142 xfs_bstat_t *buf;
143 __u64 last = 0;
144 __s32 count;
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++)
157 *dp = NULL;
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");
169 close(fsfd);
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
190 static int
191 qcompare(
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
206 typedef char *(*idtoname_t)(__uint32_t);
207
208 static void
209 quot_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);
232 if (!(flags & NO_LOOKUP_FLAG) &&
233 ((cp = (names)(dp->id)) != NULL))
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
246 static void
247 quot_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
269 static void
270 quot_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
280 now = time(NULL);
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
288 static void
289 quot_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
311 static void
312 quot_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
327 static void
328 quot_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
341 static int
342 quot_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
350 while ((c = getopt(argc, argv, "abcf:ghinpruv")) != EOF) {
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':
365 type |= XFS_GROUP_QUOTA;
366 break;
367 case 'p':
368 type |= XFS_PROJ_QUOTA;
369 break;
370 case 'u':
371 type |= XFS_USER_QUOTA;
372 break;
373 case 'a':
374 flags |= ALL_MOUNTS_FLAG;
375 break;
376 case 'c':
377 flags |= HISTOGRAM_FLAG;
378 break;
379 case 'n':
380 flags |= NO_LOOKUP_FLAG;
381 break;
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
393 if (!type) {
394 type = XFS_USER_QUOTA;
395 } else if (type != XFS_GROUP_QUOTA &&
396 type != XFS_PROJ_QUOTA &&
397 type != XFS_USER_QUOTA) {
398 return command_usage(&quot_cmd);
399 }
400
401 if ((fp = fopen_write_secure(fname)) == NULL)
402 return 0;
403
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) {
410 quot_any_type(fp, form, type, argv[optind++], flags);
411 }
412
413 if (fname)
414 fclose(fp);
415 return 0;
416 }
417
418 void
419 quot_init(void)
420 {
421 quot_cmd.name = "quot";
422 quot_cmd.cfunc = quot_f;
423 quot_cmd.argmin = 0;
424 quot_cmd.argmax = -1;
425 quot_cmd.args = _("[-bir] [-g|-p|-u] [-acv] [-f file]");
426 quot_cmd.oneline = _("summarize filesystem ownership");
427 quot_cmd.help = quot_help;
428
429 if (expert)
430 add_command(&quot_cmd);
431 }