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