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$"); 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]; 14469800Stomsoft 14569800Stomsoft DBG_ENTER; 14669800Stomsoft 147217726Smarcel time(&modtime); 14869800Stomsoft 14969800Stomsoft /* 15069800Stomsoft * Get the cylinder summary into the memory. 15169800Stomsoft */ 15277885Stomsoft fscs = (struct csum *)calloc((size_t)1, (size_t)sblock.fs_cssize); 153232548Strasz if (fscs == NULL) 15469926Stomsoft errx(1, "calloc failed"); 15569800Stomsoft for (i = 0; i < osblock.fs_cssize; i += osblock.fs_bsize) { 15669800Stomsoft rdfs(fsbtodb(&osblock, osblock.fs_csaddr + 15777885Stomsoft numfrags(&osblock, i)), (size_t)MIN(osblock.fs_cssize - i, 158232548Strasz osblock.fs_bsize), (void *)(((char *)fscs) + i), fsi); 15969800Stomsoft } 16069800Stomsoft 16169800Stomsoft#ifdef FS_DEBUG 162232548Strasz { 163232548Strasz struct csum *dbg_csp; 164284669Strasz u_int32_t dbg_csc; 165232548Strasz char dbg_line[80]; 16669800Stomsoft 167232548Strasz dbg_csp = fscs; 168232548Strasz 169232548Strasz for (dbg_csc = 0; dbg_csc < osblock.fs_ncg; dbg_csc++) { 170232548Strasz snprintf(dbg_line, sizeof(dbg_line), 171232548Strasz "%d. old csum in old location", dbg_csc); 172232548Strasz DBG_DUMP_CSUM(&osblock, dbg_line, dbg_csp++); 173232548Strasz } 17469800Stomsoft } 17569800Stomsoft#endif /* FS_DEBUG */ 17669800Stomsoft DBG_PRINT0("fscs read\n"); 17769800Stomsoft 17869800Stomsoft /* 17969800Stomsoft * Do all needed changes in the former last cylinder group. 18069800Stomsoft */ 181232548Strasz updjcg(osblock.fs_ncg - 1, modtime, fsi, fso, Nflag); 18269800Stomsoft 18369800Stomsoft /* 184223652Strasz * Dump out summary information about file system. 18569800Stomsoft */ 186234846Strasz#ifdef FS_DEBUG 187234314Strasz#define B2MBFACTOR (1 / (1024.0 * 1024.0)) 188127816Smux printf("growfs: %.1fMB (%jd sectors) block size %d, fragment size %d\n", 18969800Stomsoft (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR, 190127816Smux (intmax_t)fsbtodb(&sblock, sblock.fs_size), sblock.fs_bsize, 191127816Smux sblock.fs_fsize); 19298542Smckusick printf("\tusing %d cylinder groups of %.2fMB, %d blks, %d inodes.\n", 19398542Smckusick sblock.fs_ncg, (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR, 19498542Smckusick sblock.fs_fpg / sblock.fs_frag, sblock.fs_ipg); 19598542Smckusick if (sblock.fs_flags & FS_DOSOFTDEP) 19698542Smckusick printf("\twith soft updates\n"); 197234314Strasz#undef B2MBFACTOR 198234846Strasz#endif /* FS_DEBUG */ 19969800Stomsoft 20069800Stomsoft /* 20169800Stomsoft * Now build the cylinders group blocks and 20269800Stomsoft * then print out indices of cylinder groups. 20369800Stomsoft */ 204261963Sbrueffer printf("super-block backups (for fsck_ffs -b #) at:\n"); 20569800Stomsoft i = 0; 20669800Stomsoft width = charsperline(); 20769800Stomsoft 20869800Stomsoft /* 20969800Stomsoft * Iterate for only the new cylinder groups. 21069800Stomsoft */ 21169800Stomsoft for (cylno = osblock.fs_ncg; cylno < sblock.fs_ncg; cylno++) { 212217726Smarcel initcg(cylno, modtime, fso, Nflag); 213174706Sdas j = sprintf(tmpbuf, " %jd%s", 214174706Sdas (intmax_t)fsbtodb(&sblock, cgsblock(&sblock, cylno)), 215232548Strasz cylno < (sblock.fs_ncg - 1) ? "," : "" ); 21669800Stomsoft if (i + j >= width) { 21769800Stomsoft printf("\n"); 21869800Stomsoft i = 0; 21969800Stomsoft } 22069800Stomsoft i += j; 22169800Stomsoft printf("%s", tmpbuf); 22269800Stomsoft fflush(stdout); 22369800Stomsoft } 22469800Stomsoft printf("\n"); 22569800Stomsoft 22669800Stomsoft /* 22769800Stomsoft * Do all needed changes in the first cylinder group. 22869800Stomsoft * allocate blocks in new location 22969800Stomsoft */ 230217726Smarcel updcsloc(modtime, fsi, fso, Nflag); 23169800Stomsoft 23269800Stomsoft /* 23369800Stomsoft * Now write the cylinder summary back to disk. 23469800Stomsoft */ 23569800Stomsoft for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) { 23669800Stomsoft wtfs(fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)), 23777885Stomsoft (size_t)MIN(sblock.fs_cssize - i, sblock.fs_bsize), 23877885Stomsoft (void *)(((char *)fscs) + i), fso, Nflag); 23969800Stomsoft } 24069800Stomsoft DBG_PRINT0("fscs written\n"); 24169800Stomsoft 24269800Stomsoft#ifdef FS_DEBUG 243232548Strasz { 244232548Strasz struct csum *dbg_csp; 245284669Strasz u_int32_t dbg_csc; 246232548Strasz char dbg_line[80]; 24769800Stomsoft 248232548Strasz dbg_csp = fscs; 249232548Strasz for (dbg_csc = 0; dbg_csc < sblock.fs_ncg; dbg_csc++) { 250232548Strasz snprintf(dbg_line, sizeof(dbg_line), 251232548Strasz "%d. new csum in new location", dbg_csc); 252232548Strasz DBG_DUMP_CSUM(&sblock, dbg_line, dbg_csp++); 253232548Strasz } 25469800Stomsoft } 25569800Stomsoft#endif /* FS_DEBUG */ 25669800Stomsoft 25769800Stomsoft /* 25869800Stomsoft * Now write the new superblock back to disk. 25969800Stomsoft */ 260217726Smarcel sblock.fs_time = modtime; 26198542Smckusick wtfs(sblockloc, (size_t)SBLOCKSIZE, (void *)&sblock, fso, Nflag); 26269800Stomsoft DBG_PRINT0("sblock written\n"); 263232548Strasz DBG_DUMP_FS(&sblock, "new initial sblock"); 26469800Stomsoft 26569800Stomsoft /* 26669800Stomsoft * Clean up the dynamic fields in our superblock copies. 26769800Stomsoft */ 26869800Stomsoft sblock.fs_fmod = 0; 26969800Stomsoft sblock.fs_clean = 1; 27069800Stomsoft sblock.fs_ronly = 0; 27169800Stomsoft sblock.fs_cgrotor = 0; 27269800Stomsoft sblock.fs_state = 0; 27369800Stomsoft memset((void *)&sblock.fs_fsmnt, 0, sizeof(sblock.fs_fsmnt)); 27469800Stomsoft sblock.fs_flags &= FS_DOSOFTDEP; 27569800Stomsoft 27669800Stomsoft /* 27769800Stomsoft * XXX 278114067Sschweikh * The following fields are currently distributed from the superblock 27969800Stomsoft * to the copies: 28069800Stomsoft * fs_minfree 28169800Stomsoft * fs_rotdelay 28269800Stomsoft * fs_maxcontig 28369800Stomsoft * fs_maxbpg 28469800Stomsoft * fs_minfree, 28569800Stomsoft * fs_optim 28669800Stomsoft * fs_flags regarding SOFTPDATES 28769800Stomsoft * 28869800Stomsoft * We probably should rather change the summary for the cylinder group 28969800Stomsoft * statistics here to the value of what would be in there, if the file 290114067Sschweikh * system were created initially with the new size. Therefor we still 29169800Stomsoft * need to find an easy way of calculating that. 29269800Stomsoft * Possibly we can try to read the first superblock copy and apply the 293114067Sschweikh * "diffed" stats between the old and new superblock by still copying 29469800Stomsoft * certain parameters onto that. 29569800Stomsoft */ 29669800Stomsoft 29769800Stomsoft /* 29869800Stomsoft * Write out the duplicate super blocks. 29969800Stomsoft */ 30069800Stomsoft for (cylno = 0; cylno < sblock.fs_ncg; cylno++) { 30169800Stomsoft wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)), 30298542Smckusick (size_t)SBLOCKSIZE, (void *)&sblock, fso, Nflag); 30369800Stomsoft } 30469800Stomsoft DBG_PRINT0("sblock copies written\n"); 305232548Strasz DBG_DUMP_FS(&sblock, "new other sblocks"); 30669800Stomsoft 30769800Stomsoft DBG_LEAVE; 30869800Stomsoft return; 30969800Stomsoft} 31069800Stomsoft 31169800Stomsoft/* 312114067Sschweikh * This creates a new cylinder group structure, for more details please see 313114067Sschweikh * the source of newfs(8), as this function is taken over almost unchanged. 314114067Sschweikh * As this is never called for the first cylinder group, the special 31569800Stomsoft * provisions for that case are removed here. 31669800Stomsoft */ 31769800Stomsoftstatic void 318217726Smarcelinitcg(int cylno, time_t modtime, int fso, unsigned int Nflag) 31969800Stomsoft{ 32069800Stomsoft DBG_FUNC("initcg") 321212839Sbrian static caddr_t iobuf; 322203770Smckusick long blkno, start; 323241013Smdf ino_t ino; 324127798Sle ufs2_daddr_t i, cbase, dmax; 32598542Smckusick struct ufs1_dinode *dp1; 32692806Sobrien struct csum *cs; 327234312Strasz uint j, d, dupper, dlower; 32869800Stomsoft 329212839Sbrian if (iobuf == NULL && (iobuf = malloc(sblock.fs_bsize * 3)) == NULL) 33098542Smckusick errx(37, "panic: cannot allocate I/O buffer"); 331212839Sbrian 33269800Stomsoft /* 33369800Stomsoft * Determine block bounds for cylinder group. 33498542Smckusick * Allow space for super block summary information in first 33598542Smckusick * cylinder group. 33669800Stomsoft */ 33769800Stomsoft cbase = cgbase(&sblock, cylno); 33869800Stomsoft dmax = cbase + sblock.fs_fpg; 33998542Smckusick if (dmax > sblock.fs_size) 34069800Stomsoft dmax = sblock.fs_size; 34169800Stomsoft dlower = cgsblock(&sblock, cylno) - cbase; 34269800Stomsoft dupper = cgdmin(&sblock, cylno) - cbase; 34398542Smckusick if (cylno == 0) /* XXX fscs may be relocated */ 34469800Stomsoft dupper += howmany(sblock.fs_cssize, sblock.fs_fsize); 34598542Smckusick cs = &fscs[cylno]; 34698542Smckusick memset(&acg, 0, sblock.fs_cgsize); 347217726Smarcel acg.cg_time = modtime; 34869800Stomsoft acg.cg_magic = CG_MAGIC; 34969800Stomsoft acg.cg_cgx = cylno; 35069800Stomsoft acg.cg_niblk = sblock.fs_ipg; 351212839Sbrian acg.cg_initediblk = sblock.fs_ipg < 2 * INOPB(&sblock) ? 352212839Sbrian sblock.fs_ipg : 2 * INOPB(&sblock); 35369800Stomsoft acg.cg_ndblk = dmax - cbase; 35498542Smckusick if (sblock.fs_contigsumsize > 0) 35569800Stomsoft acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag; 35698542Smckusick start = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield); 35798542Smckusick if (sblock.fs_magic == FS_UFS2_MAGIC) { 35898542Smckusick acg.cg_iusedoff = start; 35998542Smckusick } else { 36098542Smckusick acg.cg_old_ncyl = sblock.fs_old_cpg; 36198542Smckusick acg.cg_old_time = acg.cg_time; 36298542Smckusick acg.cg_time = 0; 36398542Smckusick acg.cg_old_niblk = acg.cg_niblk; 36498542Smckusick acg.cg_niblk = 0; 365212839Sbrian acg.cg_initediblk = 0; 36698542Smckusick acg.cg_old_btotoff = start; 36798542Smckusick acg.cg_old_boff = acg.cg_old_btotoff + 36898542Smckusick sblock.fs_old_cpg * sizeof(int32_t); 36998542Smckusick acg.cg_iusedoff = acg.cg_old_boff + 37098542Smckusick sblock.fs_old_cpg * sizeof(u_int16_t); 37169800Stomsoft } 372103949Smike acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT); 373103949Smike acg.cg_nextfreeoff = acg.cg_freeoff + howmany(sblock.fs_fpg, CHAR_BIT); 37498542Smckusick if (sblock.fs_contigsumsize > 0) { 37569800Stomsoft acg.cg_clustersumoff = 37698542Smckusick roundup(acg.cg_nextfreeoff, sizeof(u_int32_t)); 37798542Smckusick acg.cg_clustersumoff -= sizeof(u_int32_t); 37869800Stomsoft acg.cg_clusteroff = acg.cg_clustersumoff + 37969800Stomsoft (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t); 38098542Smckusick acg.cg_nextfreeoff = acg.cg_clusteroff + 381103949Smike howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT); 38269800Stomsoft } 383203770Smckusick if (acg.cg_nextfreeoff > (unsigned)sblock.fs_cgsize) { 38469800Stomsoft /* 38598542Smckusick * This should never happen as we would have had that panic 386223652Strasz * already on file system creation 38769800Stomsoft */ 38869926Stomsoft errx(37, "panic: cylinder group too big"); 38969800Stomsoft } 39069800Stomsoft acg.cg_cs.cs_nifree += sblock.fs_ipg; 39169800Stomsoft if (cylno == 0) 392241013Smdf for (ino = 0; ino < ROOTINO; ino++) { 393241013Smdf setbit(cg_inosused(&acg), ino); 39469800Stomsoft acg.cg_cs.cs_nifree--; 39569800Stomsoft } 396136289Sscottl /* 397223652Strasz * For the old file system, we have to initialize all the inodes. 398136289Sscottl */ 399136289Sscottl if (sblock.fs_magic == FS_UFS1_MAGIC) { 400136289Sscottl bzero(iobuf, sblock.fs_bsize); 401203835Sgavin for (i = 0; i < sblock.fs_ipg / INOPF(&sblock); 402232548Strasz i += sblock.fs_frag) { 403212886Smarcel dp1 = (struct ufs1_dinode *)(void *)iobuf; 404201401Sgavin for (j = 0; j < INOPB(&sblock); j++) { 405249500Sdelphij dp1->di_gen = arc4random(); 406201401Sgavin dp1++; 407201401Sgavin } 408136289Sscottl wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i), 409136289Sscottl sblock.fs_bsize, iobuf, fso, Nflag); 410136289Sscottl } 41169800Stomsoft } 41298542Smckusick if (cylno > 0) { 41398542Smckusick /* 41498542Smckusick * In cylno 0, beginning space is reserved 41598542Smckusick * for boot and super blocks. 41698542Smckusick */ 41798542Smckusick for (d = 0; d < dlower; d += sblock.fs_frag) { 41898542Smckusick blkno = d / sblock.fs_frag; 41998542Smckusick setblock(&sblock, cg_blksfree(&acg), blkno); 42098542Smckusick if (sblock.fs_contigsumsize > 0) 42198542Smckusick setbit(cg_clustersfree(&acg), blkno); 42298542Smckusick acg.cg_cs.cs_nbfree++; 42369800Stomsoft } 42498542Smckusick sblock.fs_dsize += dlower; 42569800Stomsoft } 42669800Stomsoft sblock.fs_dsize += acg.cg_ndblk - dupper; 42769800Stomsoft if ((i = dupper % sblock.fs_frag)) { 42869800Stomsoft acg.cg_frsum[sblock.fs_frag - i]++; 42969800Stomsoft for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) { 43069800Stomsoft setbit(cg_blksfree(&acg), dupper); 43169800Stomsoft acg.cg_cs.cs_nffree++; 43269800Stomsoft } 43369800Stomsoft } 43498542Smckusick for (d = dupper; d + sblock.fs_frag <= acg.cg_ndblk; 435232548Strasz d += sblock.fs_frag) { 43669800Stomsoft blkno = d / sblock.fs_frag; 43769800Stomsoft setblock(&sblock, cg_blksfree(&acg), blkno); 43898542Smckusick if (sblock.fs_contigsumsize > 0) 43969800Stomsoft setbit(cg_clustersfree(&acg), blkno); 44069800Stomsoft acg.cg_cs.cs_nbfree++; 44169800Stomsoft } 44298542Smckusick if (d < acg.cg_ndblk) { 44398542Smckusick acg.cg_frsum[acg.cg_ndblk - d]++; 44498542Smckusick for (; d < acg.cg_ndblk; d++) { 44569800Stomsoft setbit(cg_blksfree(&acg), d); 44669800Stomsoft acg.cg_cs.cs_nffree++; 44769800Stomsoft } 44869800Stomsoft } 44969800Stomsoft if (sblock.fs_contigsumsize > 0) { 45098542Smckusick int32_t *sump = cg_clustersum(&acg); 45198542Smckusick u_char *mapp = cg_clustersfree(&acg); 45298542Smckusick int map = *mapp++; 45398542Smckusick int bit = 1; 45498542Smckusick int run = 0; 45569800Stomsoft 45669800Stomsoft for (i = 0; i < acg.cg_nclusterblks; i++) { 45798542Smckusick if ((map & bit) != 0) 45869800Stomsoft run++; 45998542Smckusick else if (run != 0) { 46098542Smckusick if (run > sblock.fs_contigsumsize) 46169800Stomsoft run = sblock.fs_contigsumsize; 46269800Stomsoft sump[run]++; 46369800Stomsoft run = 0; 46469800Stomsoft } 465103949Smike if ((i & (CHAR_BIT - 1)) != CHAR_BIT - 1) 46669800Stomsoft bit <<= 1; 46798542Smckusick else { 46869800Stomsoft map = *mapp++; 46969800Stomsoft bit = 1; 47069800Stomsoft } 47169800Stomsoft } 47269800Stomsoft if (run != 0) { 47398542Smckusick if (run > sblock.fs_contigsumsize) 47469800Stomsoft run = sblock.fs_contigsumsize; 47569800Stomsoft sump[run]++; 47669800Stomsoft } 47769800Stomsoft } 47869800Stomsoft sblock.fs_cstotal.cs_ndir += acg.cg_cs.cs_ndir; 47969800Stomsoft sblock.fs_cstotal.cs_nffree += acg.cg_cs.cs_nffree; 48069800Stomsoft sblock.fs_cstotal.cs_nbfree += acg.cg_cs.cs_nbfree; 48169800Stomsoft sblock.fs_cstotal.cs_nifree += acg.cg_cs.cs_nifree; 48269800Stomsoft *cs = acg.cg_cs; 483212839Sbrian 484212839Sbrian memcpy(iobuf, &acg, sblock.fs_cgsize); 485212839Sbrian memset(iobuf + sblock.fs_cgsize, '\0', 486212839Sbrian sblock.fs_bsize * 3 - sblock.fs_cgsize); 487212839Sbrian 48869800Stomsoft wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), 489212839Sbrian sblock.fs_bsize * 3, iobuf, fso, Nflag); 490212839Sbrian DBG_DUMP_CG(&sblock, "new cg", &acg); 49169800Stomsoft 49269800Stomsoft DBG_LEAVE; 49369800Stomsoft return; 49469800Stomsoft} 49569800Stomsoft 49669800Stomsoft/* 497114067Sschweikh * Here we add or subtract (sign +1/-1) the available fragments in a given 49869800Stomsoft * block to or from the fragment statistics. By subtracting before and adding 499114067Sschweikh * after an operation on the free frag map we can easy update the fragment 500108470Sschweikh * statistic, which seems to be otherwise a rather complex operation. 50169800Stomsoft */ 50269800Stomsoftstatic void 50398542Smckusickfrag_adjust(ufs2_daddr_t frag, int sign) 50469800Stomsoft{ 50569800Stomsoft DBG_FUNC("frag_adjust") 50669800Stomsoft int fragsize; 50769800Stomsoft int f; 50869800Stomsoft 50969800Stomsoft DBG_ENTER; 51069800Stomsoft 511234314Strasz fragsize = 0; 51269800Stomsoft /* 51369800Stomsoft * Here frag only needs to point to any fragment in the block we want 51469800Stomsoft * to examine. 51569800Stomsoft */ 516232548Strasz for (f = rounddown(frag, sblock.fs_frag); 517232548Strasz f < roundup(frag + 1, sblock.fs_frag); f++) { 51869800Stomsoft /* 519114067Sschweikh * Count contiguous free fragments. 52069800Stomsoft */ 521232548Strasz if (isset(cg_blksfree(&acg), f)) { 52269800Stomsoft fragsize++; 52369800Stomsoft } else { 524232548Strasz if (fragsize && fragsize < sblock.fs_frag) { 52569800Stomsoft /* 52669800Stomsoft * We found something in between. 52769800Stomsoft */ 528234189Strasz acg.cg_frsum[fragsize] += sign; 52969926Stomsoft DBG_PRINT2("frag_adjust [%d]+=%d\n", 530232548Strasz fragsize, sign); 53169800Stomsoft } 532232548Strasz fragsize = 0; 53369800Stomsoft } 53469800Stomsoft } 535232548Strasz if (fragsize && fragsize < sblock.fs_frag) { 53669800Stomsoft /* 53769800Stomsoft * We found something. 53869800Stomsoft */ 539232548Strasz acg.cg_frsum[fragsize] += sign; 540232548Strasz DBG_PRINT2("frag_adjust [%d]+=%d\n", fragsize, sign); 54169800Stomsoft } 542232548Strasz DBG_PRINT2("frag_adjust [[%d]]+=%d\n", fragsize, sign); 54369800Stomsoft 54469800Stomsoft DBG_LEAVE; 54569800Stomsoft return; 54669800Stomsoft} 54769800Stomsoft 54869800Stomsoft/* 54969800Stomsoft * Here we do all needed work for the former last cylinder group. It has to be 550223652Strasz * changed in any case, even if the file system ended exactly on the end of 551114067Sschweikh * this group, as there is some slightly inconsistent handling of the number 552114067Sschweikh * of cylinders in the cylinder group. We start again by reading the cylinder 55369800Stomsoft * group from disk. If the last block was not fully available, we first handle 554114067Sschweikh * the missing fragments, then we handle all new full blocks in that file 555114067Sschweikh * system and finally we handle the new last fragmented block in the file 556114067Sschweikh * system. We again have to handle the fragment statistics rotational layout 55769800Stomsoft * tables and cluster summary during all those operations. 55869800Stomsoft */ 55969800Stomsoftstatic void 560217726Smarcelupdjcg(int cylno, time_t modtime, int fsi, int fso, unsigned int Nflag) 56169800Stomsoft{ 56269800Stomsoft DBG_FUNC("updjcg") 563232548Strasz ufs2_daddr_t cbase, dmax, dupper; 564232548Strasz struct csum *cs; 565232548Strasz int i, k; 566232548Strasz int j = 0; 56769800Stomsoft 56869800Stomsoft DBG_ENTER; 56969800Stomsoft 57069800Stomsoft /* 57169800Stomsoft * Read the former last (joining) cylinder group from disk, and make 57269800Stomsoft * a copy. 57369800Stomsoft */ 57477885Stomsoft rdfs(fsbtodb(&osblock, cgtod(&osblock, cylno)), 57577885Stomsoft (size_t)osblock.fs_cgsize, (void *)&aocg, fsi); 57669800Stomsoft DBG_PRINT0("jcg read\n"); 577232548Strasz DBG_DUMP_CG(&sblock, "old joining cg", &aocg); 57869800Stomsoft 57969800Stomsoft memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2)); 58069800Stomsoft 58169800Stomsoft /* 582114067Sschweikh * If the cylinder group had already its new final size almost 58369800Stomsoft * nothing is to be done ... except: 58469800Stomsoft * For some reason the value of cg_ncyl in the last cylinder group has 585114067Sschweikh * to be zero instead of fs_cpg. As this is now no longer the last 58669800Stomsoft * cylinder group we have to change that value now to fs_cpg. 587114067Sschweikh */ 58869800Stomsoft 589232548Strasz if (cgbase(&osblock, cylno + 1) == osblock.fs_size) { 59098542Smckusick if (sblock.fs_magic == FS_UFS1_MAGIC) 591234314Strasz acg.cg_old_ncyl = sblock.fs_old_cpg; 59269800Stomsoft 59377885Stomsoft wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), 59477885Stomsoft (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag); 59569800Stomsoft DBG_PRINT0("jcg written\n"); 596232548Strasz DBG_DUMP_CG(&sblock, "new joining cg", &acg); 59769800Stomsoft 59869800Stomsoft DBG_LEAVE; 59969800Stomsoft return; 60069800Stomsoft } 60169800Stomsoft 60269800Stomsoft /* 60369800Stomsoft * Set up some variables needed later. 60469800Stomsoft */ 60569800Stomsoft cbase = cgbase(&sblock, cylno); 60669800Stomsoft dmax = cbase + sblock.fs_fpg; 60769800Stomsoft if (dmax > sblock.fs_size) 60869800Stomsoft dmax = sblock.fs_size; 60969800Stomsoft dupper = cgdmin(&sblock, cylno) - cbase; 610232548Strasz if (cylno == 0) /* XXX fscs may be relocated */ 61169800Stomsoft dupper += howmany(sblock.fs_cssize, sblock.fs_fsize); 61269800Stomsoft 61369800Stomsoft /* 61469800Stomsoft * Set pointer to the cylinder summary for our cylinder group. 61569800Stomsoft */ 61669800Stomsoft cs = fscs + cylno; 61769800Stomsoft 61869800Stomsoft /* 61969800Stomsoft * Touch the cylinder group, update all fields in the cylinder group as 62069800Stomsoft * needed, update the free space in the superblock. 62169800Stomsoft */ 622217726Smarcel acg.cg_time = modtime; 623203770Smckusick if ((unsigned)cylno == sblock.fs_ncg - 1) { 62469800Stomsoft /* 62569800Stomsoft * This is still the last cylinder group. 62669800Stomsoft */ 62798542Smckusick if (sblock.fs_magic == FS_UFS1_MAGIC) 62898542Smckusick acg.cg_old_ncyl = 62998542Smckusick sblock.fs_old_ncyl % sblock.fs_old_cpg; 63069800Stomsoft } else { 63198542Smckusick acg.cg_old_ncyl = sblock.fs_old_cpg; 63269800Stomsoft } 633232548Strasz DBG_PRINT2("jcg dbg: %d %u", cylno, sblock.fs_ncg); 634127798Sle#ifdef FS_DEBUG 63598542Smckusick if (sblock.fs_magic == FS_UFS1_MAGIC) 636232548Strasz DBG_PRINT2("%d %u", acg.cg_old_ncyl, sblock.fs_old_cpg); 637127798Sle#endif 63898542Smckusick DBG_PRINT0("\n"); 63969800Stomsoft acg.cg_ndblk = dmax - cbase; 640232548Strasz sblock.fs_dsize += acg.cg_ndblk - aocg.cg_ndblk; 641232548Strasz if (sblock.fs_contigsumsize > 0) 64269800Stomsoft acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag; 64369800Stomsoft 64469800Stomsoft /* 645114067Sschweikh * Now we have to update the free fragment bitmap for our new free 646114067Sschweikh * space. There again we have to handle the fragmentation and also 647114067Sschweikh * the rotational layout tables and the cluster summary. This is 648114067Sschweikh * also done per fragment for the first new block if the old file 649114067Sschweikh * system end was not on a block boundary, per fragment for the new 650223652Strasz * last block if the new file system end is not on a block boundary, 65169800Stomsoft * and per block for all space in between. 65269800Stomsoft * 65369800Stomsoft * Handle the first new block here if it was partially available 65469800Stomsoft * before. 65569800Stomsoft */ 656232548Strasz if (osblock.fs_size % sblock.fs_frag) { 657232548Strasz if (roundup(osblock.fs_size, sblock.fs_frag) <= 658232548Strasz sblock.fs_size) { 65969800Stomsoft /* 66069800Stomsoft * The new space is enough to fill at least this 66169800Stomsoft * block 66269800Stomsoft */ 663232548Strasz j = 0; 664232548Strasz for (i = roundup(osblock.fs_size - cbase, 665232548Strasz sblock.fs_frag) - 1; i >= osblock.fs_size - cbase; 66669800Stomsoft i--) { 66769800Stomsoft setbit(cg_blksfree(&acg), i); 66869800Stomsoft acg.cg_cs.cs_nffree++; 66969800Stomsoft j++; 67069800Stomsoft } 67169800Stomsoft 67269800Stomsoft /* 673114067Sschweikh * Check if the fragment just created could join an 67469800Stomsoft * already existing fragment at the former end of the 675223652Strasz * file system. 67669800Stomsoft */ 677232548Strasz if (isblock(&sblock, cg_blksfree(&acg), 678232548Strasz ((osblock.fs_size - cgbase(&sblock, cylno)) / 679232548Strasz sblock.fs_frag))) { 68069800Stomsoft /* 681114067Sschweikh * The block is now completely available. 68269800Stomsoft */ 68369800Stomsoft DBG_PRINT0("block was\n"); 684232548Strasz acg.cg_frsum[osblock.fs_size % sblock.fs_frag]--; 68569800Stomsoft acg.cg_cs.cs_nbfree++; 686232548Strasz acg.cg_cs.cs_nffree -= sblock.fs_frag; 687232548Strasz k = rounddown(osblock.fs_size - cbase, 68869800Stomsoft sblock.fs_frag); 689232548Strasz updclst((osblock.fs_size - cbase) / 690232548Strasz sblock.fs_frag); 69169800Stomsoft } else { 69269800Stomsoft /* 69369800Stomsoft * Lets rejoin a possible partially growed 69469800Stomsoft * fragment. 69569800Stomsoft */ 696232548Strasz k = 0; 697232548Strasz while (isset(cg_blksfree(&acg), i) && 698232548Strasz (i >= rounddown(osblock.fs_size - cbase, 69969800Stomsoft sblock.fs_frag))) { 70069800Stomsoft i--; 70169800Stomsoft k++; 70269800Stomsoft } 703232548Strasz if (k) 70469800Stomsoft acg.cg_frsum[k]--; 705232548Strasz acg.cg_frsum[k + j]++; 70669800Stomsoft } 70769800Stomsoft } else { 70869800Stomsoft /* 70969800Stomsoft * We only grow by some fragments within this last 71069800Stomsoft * block. 71169800Stomsoft */ 712232548Strasz for (i = sblock.fs_size - cbase - 1; 713232548Strasz i >= osblock.fs_size - cbase; i--) { 71469800Stomsoft setbit(cg_blksfree(&acg), i); 71569800Stomsoft acg.cg_cs.cs_nffree++; 71669800Stomsoft j++; 71769800Stomsoft } 71869800Stomsoft /* 71969800Stomsoft * Lets rejoin a possible partially growed fragment. 72069800Stomsoft */ 721232548Strasz k = 0; 722232548Strasz while (isset(cg_blksfree(&acg), i) && 723232548Strasz (i >= rounddown(osblock.fs_size - cbase, 72469800Stomsoft sblock.fs_frag))) { 72569800Stomsoft i--; 72669800Stomsoft k++; 72769800Stomsoft } 728232548Strasz if (k) 72969800Stomsoft acg.cg_frsum[k]--; 730232548Strasz acg.cg_frsum[k + j]++; 73169800Stomsoft } 73269800Stomsoft } 73369800Stomsoft 73469800Stomsoft /* 73569800Stomsoft * Handle all new complete blocks here. 73669800Stomsoft */ 737232548Strasz for (i = roundup(osblock.fs_size - cbase, sblock.fs_frag); 738232548Strasz i + sblock.fs_frag <= dmax - cbase; /* XXX <= or only < ? */ 739232548Strasz i += sblock.fs_frag) { 74069800Stomsoft j = i / sblock.fs_frag; 74169800Stomsoft setblock(&sblock, cg_blksfree(&acg), j); 74269800Stomsoft updclst(j); 74369800Stomsoft acg.cg_cs.cs_nbfree++; 74469800Stomsoft } 74569800Stomsoft 74669800Stomsoft /* 74769800Stomsoft * Handle the last new block if there are stll some new fragments left. 748114067Sschweikh * Here we don't have to bother about the cluster summary or the even 74969800Stomsoft * the rotational layout table. 75069800Stomsoft */ 75169800Stomsoft if (i < (dmax - cbase)) { 75269800Stomsoft acg.cg_frsum[dmax - cbase - i]++; 75369800Stomsoft for (; i < dmax - cbase; i++) { 75469800Stomsoft setbit(cg_blksfree(&acg), i); 75569800Stomsoft acg.cg_cs.cs_nffree++; 75669800Stomsoft } 75769800Stomsoft } 75869800Stomsoft 75969800Stomsoft sblock.fs_cstotal.cs_nffree += 76069800Stomsoft (acg.cg_cs.cs_nffree - aocg.cg_cs.cs_nffree); 76169800Stomsoft sblock.fs_cstotal.cs_nbfree += 76269800Stomsoft (acg.cg_cs.cs_nbfree - aocg.cg_cs.cs_nbfree); 76369800Stomsoft /* 76469800Stomsoft * The following statistics are not changed here: 76569800Stomsoft * sblock.fs_cstotal.cs_ndir 76669800Stomsoft * sblock.fs_cstotal.cs_nifree 76769800Stomsoft * As the statistics for this cylinder group are ready, copy it to 76869800Stomsoft * the summary information array. 76969800Stomsoft */ 77069800Stomsoft *cs = acg.cg_cs; 77169800Stomsoft 77269800Stomsoft /* 77369800Stomsoft * Write the updated "joining" cylinder group back to disk. 77469800Stomsoft */ 77577885Stomsoft wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), (size_t)sblock.fs_cgsize, 77677885Stomsoft (void *)&acg, fso, Nflag); 77769800Stomsoft DBG_PRINT0("jcg written\n"); 778232548Strasz DBG_DUMP_CG(&sblock, "new joining cg", &acg); 77969800Stomsoft 78069800Stomsoft DBG_LEAVE; 78169800Stomsoft return; 78269800Stomsoft} 78369800Stomsoft 78469800Stomsoft/* 785114067Sschweikh * Here we update the location of the cylinder summary. We have two possible 786234846Strasz * ways of growing the cylinder summary: 787114067Sschweikh * (1) We can try to grow the summary in the current location, and relocate 78869800Stomsoft * possibly used blocks within the current cylinder group. 78969800Stomsoft * (2) Alternatively we can relocate the whole cylinder summary to the first 790114067Sschweikh * new completely empty cylinder group. Once the cylinder summary is no 791114067Sschweikh * longer in the beginning of the first cylinder group you should never 792114067Sschweikh * use a version of fsck which is not aware of the possibility to have 79369800Stomsoft * this structure in a non standard place. 794234178Strasz * Option (2) is considered to be less intrusive to the structure of the file- 795234178Strasz * system, so that's the one being used. 79669800Stomsoft */ 79769800Stomsoftstatic void 798217726Smarcelupdcsloc(time_t modtime, int fsi, int fso, unsigned int Nflag) 79969800Stomsoft{ 80069800Stomsoft DBG_FUNC("updcsloc") 801232548Strasz struct csum *cs; 802232548Strasz int ocscg, ncscg; 803234178Strasz ufs2_daddr_t d; 804232548Strasz int lcs = 0; 805232548Strasz int block; 80669800Stomsoft 80769800Stomsoft DBG_ENTER; 80869800Stomsoft 809232548Strasz if (howmany(sblock.fs_cssize, sblock.fs_fsize) == 81069800Stomsoft howmany(osblock.fs_cssize, osblock.fs_fsize)) { 81169800Stomsoft /* 81269800Stomsoft * No new fragment needed. 81369800Stomsoft */ 81469800Stomsoft DBG_LEAVE; 81569800Stomsoft return; 81669800Stomsoft } 817232548Strasz ocscg = dtog(&osblock, osblock.fs_csaddr); 818232548Strasz cs = fscs + ocscg; 81969800Stomsoft 82069800Stomsoft /* 82169800Stomsoft * Read original cylinder group from disk, and make a copy. 82281311Schm * XXX If Nflag is set in some very rare cases we now miss 82381311Schm * some changes done in updjcg by reading the unmodified 82481311Schm * block from disk. 82569800Stomsoft */ 82677885Stomsoft rdfs(fsbtodb(&osblock, cgtod(&osblock, ocscg)), 82777885Stomsoft (size_t)osblock.fs_cgsize, (void *)&aocg, fsi); 82869800Stomsoft DBG_PRINT0("oscg read\n"); 829232548Strasz DBG_DUMP_CG(&sblock, "old summary cg", &aocg); 83069800Stomsoft 83169800Stomsoft memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2)); 83269800Stomsoft 83369800Stomsoft /* 83469800Stomsoft * Touch the cylinder group, set up local variables needed later 83569800Stomsoft * and update the superblock. 83669800Stomsoft */ 837217726Smarcel acg.cg_time = modtime; 83869800Stomsoft 83969800Stomsoft /* 84069800Stomsoft * XXX In the case of having active snapshots we may need much more 841114067Sschweikh * blocks for the copy on write. We need each block twice, and 842114067Sschweikh * also up to 8*3 blocks for indirect blocks for all possible 84369800Stomsoft * references. 84469800Stomsoft */ 845234178Strasz /* 846234178Strasz * There is not enough space in the old cylinder group to 847234178Strasz * relocate all blocks as needed, so we relocate the whole 848234178Strasz * cylinder group summary to a new group. We try to use the 849234178Strasz * first complete new cylinder group just created. Within the 850234178Strasz * cylinder group we align the area immediately after the 851234178Strasz * cylinder group information location in order to be as 852234178Strasz * close as possible to the original implementation of ffs. 853234178Strasz * 854234178Strasz * First we have to make sure we'll find enough space in the 855234178Strasz * new cylinder group. If not, then we currently give up. 856234178Strasz * We start with freeing everything which was used by the 857234178Strasz * fragments of the old cylinder summary in the current group. 858234178Strasz * Now we write back the group meta data, read in the needed 859234178Strasz * meta data from the new cylinder group, and start allocating 860234178Strasz * within that group. Here we can assume, the group to be 861234178Strasz * completely empty. Which makes the handling of fragments and 862234178Strasz * clusters a lot easier. 863234178Strasz */ 864234178Strasz DBG_TRC; 865234178Strasz if (sblock.fs_ncg - osblock.fs_ncg < 2) 866234178Strasz errx(2, "panic: not enough space"); 86769800Stomsoft 868234178Strasz /* 869234178Strasz * Point "d" to the first fragment not used by the cylinder 870234178Strasz * summary. 871234178Strasz */ 872234178Strasz d = osblock.fs_csaddr + (osblock.fs_cssize / osblock.fs_fsize); 87369800Stomsoft 874234178Strasz /* 875234178Strasz * Set up last cluster size ("lcs") already here. Calculate 876234178Strasz * the size for the trailing cluster just behind where "d" 877234178Strasz * points to. 878234178Strasz */ 879234178Strasz if (sblock.fs_contigsumsize > 0) { 880234178Strasz for (block = howmany(d % sblock.fs_fpg, sblock.fs_frag), 881234189Strasz lcs = 0; lcs < sblock.fs_contigsumsize; block++, lcs++) { 882234178Strasz if (isclr(cg_clustersfree(&acg), block)) 883234178Strasz break; 884234178Strasz } 885234178Strasz } 886234178Strasz 887234178Strasz /* 888234178Strasz * Point "d" to the last frag used by the cylinder summary. 889234178Strasz */ 890234178Strasz d--; 891234178Strasz 892234178Strasz DBG_PRINT1("d=%jd\n", (intmax_t)d); 893234178Strasz if ((d + 1) % sblock.fs_frag) { 89469800Stomsoft /* 895234178Strasz * The end of the cylinder summary is not a complete 896234178Strasz * block. 89769800Stomsoft */ 898234178Strasz DBG_TRC; 899234178Strasz frag_adjust(d % sblock.fs_fpg, -1); 900234178Strasz for (; (d + 1) % sblock.fs_frag; d--) { 901234178Strasz DBG_PRINT1("d=%jd\n", (intmax_t)d); 902234178Strasz setbit(cg_blksfree(&acg), d % sblock.fs_fpg); 903234178Strasz acg.cg_cs.cs_nffree++; 904234178Strasz sblock.fs_cstotal.cs_nffree++; 90569800Stomsoft } 90669800Stomsoft /* 907234178Strasz * Point "d" to the last fragment of the last 908234178Strasz * (incomplete) block of the cylinder summary. 90969800Stomsoft */ 910234178Strasz d++; 911234189Strasz frag_adjust(d % sblock.fs_fpg, 1); 91269800Stomsoft 913234178Strasz if (isblock(&sblock, cg_blksfree(&acg), 914234178Strasz (d % sblock.fs_fpg) / sblock.fs_frag)) { 915127798Sle DBG_PRINT1("d=%jd\n", (intmax_t)d); 916234178Strasz acg.cg_cs.cs_nffree -= sblock.fs_frag; 91769800Stomsoft acg.cg_cs.cs_nbfree++; 918234178Strasz sblock.fs_cstotal.cs_nffree -= sblock.fs_frag; 91969800Stomsoft sblock.fs_cstotal.cs_nbfree++; 920232548Strasz if (sblock.fs_contigsumsize > 0) { 92169800Stomsoft setbit(cg_clustersfree(&acg), 922234189Strasz (d % sblock.fs_fpg) / sblock.fs_frag); 923232548Strasz if (lcs < sblock.fs_contigsumsize) { 924232548Strasz if (lcs) 92569800Stomsoft cg_clustersum(&acg)[lcs]--; 92669800Stomsoft lcs++; 92769800Stomsoft cg_clustersum(&acg)[lcs]++; 92869800Stomsoft } 92969800Stomsoft } 93069800Stomsoft } 93169800Stomsoft /* 932234178Strasz * Point "d" to the first fragment of the block before 933234178Strasz * the last incomplete block. 93469800Stomsoft */ 935234178Strasz d--; 936234178Strasz } 93769800Stomsoft 938234178Strasz DBG_PRINT1("d=%jd\n", (intmax_t)d); 939234178Strasz for (d = rounddown(d, sblock.fs_frag); d >= osblock.fs_csaddr; 940234178Strasz d -= sblock.fs_frag) { 941234178Strasz DBG_TRC; 942234178Strasz DBG_PRINT1("d=%jd\n", (intmax_t)d); 943234178Strasz setblock(&sblock, cg_blksfree(&acg), 944234178Strasz (d % sblock.fs_fpg) / sblock.fs_frag); 945234178Strasz acg.cg_cs.cs_nbfree++; 946234178Strasz sblock.fs_cstotal.cs_nbfree++; 947234178Strasz if (sblock.fs_contigsumsize > 0) { 948234178Strasz setbit(cg_clustersfree(&acg), 949232548Strasz (d % sblock.fs_fpg) / sblock.fs_frag); 950234178Strasz /* 951234178Strasz * The last cluster size is already set up. 952234178Strasz */ 953234178Strasz if (lcs < sblock.fs_contigsumsize) { 954234178Strasz if (lcs) 955234178Strasz cg_clustersum(&acg)[lcs]--; 956234178Strasz lcs++; 957234178Strasz cg_clustersum(&acg)[lcs]++; 95869800Stomsoft } 95969800Stomsoft } 960234178Strasz } 961234178Strasz *cs = acg.cg_cs; 96269800Stomsoft 96369800Stomsoft /* 964234178Strasz * Now write the former cylinder group containing the cylinder 965234178Strasz * summary back to disk. 96669800Stomsoft */ 967234178Strasz wtfs(fsbtodb(&sblock, cgtod(&sblock, ocscg)), 968234178Strasz (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag); 969234178Strasz DBG_PRINT0("oscg written\n"); 970234178Strasz DBG_DUMP_CG(&sblock, "old summary cg", &acg); 97169800Stomsoft 97269800Stomsoft /* 973234178Strasz * Find the beginning of the new cylinder group containing the 974234178Strasz * cylinder summary. 97569800Stomsoft */ 976234178Strasz sblock.fs_csaddr = cgdmin(&sblock, osblock.fs_ncg); 977234178Strasz ncscg = dtog(&sblock, sblock.fs_csaddr); 978234178Strasz cs = fscs + ncscg; 97969800Stomsoft 98069800Stomsoft /* 981234178Strasz * If Nflag is specified, we would now read random data instead 982234178Strasz * of an empty cg structure from disk. So we can't simulate that 983234178Strasz * part for now. 98469800Stomsoft */ 985234178Strasz if (Nflag) { 986234178Strasz DBG_PRINT0("nscg update skipped\n"); 987234178Strasz DBG_LEAVE; 988234178Strasz return; 98969800Stomsoft } 99069800Stomsoft 99169800Stomsoft /* 992234178Strasz * Read the future cylinder group containing the cylinder 993234178Strasz * summary from disk, and make a copy. 99469800Stomsoft */ 995234178Strasz rdfs(fsbtodb(&sblock, cgtod(&sblock, ncscg)), 996234178Strasz (size_t)sblock.fs_cgsize, (void *)&aocg, fsi); 997234178Strasz DBG_PRINT0("nscg read\n"); 998234178Strasz DBG_DUMP_CG(&sblock, "new summary cg", &aocg); 99969800Stomsoft 1000234178Strasz memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2)); 1001234178Strasz 100269800Stomsoft /* 1003234178Strasz * Allocate all complete blocks used by the new cylinder 1004234178Strasz * summary. 100569800Stomsoft */ 1006234178Strasz for (d = sblock.fs_csaddr; d + sblock.fs_frag <= 1007234178Strasz sblock.fs_csaddr + (sblock.fs_cssize / sblock.fs_fsize); 1008234178Strasz d += sblock.fs_frag) { 1009234178Strasz clrblock(&sblock, cg_blksfree(&acg), 1010234178Strasz (d % sblock.fs_fpg) / sblock.fs_frag); 1011234178Strasz acg.cg_cs.cs_nbfree--; 1012234178Strasz sblock.fs_cstotal.cs_nbfree--; 1013234178Strasz if (sblock.fs_contigsumsize > 0) { 1014234178Strasz clrbit(cg_clustersfree(&acg), 1015234178Strasz (d % sblock.fs_fpg) / sblock.fs_frag); 101669800Stomsoft } 101769800Stomsoft } 101869800Stomsoft 101969800Stomsoft /* 1020234178Strasz * Allocate all fragments used by the cylinder summary in the 1021234178Strasz * last block. 102269800Stomsoft */ 1023234189Strasz if (d < sblock.fs_csaddr + (sblock.fs_cssize / sblock.fs_fsize)) { 1024234178Strasz for (; d - sblock.fs_csaddr < 1025234178Strasz sblock.fs_cssize/sblock.fs_fsize; d++) { 1026234178Strasz clrbit(cg_blksfree(&acg), d % sblock.fs_fpg); 1027234178Strasz acg.cg_cs.cs_nffree--; 1028234178Strasz sblock.fs_cstotal.cs_nffree--; 102969800Stomsoft } 1030234178Strasz acg.cg_cs.cs_nbfree--; 1031234178Strasz acg.cg_cs.cs_nffree += sblock.fs_frag; 1032234178Strasz sblock.fs_cstotal.cs_nbfree--; 1033234178Strasz sblock.fs_cstotal.cs_nffree += sblock.fs_frag; 1034234178Strasz if (sblock.fs_contigsumsize > 0) 1035234178Strasz clrbit(cg_clustersfree(&acg), 1036234178Strasz (d % sblock.fs_fpg) / sblock.fs_frag); 103769800Stomsoft 1038234178Strasz frag_adjust(d % sblock.fs_fpg, 1); 103969800Stomsoft } 104069800Stomsoft /* 1041234178Strasz * XXX Handle the cluster statistics here in the case this 1042234178Strasz * cylinder group is now almost full, and the remaining 1043234178Strasz * space is less then the maximum cluster size. This is 1044234178Strasz * probably not needed, as you would hardly find a file 1045234178Strasz * system which has only MAXCSBUFS+FS_MAXCONTIG of free 1046234178Strasz * space right behind the cylinder group information in 1047234178Strasz * any new cylinder group. 104869800Stomsoft */ 104969800Stomsoft 1050234178Strasz /* 1051234178Strasz * Update our statistics in the cylinder summary. 1052234178Strasz */ 105369800Stomsoft *cs = acg.cg_cs; 105469800Stomsoft 105569800Stomsoft /* 1056234178Strasz * Write the new cylinder group containing the cylinder summary 1057234178Strasz * back to disk. 105869800Stomsoft */ 1059234178Strasz wtfs(fsbtodb(&sblock, cgtod(&sblock, ncscg)), 1060234178Strasz (size_t)sblock.fs_cgsize, (void *)&acg, fso, Nflag); 1061234178Strasz DBG_PRINT0("nscg written\n"); 1062232548Strasz DBG_DUMP_CG(&sblock, "new summary cg", &acg); 106369800Stomsoft 106469800Stomsoft DBG_LEAVE; 106569800Stomsoft return; 106669800Stomsoft} 106769800Stomsoft 106869800Stomsoft/* 106969800Stomsoft * Here we read some block(s) from disk. 107069800Stomsoft */ 107169800Stomsoftstatic void 107298542Smckusickrdfs(ufs2_daddr_t bno, size_t size, void *bf, int fsi) 107369800Stomsoft{ 107469800Stomsoft DBG_FUNC("rdfs") 107577885Stomsoft ssize_t n; 107669800Stomsoft 107769800Stomsoft DBG_ENTER; 107869800Stomsoft 1079232548Strasz if (bno < 0) 1080140351Scharnier err(32, "rdfs: attempting to read negative block number"); 1081232548Strasz if (lseek(fsi, (off_t)bno * DEV_BSIZE, 0) < 0) 1082127798Sle err(33, "rdfs: seek error: %jd", (intmax_t)bno); 108377885Stomsoft n = read(fsi, bf, size); 1084232548Strasz if (n != (ssize_t)size) 1085127798Sle err(34, "rdfs: read error: %jd", (intmax_t)bno); 108669800Stomsoft 108769800Stomsoft DBG_LEAVE; 108869800Stomsoft return; 108969800Stomsoft} 109069800Stomsoft 109169800Stomsoft/* 109269800Stomsoft * Here we write some block(s) to disk. 109369800Stomsoft */ 109469800Stomsoftstatic void 109598542Smckusickwtfs(ufs2_daddr_t bno, size_t size, void *bf, int fso, unsigned int Nflag) 109669800Stomsoft{ 109769800Stomsoft DBG_FUNC("wtfs") 109877885Stomsoft ssize_t n; 109969800Stomsoft 110069800Stomsoft DBG_ENTER; 110169800Stomsoft 110269800Stomsoft if (Nflag) { 110369800Stomsoft DBG_LEAVE; 110469800Stomsoft return; 110569800Stomsoft } 1106232548Strasz if (lseek(fso, (off_t)bno * DEV_BSIZE, SEEK_SET) < 0) 110769926Stomsoft err(35, "wtfs: seek error: %ld", (long)bno); 110877885Stomsoft n = write(fso, bf, size); 1109232548Strasz if (n != (ssize_t)size) 111069926Stomsoft err(36, "wtfs: write error: %ld", (long)bno); 111169800Stomsoft 111269800Stomsoft DBG_LEAVE; 111369800Stomsoft return; 111469800Stomsoft} 111569800Stomsoft 111669800Stomsoft/* 1117114067Sschweikh * Here we check if all frags of a block are free. For more details again 111869800Stomsoft * please see the source of newfs(8), as this function is taken over almost 111969800Stomsoft * unchanged. 112069800Stomsoft */ 112169800Stomsoftstatic int 112269800Stomsoftisblock(struct fs *fs, unsigned char *cp, int h) 112369800Stomsoft{ 112469800Stomsoft DBG_FUNC("isblock") 1125232548Strasz unsigned char mask; 112669800Stomsoft 112769800Stomsoft DBG_ENTER; 112869800Stomsoft 112969800Stomsoft switch (fs->fs_frag) { 113069800Stomsoft case 8: 113169800Stomsoft DBG_LEAVE; 113269800Stomsoft return (cp[h] == 0xff); 113369800Stomsoft case 4: 113469800Stomsoft mask = 0x0f << ((h & 0x1) << 2); 113569800Stomsoft DBG_LEAVE; 113669800Stomsoft return ((cp[h >> 1] & mask) == mask); 113769800Stomsoft case 2: 113869800Stomsoft mask = 0x03 << ((h & 0x3) << 1); 113969800Stomsoft DBG_LEAVE; 114069800Stomsoft return ((cp[h >> 2] & mask) == mask); 114169800Stomsoft case 1: 114269800Stomsoft mask = 0x01 << (h & 0x7); 114369800Stomsoft DBG_LEAVE; 114469800Stomsoft return ((cp[h >> 3] & mask) == mask); 114569800Stomsoft default: 114669800Stomsoft fprintf(stderr, "isblock bad fs_frag %d\n", fs->fs_frag); 114769800Stomsoft DBG_LEAVE; 114869800Stomsoft return (0); 114969800Stomsoft } 115069800Stomsoft} 115169800Stomsoft 115269800Stomsoft/* 115369800Stomsoft * Here we allocate a complete block in the block map. For more details again 1154114067Sschweikh * please see the source of newfs(8), as this function is taken over almost 115569800Stomsoft * unchanged. 115669800Stomsoft */ 115769800Stomsoftstatic void 115869800Stomsoftclrblock(struct fs *fs, unsigned char *cp, int h) 115969800Stomsoft{ 116069800Stomsoft DBG_FUNC("clrblock") 116169800Stomsoft 116269800Stomsoft DBG_ENTER; 116369800Stomsoft 116469800Stomsoft switch ((fs)->fs_frag) { 116569800Stomsoft case 8: 116669800Stomsoft cp[h] = 0; 116769800Stomsoft break; 116869800Stomsoft case 4: 116969800Stomsoft cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2)); 117069800Stomsoft break; 117169800Stomsoft case 2: 117269800Stomsoft cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1)); 117369800Stomsoft break; 117469800Stomsoft case 1: 117569800Stomsoft cp[h >> 3] &= ~(0x01 << (h & 0x7)); 117669800Stomsoft break; 117769800Stomsoft default: 117869926Stomsoft warnx("clrblock bad fs_frag %d", fs->fs_frag); 117969800Stomsoft break; 118069800Stomsoft } 118169800Stomsoft 118269800Stomsoft DBG_LEAVE; 118369800Stomsoft return; 118469800Stomsoft} 118569800Stomsoft 118669800Stomsoft/* 118769800Stomsoft * Here we free a complete block in the free block map. For more details again 1188114067Sschweikh * please see the source of newfs(8), as this function is taken over almost 118969800Stomsoft * unchanged. 119069800Stomsoft */ 119169800Stomsoftstatic void 119269800Stomsoftsetblock(struct fs *fs, unsigned char *cp, int h) 119369800Stomsoft{ 119469800Stomsoft DBG_FUNC("setblock") 119569800Stomsoft 119669800Stomsoft DBG_ENTER; 119769800Stomsoft 119869800Stomsoft switch (fs->fs_frag) { 119969800Stomsoft case 8: 120069800Stomsoft cp[h] = 0xff; 120169800Stomsoft break; 120269800Stomsoft case 4: 120369800Stomsoft cp[h >> 1] |= (0x0f << ((h & 0x1) << 2)); 120469800Stomsoft break; 120569800Stomsoft case 2: 120669800Stomsoft cp[h >> 2] |= (0x03 << ((h & 0x3) << 1)); 120769800Stomsoft break; 120869800Stomsoft case 1: 120969800Stomsoft cp[h >> 3] |= (0x01 << (h & 0x7)); 121069800Stomsoft break; 121169800Stomsoft default: 121269926Stomsoft warnx("setblock bad fs_frag %d", fs->fs_frag); 121369800Stomsoft break; 121469800Stomsoft } 121569800Stomsoft 121669800Stomsoft DBG_LEAVE; 121769800Stomsoft return; 121869800Stomsoft} 121969800Stomsoft 122069800Stomsoft/* 122169800Stomsoft * Figure out how many lines our current terminal has. For more details again 1222114067Sschweikh * please see the source of newfs(8), as this function is taken over almost 122369800Stomsoft * unchanged. 122469800Stomsoft */ 122569800Stomsoftstatic int 122669800Stomsoftcharsperline(void) 122769800Stomsoft{ 122869800Stomsoft DBG_FUNC("charsperline") 1229232548Strasz int columns; 1230232548Strasz char *cp; 1231232548Strasz struct winsize ws; 123269800Stomsoft 123369800Stomsoft DBG_ENTER; 123469800Stomsoft 123569800Stomsoft columns = 0; 1236232548Strasz if (ioctl(0, TIOCGWINSZ, &ws) != -1) 123769800Stomsoft columns = ws.ws_col; 1238232548Strasz if (columns == 0 && (cp = getenv("COLUMNS"))) 123969800Stomsoft columns = atoi(cp); 1240232548Strasz if (columns == 0) 124169800Stomsoft columns = 80; /* last resort */ 124269800Stomsoft 124369800Stomsoft DBG_LEAVE; 1244234420Strasz return (columns); 124569800Stomsoft} 124669800Stomsoft 1247234846Straszstatic int 1248234846Straszis_dev(const char *name) 1249234846Strasz{ 1250234846Strasz struct stat devstat; 1251234846Strasz 1252234846Strasz if (stat(name, &devstat) != 0) 1253234846Strasz return (0); 1254234846Strasz if (!S_ISCHR(devstat.st_mode)) 1255234846Strasz return (0); 1256234846Strasz return (1); 1257234846Strasz} 1258234846Strasz 1259114936Sgrog/* 1260234846Strasz * Return mountpoint on which the device is currently mounted. 1261234846Strasz */ 1262234846Straszstatic const struct statfs * 1263234846Straszdev_to_statfs(const char *dev) 1264114936Sgrog{ 1265234846Strasz struct stat devstat, mntdevstat; 1266234846Strasz struct statfs *mntbuf, *statfsp; 1267234846Strasz char device[MAXPATHLEN]; 1268234846Strasz char *mntdevname; 1269234846Strasz int i, mntsize; 1270114936Sgrog 1271234846Strasz /* 1272234846Strasz * First check the mounted filesystems. 1273234846Strasz */ 1274234846Strasz if (stat(dev, &devstat) != 0) 1275234846Strasz return (NULL); 1276234846Strasz if (!S_ISCHR(devstat.st_mode) && !S_ISBLK(devstat.st_mode)) 1277234846Strasz return (NULL); 1278114936Sgrog 1279234846Strasz mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 1280234846Strasz for (i = 0; i < mntsize; i++) { 1281234846Strasz statfsp = &mntbuf[i]; 1282234846Strasz mntdevname = statfsp->f_mntfromname; 1283234846Strasz if (*mntdevname != '/') { 1284234846Strasz strcpy(device, _PATH_DEV); 1285234846Strasz strcat(device, mntdevname); 1286234846Strasz mntdevname = device; 1287234846Strasz } 1288234846Strasz if (stat(mntdevname, &mntdevstat) == 0 && 1289234846Strasz mntdevstat.st_rdev == devstat.st_rdev) 1290234846Strasz return (statfsp); 1291234846Strasz } 1292114936Sgrog 1293234846Strasz return (NULL); 1294114936Sgrog} 1295114936Sgrog 1296234846Straszstatic const char * 1297234846Straszmountpoint_to_dev(const char *mountpoint) 1298234846Strasz{ 1299234846Strasz struct statfs *mntbuf, *statfsp; 1300234846Strasz struct fstab *fs; 1301234846Strasz int i, mntsize; 1302234846Strasz 1303234846Strasz /* 1304234846Strasz * First check the mounted filesystems. 1305234846Strasz */ 1306234846Strasz mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 1307234846Strasz for (i = 0; i < mntsize; i++) { 1308234846Strasz statfsp = &mntbuf[i]; 1309234846Strasz 1310234846Strasz if (strcmp(statfsp->f_mntonname, mountpoint) == 0) 1311234846Strasz return (statfsp->f_mntfromname); 1312234846Strasz } 1313234846Strasz 1314234846Strasz /* 1315234846Strasz * Check the fstab. 1316234846Strasz */ 1317234846Strasz fs = getfsfile(mountpoint); 1318234846Strasz if (fs != NULL) 1319234846Strasz return (fs->fs_spec); 1320234846Strasz 1321234846Strasz return (NULL); 1322234846Strasz} 1323234846Strasz 1324234846Straszstatic const char * 1325234846Straszgetdev(const char *name) 1326234846Strasz{ 1327234846Strasz static char device[MAXPATHLEN]; 1328234846Strasz const char *cp, *dev; 1329234846Strasz 1330234846Strasz if (is_dev(name)) 1331234846Strasz return (name); 1332234846Strasz 1333234846Strasz cp = strrchr(name, '/'); 1334234846Strasz if (cp == 0) { 1335234846Strasz snprintf(device, sizeof(device), "%s%s", _PATH_DEV, name); 1336234846Strasz if (is_dev(device)) 1337234846Strasz return (device); 1338234846Strasz } 1339234846Strasz 1340234846Strasz dev = mountpoint_to_dev(name); 1341234846Strasz if (dev != NULL && is_dev(dev)) 1342234846Strasz return (dev); 1343234846Strasz 1344234846Strasz return (NULL); 1345234846Strasz} 1346234846Strasz 134769800Stomsoft/* 1348232548Strasz * growfs(8) is a utility which allows to increase the size of an existing 1349223652Strasz * ufs file system. Currently this can only be done on unmounted file system. 1350114067Sschweikh * It recognizes some command line options to specify the new desired size, 1351223652Strasz * and it does some basic checkings. The old file system size is determined 1352114067Sschweikh * and after some more checks like we can really access the new last block 135369800Stomsoft * on the disk etc. we calculate the new parameters for the superblock. After 1354233656Strasz * having done this we just call growfs() which will do the work. 135569800Stomsoft * We still have to provide support for snapshots. Therefore we first have to 1356114067Sschweikh * understand what data structures are always replicated in the snapshot on 1357114067Sschweikh * creation, for all other blocks we touch during our procedure, we have to 135869926Stomsoft * keep the old blocks unchanged somewhere available for the snapshots. If we 1359114067Sschweikh * are lucky, then we only have to handle our blocks to be relocated in that 136069800Stomsoft * way. 1361114067Sschweikh * Also we have to consider in what order we actually update the critical 1362223652Strasz * data structures of the file system to make sure, that in case of a disaster 136369800Stomsoft * fsck(8) is still able to restore any lost data. 1364114067Sschweikh * The foreseen last step then will be to provide for growing even mounted 1365223652Strasz * file systems. There we have to extend the mount() system call to provide 1366223652Strasz * userland access to the file system locking facility. 136769800Stomsoft */ 136869800Stomsoftint 136969800Stomsoftmain(int argc, char **argv) 137069800Stomsoft{ 137169800Stomsoft DBG_FUNC("main") 1372234846Strasz const char *device; 1373234846Strasz const struct statfs *statfsp; 1374234846Strasz uint64_t size = 0; 1375234846Strasz off_t mediasize; 1376234846Strasz int error, i, j, fsi, fso, ch, Nflag = 0, yflag = 0; 1377234846Strasz char *p, reply[5], oldsizebuf[6], newsizebuf[6]; 1378234846Strasz void *testbuf; 137969800Stomsoft 138069800Stomsoft DBG_ENTER; 138169800Stomsoft 1382232548Strasz while ((ch = getopt(argc, argv, "Ns:vy")) != -1) { 138369800Stomsoft switch(ch) { 138469800Stomsoft case 'N': 1385232548Strasz Nflag = 1; 138669800Stomsoft break; 138769800Stomsoft case 's': 1388234846Strasz size = (off_t)strtoumax(optarg, &p, 0); 1389234846Strasz if (p == NULL || *p == '\0') 1390234846Strasz size *= DEV_BSIZE; 1391234846Strasz else if (*p == 'b' || *p == 'B') 1392234846Strasz ; /* do nothing */ 1393234846Strasz else if (*p == 'k' || *p == 'K') 1394234846Strasz size <<= 10; 1395234846Strasz else if (*p == 'm' || *p == 'M') 1396234846Strasz size <<= 20; 1397234846Strasz else if (*p == 'g' || *p == 'G') 1398234846Strasz size <<= 30; 1399234846Strasz else if (*p == 't' || *p == 'T') { 1400234846Strasz size <<= 30; 1401234846Strasz size <<= 10; 1402234846Strasz } else 1403234846Strasz errx(1, "unknown suffix on -s argument"); 140469800Stomsoft break; 140569800Stomsoft case 'v': /* for compatibility to newfs */ 140669800Stomsoft break; 140769800Stomsoft case 'y': 1408234846Strasz yflag = 1; 140969800Stomsoft break; 141069800Stomsoft case '?': 141169800Stomsoft /* FALLTHROUGH */ 141269800Stomsoft default: 141369926Stomsoft usage(); 141469800Stomsoft } 141569800Stomsoft } 141669800Stomsoft argc -= optind; 141769800Stomsoft argv += optind; 141869800Stomsoft 1419232548Strasz if (argc != 1) 142069926Stomsoft usage(); 142169800Stomsoft 142269800Stomsoft /* 1423234846Strasz * Now try to guess the device name. 142469800Stomsoft */ 1425234846Strasz device = getdev(*argv); 1426234846Strasz if (device == NULL) 1427234846Strasz errx(1, "cannot find special device for %s", *argv); 142869800Stomsoft 1429234846Strasz statfsp = dev_to_statfs(device); 143069800Stomsoft 143169800Stomsoft fsi = open(device, O_RDONLY); 1432232548Strasz if (fsi < 0) 143369926Stomsoft err(1, "%s", device); 143469800Stomsoft 143569800Stomsoft /* 1436234846Strasz * Try to guess the slice size if not specified. 143769800Stomsoft */ 1438234846Strasz if (ioctl(fsi, DIOCGMEDIASIZE, &mediasize) == -1) 1439234846Strasz err(1,"DIOCGMEDIASIZE"); 144069800Stomsoft 144169800Stomsoft /* 1442223652Strasz * Check if that partition is suitable for growing a file system. 144369800Stomsoft */ 1444234846Strasz if (mediasize < 1) 144569926Stomsoft errx(1, "partition is unavailable"); 144669800Stomsoft 144769800Stomsoft /* 144869800Stomsoft * Read the current superblock, and take a backup. 144969800Stomsoft */ 145098542Smckusick for (i = 0; sblock_try[i] != -1; i++) { 145198542Smckusick sblockloc = sblock_try[i] / DEV_BSIZE; 145298542Smckusick rdfs(sblockloc, (size_t)SBLOCKSIZE, (void *)&(osblock), fsi); 145398542Smckusick if ((osblock.fs_magic == FS_UFS1_MAGIC || 1454232548Strasz (osblock.fs_magic == FS_UFS2_MAGIC && 1455232548Strasz osblock.fs_sblockloc == sblock_try[i])) && 145698542Smckusick osblock.fs_bsize <= MAXBSIZE && 1457127798Sle osblock.fs_bsize >= (int32_t) sizeof(struct fs)) 145898542Smckusick break; 145998542Smckusick } 1460232548Strasz if (sblock_try[i] == -1) 146169926Stomsoft errx(1, "superblock not recognized"); 146269800Stomsoft memcpy((void *)&fsun1, (void *)&fsun2, sizeof(fsun2)); 146369800Stomsoft 146469800Stomsoft DBG_OPEN("/tmp/growfs.debug"); /* already here we need a superblock */ 1465232548Strasz DBG_DUMP_FS(&sblock, "old sblock"); 146669800Stomsoft 146769800Stomsoft /* 1468233656Strasz * Determine size to grow to. Default to the device size. 146969800Stomsoft */ 1470234846Strasz if (size == 0) 1471234846Strasz size = mediasize; 1472234846Strasz else { 1473234846Strasz if (size > (uint64_t)mediasize) { 1474234846Strasz humanize_number(oldsizebuf, sizeof(oldsizebuf), size, 1475234846Strasz "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1476234846Strasz humanize_number(newsizebuf, sizeof(newsizebuf), 1477234846Strasz mediasize, 1478234846Strasz "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1479234846Strasz 1480234846Strasz errx(1, "requested size %s is larger " 1481234846Strasz "than the available %s", oldsizebuf, newsizebuf); 1482234846Strasz } 148369800Stomsoft } 148469800Stomsoft 1485244243Strasz /* 1486244243Strasz * Make sure the new size is a multiple of fs_fsize; /dev/ufssuspend 1487244243Strasz * only supports fragment-aligned IO requests. 1488244243Strasz */ 1489244243Strasz size -= size % osblock.fs_fsize; 1490244243Strasz 1491234846Strasz if (size <= (uint64_t)(osblock.fs_size * osblock.fs_fsize)) { 1492234846Strasz humanize_number(oldsizebuf, sizeof(oldsizebuf), 1493234846Strasz osblock.fs_size * osblock.fs_fsize, 1494234846Strasz "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1495234846Strasz humanize_number(newsizebuf, sizeof(newsizebuf), size, 1496234846Strasz "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1497234846Strasz 1498234846Strasz errx(1, "requested size %s is not larger than the current " 1499234846Strasz "filesystem size %s", newsizebuf, oldsizebuf); 1500234846Strasz } 1501234846Strasz 1502234846Strasz sblock.fs_size = dbtofsb(&osblock, size / DEV_BSIZE); 1503242379Strasz sblock.fs_providersize = dbtofsb(&osblock, mediasize / DEV_BSIZE); 1504234846Strasz 150569800Stomsoft /* 1506234846Strasz * Are we really growing? 150769800Stomsoft */ 1508232548Strasz if (osblock.fs_size >= sblock.fs_size) { 1509127798Sle errx(1, "we are not growing (%jd->%jd)", 1510127798Sle (intmax_t)osblock.fs_size, (intmax_t)sblock.fs_size); 151169800Stomsoft } 151269800Stomsoft 151369800Stomsoft /* 151469800Stomsoft * Check if we find an active snapshot. 151569800Stomsoft */ 1516234846Strasz if (yflag == 0) { 1517232548Strasz for (j = 0; j < FSMAXSNAP; j++) { 1518232548Strasz if (sblock.fs_snapinum[j]) { 1519223652Strasz errx(1, "active snapshot found in file system; " 1520223429Strasz "please remove all snapshots before " 1521140351Scharnier "using growfs"); 152269800Stomsoft } 1523232548Strasz if (!sblock.fs_snapinum[j]) /* list is dense */ 152469800Stomsoft break; 152569800Stomsoft } 152669800Stomsoft } 152769800Stomsoft 1528234846Strasz if (yflag == 0 && Nflag == 0) { 1529234846Strasz if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) 1530243246Strasz printf("Device is mounted read-write; resizing will " 1531243246Strasz "result in temporary write suspension for %s.\n", 1532243246Strasz statfsp->f_mntonname); 1533234846Strasz printf("It's strongly recommended to make a backup " 1534223652Strasz "before growing the file system.\n" 1535234846Strasz "OK to grow filesystem on %s", device); 1536234846Strasz if (statfsp != NULL) 1537234846Strasz printf(", mounted on %s,", statfsp->f_mntonname); 1538234846Strasz humanize_number(oldsizebuf, sizeof(oldsizebuf), 1539234846Strasz osblock.fs_size * osblock.fs_fsize, 1540234846Strasz "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1541234846Strasz humanize_number(newsizebuf, sizeof(newsizebuf), 1542234846Strasz sblock.fs_size * sblock.fs_fsize, 1543234846Strasz "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1544234846Strasz printf(" from %s to %s? [Yes/No] ", oldsizebuf, newsizebuf); 1545234846Strasz fflush(stdout); 154677885Stomsoft fgets(reply, (int)sizeof(reply), stdin); 1547250377Seadler if (strcasecmp(reply, "Yes\n")){ 1548223429Strasz printf("\nNothing done\n"); 154969800Stomsoft exit (0); 1550114067Sschweikh } 155169800Stomsoft } 155269800Stomsoft 1553234846Strasz /* 1554234846Strasz * Try to access our device for writing. If it's not mounted, 1555234846Strasz * or mounted read-only, simply open it; otherwise, use UFS 1556234846Strasz * suspension mechanism. 1557234846Strasz */ 1558234846Strasz if (Nflag) { 1559234846Strasz fso = -1; 1560234846Strasz } else { 1561243246Strasz if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) { 1562243246Strasz fso = open(_PATH_UFSSUSPEND, O_RDWR); 1563243246Strasz if (fso == -1) 1564243246Strasz err(1, "unable to open %s", _PATH_UFSSUSPEND); 1565243246Strasz error = ioctl(fso, UFSSUSPEND, &statfsp->f_fsid); 1566243246Strasz if (error != 0) 1567243246Strasz err(1, "UFSSUSPEND"); 1568243246Strasz } else { 1569243246Strasz fso = open(device, O_WRONLY); 1570243246Strasz if (fso < 0) 1571243246Strasz err(1, "%s", device); 1572243246Strasz } 1573234846Strasz } 157469800Stomsoft 157569800Stomsoft /* 1576234846Strasz * Try to access our new last block in the file system. 157769800Stomsoft */ 1578234846Strasz testbuf = malloc(sblock.fs_fsize); 1579234846Strasz if (testbuf == NULL) 1580234846Strasz err(1, "malloc"); 1581235079Strasz rdfs((ufs2_daddr_t)((size - sblock.fs_fsize) / DEV_BSIZE), 1582234846Strasz sblock.fs_fsize, testbuf, fsi); 1583235079Strasz wtfs((ufs2_daddr_t)((size - sblock.fs_fsize) / DEV_BSIZE), 1584234846Strasz sblock.fs_fsize, testbuf, fso, Nflag); 1585234846Strasz free(testbuf); 158669800Stomsoft 158769800Stomsoft /* 158869800Stomsoft * Now calculate new superblock values and check for reasonable 1589223652Strasz * bound for new file system size: 1590233656Strasz * fs_size: is derived from user input 159169800Stomsoft * fs_dsize: should get updated in the routines creating or 159269800Stomsoft * updating the cylinder groups on the fly 159369800Stomsoft * fs_cstotal: should get updated in the routines creating or 159469800Stomsoft * updating the cylinder groups 159569800Stomsoft */ 159669800Stomsoft 159769800Stomsoft /* 1598223652Strasz * Update the number of cylinders and cylinder groups in the file system. 159969800Stomsoft */ 160098542Smckusick if (sblock.fs_magic == FS_UFS1_MAGIC) { 160198542Smckusick sblock.fs_old_ncyl = 160298542Smckusick sblock.fs_size * sblock.fs_old_nspf / sblock.fs_old_spc; 160398542Smckusick if (sblock.fs_size * sblock.fs_old_nspf > 160498542Smckusick sblock.fs_old_ncyl * sblock.fs_old_spc) 160598542Smckusick sblock.fs_old_ncyl++; 160669800Stomsoft } 160798542Smckusick sblock.fs_ncg = howmany(sblock.fs_size, sblock.fs_fpg); 160869800Stomsoft 1609244295Strasz /* 1610244295Strasz * Allocate last cylinder group only if there is enough room 1611244295Strasz * for at least one data block. 1612244295Strasz */ 161398542Smckusick if (sblock.fs_size % sblock.fs_fpg != 0 && 1614244295Strasz sblock.fs_size <= cgdmin(&sblock, sblock.fs_ncg - 1)) { 1615244295Strasz humanize_number(oldsizebuf, sizeof(oldsizebuf), 1616244295Strasz (sblock.fs_size % sblock.fs_fpg) * sblock.fs_fsize, 1617244295Strasz "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); 1618244295Strasz warnx("no room to allocate last cylinder group; " 1619244295Strasz "leaving %s unused", oldsizebuf); 162069800Stomsoft sblock.fs_ncg--; 162198542Smckusick if (sblock.fs_magic == FS_UFS1_MAGIC) 162298542Smckusick sblock.fs_old_ncyl = sblock.fs_ncg * sblock.fs_old_cpg; 162398542Smckusick sblock.fs_size = sblock.fs_ncg * sblock.fs_fpg; 162469800Stomsoft } 162569800Stomsoft 162669800Stomsoft /* 162769800Stomsoft * Update the space for the cylinder group summary information in the 162869800Stomsoft * respective cylinder group data area. 162969800Stomsoft */ 163069800Stomsoft sblock.fs_cssize = 163169800Stomsoft fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum)); 1632114067Sschweikh 1633232548Strasz if (osblock.fs_size >= sblock.fs_size) 163469926Stomsoft errx(1, "not enough new space"); 163569800Stomsoft 163669800Stomsoft DBG_PRINT0("sblock calculated\n"); 163769800Stomsoft 163869800Stomsoft /* 163969800Stomsoft * Ok, everything prepared, so now let's do the tricks. 164069800Stomsoft */ 164169800Stomsoft growfs(fsi, fso, Nflag); 164269800Stomsoft 164369800Stomsoft close(fsi); 1644234846Strasz if (fso > -1) { 1645243246Strasz if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) { 1646243246Strasz error = ioctl(fso, UFSRESUME); 1647243246Strasz if (error != 0) 1648243246Strasz err(1, "UFSRESUME"); 1649243246Strasz } 1650234846Strasz error = close(fso); 1651234846Strasz if (error != 0) 1652234846Strasz err(1, "close"); 1653243246Strasz if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) != 0) 1654243246Strasz mount_reload(statfsp); 1655234846Strasz } 165669800Stomsoft 165769800Stomsoft DBG_CLOSE; 165869800Stomsoft 165969800Stomsoft DBG_LEAVE; 1660234420Strasz return (0); 166169800Stomsoft} 166269800Stomsoft 166369800Stomsoft/* 166469800Stomsoft * Dump a line of usage. 166569800Stomsoft */ 166669800Stomsoftstatic void 166769926Stomsoftusage(void) 1668114067Sschweikh{ 166969800Stomsoft DBG_FUNC("usage") 167069800Stomsoft 167169800Stomsoft DBG_ENTER; 167269800Stomsoft 1673234846Strasz fprintf(stderr, "usage: growfs [-Ny] [-s size] special | filesystem\n"); 167469926Stomsoft 167569800Stomsoft DBG_LEAVE; 167669926Stomsoft exit(1); 167769800Stomsoft} 167869800Stomsoft 167969800Stomsoft/* 1680114067Sschweikh * This updates most parameters and the bitmap related to cluster. We have to 1681114067Sschweikh * assume that sblock, osblock, acg are set up. 168269800Stomsoft */ 168369800Stomsoftstatic void 168469800Stomsoftupdclst(int block) 1685114067Sschweikh{ 168669800Stomsoft DBG_FUNC("updclst") 1687232548Strasz static int lcs = 0; 168869800Stomsoft 168969800Stomsoft DBG_ENTER; 169069800Stomsoft 1691232548Strasz if (sblock.fs_contigsumsize < 1) /* no clustering */ 169269800Stomsoft return; 169369800Stomsoft /* 169469800Stomsoft * update cluster allocation map 169569800Stomsoft */ 169669800Stomsoft setbit(cg_clustersfree(&acg), block); 169769800Stomsoft 169869800Stomsoft /* 169969800Stomsoft * update cluster summary table 170069800Stomsoft */ 1701232548Strasz if (!lcs) { 170269800Stomsoft /* 170369800Stomsoft * calculate size for the trailing cluster 170469800Stomsoft */ 1705232548Strasz for (block--; lcs < sblock.fs_contigsumsize; block--, lcs++ ) { 1706232548Strasz if (isclr(cg_clustersfree(&acg), block)) 170769800Stomsoft break; 170869800Stomsoft } 1709114067Sschweikh } 1710232548Strasz if (lcs < sblock.fs_contigsumsize) { 1711232548Strasz if (lcs) 171269800Stomsoft cg_clustersum(&acg)[lcs]--; 171369800Stomsoft lcs++; 171469800Stomsoft cg_clustersum(&acg)[lcs]++; 171569800Stomsoft } 171669800Stomsoft 171769800Stomsoft DBG_LEAVE; 171869800Stomsoft return; 171969800Stomsoft} 1720234846Strasz 1721234846Straszstatic void 1722234846Straszmount_reload(const struct statfs *stfs) 1723234846Strasz{ 1724234846Strasz char errmsg[255]; 1725234846Strasz struct iovec *iov; 1726234846Strasz int iovlen; 1727234846Strasz 1728234846Strasz iov = NULL; 1729234846Strasz iovlen = 0; 1730234846Strasz *errmsg = '\0'; 1731234846Strasz build_iovec(&iov, &iovlen, "fstype", __DECONST(char *, "ffs"), 4); 1732234846Strasz build_iovec(&iov, &iovlen, "fspath", __DECONST(char *, stfs->f_mntonname), (size_t)-1); 1733234846Strasz build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); 1734234846Strasz build_iovec(&iov, &iovlen, "update", NULL, 0); 1735234846Strasz build_iovec(&iov, &iovlen, "reload", NULL, 0); 1736234846Strasz 1737234846Strasz if (nmount(iov, iovlen, stfs->f_flags) < 0) { 1738234846Strasz errmsg[sizeof(errmsg) - 1] = '\0'; 1739234846Strasz err(9, "%s: cannot reload filesystem%s%s", stfs->f_mntonname, 1740234846Strasz *errmsg != '\0' ? ": " : "", errmsg); 1741234846Strasz } 1742234846Strasz} 1743