]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - quota/free.c
45ce2ceb04d59f6bf0053763f79dbe47f71cd587
[thirdparty/xfsprogs-dev.git] / quota / free.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5 */
6
7 #include <stdbool.h>
8 #include "command.h"
9 #include "init.h"
10 #include "quota.h"
11 #include "libfrog/logging.h"
12 #include "libfrog/fsgeom.h"
13
14 static cmdinfo_t free_cmd;
15
16 static void
17 free_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"
31 " -N -- suppress the header from the output\n"
32 "\n"));
33 }
34
35 /*
36 * The data and realtime block counts returned (count, used, and
37 * free) are all in basic block units.
38 */
39 static int
40 mount_free_space_data(
41 struct fs_path *mount,
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)
51 {
52 struct xfs_fsop_counts fscounts;
53 struct xfs_fsop_geom fsgeo;
54 struct statfs st;
55 uint64_t logsize, count, free;
56 int fd, ret;
57
58 if ((fd = open(mount->fs_dir, O_RDONLY)) < 0) {
59 exitcode = 1;
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 }
70
71 if (!(mount->fs_flags & FS_FOREIGN)) {
72 ret = xfrog_geometry(fd, &fsgeo);
73 if (ret) {
74 xfrog_perror(ret, "XFS_IOC_FSGEOMETRY");
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);
107 }
108
109
110 *icount = st.f_files;
111 *ifree = st.f_ffree;
112 *iused = st.f_files - st.f_ffree;
113
114 close(fd);
115 return 1;
116 }
117
118 /*
119 * The data and realtime block counts returned (count, used, and
120 * free) are all in basic block units.
121 */
122 static int
123 projects_free_space_data(
124 struct fs_path *path,
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)
134 {
135 fs_quota_stat_t qfs;
136 fs_disk_quota_t d;
137 struct fsxattr fsx;
138 uint type = XFS_PROJ_QUOTA;
139 char *dev = path->fs_name;
140 int fd;
141
142 if (xfsquotactl(XFS_GETQSTAT, dev, type, 0, &qfs) < 0 ||
143 !(qfs.qs_flags & XFS_QUOTA_PDQ_ACCT))
144 return 0;
145
146 if ((fd = open(path->fs_dir, O_RDONLY)) < 0) {
147 exitcode = 1;
148 fprintf(stderr, "%s: cannot open %s: %s\n",
149 progname, path->fs_dir, strerror(errno));
150 return 0;
151 }
152
153 if ((xfsctl(path->fs_dir, fd, FS_IOC_FSGETXATTR, &fsx)) < 0) {
154 exitcode = 1;
155 perror("FS_IOC_FSGETXATTR");
156 close(fd);
157 return 0;
158 }
159 if (!(fsx.fsx_xflags & FS_XFLAG_PROJINHERIT)) {
160 exitcode = 1;
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
167 if (path->fs_prid != fsx.fsx_projid) {
168 exitcode = 1;
169 fprintf(stderr,
170 _("%s: project ID %u (%s) doesn't match ID %u (%s)\n"),
171 progname, path->fs_prid, projects_file,
172 fsx.fsx_projid, path->fs_dir);
173 close(fd);
174 return 0;
175 }
176
177 xfsquotactl(XFS_QSYNC, dev, type, fsx.fsx_projid, NULL);
178 if (xfsquotactl(XFS_GETQUOTA, dev, type, fsx.fsx_projid, &d) < 0) {
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) {
191 *bcount = d.d_blk_softlimit;
192 *bfree = (d.d_blk_softlimit - d.d_bcount);
193 }
194 *bused = d.d_bcount;
195
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;
201
202 if (d.d_rtb_softlimit) {
203 *rcount = d.d_rtb_softlimit;
204 *rfree = (d.d_rtb_softlimit - d.d_rtbcount);
205 }
206 *rused = d.d_rtbcount;
207
208 close(fd);
209 return 1;
210 }
211
212 static int
213 free_space(
214 FILE *fp,
215 uint form,
216 fs_path_t *path,
217 uint flags)
218 {
219 uint64_t bcount, bused, bfree;
220 uint64_t icount, iused, ifree;
221 uint64_t rcount, rused, rfree;
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
303 static void
304 free_space_list(
305 FILE *fp,
306 uint form,
307 char *dir,
308 uint flags)
309 {
310 fs_cursor_t cursor;
311 fs_path_t *path;
312
313 fs_cursor_initialise(dir, 0, &cursor);
314 while ((path = fs_cursor_next_entry(&cursor))) {
315 if (free_space(fp, form, path, flags))
316 flags |= NO_HEADER_FLAG;
317 }
318 }
319
320 static int
321 free_f(
322 int argc,
323 char **argv)
324 {
325 FILE *fp = NULL;
326 char *fname = NULL;
327 int c, flags = 0, form = 0;
328
329 while ((c = getopt(argc, argv, "bf:hNir")) != EOF) {
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;
346 case 'N':
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
357 if ((fp = fopen_write_secure(fname)) == NULL)
358 return 0;
359
360 if (argc == optind)
361 free_space_list(fp, form, NULL, flags);
362 else while (argc > optind)
363 free_space_list(fp, form, argv[optind++], flags);
364
365 if (fname)
366 fclose(fp);
367 return 0;
368 }
369
370 void
371 free_init(void)
372 {
373 free_cmd.name = "df";
374 free_cmd.altname = "free";
375 free_cmd.cfunc = free_f;
376 free_cmd.argmin = 0;
377 free_cmd.argmax = -1;
378 free_cmd.args = _("[-bir] [-hN] [-f file]");
379 free_cmd.oneline = _("show free and used counts for blocks and inodes");
380 free_cmd.help = free_help;
381 free_cmd.flags = CMD_FLAG_FOREIGN_OK;
382
383 add_command(&free_cmd);
384 }