1/* $NetBSD: resize_lfs.c,v 1.16 2023/08/07 23:27:07 mrg Exp $ */ 2/*- 3 * Copyright (c) 2005 The NetBSD Foundation, Inc. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to The NetBSD Foundation 7 * by Konrad E. Schroder <perseant@hhhh.org>. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include <sys/param.h> 32#include <sys/types.h> 33#include <sys/stat.h> 34#include <sys/ioctl.h> 35#include <sys/disklabel.h> 36#include <sys/disk.h> 37#include <sys/file.h> 38#include <sys/mount.h> 39#include <sys/statvfs.h> 40 41#include <ufs/lfs/lfs.h> 42#include <ufs/lfs/lfs_accessors.h> 43 44#include <disktab.h> 45#include <err.h> 46#include <stdio.h> 47#include <stdlib.h> 48#include <string.h> 49#include <unistd.h> 50#include <util.h> 51 52#include "partutil.h" 53 54static void 55usage(void) 56{ 57 errx(1, "usage: resize_lfs [-v] [-s new-size] [filesystem]"); 58} 59 60int 61main(int argc, char **argv) 62{ 63 char *rdev, *fsname, buf[LFS_SBPAD]; 64 size_t rdevlen; 65 daddr_t newsize, newnsegs; 66 int devfd, rootfd; 67 int ch, i, verbose; 68 off_t secsize, sboff; 69 struct disk_geom geo; 70 struct dkwedge_info dkw; 71 struct lfs *fs; 72 struct statvfs vfs; 73 74 /* Initialize and parse arguments */ 75 verbose = 0; 76 newsize = 0; 77 while ((ch = getopt(argc, argv, "s:v")) != -1) { 78 switch(ch) { 79 case 's': /* New size, in sectors */ 80 newsize = strtoll(optarg, NULL, 10); 81 break; 82 case 'v': 83 ++verbose; 84 break; 85 default: 86 usage(); 87 } 88 } 89 fsname = argv[optind]; 90 91 if (fsname == NULL) 92 usage(); 93 94 /* 95 * If the user did not supply a filesystem size, use the 96 * length of the mounted partition. 97 */ 98 if (statvfs(fsname, &vfs) < 0) 99 err(1, "%s", fsname); 100 rdevlen = strlen(vfs.f_mntfromname) + 2; 101 rdev = malloc(rdevlen); 102 if (getdiskrawname(rdev, rdevlen, vfs.f_mntfromname) == NULL) 103 err(1, "Could not convert '%s' to raw name", vfs.f_mntfromname); 104 devfd = open(rdev, O_RDONLY); 105 if (devfd < 0) 106 err(1, "open raw device"); 107 108 /* 109 * Read the disklabel to find the sector size. Check the 110 * given size against the partition size. We can skip some 111 * error checking here since we know the fs is mountable. 112 */ 113 if (getdiskinfo(rdev, devfd, NULL, &geo, &dkw) == -1) 114 err(1, "%s: could not get info", rdev); 115 secsize = geo.dg_secsize; 116 if (newsize > dkw.dkw_size) 117 errx(1, "new size must be <= the partition size"); 118 if (newsize == 0) 119 newsize = dkw.dkw_size; 120 121 /* Open the root of the filesystem so we can fcntl() it */ 122 rootfd = open(fsname, O_RDONLY); 123 if (rootfd < 0) 124 err(1, "open filesystem root"); 125 126 /* Read the superblock, finding alternates if necessary */ 127 fs = (struct lfs *)calloc(sizeof(*fs), 1); 128 for (sboff = LFS_LABELPAD;;) { 129 pread(devfd, buf, sboff, LFS_SBPAD); 130 __CTASSERT(sizeof(struct dlfs) == sizeof(struct dlfs64)); 131 memcpy(&fs->lfs_dlfs_u, buf, sizeof(struct dlfs)); 132 if (sboff == LFS_LABELPAD && lfs_fsbtob(fs, 1) > LFS_LABELPAD) 133 sboff = lfs_fsbtob(fs, (off_t)lfs_sb_getsboff(fs, 0)); 134 else 135 break; 136 } 137 close(devfd); 138 139 /* Calculate new number of segments. */ 140 newnsegs = (newsize * secsize) / lfs_sb_getssize(fs); 141 if (newnsegs == lfs_sb_getnseg(fs)) { 142 errx(0, "the filesystem is unchanged."); 143 } 144 145 /* 146 * If the new filesystem is smaller than the old, we have to 147 * invalidate the segments that extend beyond the new boundary. 148 * Make the cleaner do this for us. 149 * (XXX make the kernel able to do this instead?) 150 */ 151 for (i = lfs_sb_getnseg(fs) - 1; i >= newnsegs; --i) { 152 char cmd[128]; 153 154 /* If it's already empty, don't call the cleaner */ 155 if (fcntl(rootfd, LFCNINVAL, &i) == 0) 156 continue; 157 158 snprintf(cmd, sizeof(cmd), "/libexec/lfs_cleanerd -q -i %d %s", 159 i, fsname); 160 if (system(cmd) != 0) 161 err(1, "invalidating segment %d", i); 162 } 163 164 /* Tell the filesystem to resize itself. */ 165 if (fcntl(rootfd, LFCNRESIZE, &newnsegs) == -1) { 166 err(1, "resizing"); 167 } 168 169 if (verbose) 170 printf("Successfully resized %s from %u to %lld segments\n", 171 fsname, lfs_sb_getnseg(fs), (long long)newnsegs); 172 return 0; 173} 174