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