1/* $NetBSD: resize_lfs.c,v 1.5 2006/11/11 14:47:28 jmmv 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/ufs/dinode.h> 42#include <ufs/lfs/lfs.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 51#include "partutil.h" 52 53static void 54usage(void) 55{ 56 errx(1, "usage: resize_lfs [-v] [-s new-size] [filesystem]"); 57} 58 59int 60main(int argc, char **argv) 61{ 62 char *rdev, *fsname, buf[LFS_SBPAD]; 63 daddr_t newsize, newnsegs; 64 int devfd, rootfd; 65 int ch, i, verbose; 66 off_t secsize, sboff; 67 struct disk_geom geo; 68 struct dkwedge_info dkw; 69 struct lfs *fs; 70 struct statvfs vfs; 71 72 /* Initialize and parse arguments */ 73 verbose = 0; 74 newsize = 0; 75 while ((ch = getopt(argc, argv, "s:v")) != -1) { 76 switch(ch) { 77 case 's': /* New size, in sectors */ 78 newsize = strtoll(optarg, NULL, 10); 79 break; 80 case 'v': 81 ++verbose; 82 break; 83 default: 84 usage(); 85 } 86 } 87 fsname = argv[optind]; 88 89 if (fsname == NULL) 90 usage(); 91 92 /* 93 * If the user did not supply a filesystem size, use the 94 * length of the mounted partition. 95 */ 96 if (statvfs(fsname, &vfs) < 0) 97 err(1, "%s", fsname); 98 rdev = (char *)malloc(strlen(vfs.f_mntfromname + 2)); 99 sprintf(rdev, "/dev/r%s", vfs.f_mntfromname + 5); 100 devfd = open(rdev, O_RDONLY); 101 if (devfd < 0) 102 err(1, "open raw device"); 103 104 /* 105 * Read the disklabel to find the sector size. Check the 106 * given size against the partition size. We can skip some 107 * error checking here since we know the fs is mountable. 108 */ 109 if (getdiskinfo(rdev, devfd, NULL, &geo, &dkw) == -1) 110 err(1, "%s: could not get info", rdev); 111 secsize = geo.dg_secsize; 112 if (newsize > dkw.dkw_size) 113 errx(1, "new size must be <= the partition size"); 114 if (newsize == 0) 115 newsize = dkw.dkw_size; 116 117 /* Open the root of the filesystem so we can fcntl() it */ 118 rootfd = open(fsname, O_RDONLY); 119 if (rootfd < 0) 120 err(1, "open filesystem root"); 121 122 /* Read the superblock, finding alternates if necessary */ 123 fs = (struct lfs *)malloc(sizeof(*fs)); 124 for (sboff = LFS_LABELPAD;;) { 125 pread(devfd, buf, sboff, LFS_SBPAD); 126 memcpy(&fs->lfs_dlfs, buf, sizeof(struct dlfs)); 127 if (sboff == LFS_LABELPAD && fsbtob(fs, 1) > LFS_LABELPAD) 128 sboff = fsbtob(fs, (off_t)fs->lfs_sboffs[0]); 129 else 130 break; 131 } 132 close(devfd); 133 134 /* Calculate new number of segments. */ 135 newnsegs = (newsize * secsize) / fs->lfs_ssize; 136 if (newnsegs == fs->lfs_nseg) { 137 errx(0, "the filesystem is unchanged."); 138 } 139 140 /* 141 * If the new filesystem is smaller than the old, we have to 142 * invalidate the segments that extend beyond the new boundary. 143 * Make the cleaner do this for us. 144 * (XXX make the kernel able to do this instead?) 145 */ 146 for (i = fs->lfs_nseg - 1; i >= newnsegs; --i) { 147 char cmd[80]; 148 149 /* If it's already empty, don't call the cleaner */ 150 if (fcntl(rootfd, LFCNINVAL, &i) == 0) 151 continue; 152 153 sprintf(cmd, "/libexec/lfs_cleanerd -q -i %d %s", i, fsname); 154 if (system(cmd) != 0) 155 err(1, "invalidating segment %d", i); 156 } 157 158 /* Tell the filesystem to resize itself. */ 159 if (fcntl(rootfd, LFCNRESIZE, &newnsegs) == -1) { 160 err(1, "resizing"); 161 } 162 163 if (verbose) 164 printf("Successfully resized %s from %d to %lld segments\n", 165 fsname, fs->lfs_nseg, (long long)newnsegs); 166 return 0; 167} 168