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