]>
Commit | Line | Data |
---|---|---|
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 |
30 | static void |
31 | usage(void) | |
32 | { | |
9440d84d | 33 | fprintf(stderr, _( |
2bd0ea18 NS |
34 | "Usage: %s [options] mountpoint\n\n\ |
35 | Options:\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 | ||
54 | void | |
55 | report_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 |
91 | int |
92 | main(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 | } |