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