]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - quota/free.c
xfsprogs: xfs_quota: don't double project block counts
[thirdparty/xfsprogs-dev.git] / quota / free.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 "init.h"
21 #include "quota.h"
22
23 static cmdinfo_t free_cmd;
24
25 static void
26 free_help(void)
27 {
28 printf(_(
29 "\n"
30 " reports the number of free disk blocks and inodes\n"
31 "\n"
32 " This command reports the number of total, used, and available disk blocks.\n"
33 " It can optionally report the same set of numbers for inodes and realtime\n"
34 " disk blocks, and will report on all known XFS filesystem mount points and\n"
35 " project quota paths by default (see 'print' command for a list).\n"
36 " -b -- report the block count values\n"
37 " -i -- report the inode count values\n"
38 " -r -- report the realtime block count values\n"
39 " -h -- report in a human-readable format\n"
40 " -N -- suppress the header from the output\n"
41 "\n"));
42 }
43
44 /*
45 * The data and realtime block counts returned (count, used, and
46 * free) are all in basic block units.
47 */
48 static int
49 mount_free_space_data(
50 struct fs_path *mount,
51 __uint64_t *bcount,
52 __uint64_t *bused,
53 __uint64_t *bfree,
54 __uint64_t *icount,
55 __uint64_t *iused,
56 __uint64_t *ifree,
57 __uint64_t *rcount,
58 __uint64_t *rused,
59 __uint64_t *rfree)
60 {
61 struct xfs_fsop_counts fscounts;
62 struct xfs_fsop_geom fsgeo;
63 struct statfs st;
64 __uint64_t logsize, count, free;
65 int fd;
66
67 if ((fd = open(mount->fs_dir, O_RDONLY)) < 0) {
68 exitcode = 1;
69 fprintf(stderr, "%s: cannot open %s: %s\n",
70 progname, mount->fs_dir, strerror(errno));
71 return 0;
72 }
73
74 if (platform_fstatfs(fd, &st) < 0) {
75 perror("fstatfs");
76 close(fd);
77 return 0;
78 }
79 if ((xfsctl(mount->fs_dir, fd, XFS_IOC_FSGEOMETRY_V1, &fsgeo)) < 0) {
80 perror("XFS_IOC_FSGEOMETRY_V1");
81 close(fd);
82 return 0;
83 }
84 if ((xfsctl(mount->fs_dir, fd, XFS_IOC_FSCOUNTS, &fscounts)) < 0) {
85 perror("XFS_IOC_FSCOUNTS");
86 close(fd);
87 return 0;
88 }
89
90 logsize = fsgeo.logstart ? fsgeo.logblocks : 0;
91 count = (fsgeo.datablocks - logsize) * fsgeo.blocksize;
92 free = fscounts.freedata * fsgeo.blocksize;
93 *bcount = BTOBB(count);
94 *bfree = BTOBB(free);
95 *bused = BTOBB(count - free);
96
97 *icount = st.f_files;
98 *ifree = st.f_ffree;
99 *iused = st.f_files - st.f_ffree;
100
101 count = fsgeo.rtextents * fsgeo.rtextsize * fsgeo.blocksize;
102 free = fscounts.freertx * fsgeo.rtextsize * fsgeo.blocksize;
103 *rcount = BTOBB(count);
104 *rfree = BTOBB(free);
105 *rused = BTOBB(count - free);
106
107 close(fd);
108 return 1;
109 }
110
111 /*
112 * The data and realtime block counts returned (count, used, and
113 * free) are all in basic block units.
114 */
115 static int
116 projects_free_space_data(
117 struct fs_path *path,
118 __uint64_t *bcount,
119 __uint64_t *bused,
120 __uint64_t *bfree,
121 __uint64_t *icount,
122 __uint64_t *iused,
123 __uint64_t *ifree,
124 __uint64_t *rcount,
125 __uint64_t *rused,
126 __uint64_t *rfree)
127 {
128 fs_quota_stat_t qfs;
129 fs_disk_quota_t d;
130 struct fsxattr fsx;
131 uint type = XFS_PROJ_QUOTA;
132 char *dev = path->fs_name;
133 int fd;
134
135 if (xfsquotactl(XFS_GETQSTAT, dev, type, 0, &qfs) < 0 ||
136 !(qfs.qs_flags & XFS_QUOTA_PDQ_ACCT))
137 return 0;
138
139 if ((fd = open(path->fs_dir, O_RDONLY)) < 0) {
140 exitcode = 1;
141 fprintf(stderr, "%s: cannot open %s: %s\n",
142 progname, path->fs_dir, strerror(errno));
143 return 0;
144 }
145
146 if ((xfsctl(path->fs_dir, fd, XFS_IOC_FSGETXATTR, &fsx)) < 0) {
147 exitcode = 1;
148 perror("XFS_IOC_FSGETXATTR");
149 close(fd);
150 return 0;
151 }
152 if (!(fsx.fsx_xflags & XFS_XFLAG_PROJINHERIT)) {
153 exitcode = 1;
154 fprintf(stderr, _("%s: project quota flag not set on %s\n"),
155 progname, path->fs_dir);
156 close(fd);
157 return 0;
158 }
159
160 if (path->fs_prid != fsx.fsx_projid) {
161 exitcode = 1;
162 fprintf(stderr,
163 _("%s: project ID %u (%s) doesn't match ID %u (%s)\n"),
164 progname, path->fs_prid, projects_file,
165 fsx.fsx_projid, path->fs_dir);
166 close(fd);
167 return 0;
168 }
169
170 xfsquotactl(XFS_QSYNC, dev, type, fsx.fsx_projid, NULL);
171 if (xfsquotactl(XFS_GETQUOTA, dev, type, fsx.fsx_projid, &d) < 0) {
172 perror("XFS_GETQUOTA");
173 close(fd);
174 return 0;
175 }
176
177 /* If no softlimit is set for any of blk/ino/rt, get actual usage */
178 if (!d.d_blk_softlimit || !d.d_ino_softlimit || !d.d_rtb_softlimit) {
179 mount_free_space_data(path, bcount, bused, bfree,
180 icount, iused, ifree, rcount, rused, rfree);
181 }
182
183 if (d.d_blk_softlimit) {
184 *bcount = d.d_blk_softlimit;
185 *bfree = (d.d_blk_softlimit - d.d_bcount);
186 }
187 *bused = d.d_bcount;
188
189 if (d.d_ino_softlimit) {
190 *icount = d.d_ino_softlimit;
191 *ifree = (d.d_ino_softlimit - d.d_icount);
192 }
193 *iused = d.d_icount;
194
195 if (d.d_rtb_softlimit) {
196 *rcount = d.d_rtb_softlimit;
197 *rfree = (d.d_rtb_softlimit - d.d_rtbcount);
198 }
199 *rused = d.d_rtbcount;
200
201 close(fd);
202 return 1;
203 }
204
205 static int
206 free_space(
207 FILE *fp,
208 uint form,
209 fs_path_t *path,
210 uint flags)
211 {
212 __uint64_t bcount, bused, bfree;
213 __uint64_t icount, iused, ifree;
214 __uint64_t rcount, rused, rfree;
215 char a[8], s[8], u[8], p[8];
216 int count;
217
218 count = (path->fs_flags & FS_PROJECT_PATH) ?
219 projects_free_space_data(path, &bcount, &bused, &bfree,
220 &icount, &iused, &ifree,
221 &rcount, &rused, &rfree) :
222 mount_free_space_data(path, &bcount, &bused, &bfree,
223 &icount, &iused, &ifree,
224 &rcount, &rused, &rfree);
225 if (!count)
226 return 0;
227
228 if (!(flags & NO_HEADER_FLAG)) {
229 fprintf(fp, (flags & HUMAN_FLAG) ?
230 _("Filesystem ") : _("Filesystem "));
231 if (form & (XFS_BLOCK_QUOTA|XFS_RTBLOCK_QUOTA))
232 fprintf(fp, (flags & HUMAN_FLAG) ?
233 _(" Size Used Avail Use%%") :
234 _(" 1K-blocks Used Available Use%%"));
235 else if (form & XFS_INODE_QUOTA)
236 fprintf(fp, (flags & HUMAN_FLAG) ?
237 _(" Inodes Used Free Use%%") :
238 _(" Inodes IUsed IFree IUse%%"));
239 fprintf(fp, _(" Pathname\n"));
240 }
241
242 if (flags & HUMAN_FLAG) {
243 count = fprintf(fp, "%-12s", path->fs_name);
244 if (count > 13)
245 fprintf(fp, "\n%12s", " ");
246 } else {
247 count = fprintf(fp, "%-19s", path->fs_name);
248 if (count > 20)
249 fprintf(fp, "\n%19s", " ");
250 }
251
252 if (form & XFS_BLOCK_QUOTA) {
253 if (flags & HUMAN_FLAG)
254 fprintf(fp, " %6s %6s %6s %3s%%",
255 bbs_to_string(bcount, s, sizeof(s)),
256 bbs_to_string(bused, u, sizeof(u)),
257 bbs_to_string(bfree, a, sizeof(a)),
258 pct_to_string(bused, bcount, p, sizeof(p)));
259 else
260 fprintf(fp, " %10llu %10llu %10llu %3s%%",
261 (unsigned long long)bcount >> 1,
262 (unsigned long long)bused >> 1,
263 (unsigned long long)bfree >> 1,
264 pct_to_string(bused, bcount, p, sizeof(p)));
265 } else if (form & XFS_INODE_QUOTA) {
266 if (flags & HUMAN_FLAG)
267 fprintf(fp, " %6s %6s %6s %3s%%",
268 num_to_string(icount, s, sizeof(s)),
269 num_to_string(iused, u, sizeof(u)),
270 num_to_string(ifree, a, sizeof(a)),
271 pct_to_string(iused, icount, p, sizeof(p)));
272 else
273 fprintf(fp, " %10llu %10llu %10llu %3s%%",
274 (unsigned long long)icount,
275 (unsigned long long)iused,
276 (unsigned long long)ifree,
277 pct_to_string(iused, icount, p, sizeof(p)));
278 } else if (form & XFS_RTBLOCK_QUOTA) {
279 if (flags & HUMAN_FLAG)
280 fprintf(fp, " %6s %6s %6s %3s%%",
281 bbs_to_string(rcount, s, sizeof(s)),
282 bbs_to_string(rused, u, sizeof(u)),
283 bbs_to_string(rfree, a, sizeof(a)),
284 pct_to_string(rused, rcount, p, sizeof(p)));
285 else
286 fprintf(fp, " %10llu %10llu %10llu %3s%%",
287 (unsigned long long)rcount >> 1,
288 (unsigned long long)rused >> 1,
289 (unsigned long long)rfree >> 1,
290 pct_to_string(rused, rcount, p, sizeof(p)));
291 }
292 fprintf(fp, " %s\n", path->fs_dir);
293 return 1;
294 }
295
296 static void
297 free_space_list(
298 FILE *fp,
299 uint form,
300 uint type,
301 char *dir,
302 uint flags)
303 {
304 fs_cursor_t cursor;
305 fs_path_t *path;
306
307 fs_cursor_initialise(dir, type, &cursor);
308 while ((path = fs_cursor_next_entry(&cursor))) {
309 if (free_space(fp, form, path, flags))
310 flags |= NO_HEADER_FLAG;
311 }
312 }
313
314 static int
315 free_f(
316 int argc,
317 char **argv)
318 {
319 FILE *fp = NULL;
320 char *fname = NULL;
321 int c, flags = 0, form = 0, type = 0;
322
323 while ((c = getopt(argc, argv, "bf:hNir")) != EOF) {
324 switch (c) {
325 case 'f':
326 fname = optarg;
327 break;
328 case 'b':
329 form |= XFS_BLOCK_QUOTA;
330 break;
331 case 'i':
332 form |= XFS_INODE_QUOTA;
333 break;
334 case 'r':
335 form |= XFS_RTBLOCK_QUOTA;
336 break;
337 case 'h':
338 flags |= HUMAN_FLAG;
339 break;
340 case 'N':
341 flags |= NO_HEADER_FLAG;
342 break;
343 default:
344 return command_usage(&free_cmd);
345 }
346 }
347
348 if (!form)
349 form = XFS_BLOCK_QUOTA;
350
351 if (!type)
352 type = FS_MOUNT_POINT|FS_PROJECT_PATH;
353
354 if ((fp = fopen_write_secure(fname)) == NULL)
355 return 0;
356
357 if (argc == optind)
358 free_space_list(fp, form, type, NULL, flags);
359 else while (argc > optind)
360 free_space_list(fp, form, type, argv[optind++], flags);
361
362 if (fname)
363 fclose(fp);
364 return 0;
365 }
366
367 void
368 free_init(void)
369 {
370 free_cmd.name = "df";
371 free_cmd.altname = "free";
372 free_cmd.cfunc = free_f;
373 free_cmd.argmin = 0;
374 free_cmd.argmax = -1;
375 free_cmd.args = _("[-bir] [-hn] [-f file]");
376 free_cmd.oneline = _("show free and used counts for blocks and inodes");
377 free_cmd.help = free_help;
378
379 add_command(&free_cmd);
380 }