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