| /* |
| * Copyright (c) 2000-2005 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it would be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "libxfs.h" |
| #include "path.h" |
| |
| static void |
| usage(void) |
| { |
| fprintf(stderr, _( |
| "Usage: %s [options] mountpoint\n\n\ |
| Options:\n\ |
| -d grow data/metadata section\n\ |
| -l grow log section\n\ |
| -r grow realtime section\n\ |
| -n don't change anything, just show geometry\n\ |
| -i convert log from external to internal format\n\ |
| -t alternate location for mount table (/etc/mtab)\n\ |
| -x convert log from internal to external format\n\ |
| -D size grow data/metadata section to size blks\n\ |
| -L size grow/shrink log section to size blks\n\ |
| -R size grow realtime section to size blks\n\ |
| -e size set realtime extent size to size blks\n\ |
| -m imaxpct set inode max percent to imaxpct\n\ |
| -V print version information\n"), |
| progname); |
| exit(2); |
| } |
| |
| void |
| report_info( |
| xfs_fsop_geom_t geo, |
| char *mntpoint, |
| int isint, |
| char *logname, |
| char *rtname, |
| int lazycount, |
| int dirversion, |
| int logversion, |
| int attrversion, |
| int projid32bit, |
| int crcs_enabled, |
| int cimode, |
| int ftype_enabled, |
| int finobt_enabled, |
| int spinodes, |
| int rmapbt_enabled, |
| int reflink_enabled) |
| { |
| printf(_( |
| "meta-data=%-22s isize=%-6u agcount=%u, agsize=%u blks\n" |
| " =%-22s sectsz=%-5u attr=%u, projid32bit=%u\n" |
| " =%-22s crc=%-8u finobt=%u spinodes=%u rmapbt=%u\n" |
| " =%-22s reflink=%u\n" |
| "data =%-22s bsize=%-6u blocks=%llu, imaxpct=%u\n" |
| " =%-22s sunit=%-6u swidth=%u blks\n" |
| "naming =version %-14u bsize=%-6u ascii-ci=%d ftype=%d\n" |
| "log =%-22s bsize=%-6u blocks=%u, version=%u\n" |
| " =%-22s sectsz=%-5u sunit=%u blks, lazy-count=%u\n" |
| "realtime =%-22s extsz=%-6u blocks=%llu, rtextents=%llu\n"), |
| |
| mntpoint, geo.inodesize, geo.agcount, geo.agblocks, |
| "", geo.sectsize, attrversion, projid32bit, |
| "", crcs_enabled, finobt_enabled, spinodes, rmapbt_enabled, |
| "", reflink_enabled, |
| "", geo.blocksize, (unsigned long long)geo.datablocks, |
| geo.imaxpct, |
| "", geo.sunit, geo.swidth, |
| dirversion, geo.dirblocksize, cimode, ftype_enabled, |
| isint ? _("internal") : logname ? logname : _("external"), |
| geo.blocksize, geo.logblocks, logversion, |
| "", geo.logsectsize, geo.logsunit / geo.blocksize, lazycount, |
| !geo.rtblocks ? _("none") : rtname ? rtname : _("external"), |
| geo.rtextsize * geo.blocksize, (unsigned long long)geo.rtblocks, |
| (unsigned long long)geo.rtextents); |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| int aflag; /* fake flag, do all pieces */ |
| int c; /* current option character */ |
| long long ddsize; /* device size in 512-byte blocks */ |
| int dflag; /* -d flag */ |
| int attrversion;/* attribute version number */ |
| int dirversion; /* directory version number */ |
| int logversion; /* log version number */ |
| long long dlsize; /* device size in 512-byte blocks */ |
| long long drsize; /* device size in 512-byte blocks */ |
| long long dsize; /* new data size in fs blocks */ |
| int error; /* we have hit an error */ |
| long esize; /* new rt extent size */ |
| int ffd; /* mount point file descriptor */ |
| xfs_fsop_geom_t geo; /* current fs geometry */ |
| int iflag; /* -i flag */ |
| int isint; /* log is currently internal */ |
| int lflag; /* -l flag */ |
| long long lsize; /* new log size in fs blocks */ |
| int maxpct; /* -m flag value */ |
| int mflag; /* -m flag */ |
| int nflag; /* -n flag */ |
| xfs_fsop_geom_t ngeo; /* new fs geometry */ |
| int rflag; /* -r flag */ |
| long long rsize; /* new rt size in fs blocks */ |
| int ci; /* ASCII case-insensitive fs */ |
| int lazycount; /* lazy superblock counters */ |
| int xflag; /* -x flag */ |
| char *fname; /* mount point name */ |
| char *datadev; /* data device name */ |
| char *logdev; /* log device name */ |
| char *rtdev; /* RT device name */ |
| fs_path_t *fs; /* mount point information */ |
| libxfs_init_t xi; /* libxfs structure */ |
| int projid32bit; |
| int crcs_enabled; |
| int ftype_enabled = 0; |
| int finobt_enabled; /* free inode btree */ |
| int spinodes; |
| int rmapbt_enabled; |
| int reflink_enabled; |
| char rpath[PATH_MAX]; |
| |
| progname = basename(argv[0]); |
| setlocale(LC_ALL, ""); |
| bindtextdomain(PACKAGE, LOCALEDIR); |
| textdomain(PACKAGE); |
| |
| maxpct = esize = 0; |
| dsize = lsize = rsize = 0LL; |
| aflag = dflag = iflag = lflag = mflag = nflag = rflag = xflag = 0; |
| |
| while ((c = getopt(argc, argv, "dD:e:ilL:m:np:rR:t:xV")) != EOF) { |
| switch (c) { |
| case 'D': |
| dsize = strtoll(optarg, NULL, 10); |
| /* fall through */ |
| case 'd': |
| dflag = 1; |
| break; |
| case 'e': |
| esize = atol(optarg); |
| rflag = 1; |
| break; |
| case 'i': |
| lflag = iflag = 1; |
| break; |
| case 'L': |
| lsize = strtoll(optarg, NULL, 10); |
| /* fall through */ |
| case 'l': |
| lflag = 1; |
| break; |
| case 'm': |
| mflag = 1; |
| maxpct = atoi(optarg); |
| break; |
| case 'n': |
| nflag = 1; |
| break; |
| case 'p': |
| progname = optarg; |
| break; |
| case 'R': |
| rsize = strtoll(optarg, NULL, 10); |
| /* fall through */ |
| case 'r': |
| rflag = 1; |
| break; |
| case 't': |
| mtab_file = optarg; |
| break; |
| case 'x': |
| lflag = xflag = 1; |
| break; |
| case 'V': |
| printf(_("%s version %s\n"), progname, VERSION); |
| exit(0); |
| case '?': |
| default: |
| usage(); |
| } |
| } |
| if (argc - optind != 1) |
| usage(); |
| if (iflag && xflag) |
| usage(); |
| if (dflag + lflag + rflag + mflag == 0) |
| aflag = 1; |
| |
| fs_table_initialise(0, NULL, 0, NULL); |
| |
| if (!realpath(argv[optind], rpath)) { |
| fprintf(stderr, _("%s: path resolution failed for %s: %s\n"), |
| progname, argv[optind], strerror(errno)); |
| return 1; |
| } |
| |
| fs = fs_table_lookup_mount(rpath); |
| if (!fs) { |
| fprintf(stderr, _("%s: %s is not a mounted XFS filesystem\n"), |
| progname, argv[optind]); |
| return 1; |
| } |
| |
| fname = fs->fs_dir; |
| datadev = fs->fs_name; |
| logdev = fs->fs_log; |
| rtdev = fs->fs_rt; |
| |
| ffd = open(fname, O_RDONLY); |
| if (ffd < 0) { |
| perror(fname); |
| return 1; |
| } |
| |
| if (!platform_test_xfs_fd(ffd)) { |
| fprintf(stderr, _("%s: specified file " |
| "[\"%s\"] is not on an XFS filesystem\n"), |
| progname, fname); |
| exit(1); |
| } |
| |
| /* get the current filesystem size & geometry */ |
| if (xfsctl(fname, ffd, XFS_IOC_FSGEOMETRY, &geo) < 0) { |
| /* |
| * OK, new xfsctl barfed - back off and try earlier version |
| * as we're probably running an older kernel version. |
| * Only field added in the v2 geometry xfsctl is "logsunit" |
| * so we'll zero that out for later display (as zero). |
| */ |
| geo.logsunit = 0; |
| if (xfsctl(fname, ffd, XFS_IOC_FSGEOMETRY_V1, &geo) < 0) { |
| fprintf(stderr, _( |
| "%s: cannot determine geometry of filesystem" |
| " mounted at %s: %s\n"), |
| progname, fname, strerror(errno)); |
| exit(1); |
| } |
| } |
| isint = geo.logstart > 0; |
| lazycount = geo.flags & XFS_FSOP_GEOM_FLAGS_LAZYSB ? 1 : 0; |
| dirversion = geo.flags & XFS_FSOP_GEOM_FLAGS_DIRV2 ? 2 : 1; |
| logversion = geo.flags & XFS_FSOP_GEOM_FLAGS_LOGV2 ? 2 : 1; |
| attrversion = geo.flags & XFS_FSOP_GEOM_FLAGS_ATTR2 ? 2 : \ |
| (geo.flags & XFS_FSOP_GEOM_FLAGS_ATTR ? 1 : 0); |
| ci = geo.flags & XFS_FSOP_GEOM_FLAGS_DIRV2CI ? 1 : 0; |
| projid32bit = geo.flags & XFS_FSOP_GEOM_FLAGS_PROJID32 ? 1 : 0; |
| crcs_enabled = geo.flags & XFS_FSOP_GEOM_FLAGS_V5SB ? 1 : 0; |
| ftype_enabled = geo.flags & XFS_FSOP_GEOM_FLAGS_FTYPE ? 1 : 0; |
| finobt_enabled = geo.flags & XFS_FSOP_GEOM_FLAGS_FINOBT ? 1 : 0; |
| spinodes = geo.flags & XFS_FSOP_GEOM_FLAGS_SPINODES ? 1 : 0; |
| rmapbt_enabled = geo.flags & XFS_FSOP_GEOM_FLAGS_RMAPBT ? 1 : 0; |
| reflink_enabled = geo.flags & XFS_FSOP_GEOM_FLAGS_REFLINK ? 1 : 0; |
| if (nflag) { |
| report_info(geo, datadev, isint, logdev, rtdev, |
| lazycount, dirversion, logversion, |
| attrversion, projid32bit, crcs_enabled, ci, |
| ftype_enabled, finobt_enabled, spinodes, |
| rmapbt_enabled, reflink_enabled); |
| exit(0); |
| } |
| |
| /* |
| * Need root access from here on (using raw devices)... |
| */ |
| |
| memset(&xi, 0, sizeof(xi)); |
| xi.dname = datadev; |
| xi.logname = logdev; |
| xi.rtname = rtdev; |
| xi.isreadonly = LIBXFS_ISREADONLY; |
| |
| if (!libxfs_init(&xi)) |
| usage(); |
| |
| /* check we got the info for all the sections we are trying to modify */ |
| if (!xi.ddev) { |
| fprintf(stderr, _("%s: failed to access data device for %s\n"), |
| progname, fname); |
| exit(1); |
| } |
| if (lflag && !isint && !xi.logdev) { |
| fprintf(stderr, _("%s: failed to access external log for %s\n"), |
| progname, fname); |
| exit(1); |
| } |
| if (rflag && !xi.rtdev) { |
| fprintf(stderr, |
| _("%s: failed to access realtime device for %s\n"), |
| progname, fname); |
| exit(1); |
| } |
| |
| report_info(geo, datadev, isint, logdev, rtdev, |
| lazycount, dirversion, logversion, |
| attrversion, projid32bit, crcs_enabled, ci, ftype_enabled, |
| finobt_enabled, spinodes, rmapbt_enabled, |
| reflink_enabled); |
| |
| ddsize = xi.dsize; |
| dlsize = ( xi.logBBsize? xi.logBBsize : |
| geo.logblocks * (geo.blocksize / BBSIZE) ); |
| drsize = xi.rtsize; |
| |
| /* |
| * Ok, Linux only has a 1024-byte resolution on device _size_, |
| * and the sizes below are in basic 512-byte blocks, |
| * so if we have (size % 2), on any partition, we can't get |
| * to the last 512 bytes. Just chop it down by a block. |
| */ |
| |
| ddsize -= (ddsize % 2); |
| dlsize -= (dlsize % 2); |
| drsize -= (drsize % 2); |
| |
| error = 0; |
| |
| if (dflag | mflag | aflag) { |
| xfs_growfs_data_t in; |
| |
| if (!mflag) |
| maxpct = geo.imaxpct; |
| if (!dflag && !aflag) /* Only mflag, no data size change */ |
| dsize = geo.datablocks; |
| else if (!dsize) |
| dsize = ddsize / (geo.blocksize / BBSIZE); |
| else if (dsize > ddsize / (geo.blocksize / BBSIZE)) { |
| fprintf(stderr, _( |
| "data size %lld too large, maximum is %lld\n"), |
| (long long)dsize, |
| (long long)(ddsize/(geo.blocksize/BBSIZE))); |
| error = 1; |
| } |
| |
| if (!error && dsize < geo.datablocks) { |
| fprintf(stderr, _("data size %lld too small," |
| " old size is %lld\n"), |
| (long long)dsize, (long long)geo.datablocks); |
| error = 1; |
| } else if (!error && |
| dsize == geo.datablocks && maxpct == geo.imaxpct) { |
| if (dflag) |
| fprintf(stderr, _( |
| "data size unchanged, skipping\n")); |
| if (mflag) |
| fprintf(stderr, _( |
| "inode max pct unchanged, skipping\n")); |
| } else if (!error && !nflag) { |
| in.newblocks = (__u64)dsize; |
| in.imaxpct = (__u32)maxpct; |
| if (xfsctl(fname, ffd, XFS_IOC_FSGROWFSDATA, &in) < 0) { |
| if (errno == EWOULDBLOCK) |
| fprintf(stderr, _( |
| "%s: growfs operation in progress already\n"), |
| progname); |
| else |
| fprintf(stderr, _( |
| "%s: XFS_IOC_FSGROWFSDATA xfsctl failed: %s\n"), |
| progname, strerror(errno)); |
| error = 1; |
| } |
| } |
| } |
| |
| if (!error && (rflag | aflag)) { |
| xfs_growfs_rt_t in; |
| |
| if (!esize) |
| esize = (__u32)geo.rtextsize; |
| if (!rsize) |
| rsize = drsize / (geo.blocksize / BBSIZE); |
| else if (rsize > drsize / (geo.blocksize / BBSIZE)) { |
| fprintf(stderr, _( |
| "realtime size %lld too large, maximum is %lld\n"), |
| rsize, drsize / (geo.blocksize / BBSIZE)); |
| error = 1; |
| } |
| if (!error && rsize < geo.rtblocks) { |
| fprintf(stderr, _( |
| "realtime size %lld too small, old size is %lld\n"), |
| (long long)rsize, (long long)geo.rtblocks); |
| error = 1; |
| } else if (!error && rsize == geo.rtblocks) { |
| if (rflag) |
| fprintf(stderr, _( |
| "realtime size unchanged, skipping\n")); |
| } else if (!error && !nflag) { |
| in.newblocks = (__u64)rsize; |
| in.extsize = (__u32)esize; |
| if (xfsctl(fname, ffd, XFS_IOC_FSGROWFSRT, &in) < 0) { |
| if (errno == EWOULDBLOCK) |
| fprintf(stderr, _( |
| "%s: growfs operation in progress already\n"), |
| progname); |
| else if (errno == ENOSYS) |
| fprintf(stderr, _( |
| "%s: realtime growth not implemented\n"), |
| progname); |
| else |
| fprintf(stderr, _( |
| "%s: XFS_IOC_FSGROWFSRT xfsctl failed: %s\n"), |
| progname, strerror(errno)); |
| error = 1; |
| } |
| } |
| } |
| |
| if (!error && (lflag | aflag)) { |
| xfs_growfs_log_t in; |
| |
| if (!lsize) |
| lsize = dlsize / (geo.blocksize / BBSIZE); |
| if (iflag) |
| in.isint = 1; |
| else if (xflag) |
| in.isint = 0; |
| else |
| in.isint = xi.logBBsize == 0; |
| if (lsize == geo.logblocks && (in.isint == isint)) { |
| if (lflag) |
| fprintf(stderr, |
| _("log size unchanged, skipping\n")); |
| } else if (!nflag) { |
| in.newblocks = (__u32)lsize; |
| if (xfsctl(fname, ffd, XFS_IOC_FSGROWFSLOG, &in) < 0) { |
| if (errno == EWOULDBLOCK) |
| fprintf(stderr, |
| _("%s: growfs operation in progress already\n"), |
| progname); |
| else if (errno == ENOSYS) |
| fprintf(stderr, |
| _("%s: log growth not supported yet\n"), |
| progname); |
| else |
| fprintf(stderr, |
| _("%s: XFS_IOC_FSGROWFSLOG xfsctl failed: %s\n"), |
| progname, strerror(errno)); |
| error = 1; |
| } |
| } |
| } |
| |
| if (xfsctl(fname, ffd, XFS_IOC_FSGEOMETRY_V1, &ngeo) < 0) { |
| fprintf(stderr, _("%s: XFS_IOC_FSGEOMETRY xfsctl failed: %s\n"), |
| progname, strerror(errno)); |
| exit(1); |
| } |
| if (geo.datablocks != ngeo.datablocks) |
| printf(_("data blocks changed from %lld to %lld\n"), |
| (long long)geo.datablocks, (long long)ngeo.datablocks); |
| if (geo.imaxpct != ngeo.imaxpct) |
| printf(_("inode max percent changed from %d to %d\n"), |
| geo.imaxpct, ngeo.imaxpct); |
| if (geo.logblocks != ngeo.logblocks) |
| printf(_("log blocks changed from %d to %d\n"), |
| geo.logblocks, ngeo.logblocks); |
| if ((geo.logstart == 0) != (ngeo.logstart == 0)) |
| printf(_("log changed from %s to %s\n"), |
| geo.logstart ? _("internal") : _("external"), |
| ngeo.logstart ? _("internal") : _("external")); |
| if (geo.rtblocks != ngeo.rtblocks) |
| printf(_("realtime blocks changed from %lld to %lld\n"), |
| (long long)geo.rtblocks, (long long)ngeo.rtblocks); |
| if (geo.rtextsize != ngeo.rtextsize) |
| printf(_("realtime extent size changed from %d to %d\n"), |
| geo.rtextsize, ngeo.rtextsize); |
| exit(error); |
| } |