growfs.c revision 243246
169800Stomsoft/* 2234846Strasz * Copyright (c) 1980, 1989, 1993 The Regents of the University of California. 369800Stomsoft * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz 4234846Strasz * Copyright (c) 2012 The FreeBSD Foundation 569800Stomsoft * All rights reserved. 6114067Sschweikh * 769800Stomsoft * This code is derived from software contributed to Berkeley by 869800Stomsoft * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt. 9114067Sschweikh * 10234846Strasz * Portions of this software were developed by Edward Tomasz Napierala 11234846Strasz * under sponsorship from the FreeBSD Foundation. 12234846Strasz * 1369800Stomsoft * Redistribution and use in source and binary forms, with or without 1469800Stomsoft * modification, are permitted provided that the following conditions 1569800Stomsoft * are met: 1669800Stomsoft * 1. Redistributions of source code must retain the above copyright 1769800Stomsoft * notice, this list of conditions and the following disclaimer. 1869800Stomsoft * 2. Redistributions in binary form must reproduce the above copyright 1969800Stomsoft * notice, this list of conditions and the following disclaimer in the 2069800Stomsoft * documentation and/or other materials provided with the distribution. 2169800Stomsoft * 3. All advertising materials mentioning features or use of this software 2269800Stomsoft * must display the following acknowledgment: 2369800Stomsoft * This product includes software developed by the University of 2469800Stomsoft * California, Berkeley and its contributors, as well as Christoph 2569800Stomsoft * Herrmann and Thomas-Henning von Kamptz. 2669800Stomsoft * 4. Neither the name of the University nor the names of its contributors 2769800Stomsoft * may be used to endorse or promote products derived from this software 2869800Stomsoft * without specific prior written permission. 29114067Sschweikh * 3069800Stomsoft * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 3169800Stomsoft * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3269800Stomsoft * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3369800Stomsoft * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3469800Stomsoft * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3569800Stomsoft * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3669800Stomsoft * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3769800Stomsoft * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3869800Stomsoft * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3969800Stomsoft * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 4069800Stomsoft * SUCH DAMAGE. 4169800Stomsoft * 4269926Stomsoft * $TSHeader: src/sbin/growfs/growfs.c,v 1.5 2000/12/12 19:31:00 tomsoft Exp $ 4369800Stomsoft * 4469800Stomsoft */ 4569800Stomsoft 4669800Stomsoft#ifndef lint 4769800Stomsoftstatic const char copyright[] = 4869800Stomsoft"@(#) Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz\n\ 4969800StomsoftCopyright (c) 1980, 1989, 1993 The Regents of the University of California.\n\ 5069800StomsoftAll rights reserved.\n"; 5169800Stomsoft#endif /* not lint */ 5269800Stomsoft 53140351Scharnier#include <sys/cdefs.h> 54140351Scharnier__FBSDID("$FreeBSD: head/sbin/growfs/growfs.c 243246 2012-11-18 19:01:00Z trasz $"); 5569800Stomsoft 5669800Stomsoft#include <sys/param.h> 5769800Stomsoft#include <sys/ioctl.h> 5869800Stomsoft#include <sys/stat.h> 59114936Sgrog#include <sys/disk.h> 60234846Strasz#include <sys/ucred.h> 61234846Strasz#include <sys/mount.h> 6269800Stomsoft 6369800Stomsoft#include <stdio.h> 6469800Stomsoft#include <paths.h> 6569800Stomsoft#include <ctype.h> 6669800Stomsoft#include <err.h> 6769800Stomsoft#include <fcntl.h> 68234846Strasz#include <fstab.h> 69234846Strasz#include <inttypes.h> 70103949Smike#include <limits.h> 71234846Strasz#include <mntopts.h> 72243246Strasz#include <paths.h> 7369800Stomsoft#include <stdlib.h> 74127798Sle#include <stdint.h> 7569800Stomsoft#include <string.h> 76127821Sbde#include <time.h> 7769800Stomsoft#include <unistd.h> 7869800Stomsoft#include <ufs/ufs/dinode.h> 7969800Stomsoft#include <ufs/ffs/fs.h> 80234846Strasz#include <libutil.h> 8169800Stomsoft 8269800Stomsoft#include "debug.h" 8369800Stomsoft 8469800Stomsoft#ifdef FS_DEBUG 8569800Stomsoftint _dbg_lvl_ = (DL_INFO); /* DL_TRC */ 8669800Stomsoft#endif /* FS_DEBUG */ 8769800Stomsoft 8869800Stomsoftstatic union { 8969800Stomsoft struct fs fs; 90234189Strasz char pad[SBLOCKSIZE]; 9169800Stomsoft} fsun1, fsun2; 9269800Stomsoft#define sblock fsun1.fs /* the new superblock */ 9369800Stomsoft#define osblock fsun2.fs /* the old superblock */ 9469800Stomsoft 9598542Smckusick/* 9698542Smckusick * Possible superblock locations ordered from most to least likely. 9798542Smckusick */ 9898542Smckusickstatic int sblock_try[] = SBLOCKSEARCH; 9998542Smckusickstatic ufs2_daddr_t sblockloc; 10098542Smckusick 10169800Stomsoftstatic union { 10269800Stomsoft struct cg cg; 103234189Strasz char pad[MAXBSIZE]; 10469800Stomsoft} cgun1, cgun2; 10569800Stomsoft#define acg cgun1.cg /* a cylinder cgroup (new) */ 10669800Stomsoft#define aocg cgun2.cg /* an old cylinder group */ 10769800Stomsoft 10898542Smckusickstatic struct csum *fscs; /* cylinder summary */ 10969800Stomsoft 11077885Stomsoftstatic void growfs(int, int, unsigned int); 11198542Smckusickstatic void rdfs(ufs2_daddr_t, size_t, void *, int); 11298542Smckusickstatic void wtfs(ufs2_daddr_t, size_t, void *, int, unsigned int); 11369800Stomsoftstatic int charsperline(void); 11469926Stomsoftstatic void usage(void); 11569800Stomsoftstatic int isblock(struct fs *, unsigned char *, int); 11669800Stomsoftstatic void clrblock(struct fs *, unsigned char *, int); 11769800Stomsoftstatic void setblock(struct fs *, unsigned char *, int); 11877885Stomsoftstatic void initcg(int, time_t, int, unsigned int); 11977885Stomsoftstatic void updjcg(int, time_t, int, int, unsigned int); 12077885Stomsoftstatic void updcsloc(time_t, int, int, unsigned int); 12198542Smckusickstatic void frag_adjust(ufs2_daddr_t, int); 12269800Stomsoftstatic void updclst(int); 123234846Straszstatic void mount_reload(const struct statfs *stfs); 12469800Stomsoft 12569800Stomsoft/* 126223652Strasz * Here we actually start growing the file system. We basically read the 127114067Sschweikh * cylinder summary from the first cylinder group as we want to update 128114067Sschweikh * this on the fly during our various operations. First we handle the 12969800Stomsoft * changes in the former last cylinder group. Afterwards we create all new 130114067Sschweikh * cylinder groups. Now we handle the cylinder group containing the 131114067Sschweikh * cylinder summary which might result in a relocation of the whole 132114067Sschweikh * structure. In the end we write back the updated cylinder summary, the 13369800Stomsoft * new superblock, and slightly patched versions of the super block 13469800Stomsoft * copies. 13569800Stomsoft */ 13669800Stomsoftstatic void 13777885Stomsoftgrowfs(int fsi, int fso, unsigned int Nflag) 13869800Stomsoft{ 13969800Stomsoft DBG_FUNC("growfs") 140232548Strasz time_t modtime; 141232548Strasz uint cylno; 142232548Strasz int i, j, width; 143232548Strasz char tmpbuf[100]; 144234314Strasz static int randinit = 0; 14569800Stomsoft 14669800Stomsoft DBG_ENTER; 14769800Stomsoft 14869800Stomsoft if (!randinit) { 14969800Stomsoft randinit = 1; 15069800Stomsoft srandomdev(); 15169800Stomsoft } 152217726Smarcel time(&modtime); 15369800Stomsoft 15469800Stomsoft /* 15569800Stomsoft * Get the cylinder summary into the memory. 15669800Stomsoft */ 15777885Stomsoft fscs = (struct csum *)calloc((size_t)1, (size_t)sblock.fs_cssize); 158232548Strasz if (fscs == NULL) 15969926Stomsoft errx(1, "calloc failed"); 16069800Stomsoft for (i = 0; i < osblock.fs_cssize; i += osblock.fs_bsize) { 16169800Stomsoft rdfs(fsbtodb(&osblock, osblock.fs_csaddr + 16277885Stomsoft numfrags(&osblock, i)), (size_t)MIN(osblock.fs_cssize - i, 163232548Strasz osblock.fs_bsize), (void *)(((char *)fscs) + i), fsi); 16469800Stomsoft } 16569800Stomsoft 16669800Stomsoft#ifdef FS_DEBUG 167232548Strasz { 168232548Strasz struct csum *dbg_csp; 169232548Strasz int dbg_csc; 170232548Strasz char dbg_line[80]; 17169800Stomsoft 172232548Strasz dbg_csp = fscs; 173232548Strasz 174232548Strasz for (dbg_csc = 0; dbg_csc < osblock.fs_ncg; dbg_csc++) { 175232548Strasz snprintf(dbg_line, sizeof(dbg_line), 176232548Strasz "%d. old csum in old location", dbg_csc); 177232548Strasz DBG_DUMP_CSUM(&osblock, dbg_line, dbg_csp++); 178232548Strasz } 17969800Stomsoft } 18069800Stomsoft#endif /* FS_DEBUG */ 18169800Stomsoft DBG_PRINT0("fscs read\n"); 18269800Stomsoft 18369800Stomsoft /* 18469800Stomsoft * Do all needed changes in the former last cylinder group. 18569800Stomsoft */ 186232548Strasz updjcg(osblock.fs_ncg - 1, modtime, fsi, fso, Nflag); 18769800Stomsoft 18869800Stomsoft /* 189223652Strasz * Dump out summary information about file system. 19069800Stomsoft */ 191234846Strasz#ifdef FS_DEBUG 192234314Strasz#define B2MBFACTOR (1 / (1024.0 * 1024.0)) 193127816Smux printf("growfs: %.1fMB (%jd sectors) block size %d, fragment size %d\n", 19469800Stomsoft (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR, 195127816Smux (intmax_t)fsbtodb(&sblock, sblock.fs_size), sblock.fs_bsize, 196127816Smux sblock.fs_fsize); 19798542Smckusick printf("\tusing %d cylinder groups of %.2fMB, %d blks, %d inodes.\n", 19898542Smckusick sblock.fs_ncg, (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR, 19998542Smckusick sblock.fs_fpg / sblock.fs_frag, sblock.fs_ipg); 20098542Smckusick if (sblock.fs_flags & FS_DOSOFTDEP) 20198542Smckusick printf("\twith soft updates\n"); 202234314Strasz#undef B2MBFACTOR 203234846Strasz#endif /* FS_DEBUG */ 20469800Stomsoft 20569800Stomsoft /* 20669800Stomsoft * Now build the cylinders group blocks and 20769800Stomsoft * then print out indices of cylinder groups. 20869800Stomsoft */ 20969800Stomsoft printf("super-block backups (for fsck -b #) at:\n"); 21069800Stomsoft i = 0; 21169800Stomsoft width = charsperline(); 21269800Stomsoft 21369800Stomsoft /* 21469800Stomsoft * Iterate for only the new cylinder groups. 21569800Stomsoft */ 21669800Stomsoft for (cylno = osblock.fs_ncg; cylno < sblock.fs_ncg; cylno++) { 217217726Smarcel initcg(cylno, modtime, fso, Nflag); 218174706Sdas j = sprintf(tmpbuf, " %jd%s", 219174706Sdas (intmax_t)fsbtodb(&sblock, cgsblock(&sblock, cylno)), 220232548Strasz cylno < (sblock.fs_ncg - 1) ? "," : "" ); 22169800Stomsoft if (i + j >= width) { 22269800Stomsoft printf("\n"); 22369800Stomsoft i = 0; 22469800Stomsoft } 22569800Stomsoft i += j; 22669800Stomsoft printf("%s", tmpbuf); 22769800Stomsoft fflush(stdout); 22869800Stomsoft } 22969800Stomsoft printf("\n"); 23069800Stomsoft 23169800Stomsoft /* 23269800Stomsoft * Do all needed changes in the first cylinder group. 23369800Stomsoft * allocate blocks in new location 23469800Stomsoft */ 235217726Smarcel updcsloc(modtime, fsi, fso, Nflag); 23669800Stomsoft 23769800Stomsoft /* 23869800Stomsoft * Now write the cylinder summary back to disk. 23969800Stomsoft */ 24069800Stomsoft for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) { 24169800Stomsoft wtfs(fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)), 24277885Stomsoft (size_t)MIN(sblock.fs_cssize - i, sblock.fs_bsize), 24377885Stomsoft (void *)(((char *)fscs) + i), fso, Nflag); 24469800Stomsoft } 24569800Stomsoft DBG_PRINT0("fscs written\n"); 24669800Stomsoft 24769800Stomsoft#ifdef FS_DEBUG 248232548Strasz { 249232548Strasz struct csum *dbg_csp; 250232548Strasz int dbg_csc; 251232548Strasz char dbg_line[80]; 25269800Stomsoft 253232548Strasz dbg_csp = fscs; 254232548Strasz for (dbg_csc = 0; dbg_csc < sblock.fs_ncg; dbg_csc++) { 255232548Strasz snprintf(dbg_line, sizeof(dbg_line), 256232548Strasz "%d. new csum in new location", dbg_csc); 257232548Strasz DBG_DUMP_CSUM(&sblock, dbg_line, dbg_csp++); 258232548Strasz } 25969800Stomsoft } 26069800Stomsoft#endif /* FS_DEBUG */ 26169800Stomsoft 26269800Stomsoft /* 26369800Stomsoft * Now write the new superblock back to disk. 26469800Stomsoft */ 265217726Smarcel sblock.fs_time = modtime; 26698542Smckusick wtfs(sblockloc, (size_t)SBLOCKSIZE, (void *)&sblock, fso, Nflag); 26769800Stomsoft DBG_PRINT0("sblock written\n"); 268232548Strasz DBG_DUMP_FS(&sblock, "new initial sblock"); 26969800Stomsoft 27069800Stomsoft /* 27169800Stomsoft * Clean up the dynamic fields in our superblock copies. 27269800Stomsoft */ 27369800Stomsoft sblock.fs_fmod = 0; 27469800Stomsoft sblock.fs_clean = 1; 27569800Stomsoft sblock.fs_ronly = 0; 27669800Stomsoft sblock.fs_cgrotor = 0; 27769800Stomsoft sblock.fs_state = 0; 27869800Stomsoft memset((void *)&sblock.fs_fsmnt, 0, sizeof(sblock.fs_fsmnt)); 27969800Stomsoft sblock.fs_flags &= FS_DOSOFTDEP; 28069800Stomsoft 28169800Stomsoft /* 28269800Stomsoft * XXX 283114067Sschweikh * The following fields are currently distributed from the superblock 28469800Stomsoft * to the copies: 28569800Stomsoft * fs_minfree 28669800Stomsoft * fs_rotdelay 28769800Stomsoft * fs_maxcontig 28869800Stomsoft * fs_maxbpg 28969800Stomsoft * fs_minfree, 29069800Stomsoft * fs_optim 29169800Stomsoft * fs_flags regarding SOFTPDATES 29269800Stomsoft * 29369800Stomsoft * We probably should rather change the summary for the cylinder group 29469800Stomsoft * statistics here to the value of what would be in there, if the file 295114067Sschweikh * system were created initially with the new size. Therefor we still 29669800Stomsoft * need to find an easy way of calculating that. 29769800Stomsoft * Possibly we can try to read the first superblock copy and apply the 298114067Sschweikh * "diffed" stats between the old and new superblock by still copying 29969800Stomsoft * certain parameters onto that. 30069800Stomsoft */ 30169800Stomsoft 30269800Stomsoft /* 30369800Stomsoft * Write out the duplicate super blocks. 30469800Stomsoft */ 30569800Stomsoft for (cylno = 0; cylno < sblock.fs_ncg; cylno++) { 30669800Stomsoft wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)), 30798542Smckusick (size_t)SBLOCKSIZE, (void *)&sblock, fso, Nflag); 30869800Stomsoft } 30969800Stomsoft DBG_PRINT0("sblock copies written\n"); 310232548Strasz DBG_DUMP_FS(&sblock, "new other sblocks"); 31169800Stomsoft 31269800Stomsoft DBG_LEAVE; 31369800Stomsoft return; 31469800Stomsoft} 31569800Stomsoft 31669800Stomsoft/* 317114067Sschweikh * This creates a new cylinder group structure, for more details please see 318114067Sschweikh * the source of newfs(8), as this function is taken over almost unchanged. 319114067Sschweikh * As this is never called for the first cylinder group, the special 32069800Stomsoft * provisions for that case are removed here. 32169800Stomsoft */ 32269800Stomsoftstatic void 323217726Smarcelinitcg(int cylno, time_t modtime, int fso, unsigned int Nflag) 32469800Stomsoft{ 32569800Stomsoft DBG_FUNC("initcg") 326212839Sbrian static caddr_t iobuf; 327203770Smckusick long blkno, start; 328241013Smdf ino_t ino; 329127798Sle ufs2_daddr_t i, cbase, dmax; 33098542Smckusick struct ufs1_dinode *dp1; 33192806Sobrien struct csum *cs; 332234312Strasz uint j, d, dupper, dlower; 33369800Stomsoft 334212839Sbrian if (iobuf == NULL && (iobuf = malloc(sblock.fs_bsize * 3)) == NULL) 33598542Smckusick errx(37, "panic: cannot allocate I/O buffer"); 336212839Sbrian 33769800Stomsoft /* 33869800Stomsoft * Determine block bounds for cylinder group. 33998542Smckusick * Allow space for super block summary information in first 34098542Smckusick * cylinder group. 34169800Stomsoft */ 34269800Stomsoft cbase = cgbase(&sblock, cylno); 34369800Stomsoft dmax = cbase + sblock.fs_fpg; 34498542Smckusick if (dmax > sblock.fs_size) 34569800Stomsoft dmax = sblock.fs_size; 34669800Stomsoft dlower = cgsblock(&sblock, cylno) - cbase; 34769800Stomsoft dupper = cgdmin(&sblock, cylno) - cbase; 34898542Smckusick if (cylno == 0) /* XXX fscs may be relocated */ 34969800Stomsoft dupper += howmany(sblock.fs_cssize, sblock.fs_fsize); 35098542Smckusick cs = &fscs[cylno]; 35198542Smckusick memset(&acg, 0, sblock.fs_cgsize); 352217726Smarcel acg.cg_time = modtime; 35369800Stomsoft acg.cg_magic = CG_MAGIC; 35469800Stomsoft acg.cg_cgx = cylno; 35569800Stomsoft acg.cg_niblk = sblock.fs_ipg; 356212839Sbrian acg.cg_initediblk = sblock.fs_ipg < 2 * INOPB(&sblock) ? 357212839Sbrian sblock.fs_ipg : 2 * INOPB(&sblock); 35869800Stomsoft acg.cg_ndblk = dmax - cbase; 35998542Smckusick if (sblock.fs_contigsumsize > 0) 36069800Stomsoft acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag; 36198542Smckusick start = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield); 36298542Smckusick if (sblock.fs_magic == FS_UFS2_MAGIC) { 36398542Smckusick acg.cg_iusedoff = start; 36498542Smckusick } else { 36598542Smckusick acg.cg_old_ncyl = sblock.fs_old_cpg; 36698542Smckusick acg.cg_old_time = acg.cg_time; 36798542Smckusick acg.cg_time = 0; 36898542Smckusick acg.cg_old_niblk = acg.cg_niblk; 36998542Smckusick acg.cg_niblk = 0; 370212839Sbrian acg.cg_initediblk = 0; 37198542Smckusick acg.cg_old_btotoff = start; 37298542Smckusick acg.cg_old_boff = acg.cg_old_btotoff + 37398542Smckusick sblock.fs_old_cpg * sizeof(int32_t); 37498542Smckusick acg.cg_iusedoff = acg.cg_old_boff + 37598542Smckusick sblock.fs_old_cpg * sizeof(u_int16_t); 37669800Stomsoft } 377103949Smike acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT); 378103949Smike acg.cg_nextfreeoff = acg.cg_freeoff + howmany(sblock.fs_fpg, CHAR_BIT); 37998542Smckusick if (sblock.fs_contigsumsize > 0) { 38069800Stomsoft acg.cg_clustersumoff = 38198542Smckusick roundup(acg.cg_nextfreeoff, sizeof(u_int32_t)); 38298542Smckusick acg.cg_clustersumoff -= sizeof(u_int32_t); 38369800Stomsoft acg.cg_clusteroff = acg.cg_clustersumoff + 38469800Stomsoft (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t); 38598542Smckusick acg.cg_nextfreeoff = acg.cg_clusteroff + 386103949Smike howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT); 38769800Stomsoft } 388203770Smckusick if (acg.cg_nextfreeoff > (unsigned)sblock.fs_cgsize) { 38969800Stomsoft /* 39098542Smckusick * This should never happen as we would have had that panic 391223652Strasz * already on file system creation 39269800Stomsoft */ 39369926Stomsoft errx(37, "panic: cylinder group too big"); 39469800Stomsoft } 39569800Stomsoft acg.cg_cs.cs_nifree += sblock.fs_ipg; 39669800Stomsoft if (cylno == 0) 397241013Smdf for (ino = 0; ino < ROOTINO; ino++) { 398241013Smdf setbit(cg_inosused(&acg), ino); 39969800Stomsoft acg.cg_cs.cs_nifree--; 40069800Stomsoft } 401136289Sscottl /* 402223652Strasz * For the old file system, we have to initialize all the inodes. 403136289Sscottl */ 404136289Sscottl if (sblock.fs_magic == FS_UFS1_MAGIC) { 405136289Sscottl bzero(iobuf, sblock.fs_bsize); 406203835Sgavin for (i = 0; i < sblock.fs_ipg / INOPF(&sblock); 407232548Strasz i += sblock.fs_frag) { 408212886Smarcel dp1 = (struct ufs1_dinode *)(void *)iobuf; 409201401Sgavin for (j = 0; j < INOPB(&sblock); j++) { 410201401Sgavin dp1->di_gen = random(); 411201401Sgavin dp1++; 412201401Sgavin } 413136289Sscottl wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i), 414136289Sscottl sblock.fs_bsize, iobuf, fso, Nflag); 415136289Sscottl } 41669800Stomsoft } 41798542Smckusick if (cylno > 0) { 41898542Smckusick /* 41998542Smckusick * In cylno 0, beginning space is reserved 42098542Smckusick * for boot and super blocks. 42198542Smckusick */ 42298542Smckusick for (d = 0; d < dlower; d += sblock.fs_frag) { 42398542Smckusick blkno = d / sblock.fs_frag; 42498542Smckusick setblock(&sblock, cg_blksfree(&acg), blkno); 42598542Smckusick if (sblock.fs_contigsumsize > 0) 42698542Smckusick setbit(cg_clustersfree(&acg), blkno); 42798542Smckusick acg.cg_cs.cs_nbfree++; 42869800Stomsoft } 42998542Smckusick sblock.fs_dsize += dlower; 43069800Stomsoft } 43169800Stomsoft sblock.fs_dsize += acg.cg_ndblk - dupper; 43269800Stomsoft if ((i = dupper % sblock.fs_frag)) { 43369800Stomsoft acg.cg_frsum[sblock.fs_frag - i]++; 43469800Stomsoft for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) { 43569800Stomsoft setbit(cg_blksfree(&acg), dupper); 43669800Stomsoft acg.cg_cs.cs_nffree++; 43769800Stomsoft } 43869800Stomsoft } 43998542Smckusick for (d = dupper; d + sblock.fs_frag <= acg.cg_ndblk; 440232548Strasz d += sblock.fs_frag) { 44169800Stomsoft blkno = d / sblock.fs_frag; 44269800Stomsoft setblock(&sblock, cg_blksfree(&acg), blkno); 44398542Smckusick if (sblock.fs_contigsumsize > 0) 44469800Stomsoft setbit(cg_clustersfree(&acg), blkno); 44569800Stomsoft acg.cg_cs.cs_nbfree++; 44669800Stomsoft } 44798542Smckusick if (d < acg.cg_ndblk) { 44898542Smckusick acg.cg_frsum[acg.cg_ndblk - d]++; 44998542Smckusick for (; d < acg.cg_ndblk; d++) { 45069800Stomsoft setbit(cg_blksfree(&acg), d); 45169800Stomsoft acg.cg_cs.cs_nffree++; 45269800Stomsoft } 45369800Stomsoft } 45469800Stomsoft if (sblock.fs_contigsumsize > 0) { 45598542Smckusick int32_t *sump = cg_clustersum(&acg); 45698542Smckusick u_char *mapp = cg_clustersfree(&acg); 45798542Smckusick int map = *mapp++; 45898542Smckusick int bit = 1; 45998542Smckusick int run = 0; 46069800Stomsoft 46169800Stomsoft for (i = 0; i < acg.cg_nclusterblks; i++) { 46298542Smckusick if ((map & bit) != 0) 46369800Stomsoft run++; 46498542Smckusick else if (run != 0) { 46598542Smckusick if (run > sblock.fs_contigsumsize) 46669800Stomsoft run = sblock.fs_contigsumsize; 46769800Stomsoft sump[run]++; 46869800Stomsoft run = 0; 46969800Stomsoft } 470103949Smike if ((i & (CHAR_BIT - 1)) != CHAR_BIT - 1) 47169800Stomsoft bit <<= 1; 47298542Smckusick else { 47369800Stomsoft map = *mapp++; 47469800Stomsoft bit = 1; 47569800Stomsoft } 47669800Stomsoft } 47769800Stomsoft if (run != 0) { 47898542Smckusick if (run > sblock.fs_contigsumsize) 47969800Stomsoft run = sblock.fs_contigsumsize; 48069800Stomsoft sump[run]++; 48169800Stomsoft } 48269800Stomsoft } 48369800Stomsoft sblock.fs_cstotal.cs_ndir += acg.cg_cs.cs_ndir; 48469800Stomsoft sblock.fs_cstotal.cs_nffree += acg.cg_cs.cs_nffree; 48569800Stomsoft sblock.fs_cstotal.cs_nbfree += acg.cg_cs.cs_nbfree; 48669800Stomsoft sblock.fs_cstotal.cs_nifree += acg.cg_cs.cs_nifree; 48769800Stomsoft *cs = acg.cg_cs; 488212839Sbrian 489212839Sbrian memcpy(iobuf, &acg, sblock.fs_cgsize); 490212839Sbrian memset(iobuf + sblock.fs_cgsize, '\0', 491212839Sbrian sblock.fs_bsize * 3 - sblock.fs_cgsize); 492212839Sbrian 49369800Stomsoft wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), 494212839Sbrian sblock.fs_bsize * 3, iobuf, fso, Nflag); 495212839Sbrian DBG_DUMP_CG(&sblock, "new cg", &acg); 49669800Stomsoft 49769800Stomsoft DBG_LEAVE; 49869800Stomsoft return; 49969800Stomsoft} 50069800Stomsoft 50169800Stomsoft/* 502114067Sschweikh * Here we add or subtract (sign +1/-1) the available fragments in a given 50369800Stomsoft * block to or from the fragment statistics. By subtracting before and adding 504114067Sschweikh * after an operation on the free frag map we can easy update the fragment 505108470Sschweikh * statistic, which seems to be otherwise a rather complex operation. 50669800Stomsoft */ 50769800Stomsoftstatic void 50898542Smckusickfrag_adjust(ufs2_daddr_t frag, int sign) 50969800Stomsoft{ 51069800Stomsoft DBG_FUNC("frag_adjust") 51169800Stomsoft int fragsize; 51269800Stomsoft int f; 51369800Stomsoft 51469800Stomsoft DBG_ENTER; 51569800Stomsoft 516234314Strasz fragsize = 0; 51769800Stomsoft /* 51869800Stomsoft * Here frag only needs to point to any fragment in the block we want 51969800Stomsoft * to examine. 52069800Stomsoft */ 521232548Strasz for (f = rounddown(frag, sblock.fs_frag); 522232548Strasz f < roundup(frag + 1, sblock.fs_frag); f++) { 52369800Stomsoft /* 524114067Sschweikh * Count contiguous free fragments. 52569800Stomsoft */ 526232548Strasz if (isset(cg_blksfree(&acg), f)) { 52769800Stomsoft fragsize++; 52869800Stomsoft } else { 529232548Strasz if (fragsize && fragsize < sblock.fs_frag) { 53069800Stomsoft /* 53169800Stomsoft * We found something in between. 53269800Stomsoft */ 533234189Strasz acg.cg_frsum[fragsize] += sign; 53469926Stomsoft DBG_PRINT2("frag_adjust [%d]+=%d\n", 535232548Strasz fragsize, sign); 53669800Stomsoft } 537232548Strasz fragsize = 0; 53869800Stomsoft } 53969800Stomsoft } 540232548Strasz if (fragsize && fragsize < sblock.fs_frag) { 54169800Stomsoft /* 54269800Stomsoft * We found something. 54369800Stomsoft */ 544232548Strasz acg.cg_frsum[fragsize] += sign; 545232548Strasz DBG_PRINT2("frag_adjust [%d]+=%d\n", fragsize, sign); 54669800Stomsoft } 547232548Strasz DBG_PRINT2("frag_adjust [[%d]]+=%d\n", fragsize, sign); 54869800Stomsoft 54969800Stomsoft DBG_LEAVE; 55069800Stomsoft return; 55169800Stomsoft} 55269800Stomsoft 55369800Stomsoft/* 55469800Stomsoft * Here we do all needed work for the former last cylinder group. It has to be 555223652Strasz * changed in any case, even if the file system ended exactly on the end of 556114067Sschweikh * this group, as there is some slightly inconsistent handling of the number 557114067Sschweikh * of cylinders in the cylinder group. We start again by reading the cylinder 55869800Stomsoft * group from disk. If the last block was not fully available, we first handle 559114067Sschweikh * the missing fragments, then we handle all new full blocks in that file 560114067Sschweikh * system and finally we handle the new last fragmented block in the file 561114067Sschweikh * system. We again have to handle the fragment statistics rotational layout 56269800Stomsoft * tables and cluster summary during all those operations. 56369800Stomsoft */ 56469800Stomsoftstatic void 565217726Smarcelupdjcg(int cylno, time_t modtime, int fsi, int fso, unsigned int Nflag) 56669800Stomsoft{ 56769800Stomsoft DBG_FUNC("updjcg") 568232548Strasz ufs2_daddr_t cbase, dmax, dupper; 569232548Strasz struct csum *cs; 570232548Strasz int i, k; 571232548Strasz int j = 0; 57269800Stomsoft 57369800Stomsoft DBG_ENTER; 57469800Stomsoft 57569800Stomsoft /* 57669800Stomsoft * Read the former last (joining) cylinder group from disk, and make 57769800Stomsoft * a copy. 57869800Stomsoft */ 57977885Stomsoft rdfs(fsbtodb(&osblock, cgtod(&osblock, cylno)), 58077885Stomsoft (size_t)osblock.fs_cgsize, (void *)&aocg, fsi); 58169800Stomsoft DBG_PRINT0("jcg read\n"); 582232548Strasz DBG_DUMP_CG(&sblock, "old joining cg", &aocg); 58369800Stomsoft 58469800Stomsoft memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2)); 58569800Stomsoft 58669800Stomsoft /* 587114067Sschweikh * If the cylinder group had already its new final size almost 58869800Stomsoft * nothing is to be done ... except: 58969800Stomsoft * For some reason the value of cg_ncyl in the last cylinder group has 590114067Sschweikh * to be zero instead of fs_cpg. As this is now no longer the last 59169800Stomsoft * cylinder group we have to change that value now to fs_cpg. 592114067Sschweikh */ 59369800Stomsoft 594232548Strasz if (cgbase(&osblock, cylno + 1) == osblock.fs_size) { 59598542Smckusick if (sblock.fs_magic == FS_UFS1_MAGIC) 596234314Strasz acg.cg_old_ncyl = sblock.fs_old_cpg; 59769800Stomsoft 59877885Stomsoft wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), 59977885Stomsoft (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag); 60069800Stomsoft DBG_PRINT0("jcg written\n"); 601232548Strasz DBG_DUMP_CG(&sblock, "new joining cg", &acg); 60269800Stomsoft 60369800Stomsoft DBG_LEAVE; 60469800Stomsoft return; 60569800Stomsoft } 60669800Stomsoft 60769800Stomsoft /* 60869800Stomsoft * Set up some variables needed later. 60969800Stomsoft */ 61069800Stomsoft cbase = cgbase(&sblock, cylno); 61169800Stomsoft dmax = cbase + sblock.fs_fpg; 61269800Stomsoft if (dmax > sblock.fs_size) 61369800Stomsoft dmax = sblock.fs_size; 61469800Stomsoft dupper = cgdmin(&sblock, cylno) - cbase; 615232548Strasz if (cylno == 0) /* XXX fscs may be relocated */ 61669800Stomsoft dupper += howmany(sblock.fs_cssize, sblock.fs_fsize); 61769800Stomsoft 61869800Stomsoft /* 61969800Stomsoft * Set pointer to the cylinder summary for our cylinder group. 62069800Stomsoft */ 62169800Stomsoft cs = fscs + cylno; 62269800Stomsoft 62369800Stomsoft /* 62469800Stomsoft * Touch the cylinder group, update all fields in the cylinder group as 62569800Stomsoft * needed, update the free space in the superblock. 62669800Stomsoft */ 627217726Smarcel acg.cg_time = modtime; 628203770Smckusick if ((unsigned)cylno == sblock.fs_ncg - 1) { 62969800Stomsoft /* 63069800Stomsoft * This is still the last cylinder group. 63169800Stomsoft */ 63298542Smckusick if (sblock.fs_magic == FS_UFS1_MAGIC) 63398542Smckusick acg.cg_old_ncyl = 63498542Smckusick sblock.fs_old_ncyl % sblock.fs_old_cpg; 63569800Stomsoft } else { 63698542Smckusick acg.cg_old_ncyl = sblock.fs_old_cpg; 63769800Stomsoft } 638232548Strasz DBG_PRINT2("jcg dbg: %d %u", cylno, sblock.fs_ncg); 639127798Sle#ifdef FS_DEBUG 64098542Smckusick if (sblock.fs_magic == FS_UFS1_MAGIC) 641232548Strasz DBG_PRINT2("%d %u", acg.cg_old_ncyl, sblock.fs_old_cpg); 642127798Sle#endif 64398542Smckusick DBG_PRINT0("\n"); 64469800Stomsoft acg.cg_ndblk = dmax - cbase; 645232548Strasz sblock.fs_dsize += acg.cg_ndblk - aocg.cg_ndblk; 646232548Strasz if (sblock.fs_contigsumsize > 0) 64769800Stomsoft acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag; 64869800Stomsoft 64969800Stomsoft /* 650114067Sschweikh * Now we have to update the free fragment bitmap for our new free 651114067Sschweikh * space. There again we have to handle the fragmentation and also 652114067Sschweikh * the rotational layout tables and the cluster summary. This is 653114067Sschweikh * also done per fragment for the first new block if the old file 654114067Sschweikh * system end was not on a block boundary, per fragment for the new 655223652Strasz * last block if the new file system end is not on a block boundary, 65669800Stomsoft * and per block for all space in between. 65769800Stomsoft * 65869800Stomsoft * Handle the first new block here if it was partially available 65969800Stomsoft * before. 66069800Stomsoft */ 661232548Strasz if (osblock.fs_size % sblock.fs_frag) { 662232548Strasz if (roundup(osblock.fs_size, sblock.fs_frag) <= 663232548Strasz sblock.fs_size) { 66469800Stomsoft /* 66569800Stomsoft * The new space is enough to fill at least this 66669800Stomsoft * block 66769800Stomsoft */ 668232548Strasz j = 0; 669232548Strasz for (i = roundup(osblock.fs_size - cbase, 670232548Strasz sblock.fs_frag) - 1; i >= osblock.fs_size - cbase; 67169800Stomsoft i--) { 67269800Stomsoft setbit(cg_blksfree(&acg), i); 67369800Stomsoft acg.cg_cs.cs_nffree++; 67469800Stomsoft j++; 67569800Stomsoft } 67669800Stomsoft 67769800Stomsoft /* 678114067Sschweikh * Check if the fragment just created could join an 67969800Stomsoft * already existing fragment at the former end of the 680223652Strasz * file system. 68169800Stomsoft */ 682232548Strasz if (isblock(&sblock, cg_blksfree(&acg), 683232548Strasz ((osblock.fs_size - cgbase(&sblock, cylno)) / 684232548Strasz sblock.fs_frag))) { 68569800Stomsoft /* 686114067Sschweikh * The block is now completely available. 68769800Stomsoft */ 68869800Stomsoft DBG_PRINT0("block was\n"); 689232548Strasz acg.cg_frsum[osblock.fs_size % sblock.fs_frag]--; 69069800Stomsoft acg.cg_cs.cs_nbfree++; 691232548Strasz acg.cg_cs.cs_nffree -= sblock.fs_frag; 692232548Strasz k = rounddown(osblock.fs_size - cbase, 69369800Stomsoft sblock.fs_frag); 694232548Strasz updclst((osblock.fs_size - cbase) / 695232548Strasz sblock.fs_frag); 69669800Stomsoft } else { 69769800Stomsoft /* 69869800Stomsoft * Lets rejoin a possible partially growed 69969800Stomsoft * fragment. 70069800Stomsoft */ 701232548Strasz k = 0; 702232548Strasz while (isset(cg_blksfree(&acg), i) && 703232548Strasz (i >= rounddown(osblock.fs_size - cbase, 70469800Stomsoft sblock.fs_frag))) { 70569800Stomsoft i--; 70669800Stomsoft k++; 70769800Stomsoft } 708232548Strasz if (k) 70969800Stomsoft acg.cg_frsum[k]--; 710232548Strasz acg.cg_frsum[k + j]++; 71169800Stomsoft } 71269800Stomsoft } else { 71369800Stomsoft /* 71469800Stomsoft * We only grow by some fragments within this last 71569800Stomsoft * block. 71669800Stomsoft */ 717232548Strasz for (i = sblock.fs_size - cbase - 1; 718232548Strasz i >= osblock.fs_size - cbase; i--) { 71969800Stomsoft setbit(cg_blksfree(&acg), i); 72069800Stomsoft acg.cg_cs.cs_nffree++; 72169800Stomsoft j++; 72269800Stomsoft } 72369800Stomsoft /* 72469800Stomsoft * Lets rejoin a possible partially growed fragment. 72569800Stomsoft */ 726232548Strasz k = 0; 727232548Strasz while (isset(cg_blksfree(&acg), i) && 728232548Strasz (i >= rounddown(osblock.fs_size - cbase, 72969800Stomsoft sblock.fs_frag))) { 73069800Stomsoft i--; 73169800Stomsoft k++; 73269800Stomsoft } 733232548Strasz if (k) 73469800Stomsoft acg.cg_frsum[k]--; 735232548Strasz acg.cg_frsum[k + j]++; 73669800Stomsoft } 73769800Stomsoft } 73869800Stomsoft 73969800Stomsoft /* 74069800Stomsoft * Handle all new complete blocks here. 74169800Stomsoft */ 742232548Strasz for (i = roundup(osblock.fs_size - cbase, sblock.fs_frag); 743232548Strasz i + sblock.fs_frag <= dmax - cbase; /* XXX <= or only < ? */ 744232548Strasz i += sblock.fs_frag) { 74569800Stomsoft j = i / sblock.fs_frag; 74669800Stomsoft setblock(&sblock, cg_blksfree(&acg), j); 74769800Stomsoft updclst(j); 74869800Stomsoft acg.cg_cs.cs_nbfree++; 74969800Stomsoft } 75069800Stomsoft 75169800Stomsoft /* 75269800Stomsoft * Handle the last new block if there are stll some new fragments left. 753114067Sschweikh * Here we don't have to bother about the cluster summary or the even 75469800Stomsoft * the rotational layout table. 75569800Stomsoft */ 75669800Stomsoft if (i < (dmax - cbase)) { 75769800Stomsoft acg.cg_frsum[dmax - cbase - i]++; 75869800Stomsoft for (; i < dmax - cbase; i++) { 75969800Stomsoft setbit(cg_blksfree(&acg), i); 76069800Stomsoft acg.cg_cs.cs_nffree++; 76169800Stomsoft } 76269800Stomsoft } 76369800Stomsoft 76469800Stomsoft sblock.fs_cstotal.cs_nffree += 76569800Stomsoft (acg.cg_cs.cs_nffree - aocg.cg_cs.cs_nffree); 76669800Stomsoft sblock.fs_cstotal.cs_nbfree += 76769800Stomsoft (acg.cg_cs.cs_nbfree - aocg.cg_cs.cs_nbfree); 76869800Stomsoft /* 76969800Stomsoft * The following statistics are not changed here: 77069800Stomsoft * sblock.fs_cstotal.cs_ndir 77169800Stomsoft * sblock.fs_cstotal.cs_nifree 77269800Stomsoft * As the statistics for this cylinder group are ready, copy it to 77369800Stomsoft * the summary information array. 77469800Stomsoft */ 77569800Stomsoft *cs = acg.cg_cs; 77669800Stomsoft 77769800Stomsoft /* 77869800Stomsoft * Write the updated "joining" cylinder group back to disk. 77969800Stomsoft */ 78077885Stomsoft wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), (size_t)sblock.fs_cgsize, 78177885Stomsoft (void *)&acg, fso, Nflag); 78269800Stomsoft DBG_PRINT0("jcg written\n"); 783232548Strasz DBG_DUMP_CG(&sblock, "new joining cg", &acg); 78469800Stomsoft 78569800Stomsoft DBG_LEAVE; 78669800Stomsoft return; 78769800Stomsoft} 78869800Stomsoft 78969800Stomsoft/* 790114067Sschweikh * Here we update the location of the cylinder summary. We have two possible 791234846Strasz * ways of growing the cylinder summary: 792114067Sschweikh * (1) We can try to grow the summary in the current location, and relocate 79369800Stomsoft * possibly used blocks within the current cylinder group. 79469800Stomsoft * (2) Alternatively we can relocate the whole cylinder summary to the first 795114067Sschweikh * new completely empty cylinder group. Once the cylinder summary is no 796114067Sschweikh * longer in the beginning of the first cylinder group you should never 797114067Sschweikh * use a version of fsck which is not aware of the possibility to have 79869800Stomsoft * this structure in a non standard place. 799234178Strasz * Option (2) is considered to be less intrusive to the structure of the file- 800234178Strasz * system, so that's the one being used. 80169800Stomsoft */ 80269800Stomsoftstatic void 803217726Smarcelupdcsloc(time_t modtime, int fsi, int fso, unsigned int Nflag) 80469800Stomsoft{ 80569800Stomsoft DBG_FUNC("updcsloc") 806232548Strasz struct csum *cs; 807232548Strasz int ocscg, ncscg; 808234178Strasz ufs2_daddr_t d; 809232548Strasz int lcs = 0; 810232548Strasz int block; 81169800Stomsoft 81269800Stomsoft DBG_ENTER; 81369800Stomsoft 814232548Strasz if (howmany(sblock.fs_cssize, sblock.fs_fsize) == 81569800Stomsoft howmany(osblock.fs_cssize, osblock.fs_fsize)) { 81669800Stomsoft /* 81769800Stomsoft * No new fragment needed. 81869800Stomsoft */ 81969800Stomsoft DBG_LEAVE; 82069800Stomsoft return; 82169800Stomsoft } 822232548Strasz ocscg = dtog(&osblock, osblock.fs_csaddr); 823232548Strasz cs = fscs + ocscg; 82469800Stomsoft 82569800Stomsoft /* 82669800Stomsoft * Read original cylinder group from disk, and make a copy. 82781311Schm * XXX If Nflag is set in some very rare cases we now miss 82881311Schm * some changes done in updjcg by reading the unmodified 82981311Schm * block from disk. 83069800Stomsoft */ 83177885Stomsoft rdfs(fsbtodb(&osblock, cgtod(&osblock, ocscg)), 83277885Stomsoft (size_t)osblock.fs_cgsize, (void *)&aocg, fsi); 83369800Stomsoft DBG_PRINT0("oscg read\n"); 834232548Strasz DBG_DUMP_CG(&sblock, "old summary cg", &aocg); 83569800Stomsoft 83669800Stomsoft memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2)); 83769800Stomsoft 83869800Stomsoft /* 83969800Stomsoft * Touch the cylinder group, set up local variables needed later 84069800Stomsoft * and update the superblock. 84169800Stomsoft */ 842217726Smarcel acg.cg_time = modtime; 84369800Stomsoft 84469800Stomsoft /* 84569800Stomsoft * XXX In the case of having active snapshots we may need much more 846114067Sschweikh * blocks for the copy on write. We need each block twice, and 847114067Sschweikh * also up to 8*3 blocks for indirect blocks for all possible 84869800Stomsoft * references. 84969800Stomsoft */ 850234178Strasz /* 851234178Strasz * There is not enough space in the old cylinder group to 852234178Strasz * relocate all blocks as needed, so we relocate the whole 853234178Strasz * cylinder group summary to a new group. We try to use the 854234178Strasz * first complete new cylinder group just created. Within the 855234178Strasz * cylinder group we align the area immediately after the 856234178Strasz * cylinder group information location in order to be as 857234178Strasz * close as possible to the original implementation of ffs. 858234178Strasz * 859234178Strasz * First we have to make sure we'll find enough space in the 860234178Strasz * new cylinder group. If not, then we currently give up. 861234178Strasz * We start with freeing everything which was used by the 862234178Strasz * fragments of the old cylinder summary in the current group. 863234178Strasz * Now we write back the group meta data, read in the needed 864234178Strasz * meta data from the new cylinder group, and start allocating 865234178Strasz * within that group. Here we can assume, the group to be 866234178Strasz * completely empty. Which makes the handling of fragments and 867234178Strasz * clusters a lot easier. 868234178Strasz */ 869234178Strasz DBG_TRC; 870234178Strasz if (sblock.fs_ncg - osblock.fs_ncg < 2) 871234178Strasz errx(2, "panic: not enough space"); 87269800Stomsoft 873234178Strasz /* 874234178Strasz * Point "d" to the first fragment not used by the cylinder 875234178Strasz * summary. 876234178Strasz */ 877234178Strasz d = osblock.fs_csaddr + (osblock.fs_cssize / osblock.fs_fsize); 87869800Stomsoft 879234178Strasz /* 880234178Strasz * Set up last cluster size ("lcs") already here. Calculate 881234178Strasz * the size for the trailing cluster just behind where "d" 882234178Strasz * points to. 883234178Strasz */ 884234178Strasz if (sblock.fs_contigsumsize > 0) { 885234178Strasz for (block = howmany(d % sblock.fs_fpg, sblock.fs_frag), 886234189Strasz lcs = 0; lcs < sblock.fs_contigsumsize; block++, lcs++) { 887234178Strasz if (isclr(cg_clustersfree(&acg), block)) 888234178Strasz break; 889234178Strasz } 890234178Strasz } 891234178Strasz 892234178Strasz /* 893234178Strasz * Point "d" to the last frag used by the cylinder summary. 894234178Strasz */ 895234178Strasz d--; 896234178Strasz 897234178Strasz DBG_PRINT1("d=%jd\n", (intmax_t)d); 898234178Strasz if ((d + 1) % sblock.fs_frag) { 89969800Stomsoft /* 900234178Strasz * The end of the cylinder summary is not a complete 901234178Strasz * block. 90269800Stomsoft */ 903234178Strasz DBG_TRC; 904234178Strasz frag_adjust(d % sblock.fs_fpg, -1); 905234178Strasz for (; (d + 1) % sblock.fs_frag; d--) { 906234178Strasz DBG_PRINT1("d=%jd\n", (intmax_t)d); 907234178Strasz setbit(cg_blksfree(&acg), d % sblock.fs_fpg); 908234178Strasz acg.cg_cs.cs_nffree++; 909234178Strasz sblock.fs_cstotal.cs_nffree++; 91069800Stomsoft } 91169800Stomsoft /* 912234178Strasz * Point "d" to the last fragment of the last 913234178Strasz * (incomplete) block of the cylinder summary. 91469800Stomsoft */ 915234178Strasz d++; 916234189Strasz frag_adjust(d % sblock.fs_fpg, 1); 91769800Stomsoft 918234178Strasz if (isblock(&sblock, cg_blksfree(&acg), 919234178Strasz (d % sblock.fs_fpg) / sblock.fs_frag)) { 920127798Sle DBG_PRINT1("d=%jd\n", (intmax_t)d); 921234178Strasz acg.cg_cs.cs_nffree -= sblock.fs_frag; 92269800Stomsoft acg.cg_cs.cs_nbfree++; 923234178Strasz sblock.fs_cstotal.cs_nffree -= sblock.fs_frag; 92469800Stomsoft sblock.fs_cstotal.cs_nbfree++; 925232548Strasz if (sblock.fs_contigsumsize > 0) { 92669800Stomsoft setbit(cg_clustersfree(&acg), 927234189Strasz (d % sblock.fs_fpg) / sblock.fs_frag); 928232548Strasz if (lcs < sblock.fs_contigsumsize) { 929232548Strasz if (lcs) 93069800Stomsoft cg_clustersum(&acg)[lcs]--; 93169800Stomsoft lcs++; 93269800Stomsoft cg_clustersum(&acg)[lcs]++; 93369800Stomsoft } 93469800Stomsoft } 93569800Stomsoft } 93669800Stomsoft /* 937234178Strasz * Point "d" to the first fragment of the block before 938234178Strasz * the last incomplete block. 93969800Stomsoft */ 940234178Strasz d--; 941234178Strasz } 94269800Stomsoft 943234178Strasz DBG_PRINT1("d=%jd\n", (intmax_t)d); 944234178Strasz for (d = rounddown(d, sblock.fs_frag); d >= osblock.fs_csaddr; 945234178Strasz d -= sblock.fs_frag) { 946234178Strasz DBG_TRC; 947234178Strasz DBG_PRINT1("d=%jd\n", (intmax_t)d); 948234178Strasz setblock(&sblock, cg_blksfree(&acg), 949234178Strasz (d % sblock.fs_fpg) / sblock.fs_frag); 950234178Strasz acg.cg_cs.cs_nbfree++; 951234178Strasz sblock.fs_cstotal.cs_nbfree++; 952234178Strasz if (sblock.fs_contigsumsize > 0) { 953234178Strasz setbit(cg_clustersfree(&acg), 954232548Strasz (d % sblock.fs_fpg) / sblock.fs_frag); 955234178Strasz /* 956234178Strasz * The last cluster size is already set up. 957234178Strasz */ 958234178Strasz if (lcs < sblock.fs_contigsumsize) { 959234178Strasz if (lcs) 960234178Strasz cg_clustersum(&acg)[lcs]--; 961234178Strasz lcs++; 962234178Strasz cg_clustersum(&acg)[lcs]++; 96369800Stomsoft } 96469800Stomsoft } 965234178Strasz } 966234178Strasz *cs = acg.cg_cs; 96769800Stomsoft 96869800Stomsoft /* 969234178Strasz * Now write the former cylinder group containing the cylinder 970234178Strasz * summary back to disk. 97169800Stomsoft */ 972234178Strasz wtfs(fsbtodb(&sblock, cgtod(&sblock, ocscg)), 973234178Strasz (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag); 974234178Strasz DBG_PRINT0("oscg written\n"); 975234178Strasz DBG_DUMP_CG(&sblock, "old summary cg", &acg); 97669800Stomsoft 97769800Stomsoft /* 978234178Strasz * Find the beginning of the new cylinder group containing the 979234178Strasz * cylinder summary. 98069800Stomsoft */ 981234178Strasz sblock.fs_csaddr = cgdmin(&sblock, osblock.fs_ncg); 982234178Strasz ncscg = dtog(&sblock, sblock.fs_csaddr); 983234178Strasz cs = fscs + ncscg; 98469800Stomsoft 98569800Stomsoft /* 986234178Strasz * If Nflag is specified, we would now read random data instead 987234178Strasz * of an empty cg structure from disk. So we can't simulate that 988234178Strasz * part for now. 98969800Stomsoft */ 990234178Strasz if (Nflag) { 991234178Strasz DBG_PRINT0("nscg update skipped\n"); 992234178Strasz DBG_LEAVE; 993234178Strasz return; 99469800Stomsoft } 99569800Stomsoft 99669800Stomsoft /* 997234178Strasz * Read the future cylinder group containing the cylinder 998234178Strasz * summary from disk, and make a copy. 99969800Stomsoft */ 1000234178Strasz rdfs(fsbtodb(&sblock, cgtod(&sblock, ncscg)), 1001234178Strasz (size_t)sblock.fs_cgsize, (void *)&aocg, fsi); 1002234178Strasz DBG_PRINT0("nscg read\n"); 1003234178Strasz DBG_DUMP_CG(&sblock, "new summary cg", &aocg); 100469800Stomsoft 1005234178Strasz memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2)); 1006234178Strasz 100769800Stomsoft /* 1008234178Strasz * Allocate all complete blocks used by the new cylinder 1009234178Strasz * summary. 101069800Stomsoft */ 1011234178Strasz for (d = sblock.fs_csaddr; d + sblock.fs_frag <= 1012234178Strasz sblock.fs_csaddr + (sblock.fs_cssize / sblock.fs_fsize); 1013234178Strasz d += sblock.fs_frag) { 1014234178Strasz clrblock(&sblock, cg_blksfree(&acg), 1015234178Strasz (d % sblock.fs_fpg) / sblock.fs_frag); 1016234178Strasz acg.cg_cs.cs_nbfree--; 1017234178Strasz sblock.fs_cstotal.cs_nbfree--; 1018234178Strasz if (sblock.fs_contigsumsize > 0) { 1019234178Strasz clrbit(cg_clustersfree(&acg), 1020234178Strasz (d % sblock.fs_fpg) / sblock.fs_frag); 102169800Stomsoft } 102269800Stomsoft } 102369800Stomsoft 102469800Stomsoft /* 1025234178Strasz * Allocate all fragments used by the cylinder summary in the 1026234178Strasz * last block. 102769800Stomsoft */ 1028234189Strasz if (d < sblock.fs_csaddr + (sblock.fs_cssize / sblock.fs_fsize)) { 1029234178Strasz for (; d - sblock.fs_csaddr < 1030234178Strasz sblock.fs_cssize/sblock.fs_fsize; d++) { 1031234178Strasz clrbit(cg_blksfree(&acg), d % sblock.fs_fpg); 1032234178Strasz acg.cg_cs.cs_nffree--; 1033234178Strasz sblock.fs_cstotal.cs_nffree--; 103469800Stomsoft } 1035234178Strasz acg.cg_cs.cs_nbfree--; 1036234178Strasz acg.cg_cs.cs_nffree += sblock.fs_frag; 1037234178Strasz sblock.fs_cstotal.cs_nbfree--; 1038234178Strasz sblock.fs_cstotal.cs_nffree += sblock.fs_frag; 1039234178Strasz if (sblock.fs_contigsumsize > 0) 1040234178Strasz clrbit(cg_clustersfree(&acg), 1041234178Strasz (d % sblock.fs_fpg) / sblock.fs_frag); 104269800Stomsoft 1043234178Strasz frag_adjust(d % sblock.fs_fpg, 1); 104469800Stomsoft } 104569800Stomsoft /* 1046234178Strasz * XXX Handle the cluster statistics here in the case this 1047234178Strasz * cylinder group is now almost full, and the remaining 1048234178Strasz * space is less then the maximum cluster size. This is 1049234178Strasz * probably not needed, as you would hardly find a file 1050234178Strasz * system which has only MAXCSBUFS+FS_MAXCONTIG of free 1051234178Strasz * space right behind the cylinder group information in 1052234178Strasz * any new cylinder group. 105369800Stomsoft */ 105469800Stomsoft 1055234178Strasz /* 1056234178Strasz * Update our statistics in the cylinder summary. 1057234178Strasz */ 105869800Stomsoft *cs = acg.cg_cs; 105969800Stomsoft 106069800Stomsoft /* 1061234178Strasz * Write the new cylinder group containing the cylinder summary 1062234178Strasz * back to disk. 106369800Stomsoft */ 1064234178Strasz wtfs(fsbtodb(&sblock, cgtod(&sblock, ncscg)), 1065234178Strasz (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag); 1066234178Strasz DBG_PRINT0("nscg written\n"); 1067232548Strasz DBG_DUMP_CG(&sblock, "new summary cg", &acg); 106869800Stomsoft 106969800Stomsoft DBG_LEAVE; 107069800Stomsoft return; 107169800Stomsoft} 107269800Stomsoft 107369800Stomsoft/* 107469800Stomsoft * Here we read some block(s) from disk. 107569800Stomsoft */ 107669800Stomsoftstatic void 107798542Smckusickrdfs(ufs2_daddr_t bno, size_t size, void *bf, int fsi) 107869800Stomsoft{ 107969800Stomsoft DBG_FUNC("rdfs") 108077885Stomsoft ssize_t n; 108169800Stomsoft 108269800Stomsoft DBG_ENTER; 108369800Stomsoft 1084232548Strasz if (bno < 0) 1085140351Scharnier err(32, "rdfs: attempting to read negative block number"); 1086232548Strasz if (lseek(fsi, (off_t)bno * DEV_BSIZE, 0) < 0) 1087127798Sle err(33, "rdfs: seek error: %jd", (intmax_t)bno); 108877885Stomsoft n = read(fsi, bf, size); 1089232548Strasz if (n != (ssize_t)size) 1090127798Sle err(34, "rdfs: read error: %jd", (intmax_t)bno); 109169800Stomsoft 109269800Stomsoft DBG_LEAVE; 109369800Stomsoft return; 109469800Stomsoft} 109569800Stomsoft 109669800Stomsoft/* 109769800Stomsoft * Here we write some block(s) to disk. 109869800Stomsoft */ 109969800Stomsoftstatic void 110098542Smckusickwtfs(ufs2_daddr_t bno, size_t size, void *bf, int fso, unsigned int Nflag) 110169800Stomsoft{ 110269800Stomsoft DBG_FUNC("wtfs") 110377885Stomsoft ssize_t n; 110469800Stomsoft 110569800Stomsoft DBG_ENTER; 110669800Stomsoft 110769800Stomsoft if (Nflag) { 110869800Stomsoft DBG_LEAVE; 110969800Stomsoft return; 111069800Stomsoft } 1111232548Strasz if (lseek(fso, (off_t)bno * DEV_BSIZE, SEEK_SET) < 0) 111269926Stomsoft err(35, "wtfs: seek error: %ld", (long)bno); 111377885Stomsoft n = write(fso, bf, size); 1114232548Strasz if (n != (ssize_t)size) 111569926Stomsoft err(36, "wtfs: write error: %ld", (long)bno); 111669800Stomsoft 111769800Stomsoft DBG_LEAVE; 111869800Stomsoft return; 111969800Stomsoft} 112069800Stomsoft 112169800Stomsoft/* 1122114067Sschweikh * Here we check if all frags of a block are free. For more details again 112369800Stomsoft * please see the source of newfs(8), as this function is taken over almost 112469800Stomsoft * unchanged. 112569800Stomsoft */ 112669800Stomsoftstatic int 112769800Stomsoftisblock(struct fs *fs, unsigned char *cp, int h) 112869800Stomsoft{ 112969800Stomsoft DBG_FUNC("isblock") 1130232548Strasz unsigned char mask; 113169800Stomsoft 113269800Stomsoft DBG_ENTER; 113369800Stomsoft 113469800Stomsoft switch (fs->fs_frag) { 113569800Stomsoft case 8: 113669800Stomsoft DBG_LEAVE; 113769800Stomsoft return (cp[h] == 0xff); 113869800Stomsoft case 4: 113969800Stomsoft mask = 0x0f << ((h & 0x1) << 2); 114069800Stomsoft DBG_LEAVE; 114169800Stomsoft return ((cp[h >> 1] & mask) == mask); 114269800Stomsoft case 2: 114369800Stomsoft mask = 0x03 << ((h & 0x3) << 1); 114469800Stomsoft DBG_LEAVE; 114569800Stomsoft return ((cp[h >> 2] & mask) == mask); 114669800Stomsoft case 1: 114769800Stomsoft mask = 0x01 << (h & 0x7); 114869800Stomsoft DBG_LEAVE; 114969800Stomsoft return ((cp[h >> 3] & mask) == mask); 115069800Stomsoft default: 115169800Stomsoft fprintf(stderr, "isblock bad fs_frag %d\n", fs->fs_frag); 115269800Stomsoft DBG_LEAVE; 115369800Stomsoft return (0); 115469800Stomsoft } 115569800Stomsoft} 115669800Stomsoft 115769800Stomsoft/* 115869800Stomsoft * Here we allocate a complete block in the block map. For more details again 1159114067Sschweikh * please see the source of newfs(8), as this function is taken over almost 116069800Stomsoft * unchanged. 116169800Stomsoft */ 116269800Stomsoftstatic void 116369800Stomsoftclrblock(struct fs *fs, unsigned char *cp, int h) 116469800Stomsoft{ 116569800Stomsoft DBG_FUNC("clrblock") 116669800Stomsoft 116769800Stomsoft DBG_ENTER; 116869800Stomsoft 116969800Stomsoft switch ((fs)->fs_frag) { 117069800Stomsoft case 8: 117169800Stomsoft cp[h] = 0; 117269800Stomsoft break; 117369800Stomsoft case 4: 117469800Stomsoft cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2)); 117569800Stomsoft break; 117669800Stomsoft case 2: 117769800Stomsoft cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1)); 117869800Stomsoft break; 117969800Stomsoft case 1: 118069800Stomsoft cp[h >> 3] &= ~(0x01 << (h & 0x7)); 118169800Stomsoft break; 118269800Stomsoft default: 118369926Stomsoft warnx("clrblock bad fs_frag %d", fs->fs_frag); 118469800Stomsoft break; 118569800Stomsoft } 118669800Stomsoft 118769800Stomsoft DBG_LEAVE; 118869800Stomsoft return; 118969800Stomsoft} 119069800Stomsoft 119169800Stomsoft/* 119269800Stomsoft * Here we free a complete block in the free block map. For more details again 1193114067Sschweikh * please see the source of newfs(8), as this function is taken over almost 119469800Stomsoft * unchanged. 119569800Stomsoft */ 119669800Stomsoftstatic void 119769800Stomsoftsetblock(struct fs *fs, unsigned char *cp, int h) 119869800Stomsoft{ 119969800Stomsoft DBG_FUNC("setblock") 120069800Stomsoft 120169800Stomsoft DBG_ENTER; 120269800Stomsoft 120369800Stomsoft switch (fs->fs_frag) { 120469800Stomsoft case 8: 120569800Stomsoft cp[h] = 0xff; 120669800Stomsoft break; 120769800Stomsoft case 4: 120869800Stomsoft cp[h >> 1] |= (0x0f << ((h & 0x1) << 2)); 120969800Stomsoft break; 121069800Stomsoft case 2: 121169800Stomsoft cp[h >> 2] |= (0x03 << ((h & 0x3) << 1)); 121269800Stomsoft break; 121369800Stomsoft case 1: 121469800Stomsoft cp[h >> 3] |= (0x01 << (h & 0x7)); 121569800Stomsoft break; 121669800Stomsoft default: 121769926Stomsoft warnx("setblock bad fs_frag %d", fs->fs_frag); 121869800Stomsoft break; 121969800Stomsoft } 122069800Stomsoft 122169800Stomsoft DBG_LEAVE; 122269800Stomsoft return; 122369800Stomsoft} 122469800Stomsoft 122569800Stomsoft/* 122669800Stomsoft * Figure out how many lines our current terminal has. For more details again 1227114067Sschweikh * please see the source of newfs(8), as this function is taken over almost 122869800Stomsoft * unchanged. 122969800Stomsoft */ 123069800Stomsoftstatic int 123169800Stomsoftcharsperline(void) 123269800Stomsoft{ 123369800Stomsoft DBG_FUNC("charsperline") 1234232548Strasz int columns; 1235232548Strasz char *cp; 1236232548Strasz struct winsize ws; 123769800Stomsoft 123869800Stomsoft DBG_ENTER; 123969800Stomsoft 124069800Stomsoft columns = 0; 1241232548Strasz if (ioctl(0, TIOCGWINSZ, &ws) != -1) 124269800Stomsoft columns = ws.ws_col; 1243232548Strasz if (columns == 0 && (cp = getenv("COLUMNS"))) 124469800Stomsoft columns = atoi(cp); 1245232548Strasz if (columns == 0) 124669800Stomsoft columns = 80; /* last resort */ 124769800Stomsoft 124869800Stomsoft DBG_LEAVE; 1249234420Strasz return (columns); 125069800Stomsoft} 125169800Stomsoft 1252234846Straszstatic int 1253234846Straszis_dev(const char *name) 1254234846Strasz{ 1255234846Strasz struct stat devstat; 1256234846Strasz 1257234846Strasz if (stat(name, &devstat) != 0) 1258234846Strasz return (0); 1259234846Strasz if (!S_ISCHR(devstat.st_mode)) 1260234846Strasz return (0); 1261234846Strasz return (1); 1262234846Strasz} 1263234846Strasz 1264114936Sgrog/* 1265234846Strasz * Return mountpoint on which the device is currently mounted. 1266234846Strasz */ 1267234846Straszstatic const struct statfs * 1268234846Straszdev_to_statfs(const char *dev) 1269114936Sgrog{ 1270234846Strasz struct stat devstat, mntdevstat; 1271234846Strasz struct statfs *mntbuf, *statfsp; 1272234846Strasz char device[MAXPATHLEN]; 1273234846Strasz char *mntdevname; 1274234846Strasz int i, mntsize; 1275114936Sgrog 1276234846Strasz /* 1277234846Strasz * First check the mounted filesystems. 1278234846Strasz */ 1279234846Strasz if (stat(dev, &devstat) != 0) 1280234846Strasz return (NULL); 1281234846Strasz if (!S_ISCHR(devstat.st_mode) && !S_ISBLK(devstat.st_mode)) 1282234846Strasz return (NULL); 1283114936Sgrog 1284234846Strasz mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 1285234846Strasz for (i = 0; i < mntsize; i++) { 1286234846Strasz statfsp = &mntbuf[i]; 1287234846Strasz mntdevname = statfsp->f_mntfromname; 1288234846Strasz if (*mntdevname != '/') { 1289234846Strasz strcpy(device, _PATH_DEV); 1290234846Strasz strcat(device, mntdevname); 1291234846Strasz mntdevname = device; 1292234846Strasz } 1293234846Strasz if (stat(mntdevname, &mntdevstat) == 0 && 1294234846Strasz mntdevstat.st_rdev == devstat.st_rdev) 1295234846Strasz return (statfsp); 1296234846Strasz } 1297114936Sgrog 1298234846Strasz return (NULL); 1299114936Sgrog} 1300114936Sgrog 1301234846Straszstatic const char * 1302234846Straszmountpoint_to_dev(const char *mountpoint) 1303234846Strasz{ 1304234846Strasz struct statfs *mntbuf, *statfsp; 1305234846Strasz struct fstab *fs; 1306234846Strasz int i, mntsize; 1307234846Strasz 1308234846Strasz /* 1309234846Strasz * First check the mounted filesystems. 1310234846Strasz */ 1311234846Strasz mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 1312234846Strasz for (i = 0; i < mntsize; i++) { 1313234846Strasz statfsp = &mntbuf[i]; 1314234846Strasz 1315234846Strasz if (strcmp(statfsp->f_mntonname, mountpoint) == 0) 1316234846Strasz return (statfsp->f_mntfromname); 1317234846Strasz } 1318234846Strasz 1319234846Strasz /* 1320234846Strasz * Check the fstab. 1321234846Strasz */ 1322234846Strasz fs = getfsfile(mountpoint); 1323234846Strasz if (fs != NULL) 1324234846Strasz return (fs->fs_spec); 1325234846Strasz 1326234846Strasz return (NULL); 1327234846Strasz} 1328234846Strasz 1329234846Straszstatic const char * 1330234846Straszgetdev(const char *name) 1331234846Strasz{ 1332234846Strasz static char device[MAXPATHLEN]; 1333234846Strasz const char *cp, *dev; 1334234846Strasz 1335234846Strasz if (is_dev(name)) 1336234846Strasz return (name); 1337234846Strasz 1338234846Strasz cp = strrchr(name, '/'); 1339234846Strasz if (cp == 0) { 1340234846Strasz snprintf(device, sizeof(device), "%s%s", _PATH_DEV, name); 1341234846Strasz if (is_dev(device)) 1342234846Strasz return (device); 1343234846Strasz } 1344234846Strasz 1345234846Strasz dev = mountpoint_to_dev(name); 1346234846Strasz if (dev != NULL && is_dev(dev)) 1347234846Strasz return (dev); 1348234846Strasz 1349234846Strasz return (NULL); 1350234846Strasz} 1351234846Strasz 135269800Stomsoft/* 1353232548Strasz * growfs(8) is a utility which allows to increase the size of an existing 1354223652Strasz * ufs file system. Currently this can only be done on unmounted file system. 1355114067Sschweikh * It recognizes some command line options to specify the new desired size, 1356223652Strasz * and it does some basic checkings. The old file system size is determined 1357114067Sschweikh * and after some more checks like we can really access the new last block 135869800Stomsoft * on the disk etc. we calculate the new parameters for the superblock. After 1359233656Strasz * having done this we just call growfs() which will do the work. 136069800Stomsoft * We still have to provide support for snapshots. Therefore we first have to 1361114067Sschweikh * understand what data structures are always replicated in the snapshot on 1362114067Sschweikh * creation, for all other blocks we touch during our procedure, we have to 136369926Stomsoft * keep the old blocks unchanged somewhere available for the snapshots. If we 1364114067Sschweikh * are lucky, then we only have to handle our blocks to be relocated in that 136569800Stomsoft * way. 1366114067Sschweikh * Also we have to consider in what order we actually update the critical 1367223652Strasz * data structures of the file system to make sure, that in case of a disaster 136869800Stomsoft * fsck(8) is still able to restore any lost data. 1369114067Sschweikh * The foreseen last step then will be to provide for growing even mounted 1370223652Strasz * file systems. There we have to extend the mount() system call to provide 1371223652Strasz * userland access to the file system locking facility. 137269800Stomsoft */ 137369800Stomsoftint 137469800Stomsoftmain(int argc, char **argv) 137569800Stomsoft{ 137669800Stomsoft DBG_FUNC("main") 1377234846Strasz const char *device; 1378234846Strasz const struct statfs *statfsp; 1379234846Strasz uint64_t size = 0; 1380234846Strasz off_t mediasize; 1381234846Strasz int error, i, j, fsi, fso, ch, Nflag = 0, yflag = 0; 1382234846Strasz char *p, reply[5], oldsizebuf[6], newsizebuf[6]; 1383234846Strasz void *testbuf; 138469800Stomsoft 138569800Stomsoft DBG_ENTER; 138669800Stomsoft 1387232548Strasz while ((ch = getopt(argc, argv, "Ns:vy")) != -1) { 138869800Stomsoft switch(ch) { 138969800Stomsoft case 'N': 1390232548Strasz Nflag = 1; 139169800Stomsoft break; 139269800Stomsoft case 's': 1393234846Strasz size = (off_t)strtoumax(optarg, &p, 0); 1394234846Strasz if (p == NULL || *p == '\0') 1395234846Strasz size *= DEV_BSIZE; 1396234846Strasz else if (*p == 'b' || *p == 'B') 1397234846Strasz ; /* do nothing */ 1398234846Strasz else if (*p == 'k' || *p == 'K') 1399234846Strasz size <<= 10; 1400234846Strasz else if (*p == 'm' || *p == 'M') 1401234846Strasz size <<= 20; 1402234846Strasz else if (*p == 'g' || *p == 'G') 1403234846Strasz size <<= 30; 1404234846Strasz else if (*p == 't' || *p == 'T') { 1405234846Strasz size <<= 30; 1406234846Strasz size <<= 10; 1407234846Strasz } else 1408234846Strasz errx(1, "unknown suffix on -s argument"); 140969800Stomsoft break; 141069800Stomsoft case 'v': /* for compatibility to newfs */ 141169800Stomsoft break; 141269800Stomsoft case 'y': 1413234846Strasz yflag = 1; 141469800Stomsoft break; 141569800Stomsoft case '?': 141669800Stomsoft /* FALLTHROUGH */ 141769800Stomsoft default: 141869926Stomsoft usage(); 141969800Stomsoft } 142069800Stomsoft } 142169800Stomsoft argc -= optind; 142269800Stomsoft argv += optind; 142369800Stomsoft 1424232548Strasz if (argc != 1) 142569926Stomsoft usage(); 142669800Stomsoft 142769800Stomsoft /* 1428234846Strasz * Now try to guess the device name. 142969800Stomsoft */ 1430234846Strasz device = getdev(*argv); 1431234846Strasz if (device == NULL) 1432234846Strasz errx(1, "cannot find special device for %s", *argv); 143369800Stomsoft 1434234846Strasz statfsp = dev_to_statfs(device); 143569800Stomsoft 143669800Stomsoft fsi = open(device, O_RDONLY); 1437232548Strasz if (fsi < 0) 143869926Stomsoft err(1, "%s", device); 143969800Stomsoft 144069800Stomsoft /* 1441234846Strasz * Try to guess the slice size if not specified. 144269800Stomsoft */ 1443234846Strasz if (ioctl(fsi, DIOCGMEDIASIZE, &mediasize) == -1) 1444234846Strasz err(1,"DIOCGMEDIASIZE"); 144569800Stomsoft 144669800Stomsoft /* 1447223652Strasz * Check if that partition is suitable for growing a file system. 144869800Stomsoft */ 1449234846Strasz if (mediasize < 1) 145069926Stomsoft errx(1, "partition is unavailable"); 145169800Stomsoft 145269800Stomsoft /* 145369800Stomsoft * Read the current superblock, and take a backup. 145469800Stomsoft */ 145598542Smckusick for (i = 0; sblock_try[i] != -1; i++) { 145698542Smckusick sblockloc = sblock_try[i] / DEV_BSIZE; 145798542Smckusick rdfs(sblockloc, (size_t)SBLOCKSIZE, (void *)&(osblock), fsi); 145898542Smckusick if ((osblock.fs_magic == FS_UFS1_MAGIC || 1459232548Strasz (osblock.fs_magic == FS_UFS2_MAGIC && 1460232548Strasz osblock.fs_sblockloc == sblock_try[i])) && 146198542Smckusick osblock.fs_bsize <= MAXBSIZE && 1462127798Sle osblock.fs_bsize >= (int32_t) sizeof(struct fs)) 146398542Smckusick break; 146498542Smckusick } 1465232548Strasz if (sblock_try[i] == -1) 146669926Stomsoft errx(1, "superblock not recognized"); 146769800Stomsoft memcpy((void *)&fsun1, (void *)&fsun2, sizeof(fsun2)); 146869800Stomsoft 146969800Stomsoft DBG_OPEN("/tmp/growfs.debug"); /* already here we need a superblock */ 1470232548Strasz DBG_DUMP_FS(&sblock, "old sblock"); 147169800Stomsoft 147269800Stomsoft /* 1473233656Strasz * Determine size to grow to. Default to the device size. 147469800Stomsoft */ 1475234846Strasz if (size == 0) 1476234846Strasz size = mediasize; 1477234846Strasz else { 1478234846Strasz if (size > (uint64_t)mediasize) { 1479234846Strasz humanize_number(oldsizebuf, sizeof(oldsizebuf), size, 1480234846Strasz "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1481234846Strasz humanize_number(newsizebuf, sizeof(newsizebuf), 1482234846Strasz mediasize, 1483234846Strasz "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1484234846Strasz 1485234846Strasz errx(1, "requested size %s is larger " 1486234846Strasz "than the available %s", oldsizebuf, newsizebuf); 1487234846Strasz } 148869800Stomsoft } 148969800Stomsoft 1490234846Strasz if (size <= (uint64_t)(osblock.fs_size * osblock.fs_fsize)) { 1491234846Strasz humanize_number(oldsizebuf, sizeof(oldsizebuf), 1492234846Strasz osblock.fs_size * osblock.fs_fsize, 1493234846Strasz "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1494234846Strasz humanize_number(newsizebuf, sizeof(newsizebuf), size, 1495234846Strasz "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1496234846Strasz 1497234846Strasz errx(1, "requested size %s is not larger than the current " 1498234846Strasz "filesystem size %s", newsizebuf, oldsizebuf); 1499234846Strasz } 1500234846Strasz 1501234846Strasz sblock.fs_size = dbtofsb(&osblock, size / DEV_BSIZE); 1502242379Strasz sblock.fs_providersize = dbtofsb(&osblock, mediasize / DEV_BSIZE); 1503234846Strasz 150469800Stomsoft /* 1505234846Strasz * Are we really growing? 150669800Stomsoft */ 1507232548Strasz if (osblock.fs_size >= sblock.fs_size) { 1508127798Sle errx(1, "we are not growing (%jd->%jd)", 1509127798Sle (intmax_t)osblock.fs_size, (intmax_t)sblock.fs_size); 151069800Stomsoft } 151169800Stomsoft 151269800Stomsoft /* 151369800Stomsoft * Check if we find an active snapshot. 151469800Stomsoft */ 1515234846Strasz if (yflag == 0) { 1516232548Strasz for (j = 0; j < FSMAXSNAP; j++) { 1517232548Strasz if (sblock.fs_snapinum[j]) { 1518223652Strasz errx(1, "active snapshot found in file system; " 1519223429Strasz "please remove all snapshots before " 1520140351Scharnier "using growfs"); 152169800Stomsoft } 1522232548Strasz if (!sblock.fs_snapinum[j]) /* list is dense */ 152369800Stomsoft break; 152469800Stomsoft } 152569800Stomsoft } 152669800Stomsoft 1527234846Strasz if (yflag == 0 && Nflag == 0) { 1528234846Strasz if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) 1529243246Strasz printf("Device is mounted read-write; resizing will " 1530243246Strasz "result in temporary write suspension for %s.\n", 1531243246Strasz statfsp->f_mntonname); 1532234846Strasz printf("It's strongly recommended to make a backup " 1533223652Strasz "before growing the file system.\n" 1534234846Strasz "OK to grow filesystem on %s", device); 1535234846Strasz if (statfsp != NULL) 1536234846Strasz printf(", mounted on %s,", statfsp->f_mntonname); 1537234846Strasz humanize_number(oldsizebuf, sizeof(oldsizebuf), 1538234846Strasz osblock.fs_size * osblock.fs_fsize, 1539234846Strasz "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1540234846Strasz humanize_number(newsizebuf, sizeof(newsizebuf), 1541234846Strasz sblock.fs_size * sblock.fs_fsize, 1542234846Strasz "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1543234846Strasz printf(" from %s to %s? [Yes/No] ", oldsizebuf, newsizebuf); 1544234846Strasz fflush(stdout); 154577885Stomsoft fgets(reply, (int)sizeof(reply), stdin); 154669800Stomsoft if (strcmp(reply, "Yes\n")){ 1547223429Strasz printf("\nNothing done\n"); 154869800Stomsoft exit (0); 1549114067Sschweikh } 155069800Stomsoft } 155169800Stomsoft 1552234846Strasz /* 1553234846Strasz * Try to access our device for writing. If it's not mounted, 1554234846Strasz * or mounted read-only, simply open it; otherwise, use UFS 1555234846Strasz * suspension mechanism. 1556234846Strasz */ 1557234846Strasz if (Nflag) { 1558234846Strasz fso = -1; 1559234846Strasz } else { 1560243246Strasz if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) { 1561243246Strasz fso = open(_PATH_UFSSUSPEND, O_RDWR); 1562243246Strasz if (fso == -1) 1563243246Strasz err(1, "unable to open %s", _PATH_UFSSUSPEND); 1564243246Strasz error = ioctl(fso, UFSSUSPEND, &statfsp->f_fsid); 1565243246Strasz if (error != 0) 1566243246Strasz err(1, "UFSSUSPEND"); 1567243246Strasz } else { 1568243246Strasz fso = open(device, O_WRONLY); 1569243246Strasz if (fso < 0) 1570243246Strasz err(1, "%s", device); 1571243246Strasz } 1572234846Strasz } 157369800Stomsoft 157469800Stomsoft /* 1575234846Strasz * Try to access our new last block in the file system. 157669800Stomsoft */ 1577234846Strasz testbuf = malloc(sblock.fs_fsize); 1578234846Strasz if (testbuf == NULL) 1579234846Strasz err(1, "malloc"); 1580235079Strasz rdfs((ufs2_daddr_t)((size - sblock.fs_fsize) / DEV_BSIZE), 1581234846Strasz sblock.fs_fsize, testbuf, fsi); 1582235079Strasz wtfs((ufs2_daddr_t)((size - sblock.fs_fsize) / DEV_BSIZE), 1583234846Strasz sblock.fs_fsize, testbuf, fso, Nflag); 1584234846Strasz free(testbuf); 158569800Stomsoft 158669800Stomsoft /* 158769800Stomsoft * Now calculate new superblock values and check for reasonable 1588223652Strasz * bound for new file system size: 1589233656Strasz * fs_size: is derived from user input 159069800Stomsoft * fs_dsize: should get updated in the routines creating or 159169800Stomsoft * updating the cylinder groups on the fly 159269800Stomsoft * fs_cstotal: should get updated in the routines creating or 159369800Stomsoft * updating the cylinder groups 159469800Stomsoft */ 159569800Stomsoft 159669800Stomsoft /* 1597223652Strasz * Update the number of cylinders and cylinder groups in the file system. 159869800Stomsoft */ 159998542Smckusick if (sblock.fs_magic == FS_UFS1_MAGIC) { 160098542Smckusick sblock.fs_old_ncyl = 160198542Smckusick sblock.fs_size * sblock.fs_old_nspf / sblock.fs_old_spc; 160298542Smckusick if (sblock.fs_size * sblock.fs_old_nspf > 160398542Smckusick sblock.fs_old_ncyl * sblock.fs_old_spc) 160498542Smckusick sblock.fs_old_ncyl++; 160569800Stomsoft } 160698542Smckusick sblock.fs_ncg = howmany(sblock.fs_size, sblock.fs_fpg); 160769800Stomsoft 160898542Smckusick if (sblock.fs_size % sblock.fs_fpg != 0 && 160998542Smckusick sblock.fs_size % sblock.fs_fpg < cgdmin(&sblock, sblock.fs_ncg)) { 161069800Stomsoft /* 161169800Stomsoft * The space in the new last cylinder group is too small, 161269800Stomsoft * so revert back. 161369800Stomsoft */ 161469800Stomsoft sblock.fs_ncg--; 161598542Smckusick if (sblock.fs_magic == FS_UFS1_MAGIC) 161698542Smckusick sblock.fs_old_ncyl = sblock.fs_ncg * sblock.fs_old_cpg; 1617127798Sle printf("Warning: %jd sector(s) cannot be allocated.\n", 1618127798Sle (intmax_t)fsbtodb(&sblock, sblock.fs_size % sblock.fs_fpg)); 161998542Smckusick sblock.fs_size = sblock.fs_ncg * sblock.fs_fpg; 162069800Stomsoft } 162169800Stomsoft 162269800Stomsoft /* 162369800Stomsoft * Update the space for the cylinder group summary information in the 162469800Stomsoft * respective cylinder group data area. 162569800Stomsoft */ 162669800Stomsoft sblock.fs_cssize = 162769800Stomsoft fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum)); 1628114067Sschweikh 1629232548Strasz if (osblock.fs_size >= sblock.fs_size) 163069926Stomsoft errx(1, "not enough new space"); 163169800Stomsoft 163269800Stomsoft DBG_PRINT0("sblock calculated\n"); 163369800Stomsoft 163469800Stomsoft /* 163569800Stomsoft * Ok, everything prepared, so now let's do the tricks. 163669800Stomsoft */ 163769800Stomsoft growfs(fsi, fso, Nflag); 163869800Stomsoft 163969800Stomsoft close(fsi); 1640234846Strasz if (fso > -1) { 1641243246Strasz if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) { 1642243246Strasz error = ioctl(fso, UFSRESUME); 1643243246Strasz if (error != 0) 1644243246Strasz err(1, "UFSRESUME"); 1645243246Strasz } 1646234846Strasz error = close(fso); 1647234846Strasz if (error != 0) 1648234846Strasz err(1, "close"); 1649243246Strasz if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) != 0) 1650243246Strasz mount_reload(statfsp); 1651234846Strasz } 165269800Stomsoft 165369800Stomsoft DBG_CLOSE; 165469800Stomsoft 165569800Stomsoft DBG_LEAVE; 1656234420Strasz return (0); 165769800Stomsoft} 165869800Stomsoft 165969800Stomsoft/* 166069800Stomsoft * Dump a line of usage. 166169800Stomsoft */ 166269800Stomsoftstatic void 166369926Stomsoftusage(void) 1664114067Sschweikh{ 166569800Stomsoft DBG_FUNC("usage") 166669800Stomsoft 166769800Stomsoft DBG_ENTER; 166869800Stomsoft 1669234846Strasz fprintf(stderr, "usage: growfs [-Ny] [-s size] special | filesystem\n"); 167069926Stomsoft 167169800Stomsoft DBG_LEAVE; 167269926Stomsoft exit(1); 167369800Stomsoft} 167469800Stomsoft 167569800Stomsoft/* 1676114067Sschweikh * This updates most parameters and the bitmap related to cluster. We have to 1677114067Sschweikh * assume that sblock, osblock, acg are set up. 167869800Stomsoft */ 167969800Stomsoftstatic void 168069800Stomsoftupdclst(int block) 1681114067Sschweikh{ 168269800Stomsoft DBG_FUNC("updclst") 1683232548Strasz static int lcs = 0; 168469800Stomsoft 168569800Stomsoft DBG_ENTER; 168669800Stomsoft 1687232548Strasz if (sblock.fs_contigsumsize < 1) /* no clustering */ 168869800Stomsoft return; 168969800Stomsoft /* 169069800Stomsoft * update cluster allocation map 169169800Stomsoft */ 169269800Stomsoft setbit(cg_clustersfree(&acg), block); 169369800Stomsoft 169469800Stomsoft /* 169569800Stomsoft * update cluster summary table 169669800Stomsoft */ 1697232548Strasz if (!lcs) { 169869800Stomsoft /* 169969800Stomsoft * calculate size for the trailing cluster 170069800Stomsoft */ 1701232548Strasz for (block--; lcs < sblock.fs_contigsumsize; block--, lcs++ ) { 1702232548Strasz if (isclr(cg_clustersfree(&acg), block)) 170369800Stomsoft break; 170469800Stomsoft } 1705114067Sschweikh } 1706232548Strasz if (lcs < sblock.fs_contigsumsize) { 1707232548Strasz if (lcs) 170869800Stomsoft cg_clustersum(&acg)[lcs]--; 170969800Stomsoft lcs++; 171069800Stomsoft cg_clustersum(&acg)[lcs]++; 171169800Stomsoft } 171269800Stomsoft 171369800Stomsoft DBG_LEAVE; 171469800Stomsoft return; 171569800Stomsoft} 1716234846Strasz 1717234846Straszstatic void 1718234846Straszmount_reload(const struct statfs *stfs) 1719234846Strasz{ 1720234846Strasz char errmsg[255]; 1721234846Strasz struct iovec *iov; 1722234846Strasz int iovlen; 1723234846Strasz 1724234846Strasz iov = NULL; 1725234846Strasz iovlen = 0; 1726234846Strasz *errmsg = '\0'; 1727234846Strasz build_iovec(&iov, &iovlen, "fstype", __DECONST(char *, "ffs"), 4); 1728234846Strasz build_iovec(&iov, &iovlen, "fspath", __DECONST(char *, stfs->f_mntonname), (size_t)-1); 1729234846Strasz build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 1730234846Strasz build_iovec(&iov, &iovlen, "update", NULL, 0); 1731234846Strasz build_iovec(&iov, &iovlen, "reload", NULL, 0); 1732234846Strasz 1733234846Strasz if (nmount(iov, iovlen, stfs->f_flags) < 0) { 1734234846Strasz errmsg[sizeof(errmsg) - 1] = '\0'; 1735234846Strasz err(9, "%s: cannot reload filesystem%s%s", stfs->f_mntonname, 1736234846Strasz *errmsg != '\0' ? ": " : "", errmsg); 1737234846Strasz } 1738234846Strasz} 1739