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