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