]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - growfs/xfs_growfs.c
Userspace support for lazy superblock counters
[thirdparty/xfsprogs-dev.git] / growfs / xfs_growfs.c
CommitLineData
2bd0ea18 1/*
da23017d
NS
2 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
2bd0ea18 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
2bd0ea18
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.
2bd0ea18 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
2bd0ea18
NS
17 */
18
1d7e80ee 19#include <xfs/libxfs.h>
3d93ccb7 20#include <xfs/path.h>
2bd0ea18 21
8e4b2fda
ES
22/*
23 * When growing a filesystem, this is the most significant
24 * bits we'll accept in the resulting inode numbers
25 * without warning the user.
26 */
27
28#define XFS_MAX_INODE_SIG_BITS 32
29
2bd0ea18
NS
30static void
31usage(void)
32{
9440d84d 33 fprintf(stderr, _(
2bd0ea18
NS
34"Usage: %s [options] mountpoint\n\n\
35Options:\n\
dfc130f3
RC
36 -d grow data/metadata section\n\
37 -l grow log section\n\
38 -r grow realtime section\n\
39 -n don't change anything, just show geometry\n\
40 -I allow inode numbers to exceed %d significant bits\n\
41 -i convert log from external to internal format\n\
42 -t alternate location for mount table (/etc/mtab)\n\
43 -x convert log from internal to external format\n\
44 -D size grow data/metadata section to size blks\n\
45 -L size grow/shrink log section to size blks\n\
46 -R size grow realtime section to size blks\n\
47 -e size set realtime extent size to size blks\n\
48 -m imaxpct set inode max percent to imaxpct\n\
49 -V print version information\n"),
8e4b2fda 50 progname, XFS_MAX_INODE_SIG_BITS);
2bd0ea18
NS
51 exit(2);
52}
53
54void
55report_info(
56 xfs_fsop_geom_t geo,
57 char *mntpoint,
8fc372bc
NS
58 int isint,
59 char *logname,
60 char *rtname,
2bd0ea18 61 int unwritten,
cdded3d8 62 int lazycount,
2bd0ea18 63 int dirversion,
e78b9efb
NS
64 int logversion,
65 int attrversion)
2bd0ea18 66{
9440d84d
NS
67 printf(_(
68 "meta-data=%-22s isize=%-6u agcount=%u, agsize=%u blks\n"
e78b9efb 69 " =%-22s sectsz=%-5u attr=%u\n"
9440d84d
NS
70 "data =%-22s bsize=%-6u blocks=%llu, imaxpct=%u\n"
71 " =%-22s sunit=%-6u swidth=%u blks, unwritten=%u\n"
72 "naming =version %-14u bsize=%-6u\n"
73 "log =%-22s bsize=%-6u blocks=%u, version=%u\n"
cdded3d8 74 " =%-22s sectsz=%-5u sunit=%u blks, lazy-count=%u\n"
9440d84d
NS
75 "realtime =%-22s extsz=%-6u blocks=%llu, rtextents=%llu\n"),
76
77 mntpoint, geo.inodesize, geo.agcount, geo.agblocks,
e78b9efb 78 "", geo.sectsize, attrversion,
9440d84d
NS
79 "", geo.blocksize, (unsigned long long)geo.datablocks,
80 geo.imaxpct,
81 "", geo.sunit, geo.swidth, unwritten,
8fc372bc
NS
82 dirversion, geo.dirblocksize,
83 isint ? _("internal") : logname ? logname : _("external"),
84 geo.blocksize, geo.logblocks, logversion,
cdded3d8 85 "", geo.logsectsize, geo.logsunit / geo.blocksize, lazycount,
8fc372bc 86 !geo.rtblocks ? _("none") : rtname ? rtname : _("external"),
9440d84d
NS
87 geo.rtextsize * geo.blocksize, (unsigned long long)geo.rtblocks,
88 (unsigned long long)geo.rtextents);
2bd0ea18
NS
89}
90
2bd0ea18
NS
91int
92main(int argc, char **argv)
93{
94 int aflag; /* fake flag, do all pieces */
95 int c; /* current option character */
96 long long ddsize; /* device size in 512-byte blocks */
97 int dflag; /* -d flag */
e78b9efb 98 int attrversion;/* attribute version number */
2bd0ea18 99 int dirversion; /* directory version number */
aedf62b9 100 int logversion; /* log version number */
2bd0ea18
NS
101 long long dlsize; /* device size in 512-byte blocks */
102 long long drsize; /* device size in 512-byte blocks */
103 long long dsize; /* new data size in fs blocks */
104 int error; /* we have hit an error */
105 long esize; /* new rt extent size */
106 int ffd; /* mount point file descriptor */
107 xfs_fsop_geom_t geo; /* current fs geometry */
108 int iflag; /* -i flag */
109 int isint; /* log is currently internal */
110 int lflag; /* -l flag */
111 long long lsize; /* new log size in fs blocks */
112 int maxpct; /* -m flag value */
113 int mflag; /* -m flag */
2bd0ea18
NS
114 int nflag; /* -n flag */
115 xfs_fsop_geom_t ngeo; /* new fs geometry */
116 int rflag; /* -r flag */
117 long long rsize; /* new rt size in fs blocks */
118 int unwritten; /* unwritten extent flag */
cdded3d8 119 int lazycount; /* lazy superblock counters */
2bd0ea18 120 int xflag; /* -x flag */
3d93ccb7
NS
121 char *fname; /* mount point name */
122 char *datadev; /* data device name */
123 char *logdev; /* log device name */
124 char *rtdev; /* RT device name */
125 fs_path_t *fs; /* mount point information */
2bd0ea18
NS
126 libxfs_init_t xi; /* libxfs structure */
127
2bd0ea18 128 progname = basename(argv[0]);
9440d84d
NS
129 setlocale(LC_ALL, "");
130 bindtextdomain(PACKAGE, LOCALEDIR);
131 textdomain(PACKAGE);
53f8ad6d 132
2bd0ea18
NS
133 maxpct = esize = 0;
134 dsize = lsize = rsize = 0LL;
53f8ad6d
NS
135 aflag = dflag = iflag = lflag = mflag = nflag = rflag = xflag = 0;
136
6d1d29a7 137 while ((c = getopt(argc, argv, "dD:e:ilL:m:np:rR:t:xV")) != EOF) {
2bd0ea18
NS
138 switch (c) {
139 case 'D':
53f8ad6d 140 dsize = strtoll(optarg, NULL, 10);
2bd0ea18
NS
141 /* fall through */
142 case 'd':
143 dflag = 1;
144 break;
145 case 'e':
146 esize = atol(optarg);
147 rflag = 1;
148 break;
149 case 'i':
150 lflag = iflag = 1;
151 break;
152 case 'L':
53f8ad6d 153 lsize = strtoll(optarg, NULL, 10);
2bd0ea18
NS
154 /* fall through */
155 case 'l':
156 lflag = 1;
157 break;
158 case 'm':
159 mflag = 1;
160 maxpct = atoi(optarg);
161 break;
162 case 'n':
163 nflag = 1;
164 break;
165 case 'p':
166 progname = optarg;
167 break;
168 case 'R':
53f8ad6d 169 rsize = strtoll(optarg, NULL, 10);
2bd0ea18
NS
170 /* fall through */
171 case 'r':
172 rflag = 1;
173 break;
174 case 't':
3d93ccb7 175 mtab_file = optarg;
2bd0ea18
NS
176 break;
177 case 'x':
178 lflag = xflag = 1;
179 break;
180 case 'V':
9440d84d 181 printf(_("%s version %s\n"), progname, VERSION);
3d98fe63 182 exit(0);
2bd0ea18
NS
183 case '?':
184 default:
185 usage();
186 }
187 }
188 if (argc - optind != 1)
189 usage();
190 if (iflag && xflag)
191 usage();
192 if (dflag + lflag + rflag == 0)
193 aflag = 1;
194
3d93ccb7
NS
195 fs_table_initialise();
196 fs = fs_table_lookup(argv[optind], FS_MOUNT_POINT);
197 if (!fs) {
198 fprintf(stderr, _("%s: %s is not a mounted XFS filesystem\n"),
199 progname, argv[optind]);
200 return 1;
201 }
202
203 fname = fs->fs_dir;
204 datadev = fs->fs_name;
205 logdev = fs->fs_log;
206 rtdev = fs->fs_rt;
2bd0ea18
NS
207
208 ffd = open(fname, O_RDONLY);
209 if (ffd < 0) {
210 perror(fname);
211 return 1;
212 }
213
93d9f139
NS
214 if (!platform_test_xfs_fd(ffd)) {
215 fprintf(stderr, _("%s: specified file "
216 "[\"%s\"] is not on an XFS filesystem\n"),
217 progname, fname);
218 exit(1);
219 }
220
2bd0ea18 221 /* get the current filesystem size & geometry */
93d9f139 222 if (xfsctl(fname, ffd, XFS_IOC_FSGEOMETRY, &geo) < 0) {
9d77aadd 223 /*
93d9f139 224 * OK, new xfsctl barfed - back off and try earlier version
9d77aadd 225 * as we're probably running an older kernel version.
93d9f139 226 * Only field added in the v2 geometry xfsctl is "logsunit"
9d77aadd
NS
227 * so we'll zero that out for later display (as zero).
228 */
9440d84d 229 geo.logsunit = 0;
93d9f139 230 if (xfsctl(fname, ffd, XFS_IOC_FSGEOMETRY_V1, &geo) < 0) {
9440d84d 231 fprintf(stderr, _(
9d77aadd 232 "%s: cannot determine geometry of filesystem"
9440d84d 233 " mounted at %s: %s\n"),
9d77aadd
NS
234 progname, fname, strerror(errno));
235 exit(1);
236 }
2bd0ea18
NS
237 }
238 isint = geo.logstart > 0;
239 unwritten = geo.flags & XFS_FSOP_GEOM_FLAGS_EXTFLG ? 1 : 0;
cdded3d8 240 lazycount = geo.flags & XFS_FSOP_GEOM_FLAGS_LAZYSB ? 1 : 0;
2bd0ea18 241 dirversion = geo.flags & XFS_FSOP_GEOM_FLAGS_DIRV2 ? 2 : 1;
aedf62b9 242 logversion = geo.flags & XFS_FSOP_GEOM_FLAGS_LOGV2 ? 2 : 1;
e78b9efb
NS
243 attrversion = geo.flags & XFS_FSOP_GEOM_FLAGS_ATTR2 ? 2 : \
244 (geo.flags & XFS_FSOP_GEOM_FLAGS_ATTR ? 1 : 0);
2bd0ea18
NS
245
246 if (nflag) {
8fc372bc 247 report_info(geo, datadev, isint, logdev, rtdev,
cdded3d8
DC
248 unwritten, lazycount, dirversion, logversion,
249 attrversion);
2bd0ea18
NS
250 exit(0);
251 }
252
253 /*
254 * Need root access from here on (using raw devices)...
255 */
256
257 bzero(&xi, sizeof(xi));
258 xi.dname = datadev;
259 xi.logname = logdev;
260 xi.rtname = rtdev;
2bd0ea18
NS
261 xi.isreadonly = LIBXFS_ISREADONLY;
262
263 if (!libxfs_init(&xi))
264 usage();
265
266 /* check we got the info for all the sections we are trying to modify */
267 if (!xi.ddev) {
9440d84d 268 fprintf(stderr, _("%s: failed to access data device for %s\n"),
2bd0ea18
NS
269 progname, fname);
270 exit(1);
271 }
272 if (lflag && !isint && !xi.logdev) {
9440d84d 273 fprintf(stderr, _("%s: failed to access external log for %s\n"),
2bd0ea18
NS
274 progname, fname);
275 exit(1);
276 }
277 if (rflag && !xi.rtdev) {
9440d84d
NS
278 fprintf(stderr,
279 _("%s: failed to access realtime device for %s\n"),
2bd0ea18
NS
280 progname, fname);
281 exit(1);
282 }
283
8fc372bc 284 report_info(geo, datadev, isint, logdev, rtdev,
cdded3d8
DC
285 unwritten, lazycount, dirversion, logversion,
286 attrversion);
2bd0ea18
NS
287
288 ddsize = xi.dsize;
289 dlsize = ( xi.logBBsize? xi.logBBsize :
290 geo.logblocks * (geo.blocksize / BBSIZE) );
291 drsize = xi.rtsize;
292
b7abc846
ES
293 /*
294 * Ok, Linux only has a 1024-byte resolution on device _size_,
295 * and the sizes below are in basic 512-byte blocks,
296 * so if we have (size % 2), on any partition, we can't get
297 * to the last 512 bytes. Just chop it down by a block.
298 */
dfc130f3 299
b7abc846
ES
300 ddsize -= (ddsize % 2);
301 dlsize -= (dlsize % 2);
302 drsize -= (drsize % 2);
303
2bd0ea18
NS
304 error = 0;
305 if (dflag | aflag) {
306 xfs_growfs_data_t in;
8e4b2fda
ES
307 __uint64_t new_agcount;
308
2bd0ea18
NS
309 if (!mflag)
310 maxpct = geo.imaxpct;
311 if (!dsize)
312 dsize = ddsize / (geo.blocksize / BBSIZE);
313 else if (dsize > ddsize / (geo.blocksize / BBSIZE)) {
9440d84d
NS
314 fprintf(stderr, _(
315 "data size %lld too large, maximum is %lld\n"),
5b64e00a
NS
316 (long long)dsize,
317 (long long)(ddsize/(geo.blocksize/BBSIZE)));
2bd0ea18
NS
318 error = 1;
319 }
8e4b2fda 320
dfc130f3 321 new_agcount = dsize / geo.agblocks
8e4b2fda
ES
322 + (dsize % geo.agblocks != 0);
323
2bd0ea18 324 if (!error && dsize < geo.datablocks) {
9440d84d
NS
325 fprintf(stderr, _("data size %lld too small,"
326 " old size is %lld\n"),
5b64e00a 327 (long long)dsize, (long long)geo.datablocks);
2bd0ea18
NS
328 error = 1;
329 } else if (!error &&
330 dsize == geo.datablocks && maxpct == geo.imaxpct) {
331 if (dflag)
9440d84d
NS
332 fprintf(stderr, _(
333 "data size unchanged, skipping\n"));
2bd0ea18 334 if (mflag)
9440d84d
NS
335 fprintf(stderr, _(
336 "inode max pct unchanged, skipping\n"));
2bd0ea18
NS
337 } else if (!error && !nflag) {
338 in.newblocks = (__u64)dsize;
339 in.imaxpct = (__u32)maxpct;
93d9f139 340 if (xfsctl(fname, ffd, XFS_IOC_FSGROWFSDATA, &in) < 0) {
2bd0ea18 341 if (errno == EWOULDBLOCK)
9440d84d
NS
342 fprintf(stderr, _(
343 "%s: growfs operation in progress already\n"),
2bd0ea18
NS
344 progname);
345 else
9440d84d 346 fprintf(stderr, _(
93d9f139 347 "%s: XFS_IOC_FSGROWFSDATA xfsctl failed: %s\n"),
2bd0ea18
NS
348 progname, strerror(errno));
349 error = 1;
350 }
351 }
352 }
353
354 if (!error && (rflag | aflag)) {
355 xfs_growfs_rt_t in;
356
357 if (!esize)
358 esize = (__u32)geo.rtextsize;
359 if (!rsize)
360 rsize = drsize / (geo.blocksize / BBSIZE);
361 else if (rsize > drsize / (geo.blocksize / BBSIZE)) {
9440d84d
NS
362 fprintf(stderr, _(
363 "realtime size %lld too large, maximum is %lld\n"),
2bd0ea18
NS
364 rsize, drsize / (geo.blocksize / BBSIZE));
365 error = 1;
366 }
367 if (!error && rsize < geo.rtblocks) {
9440d84d
NS
368 fprintf(stderr, _(
369 "realtime size %lld too small, old size is %lld\n"),
5b64e00a 370 (long long)rsize, (long long)geo.rtblocks);
2bd0ea18
NS
371 error = 1;
372 } else if (!error && rsize == geo.rtblocks) {
373 if (rflag)
9440d84d
NS
374 fprintf(stderr, _(
375 "realtime size unchanged, skipping\n"));
2bd0ea18
NS
376 } else if (!error && !nflag) {
377 in.newblocks = (__u64)rsize;
378 in.extsize = (__u32)esize;
93d9f139 379 if (xfsctl(fname, ffd, XFS_IOC_FSGROWFSRT, &in) < 0) {
2bd0ea18 380 if (errno == EWOULDBLOCK)
9440d84d
NS
381 fprintf(stderr, _(
382 "%s: growfs operation in progress already\n"),
2bd0ea18
NS
383 progname);
384 else if (errno == ENOSYS)
9440d84d
NS
385 fprintf(stderr, _(
386 "%s: realtime growth not implemented\n"),
2bd0ea18
NS
387 progname);
388 else
9440d84d 389 fprintf(stderr, _(
93d9f139 390 "%s: XFS_IOC_FSGROWFSRT xfsctl failed: %s\n"),
2bd0ea18
NS
391 progname, strerror(errno));
392 error = 1;
393 }
394 }
395 }
396
397 if (!error && (lflag | aflag)) {
398 xfs_growfs_log_t in;
399
400 if (!lsize)
401 lsize = dlsize / (geo.blocksize / BBSIZE);
402 if (iflag)
403 in.isint = 1;
404 else if (xflag)
405 in.isint = 0;
dfc130f3 406 else
2bd0ea18
NS
407 in.isint = xi.logBBsize == 0;
408 if (lsize == geo.logblocks && (in.isint == isint)) {
409 if (lflag)
410 fprintf(stderr,
9440d84d 411 _("log size unchanged, skipping\n"));
2bd0ea18
NS
412 } else if (!nflag) {
413 in.newblocks = (__u32)lsize;
93d9f139 414 if (xfsctl(fname, ffd, XFS_IOC_FSGROWFSLOG, &in) < 0) {
2bd0ea18
NS
415 if (errno == EWOULDBLOCK)
416 fprintf(stderr,
9440d84d 417 _("%s: growfs operation in progress already\n"),
2bd0ea18
NS
418 progname);
419 else if (errno == ENOSYS)
420 fprintf(stderr,
9440d84d
NS
421 _("%s: log growth not supported yet\n"),
422 progname);
2bd0ea18
NS
423 else
424 fprintf(stderr,
93d9f139 425 _("%s: XFS_IOC_FSGROWFSLOG xfsctl failed: %s\n"),
2bd0ea18
NS
426 progname, strerror(errno));
427 error = 1;
428 }
429 }
430 }
431
93d9f139
NS
432 if (xfsctl(fname, ffd, XFS_IOC_FSGEOMETRY_V1, &ngeo) < 0) {
433 fprintf(stderr, _("%s: XFS_IOC_FSGEOMETRY xfsctl failed: %s\n"),
2bd0ea18
NS
434 progname, strerror(errno));
435 exit(1);
436 }
437 if (geo.datablocks != ngeo.datablocks)
9440d84d 438 printf(_("data blocks changed from %lld to %lld\n"),
5b64e00a 439 (long long)geo.datablocks, (long long)ngeo.datablocks);
2bd0ea18 440 if (geo.imaxpct != ngeo.imaxpct)
9440d84d 441 printf(_("inode max percent changed from %d to %d\n"),
2bd0ea18
NS
442 geo.imaxpct, ngeo.imaxpct);
443 if (geo.logblocks != ngeo.logblocks)
9440d84d 444 printf(_("log blocks changed from %d to %d\n"),
2bd0ea18
NS
445 geo.logblocks, ngeo.logblocks);
446 if ((geo.logstart == 0) != (ngeo.logstart == 0))
9440d84d
NS
447 printf(_("log changed from %s to %s\n"),
448 geo.logstart ? _("internal") : _("external"),
449 ngeo.logstart ? _("internal") : _("external"));
2bd0ea18 450 if (geo.rtblocks != ngeo.rtblocks)
9440d84d 451 printf(_("realtime blocks changed from %lld to %lld\n"),
5b64e00a 452 (long long)geo.rtblocks, (long long)ngeo.rtblocks);
2bd0ea18 453 if (geo.rtextsize != ngeo.rtextsize)
9440d84d 454 printf(_("realtime extent size changed from %d to %d\n"),
2bd0ea18 455 geo.rtextsize, ngeo.rtextsize);
9beae830 456 exit(error);
2bd0ea18 457}