1*f298a94bSchs /* $NetBSD: resize_ffs.c,v 1.58 2023/01/07 19:41:30 chs Exp $ */
2ec57cc7eSjtk /* From sources sent on February 17, 2003 */
3ec57cc7eSjtk /*-
4ec57cc7eSjtk * As its sole author, I explicitly place this code in the public
5ec57cc7eSjtk * domain. Anyone may use it for any purpose (though I would
6ec57cc7eSjtk * appreciate credit where it is due).
7ec57cc7eSjtk *
8ec57cc7eSjtk * der Mouse
9ec57cc7eSjtk *
10ec57cc7eSjtk * mouse@rodents.montreal.qc.ca
11ec57cc7eSjtk * 7D C8 61 52 5D E7 2D 39 4E F1 31 3E E8 B3 27 4B
12ec57cc7eSjtk */
13ec57cc7eSjtk /*
1484cb7014Swiz * resize_ffs:
15ec57cc7eSjtk *
16ec57cc7eSjtk * Resize a file system. Is capable of both growing and shrinking.
17ec57cc7eSjtk *
18c93380ebSwiz * Usage: resize_ffs [-s newsize] [-y] file_system
19ec57cc7eSjtk *
2018174be8Sriz * Example: resize_ffs -s 29574 /dev/rsd1e
21ec57cc7eSjtk *
22ec57cc7eSjtk * newsize is in DEV_BSIZE units (ie, disk sectors, usually 512 bytes
23ec57cc7eSjtk * each).
24ec57cc7eSjtk *
25ec57cc7eSjtk * Note: this currently requires gcc to build, since it is written
26ec57cc7eSjtk * depending on gcc-specific features, notably nested function
27ec57cc7eSjtk * definitions (which in at least a few cases depend on the lexical
28ec57cc7eSjtk * scoping gcc provides, so they can't be trivially moved outside).
29ec57cc7eSjtk *
30b75abedaSsalo * Many thanks go to John Kohl <jtk@NetBSD.org> for finding bugs: the
31ec57cc7eSjtk * one responsible for the "realloccgblk: can't find blk in cyl"
32ec57cc7eSjtk * problem and a more minor one which left fs_dsize wrong when
33ec57cc7eSjtk * shrinking. (These actually indicate bugs in fsck too - it should
34ec57cc7eSjtk * have caught and fixed them.)
35ec57cc7eSjtk *
36ec57cc7eSjtk */
37ec57cc7eSjtk
3836c7456dSperry #include <sys/cdefs.h>
39*f298a94bSchs __RCSID("$NetBSD: resize_ffs.c,v 1.58 2023/01/07 19:41:30 chs Exp $");
4066ed9af4Sdholland
411e891081Shaad #include <sys/disk.h>
421e891081Shaad #include <sys/disklabel.h>
431e891081Shaad #include <sys/dkio.h>
441e891081Shaad #include <sys/ioctl.h>
45ec57cc7eSjtk #include <sys/stat.h>
46ec57cc7eSjtk #include <sys/mman.h>
47ec57cc7eSjtk #include <sys/param.h> /* MAXFRAG */
48ec57cc7eSjtk #include <ufs/ffs/fs.h>
49d765f2d2Sriz #include <ufs/ffs/ffs_extern.h>
50ec57cc7eSjtk #include <ufs/ufs/dir.h>
51ec57cc7eSjtk #include <ufs/ufs/dinode.h>
52ec57cc7eSjtk #include <ufs/ufs/ufs_bswap.h> /* ufs_rw32 */
53ec57cc7eSjtk
5418174be8Sriz #include <err.h>
5518174be8Sriz #include <errno.h>
5618174be8Sriz #include <fcntl.h>
5718174be8Sriz #include <stdio.h>
5818174be8Sriz #include <stdlib.h>
5918174be8Sriz #include <strings.h>
6018174be8Sriz #include <unistd.h>
612cf653d8Sjmcneill #include <util.h>
6218174be8Sriz
63d0b93bc8Sjmcneill #include "progress.h"
64d0b93bc8Sjmcneill
65ec57cc7eSjtk /* new size of file system, in sectors */
667a8d9bb4Sdholland static int64_t newsize;
67ec57cc7eSjtk
68a3a3268fSriz /* fd open onto disk device or file */
69ec57cc7eSjtk static int fd;
70ec57cc7eSjtk
71d0b93bc8Sjmcneill /* disk device or file path */
722cf653d8Sjmcneill const char *special;
73d0b93bc8Sjmcneill
74ec57cc7eSjtk /* must we break up big I/O operations - see checksmallio() */
75ec57cc7eSjtk static int smallio;
76ec57cc7eSjtk
77ec57cc7eSjtk /* size of a cg, in bytes, rounded up to a frag boundary */
78ec57cc7eSjtk static int cgblksz;
79ec57cc7eSjtk
80628c00a0Schristos /* possible superblock localtions */
81628c00a0Schristos static int search[] = SBLOCKSEARCH;
82628c00a0Schristos /* location of the superblock */
83628c00a0Schristos static off_t where;
84628c00a0Schristos
85ec57cc7eSjtk /* Superblocks. */
86ec57cc7eSjtk static struct fs *oldsb; /* before we started */
87ec57cc7eSjtk static struct fs *newsb; /* copy to work with */
88ec57cc7eSjtk /* Buffer to hold the above. Make sure it's aligned correctly. */
8918174be8Sriz static char sbbuf[2 * SBLOCKSIZE]
9018174be8Sriz __attribute__((__aligned__(__alignof__(struct fs))));
91ec57cc7eSjtk
92d765f2d2Sriz union dinode {
93d765f2d2Sriz struct ufs1_dinode dp1;
94d765f2d2Sriz struct ufs2_dinode dp2;
95d765f2d2Sriz };
96d765f2d2Sriz #define DIP(dp, field) \
97d765f2d2Sriz ((is_ufs2) ? \
98d765f2d2Sriz (dp)->dp2.field : (dp)->dp1.field)
99d765f2d2Sriz
100d765f2d2Sriz #define DIP_ASSIGN(dp, field, value) \
101d765f2d2Sriz do { \
102d765f2d2Sriz if (is_ufs2) \
103d765f2d2Sriz (dp)->dp2.field = (value); \
104d765f2d2Sriz else \
105d765f2d2Sriz (dp)->dp1.field = (value); \
106d765f2d2Sriz } while (0)
107d765f2d2Sriz
108ec57cc7eSjtk /* a cg's worth of brand new squeaky-clean inodes */
109fbf36969Sdholland static struct ufs1_dinode *zinodes1;
110fbf36969Sdholland static struct ufs2_dinode *zinodes2;
111ec57cc7eSjtk
112ec57cc7eSjtk /* pointers to the in-core cgs, read off disk and possibly modified */
113ec57cc7eSjtk static struct cg **cgs;
114ec57cc7eSjtk
115ec57cc7eSjtk /* pointer to csum array - the stuff pointed to on-disk by fs_csaddr */
116ec57cc7eSjtk static struct csum *csums;
117ec57cc7eSjtk
118ec57cc7eSjtk /* per-cg flags, indexed by cg number */
119ec57cc7eSjtk static unsigned char *cgflags;
120ec57cc7eSjtk #define CGF_DIRTY 0x01 /* needs to be written to disk */
121ec57cc7eSjtk #define CGF_BLKMAPS 0x02 /* block bitmaps need rebuilding */
122ec57cc7eSjtk #define CGF_INOMAPS 0x04 /* inode bitmaps need rebuilding */
123ec57cc7eSjtk
124ec57cc7eSjtk /* when shrinking, these two arrays record how we want blocks to move. */
125ec57cc7eSjtk /* if blkmove[i] is j, the frag that started out as frag #i should end */
126ec57cc7eSjtk /* up as frag #j. inomove[i]=j means, similarly, that the inode that */
127ec57cc7eSjtk /* started out as inode i should end up as inode j. */
128ec57cc7eSjtk static unsigned int *blkmove;
129ec57cc7eSjtk static unsigned int *inomove;
130ec57cc7eSjtk
131ec57cc7eSjtk /* in-core copies of all inodes in the fs, indexed by inumber */
132d765f2d2Sriz union dinode *inodes;
133d765f2d2Sriz
134d765f2d2Sriz void *ibuf; /* ptr to fs block-sized buffer for reading/writing inodes */
135d765f2d2Sriz
136d765f2d2Sriz /* byteswapped inodes */
137d765f2d2Sriz union dinode *sinodes;
138ec57cc7eSjtk
139ec57cc7eSjtk /* per-inode flags, indexed by inumber */
140ec57cc7eSjtk static unsigned char *iflags;
141ec57cc7eSjtk #define IF_DIRTY 0x01 /* needs to be written to disk */
142ec57cc7eSjtk #define IF_BDIRTY 0x02 /* like DIRTY, but is set on first inode in a
143ec57cc7eSjtk * block of inodes, and applies to the whole
144ec57cc7eSjtk * block. */
145ec57cc7eSjtk
14618174be8Sriz /* resize_ffs works directly on dinodes, adapt blksize() */
1477a8d9bb4Sdholland #define dblksize(fs, dip, lbn, filesize) \
148e1610ba4Sdholland (((lbn) >= UFS_NDADDR || (uint64_t)(filesize) >= ffs_lblktosize(fs, (lbn) + 1)) \
149628c00a0Schristos ? (fs)->fs_bsize \
150e1610ba4Sdholland : (ffs_fragroundup(fs, ffs_blkoff(fs, (filesize)))))
151628c00a0Schristos
152628c00a0Schristos
153628c00a0Schristos /*
154d765f2d2Sriz * Number of disk sectors per block/fragment
155628c00a0Schristos */
1562737439dSdholland #define NSPB(fs) (FFS_FSBTODB((fs),1) << (fs)->fs_fragshift)
1572737439dSdholland #define NSPF(fs) (FFS_FSBTODB((fs),1))
158628c00a0Schristos
159a3a3268fSriz /* global flags */
160a3a3268fSriz int is_ufs2 = 0;
161a3a3268fSriz int needswap = 0;
162ae3fcf09Schopps int verbose = 0;
163d0b93bc8Sjmcneill int progress = 0;
164a3a3268fSriz
1651e891081Shaad static void usage(void) __dead;
1661e891081Shaad
167ec57cc7eSjtk /*
168ec57cc7eSjtk * See if we need to break up large I/O operations. This should never
169ec57cc7eSjtk * be needed, but under at least one <version,platform> combination,
170ec57cc7eSjtk * large enough disk transfers to the raw device hang. So if we're
171ec57cc7eSjtk * talking to a character special device, play it safe; in this case,
172ec57cc7eSjtk * readat() and writeat() break everything up into pieces no larger
173ec57cc7eSjtk * than 8K, doing multiple syscalls for larger operations.
174ec57cc7eSjtk */
175ec57cc7eSjtk static void
checksmallio(void)176ec57cc7eSjtk checksmallio(void)
177ec57cc7eSjtk {
178ec57cc7eSjtk struct stat stb;
179ec57cc7eSjtk
180ec57cc7eSjtk fstat(fd, &stb);
181ec57cc7eSjtk smallio = ((stb.st_mode & S_IFMT) == S_IFCHR);
182ec57cc7eSjtk }
1838966ecedSriz
1848966ecedSriz static int
isplainfile(void)1858966ecedSriz isplainfile(void)
1868966ecedSriz {
1878966ecedSriz struct stat stb;
1888966ecedSriz
1898966ecedSriz fstat(fd, &stb);
1908966ecedSriz return S_ISREG(stb.st_mode);
1918966ecedSriz }
192ec57cc7eSjtk /*
193ec57cc7eSjtk * Read size bytes starting at blkno into buf. blkno is in DEV_BSIZE
1942737439dSdholland * units, ie, after FFS_FSBTODB(); size is in bytes.
195ec57cc7eSjtk */
196ec57cc7eSjtk static void
readat(off_t blkno,void * buf,int size)197ec57cc7eSjtk readat(off_t blkno, void *buf, int size)
198ec57cc7eSjtk {
199ec57cc7eSjtk /* Seek to the correct place. */
200628c00a0Schristos if (lseek(fd, blkno * DEV_BSIZE, L_SET) < 0)
20118174be8Sriz err(EXIT_FAILURE, "lseek failed");
202628c00a0Schristos
203ec57cc7eSjtk /* See if we have to break up the transfer... */
204ec57cc7eSjtk if (smallio) {
205ec57cc7eSjtk char *bp; /* pointer into buf */
206ec57cc7eSjtk int left; /* bytes left to go */
207ec57cc7eSjtk int n; /* number to do this time around */
208ec57cc7eSjtk int rv; /* syscall return value */
209ec57cc7eSjtk bp = buf;
210ec57cc7eSjtk left = size;
211ec57cc7eSjtk while (left > 0) {
212ec57cc7eSjtk n = (left > 8192) ? 8192 : left;
213ec57cc7eSjtk rv = read(fd, bp, n);
214628c00a0Schristos if (rv < 0)
2151e891081Shaad err(EXIT_FAILURE, "read failed");
216628c00a0Schristos if (rv != n)
21718174be8Sriz errx(EXIT_FAILURE,
21818174be8Sriz "read: wanted %d, got %d", n, rv);
219ec57cc7eSjtk bp += n;
220ec57cc7eSjtk left -= n;
221ec57cc7eSjtk }
222ec57cc7eSjtk } else {
223ec57cc7eSjtk int rv;
224ec57cc7eSjtk rv = read(fd, buf, size);
225628c00a0Schristos if (rv < 0)
2261e891081Shaad err(EXIT_FAILURE, "read failed");
227628c00a0Schristos if (rv != size)
228d765f2d2Sriz errx(EXIT_FAILURE, "read: wanted %d, got %d",
229d765f2d2Sriz size, rv);
230ec57cc7eSjtk }
231ec57cc7eSjtk }
232ec57cc7eSjtk /*
233ec57cc7eSjtk * Write size bytes from buf starting at blkno. blkno is in DEV_BSIZE
2342737439dSdholland * units, ie, after FFS_FSBTODB(); size is in bytes.
235ec57cc7eSjtk */
236ec57cc7eSjtk static void
writeat(off_t blkno,const void * buf,int size)237ec57cc7eSjtk writeat(off_t blkno, const void *buf, int size)
238ec57cc7eSjtk {
239ec57cc7eSjtk /* Seek to the correct place. */
240628c00a0Schristos if (lseek(fd, blkno * DEV_BSIZE, L_SET) < 0)
2411e891081Shaad err(EXIT_FAILURE, "lseek failed");
242ec57cc7eSjtk /* See if we have to break up the transfer... */
243ec57cc7eSjtk if (smallio) {
244ec57cc7eSjtk const char *bp; /* pointer into buf */
245ec57cc7eSjtk int left; /* bytes left to go */
246ec57cc7eSjtk int n; /* number to do this time around */
247ec57cc7eSjtk int rv; /* syscall return value */
248ec57cc7eSjtk bp = buf;
249ec57cc7eSjtk left = size;
250ec57cc7eSjtk while (left > 0) {
251ec57cc7eSjtk n = (left > 8192) ? 8192 : left;
252ec57cc7eSjtk rv = write(fd, bp, n);
253628c00a0Schristos if (rv < 0)
2541e891081Shaad err(EXIT_FAILURE, "write failed");
255628c00a0Schristos if (rv != n)
25618174be8Sriz errx(EXIT_FAILURE,
25718174be8Sriz "write: wanted %d, got %d", n, rv);
258ec57cc7eSjtk bp += n;
259ec57cc7eSjtk left -= n;
260ec57cc7eSjtk }
261ec57cc7eSjtk } else {
262ec57cc7eSjtk int rv;
263ec57cc7eSjtk rv = write(fd, buf, size);
264628c00a0Schristos if (rv < 0)
2651e891081Shaad err(EXIT_FAILURE, "write failed");
266628c00a0Schristos if (rv != size)
26718174be8Sriz errx(EXIT_FAILURE,
26818174be8Sriz "write: wanted %d, got %d", size, rv);
269ec57cc7eSjtk }
270ec57cc7eSjtk }
271ec57cc7eSjtk /*
272ec57cc7eSjtk * Never-fail versions of malloc() and realloc(), and an allocation
273ec57cc7eSjtk * routine (which also never fails) for allocating memory that will
274ec57cc7eSjtk * never be freed until exit.
275ec57cc7eSjtk */
276ec57cc7eSjtk
277ec57cc7eSjtk /*
278ec57cc7eSjtk * Never-fail malloc.
279ec57cc7eSjtk */
280ec57cc7eSjtk static void *
nfmalloc(size_t nb,const char * tag)281ec57cc7eSjtk nfmalloc(size_t nb, const char *tag)
282ec57cc7eSjtk {
283ec57cc7eSjtk void *rv;
284ec57cc7eSjtk
285ec57cc7eSjtk rv = malloc(nb);
286ec57cc7eSjtk if (rv)
287ec57cc7eSjtk return (rv);
2881e891081Shaad err(EXIT_FAILURE, "Can't allocate %lu bytes for %s",
289628c00a0Schristos (unsigned long int) nb, tag);
290ec57cc7eSjtk }
291ec57cc7eSjtk /*
292ec57cc7eSjtk * Never-fail realloc.
293ec57cc7eSjtk */
294ec57cc7eSjtk static void *
nfrealloc(void * blk,size_t nb,const char * tag)295ec57cc7eSjtk nfrealloc(void *blk, size_t nb, const char *tag)
296ec57cc7eSjtk {
297ec57cc7eSjtk void *rv;
298ec57cc7eSjtk
299ec57cc7eSjtk rv = realloc(blk, nb);
300ec57cc7eSjtk if (rv)
301ec57cc7eSjtk return (rv);
3021e891081Shaad err(EXIT_FAILURE, "Can't re-allocate %lu bytes for %s",
303628c00a0Schristos (unsigned long int) nb, tag);
304ec57cc7eSjtk }
305ec57cc7eSjtk /*
306ec57cc7eSjtk * Allocate memory that will never be freed or reallocated. Arguably
307ec57cc7eSjtk * this routine should handle small allocations by chopping up pages,
308ec57cc7eSjtk * but that's not worth the bother; it's not called more than a
309ec57cc7eSjtk * handful of times per run, and if the allocations are that small the
310ec57cc7eSjtk * waste in giving each one its own page is ignorable.
311ec57cc7eSjtk */
312ec57cc7eSjtk static void *
alloconce(size_t nb,const char * tag)313ec57cc7eSjtk alloconce(size_t nb, const char *tag)
314ec57cc7eSjtk {
315ec57cc7eSjtk void *rv;
316ec57cc7eSjtk
317ec57cc7eSjtk rv = mmap(0, nb, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
318ec57cc7eSjtk if (rv != MAP_FAILED)
319ec57cc7eSjtk return (rv);
3201e891081Shaad err(EXIT_FAILURE, "Can't map %lu bytes for %s",
321ec57cc7eSjtk (unsigned long int) nb, tag);
322ec57cc7eSjtk }
323ec57cc7eSjtk /*
324ec57cc7eSjtk * Load the cgs and csums off disk. Also allocates the space to load
325ec57cc7eSjtk * them into and initializes the per-cg flags.
326ec57cc7eSjtk */
327ec57cc7eSjtk static void
loadcgs(void)328ec57cc7eSjtk loadcgs(void)
329ec57cc7eSjtk {
330*f298a94bSchs uint32_t cg;
331ec57cc7eSjtk char *cgp;
332ec57cc7eSjtk
333ec57cc7eSjtk cgblksz = roundup(oldsb->fs_cgsize, oldsb->fs_fsize);
33417187885Schristos cgs = nfmalloc(oldsb->fs_ncg * sizeof(*cgs), "cg pointers");
335ec57cc7eSjtk cgp = alloconce(oldsb->fs_ncg * cgblksz, "cgs");
336ec57cc7eSjtk cgflags = nfmalloc(oldsb->fs_ncg, "cg flags");
337ec57cc7eSjtk csums = nfmalloc(oldsb->fs_cssize, "cg summary");
338ec57cc7eSjtk for (cg = 0; cg < oldsb->fs_ncg; cg++) {
339ec57cc7eSjtk cgs[cg] = (struct cg *) cgp;
3402737439dSdholland readat(FFS_FSBTODB(oldsb, cgtod(oldsb, cg)), cgp, cgblksz);
341d765f2d2Sriz if (needswap)
342d765f2d2Sriz ffs_cg_swap(cgs[cg],cgs[cg],oldsb);
343ec57cc7eSjtk cgflags[cg] = 0;
344ec57cc7eSjtk cgp += cgblksz;
345ec57cc7eSjtk }
3462737439dSdholland readat(FFS_FSBTODB(oldsb, oldsb->fs_csaddr), csums, oldsb->fs_cssize);
347d765f2d2Sriz if (needswap)
348d765f2d2Sriz ffs_csum_swap(csums,csums,oldsb->fs_cssize);
349ec57cc7eSjtk }
350ec57cc7eSjtk /*
351ec57cc7eSjtk * Set n bits, starting with bit #base, in the bitmap pointed to by
352ec57cc7eSjtk * bitvec (which is assumed to be large enough to include bits base
353ec57cc7eSjtk * through base+n-1).
354ec57cc7eSjtk */
355ec57cc7eSjtk static void
set_bits(unsigned char * bitvec,unsigned int base,unsigned int n)356ec57cc7eSjtk set_bits(unsigned char *bitvec, unsigned int base, unsigned int n)
357ec57cc7eSjtk {
358ec57cc7eSjtk if (n < 1)
359ec57cc7eSjtk return; /* nothing to do */
360ec57cc7eSjtk if (base & 7) { /* partial byte at beginning */
361ec57cc7eSjtk if (n <= 8 - (base & 7)) { /* entirely within one byte */
362ec57cc7eSjtk bitvec[base >> 3] |= (~((~0U) << n)) << (base & 7);
363ec57cc7eSjtk return;
364ec57cc7eSjtk }
365ec57cc7eSjtk bitvec[base >> 3] |= (~0U) << (base & 7);
366ec57cc7eSjtk n -= 8 - (base & 7);
367ec57cc7eSjtk base = (base & ~7) + 8;
368ec57cc7eSjtk }
369ec57cc7eSjtk if (n >= 8) { /* do full bytes */
370ec57cc7eSjtk memset(bitvec + (base >> 3), 0xff, n >> 3);
371ec57cc7eSjtk base += n & ~7;
372ec57cc7eSjtk n &= 7;
373ec57cc7eSjtk }
374ec57cc7eSjtk if (n) { /* partial byte at end */
375ec57cc7eSjtk bitvec[base >> 3] |= ~((~0U) << n);
376ec57cc7eSjtk }
377ec57cc7eSjtk }
378ec57cc7eSjtk /*
379ec57cc7eSjtk * Clear n bits, starting with bit #base, in the bitmap pointed to by
380ec57cc7eSjtk * bitvec (which is assumed to be large enough to include bits base
381ec57cc7eSjtk * through base+n-1). Code parallels set_bits().
382ec57cc7eSjtk */
383ec57cc7eSjtk static void
clr_bits(unsigned char * bitvec,int base,int n)384ec57cc7eSjtk clr_bits(unsigned char *bitvec, int base, int n)
385ec57cc7eSjtk {
386ec57cc7eSjtk if (n < 1)
387ec57cc7eSjtk return;
388ec57cc7eSjtk if (base & 7) {
389ec57cc7eSjtk if (n <= 8 - (base & 7)) {
390ec57cc7eSjtk bitvec[base >> 3] &= ~((~((~0U) << n)) << (base & 7));
391ec57cc7eSjtk return;
392ec57cc7eSjtk }
393ec57cc7eSjtk bitvec[base >> 3] &= ~((~0U) << (base & 7));
394ec57cc7eSjtk n -= 8 - (base & 7);
395ec57cc7eSjtk base = (base & ~7) + 8;
396ec57cc7eSjtk }
397ec57cc7eSjtk if (n >= 8) {
398d765f2d2Sriz memset(bitvec + (base >> 3), 0, n >> 3);
399ec57cc7eSjtk base += n & ~7;
400ec57cc7eSjtk n &= 7;
401ec57cc7eSjtk }
402ec57cc7eSjtk if (n) {
403ec57cc7eSjtk bitvec[base >> 3] &= (~0U) << n;
404ec57cc7eSjtk }
405ec57cc7eSjtk }
406ec57cc7eSjtk /*
407ec57cc7eSjtk * Test whether bit #bit is set in the bitmap pointed to by bitvec.
408ec57cc7eSjtk */
4091e891081Shaad static int
bit_is_set(unsigned char * bitvec,int bit)410ec57cc7eSjtk bit_is_set(unsigned char *bitvec, int bit)
411ec57cc7eSjtk {
412ec57cc7eSjtk return (bitvec[bit >> 3] & (1 << (bit & 7)));
413ec57cc7eSjtk }
414ec57cc7eSjtk /*
415ec57cc7eSjtk * Test whether bit #bit is clear in the bitmap pointed to by bitvec.
416ec57cc7eSjtk */
4171e891081Shaad static int
bit_is_clr(unsigned char * bitvec,int bit)418ec57cc7eSjtk bit_is_clr(unsigned char *bitvec, int bit)
419ec57cc7eSjtk {
420ec57cc7eSjtk return (!bit_is_set(bitvec, bit));
421ec57cc7eSjtk }
422ec57cc7eSjtk /*
423ec57cc7eSjtk * Test whether a whole block of bits is set in a bitmap. This is
424ec57cc7eSjtk * designed for testing (aligned) disk blocks in a bit-per-frag
425ec57cc7eSjtk * bitmap; it has assumptions wired into it based on that, essentially
426ec57cc7eSjtk * that the entire block fits into a single byte. This returns true
427ec57cc7eSjtk * iff _all_ the bits are set; it is not just the complement of
428ec57cc7eSjtk * blk_is_clr on the same arguments (unless blkfrags==1).
429ec57cc7eSjtk */
4301e891081Shaad static int
blk_is_set(unsigned char * bitvec,int blkbase,int blkfrags)431ec57cc7eSjtk blk_is_set(unsigned char *bitvec, int blkbase, int blkfrags)
432ec57cc7eSjtk {
433ec57cc7eSjtk unsigned int mask;
434ec57cc7eSjtk
435ec57cc7eSjtk mask = (~((~0U) << blkfrags)) << (blkbase & 7);
436ec57cc7eSjtk return ((bitvec[blkbase >> 3] & mask) == mask);
437ec57cc7eSjtk }
438ec57cc7eSjtk /*
439ec57cc7eSjtk * Test whether a whole block of bits is clear in a bitmap. See
440ec57cc7eSjtk * blk_is_set (above) for assumptions. This returns true iff _all_
441ec57cc7eSjtk * the bits are clear; it is not just the complement of blk_is_set on
442ec57cc7eSjtk * the same arguments (unless blkfrags==1).
443ec57cc7eSjtk */
4441e891081Shaad static int
blk_is_clr(unsigned char * bitvec,int blkbase,int blkfrags)445ec57cc7eSjtk blk_is_clr(unsigned char *bitvec, int blkbase, int blkfrags)
446ec57cc7eSjtk {
447ec57cc7eSjtk unsigned int mask;
448ec57cc7eSjtk
449ec57cc7eSjtk mask = (~((~0U) << blkfrags)) << (blkbase & 7);
450ec57cc7eSjtk return ((bitvec[blkbase >> 3] & mask) == 0);
451ec57cc7eSjtk }
452ec57cc7eSjtk /*
453ec57cc7eSjtk * Initialize a new cg. Called when growing. Assumes memory has been
454ec57cc7eSjtk * allocated but not otherwise set up. This code sets the fields of
455ec57cc7eSjtk * the cg, initializes the bitmaps (and cluster summaries, if
456ec57cc7eSjtk * applicable), updates both per-cylinder summary info and the global
457ec57cc7eSjtk * summary info in newsb; it also writes out new inodes for the cg.
458ec57cc7eSjtk *
459ec57cc7eSjtk * This code knows it can never be called for cg 0, which makes it a
460ec57cc7eSjtk * bit simpler than it would otherwise be.
461ec57cc7eSjtk */
462ec57cc7eSjtk static void
initcg(uint32_t cgn)463*f298a94bSchs initcg(uint32_t cgn)
464ec57cc7eSjtk {
465ec57cc7eSjtk struct cg *cg; /* The in-core cg, of course */
4660e0ce305Ssborrill int64_t base; /* Disk address of cg base */
4670e0ce305Ssborrill int64_t dlow; /* Size of pre-cg data area */
4680e0ce305Ssborrill int64_t dhigh; /* Offset of post-inode data area, from base */
4690e0ce305Ssborrill int64_t dmax; /* Offset of end of post-inode data area */
470ec57cc7eSjtk int i; /* Generic loop index */
471ec57cc7eSjtk int n; /* Generic count */
472d765f2d2Sriz int start; /* start of cg maps */
473ec57cc7eSjtk
474ec57cc7eSjtk cg = cgs[cgn];
475ec57cc7eSjtk /* Place the data areas */
476ec57cc7eSjtk base = cgbase(newsb, cgn);
477ec57cc7eSjtk dlow = cgsblock(newsb, cgn) - base;
478ec57cc7eSjtk dhigh = cgdmin(newsb, cgn) - base;
479ec57cc7eSjtk dmax = newsb->fs_size - base;
480ec57cc7eSjtk if (dmax > newsb->fs_fpg)
481ec57cc7eSjtk dmax = newsb->fs_fpg;
482fa4874d5Smlelstv start = (unsigned char *)&cg->cg_space[0] - (unsigned char *) cg;
483ec57cc7eSjtk /*
484ec57cc7eSjtk * Clear out the cg - assumes all-0-bytes is the correct way
485ec57cc7eSjtk * to initialize fields we don't otherwise touch, which is
486ec57cc7eSjtk * perhaps not the right thing to do, but it's what fsck and
487ec57cc7eSjtk * mkfs do.
488ec57cc7eSjtk */
489d765f2d2Sriz memset(cg, 0, newsb->fs_cgsize);
490a7e78491Smhitch if (newsb->fs_old_flags & FS_FLAGS_UPDATED)
491ec57cc7eSjtk cg->cg_time = newsb->fs_time;
492ec57cc7eSjtk cg->cg_magic = CG_MAGIC;
493ec57cc7eSjtk cg->cg_cgx = cgn;
494d765f2d2Sriz cg->cg_niblk = newsb->fs_ipg;
495d765f2d2Sriz cg->cg_ndblk = dmax;
496d765f2d2Sriz
497d765f2d2Sriz if (is_ufs2) {
498d765f2d2Sriz cg->cg_time = newsb->fs_time;
499f1333577Sdholland cg->cg_initediblk = newsb->fs_ipg < 2 * FFS_INOPB(newsb) ?
500f1333577Sdholland newsb->fs_ipg : 2 * FFS_INOPB(newsb);
501d765f2d2Sriz cg->cg_iusedoff = start;
502d765f2d2Sriz } else {
503d765f2d2Sriz cg->cg_old_time = newsb->fs_time;
504d765f2d2Sriz cg->cg_old_niblk = cg->cg_niblk;
505d765f2d2Sriz cg->cg_niblk = 0;
506d765f2d2Sriz cg->cg_initediblk = 0;
507d765f2d2Sriz
508d765f2d2Sriz
509628c00a0Schristos cg->cg_old_ncyl = newsb->fs_old_cpg;
5106f8b62aaSriz /* Update the cg_old_ncyl value for the last cylinder. */
511a7e78491Smhitch if (cgn == newsb->fs_ncg - 1) {
512a7e78491Smhitch if ((newsb->fs_old_flags & FS_FLAGS_UPDATED) == 0)
513d765f2d2Sriz cg->cg_old_ncyl = newsb->fs_old_ncyl %
514d765f2d2Sriz newsb->fs_old_cpg;
515ec57cc7eSjtk }
516d765f2d2Sriz
517d765f2d2Sriz /* Set up the bitmap pointers. We have to be careful
518d765f2d2Sriz * to lay out the cg _exactly_ the way mkfs and fsck
519d765f2d2Sriz * do it, since fsck compares the _entire_ cg against
520d765f2d2Sriz * a recomputed cg, and whines if there is any
521ec57cc7eSjtk * mismatch, including the bitmap offsets. */
522ec57cc7eSjtk /* XXX update this comment when fsck is fixed */
523d765f2d2Sriz cg->cg_old_btotoff = start;
524628c00a0Schristos cg->cg_old_boff = cg->cg_old_btotoff
525628c00a0Schristos + (newsb->fs_old_cpg * sizeof(int32_t));
526628c00a0Schristos cg->cg_iusedoff = cg->cg_old_boff +
527628c00a0Schristos (newsb->fs_old_cpg * newsb->fs_old_nrpos * sizeof(int16_t));
528d765f2d2Sriz }
529ec57cc7eSjtk cg->cg_freeoff = cg->cg_iusedoff + howmany(newsb->fs_ipg, NBBY);
530ec57cc7eSjtk if (newsb->fs_contigsumsize > 0) {
531ec57cc7eSjtk cg->cg_nclusterblks = cg->cg_ndblk / newsb->fs_frag;
532ec57cc7eSjtk cg->cg_clustersumoff = cg->cg_freeoff +
533d765f2d2Sriz howmany(newsb->fs_fpg, NBBY) - sizeof(int32_t);
534ec57cc7eSjtk cg->cg_clustersumoff =
535ec57cc7eSjtk roundup(cg->cg_clustersumoff, sizeof(int32_t));
536ec57cc7eSjtk cg->cg_clusteroff = cg->cg_clustersumoff +
537ec57cc7eSjtk ((newsb->fs_contigsumsize + 1) * sizeof(int32_t));
538ec57cc7eSjtk cg->cg_nextfreeoff = cg->cg_clusteroff +
53975571afdSdholland howmany(ffs_fragstoblks(newsb,newsb->fs_fpg), NBBY);
540ec57cc7eSjtk n = dlow / newsb->fs_frag;
541ec57cc7eSjtk if (n > 0) {
542ec57cc7eSjtk set_bits(cg_clustersfree(cg, 0), 0, n);
543ec57cc7eSjtk cg_clustersum(cg, 0)[(n > newsb->fs_contigsumsize) ?
544ec57cc7eSjtk newsb->fs_contigsumsize : n]++;
545ec57cc7eSjtk }
546ec57cc7eSjtk } else {
547ec57cc7eSjtk cg->cg_nextfreeoff = cg->cg_freeoff +
548d765f2d2Sriz howmany(newsb->fs_fpg, NBBY);
549ec57cc7eSjtk }
550ec57cc7eSjtk /* Mark the data areas as free; everything else is marked busy by the
551d765f2d2Sriz * memset() up at the top. */
552ec57cc7eSjtk set_bits(cg_blksfree(cg, 0), 0, dlow);
553ec57cc7eSjtk set_bits(cg_blksfree(cg, 0), dhigh, dmax - dhigh);
554ec57cc7eSjtk /* Initialize summary info */
555ec57cc7eSjtk cg->cg_cs.cs_ndir = 0;
556ec57cc7eSjtk cg->cg_cs.cs_nifree = newsb->fs_ipg;
557ec57cc7eSjtk cg->cg_cs.cs_nbfree = dlow / newsb->fs_frag;
558ec57cc7eSjtk cg->cg_cs.cs_nffree = 0;
559ec57cc7eSjtk
560d765f2d2Sriz /* This is the simplest way of doing this; we perhaps could
561d765f2d2Sriz * compute the correct cg_blktot()[] and cg_blks()[] values
562d765f2d2Sriz * other ways, but it would be complicated and hardly seems
563d765f2d2Sriz * worth the effort. (The reason there isn't
564d765f2d2Sriz * frag-at-beginning and frag-at-end code here, like the code
565d765f2d2Sriz * below for the post-inode data area, is that the pre-sb data
566d765f2d2Sriz * area always starts at 0, and thus is block-aligned, and
567ec57cc7eSjtk * always ends at the sb, which is block-aligned.) */
5689d73fa56Skre if ((newsb->fs_old_flags & FS_FLAGS_UPDATED) == 0) {
56984bea879Sriastradh int64_t di;
5709d73fa56Skre
57184bea879Sriastradh for (di = 0; di < dlow; di += newsb->fs_frag) {
57284bea879Sriastradh old_cg_blktot(cg, 0)[old_cbtocylno(newsb, di)]++;
57318174be8Sriz old_cg_blks(newsb, cg,
57484bea879Sriastradh old_cbtocylno(newsb, di),
57584bea879Sriastradh 0)[old_cbtorpos(newsb, di)]++;
576ec57cc7eSjtk }
5779d73fa56Skre }
578d765f2d2Sriz
579ec57cc7eSjtk /* Deal with a partial block at the beginning of the post-inode area.
580ec57cc7eSjtk * I'm not convinced this can happen - I think the inodes are always
581ec57cc7eSjtk * block-aligned and always an integral number of blocks - but it's
582ec57cc7eSjtk * cheap to do the right thing just in case. */
583ec57cc7eSjtk if (dhigh % newsb->fs_frag) {
584ec57cc7eSjtk n = newsb->fs_frag - (dhigh % newsb->fs_frag);
585ec57cc7eSjtk cg->cg_frsum[n]++;
586ec57cc7eSjtk cg->cg_cs.cs_nffree += n;
587ec57cc7eSjtk dhigh += n;
588ec57cc7eSjtk }
589ec57cc7eSjtk n = (dmax - dhigh) / newsb->fs_frag;
590ec57cc7eSjtk /* We have n full-size blocks in the post-inode data area. */
591ec57cc7eSjtk if (n > 0) {
592ec57cc7eSjtk cg->cg_cs.cs_nbfree += n;
593ec57cc7eSjtk if (newsb->fs_contigsumsize > 0) {
594ec57cc7eSjtk i = dhigh / newsb->fs_frag;
595ec57cc7eSjtk set_bits(cg_clustersfree(cg, 0), i, n);
596ec57cc7eSjtk cg_clustersum(cg, 0)[(n > newsb->fs_contigsumsize) ?
597ec57cc7eSjtk newsb->fs_contigsumsize : n]++;
598ec57cc7eSjtk }
599ec57cc7eSjtk for (i = n; i > 0; i--) {
6009ffedbd0Smlelstv if (is_ufs2 == 0) {
601d765f2d2Sriz old_cg_blktot(cg, 0)[old_cbtocylno(newsb,
602d765f2d2Sriz dhigh)]++;
60318174be8Sriz old_cg_blks(newsb, cg,
604d765f2d2Sriz old_cbtocylno(newsb, dhigh),
605d765f2d2Sriz 0)[old_cbtorpos(newsb,
606ec57cc7eSjtk dhigh)]++;
6079ffedbd0Smlelstv }
608ec57cc7eSjtk dhigh += newsb->fs_frag;
609ec57cc7eSjtk }
610ec57cc7eSjtk }
611ec57cc7eSjtk /* Deal with any leftover frag at the end of the cg. */
612ec57cc7eSjtk i = dmax - dhigh;
613ec57cc7eSjtk if (i) {
614ec57cc7eSjtk cg->cg_frsum[i]++;
615ec57cc7eSjtk cg->cg_cs.cs_nffree += i;
616ec57cc7eSjtk }
617ec57cc7eSjtk /* Update the csum info. */
618ec57cc7eSjtk csums[cgn] = cg->cg_cs;
619ec57cc7eSjtk newsb->fs_cstotal.cs_nffree += cg->cg_cs.cs_nffree;
620ec57cc7eSjtk newsb->fs_cstotal.cs_nbfree += cg->cg_cs.cs_nbfree;
621ec57cc7eSjtk newsb->fs_cstotal.cs_nifree += cg->cg_cs.cs_nifree;
622fbf36969Sdholland if (is_ufs2) {
623ec57cc7eSjtk /* Write out the cleared inodes. */
624fbf36969Sdholland writeat(FFS_FSBTODB(newsb, cgimin(newsb, cgn)), zinodes2,
625fbf36969Sdholland cg->cg_initediblk * sizeof(*zinodes2));
626fbf36969Sdholland } else {
627fbf36969Sdholland /* Write out the cleared inodes. */
628fbf36969Sdholland writeat(FFS_FSBTODB(newsb, cgimin(newsb, cgn)), zinodes1,
629fbf36969Sdholland newsb->fs_ipg * sizeof(*zinodes1));
630fbf36969Sdholland }
631ec57cc7eSjtk /* Dirty the cg. */
632ec57cc7eSjtk cgflags[cgn] |= CGF_DIRTY;
633ec57cc7eSjtk }
634ec57cc7eSjtk /*
635ec57cc7eSjtk * Find free space, at least nfrags consecutive frags of it. Pays no
636ec57cc7eSjtk * attention to block boundaries, but refuses to straddle cg
637ec57cc7eSjtk * boundaries, even if the disk blocks involved are in fact
638ec57cc7eSjtk * consecutive. Return value is the frag number of the first frag of
639ec57cc7eSjtk * the block, or -1 if no space was found. Uses newsb for sb values,
640ec57cc7eSjtk * and assumes the cgs[] structures correctly describe the area to be
641ec57cc7eSjtk * searched.
642ec57cc7eSjtk *
643ec57cc7eSjtk * XXX is there a bug lurking in the ignoring of block boundaries by
644ec57cc7eSjtk * the routine used by fragmove() in evict_data()? Can an end-of-file
645ec57cc7eSjtk * frag legally straddle a block boundary? If not, this should be
646ec57cc7eSjtk * cloned and fixed to stop at block boundaries for that use. The
647ec57cc7eSjtk * current one may still be needed for csum info motion, in case that
648ec57cc7eSjtk * takes up more than a whole block (is the csum info allowed to begin
649ec57cc7eSjtk * partway through a block and continue into the following block?).
650ec57cc7eSjtk *
651ec57cc7eSjtk * If we wrap off the end of the file system back to the beginning, we
652ec57cc7eSjtk * can end up searching the end of the file system twice. I ignore
653ec57cc7eSjtk * this inefficiency, since if that happens we're going to croak with
654ec57cc7eSjtk * a no-space error anyway, so it happens at most once.
655ec57cc7eSjtk */
656ec57cc7eSjtk static int
find_freespace(unsigned int nfrags)657ec57cc7eSjtk find_freespace(unsigned int nfrags)
658ec57cc7eSjtk {
659ec57cc7eSjtk static int hand = 0; /* hand rotates through all frags in the fs */
660ec57cc7eSjtk int cgsize; /* size of the cg hand currently points into */
661*f298a94bSchs uint32_t cgn; /* number of cg hand currently points into */
662ec57cc7eSjtk int fwc; /* frag-within-cg number of frag hand points
663ec57cc7eSjtk * to */
6647a8d9bb4Sdholland unsigned int run; /* length of run of free frags seen so far */
665ec57cc7eSjtk int secondpass; /* have we wrapped from end of fs to
666ec57cc7eSjtk * beginning? */
667ec57cc7eSjtk unsigned char *bits; /* cg_blksfree()[] for cg hand points into */
668ec57cc7eSjtk
669ec57cc7eSjtk cgn = dtog(newsb, hand);
670ec57cc7eSjtk fwc = dtogd(newsb, hand);
671ec57cc7eSjtk secondpass = (hand == 0);
672ec57cc7eSjtk run = 0;
673ec57cc7eSjtk bits = cg_blksfree(cgs[cgn], 0);
674ec57cc7eSjtk cgsize = cgs[cgn]->cg_ndblk;
675ec57cc7eSjtk while (1) {
676ec57cc7eSjtk if (bit_is_set(bits, fwc)) {
677ec57cc7eSjtk run++;
678ec57cc7eSjtk if (run >= nfrags)
679ec57cc7eSjtk return (hand + 1 - run);
680ec57cc7eSjtk } else {
681ec57cc7eSjtk run = 0;
682ec57cc7eSjtk }
683ec57cc7eSjtk hand++;
684ec57cc7eSjtk fwc++;
685ec57cc7eSjtk if (fwc >= cgsize) {
686ec57cc7eSjtk fwc = 0;
687ec57cc7eSjtk cgn++;
688ec57cc7eSjtk if (cgn >= newsb->fs_ncg) {
689ec57cc7eSjtk hand = 0;
690ec57cc7eSjtk if (secondpass)
691ec57cc7eSjtk return (-1);
692ec57cc7eSjtk secondpass = 1;
693ec57cc7eSjtk cgn = 0;
694ec57cc7eSjtk }
695ec57cc7eSjtk bits = cg_blksfree(cgs[cgn], 0);
696ec57cc7eSjtk cgsize = cgs[cgn]->cg_ndblk;
697ec57cc7eSjtk run = 0;
698ec57cc7eSjtk }
699ec57cc7eSjtk }
700ec57cc7eSjtk }
701ec57cc7eSjtk /*
702ec57cc7eSjtk * Find a free block of disk space. Finds an entire block of frags,
703ec57cc7eSjtk * all of which are free. Return value is the frag number of the
704ec57cc7eSjtk * first frag of the block, or -1 if no space was found. Uses newsb
705ec57cc7eSjtk * for sb values, and assumes the cgs[] structures correctly describe
706ec57cc7eSjtk * the area to be searched.
707ec57cc7eSjtk *
708ec57cc7eSjtk * See find_freespace(), above, for remarks about hand wrapping around.
709ec57cc7eSjtk */
710ec57cc7eSjtk static int
find_freeblock(void)711ec57cc7eSjtk find_freeblock(void)
712ec57cc7eSjtk {
713ec57cc7eSjtk static int hand = 0; /* hand rotates through all frags in fs */
714*f298a94bSchs uint32_t cgn; /* cg number of cg hand points into */
715ec57cc7eSjtk int fwc; /* frag-within-cg number of frag hand points
716ec57cc7eSjtk * to */
717ec57cc7eSjtk int cgsize; /* size of cg hand points into */
718ec57cc7eSjtk int secondpass; /* have we wrapped from end to beginning? */
719ec57cc7eSjtk unsigned char *bits; /* cg_blksfree()[] for cg hand points into */
720ec57cc7eSjtk
721ec57cc7eSjtk cgn = dtog(newsb, hand);
722ec57cc7eSjtk fwc = dtogd(newsb, hand);
723ec57cc7eSjtk secondpass = (hand == 0);
724ec57cc7eSjtk bits = cg_blksfree(cgs[cgn], 0);
72575571afdSdholland cgsize = ffs_blknum(newsb, cgs[cgn]->cg_ndblk);
726ec57cc7eSjtk while (1) {
727ec57cc7eSjtk if (blk_is_set(bits, fwc, newsb->fs_frag))
728ec57cc7eSjtk return (hand);
729ec57cc7eSjtk fwc += newsb->fs_frag;
730ec57cc7eSjtk hand += newsb->fs_frag;
731ec57cc7eSjtk if (fwc >= cgsize) {
732ec57cc7eSjtk fwc = 0;
733ec57cc7eSjtk cgn++;
734ec57cc7eSjtk if (cgn >= newsb->fs_ncg) {
735ec57cc7eSjtk hand = 0;
736ec57cc7eSjtk if (secondpass)
737ec57cc7eSjtk return (-1);
738ec57cc7eSjtk secondpass = 1;
739ec57cc7eSjtk cgn = 0;
740ec57cc7eSjtk }
741ec57cc7eSjtk bits = cg_blksfree(cgs[cgn], 0);
74275571afdSdholland cgsize = ffs_blknum(newsb, cgs[cgn]->cg_ndblk);
743ec57cc7eSjtk }
744ec57cc7eSjtk }
745ec57cc7eSjtk }
746ec57cc7eSjtk /*
747ec57cc7eSjtk * Find a free inode, returning its inumber or -1 if none was found.
748ec57cc7eSjtk * Uses newsb for sb values, and assumes the cgs[] structures
749ec57cc7eSjtk * correctly describe the area to be searched.
750ec57cc7eSjtk *
751ec57cc7eSjtk * See find_freespace(), above, for remarks about hand wrapping around.
752ec57cc7eSjtk */
753ec57cc7eSjtk static int
find_freeinode(void)754ec57cc7eSjtk find_freeinode(void)
755ec57cc7eSjtk {
756ec57cc7eSjtk static int hand = 0; /* hand rotates through all inodes in fs */
757*f298a94bSchs uint32_t cgn; /* cg number of cg hand points into */
758*f298a94bSchs uint32_t iwc; /* inode-within-cg number of inode hand points
759ec57cc7eSjtk * to */
760ec57cc7eSjtk int secondpass; /* have we wrapped from end to beginning? */
761ec57cc7eSjtk unsigned char *bits; /* cg_inosused()[] for cg hand points into */
762ec57cc7eSjtk
763ec57cc7eSjtk cgn = hand / newsb->fs_ipg;
764ec57cc7eSjtk iwc = hand % newsb->fs_ipg;
765ec57cc7eSjtk secondpass = (hand == 0);
766ec57cc7eSjtk bits = cg_inosused(cgs[cgn], 0);
767ec57cc7eSjtk while (1) {
768ec57cc7eSjtk if (bit_is_clr(bits, iwc))
769ec57cc7eSjtk return (hand);
770ec57cc7eSjtk hand++;
771ec57cc7eSjtk iwc++;
772ec57cc7eSjtk if (iwc >= newsb->fs_ipg) {
773ec57cc7eSjtk iwc = 0;
774ec57cc7eSjtk cgn++;
775ec57cc7eSjtk if (cgn >= newsb->fs_ncg) {
776ec57cc7eSjtk hand = 0;
777ec57cc7eSjtk if (secondpass)
778ec57cc7eSjtk return (-1);
779ec57cc7eSjtk secondpass = 1;
780ec57cc7eSjtk cgn = 0;
781ec57cc7eSjtk }
782ec57cc7eSjtk bits = cg_inosused(cgs[cgn], 0);
783ec57cc7eSjtk }
784ec57cc7eSjtk }
785ec57cc7eSjtk }
786ec57cc7eSjtk /*
787ec57cc7eSjtk * Mark a frag as free. Sets the frag's bit in the cg_blksfree bitmap
788ec57cc7eSjtk * for the appropriate cg, and marks the cg as dirty.
789ec57cc7eSjtk */
790ec57cc7eSjtk static void
free_frag(int fno)791ec57cc7eSjtk free_frag(int fno)
792ec57cc7eSjtk {
793ec57cc7eSjtk int cgn;
794ec57cc7eSjtk
795ec57cc7eSjtk cgn = dtog(newsb, fno);
796ec57cc7eSjtk set_bits(cg_blksfree(cgs[cgn], 0), dtogd(newsb, fno), 1);
797ec57cc7eSjtk cgflags[cgn] |= CGF_DIRTY | CGF_BLKMAPS;
798ec57cc7eSjtk }
799ec57cc7eSjtk /*
800ec57cc7eSjtk * Allocate a frag. Clears the frag's bit in the cg_blksfree bitmap
801ec57cc7eSjtk * for the appropriate cg, and marks the cg as dirty.
802ec57cc7eSjtk */
803ec57cc7eSjtk static void
alloc_frag(int fno)804ec57cc7eSjtk alloc_frag(int fno)
805ec57cc7eSjtk {
806ec57cc7eSjtk int cgn;
807ec57cc7eSjtk
808ec57cc7eSjtk cgn = dtog(newsb, fno);
809ec57cc7eSjtk clr_bits(cg_blksfree(cgs[cgn], 0), dtogd(newsb, fno), 1);
810ec57cc7eSjtk cgflags[cgn] |= CGF_DIRTY | CGF_BLKMAPS;
811ec57cc7eSjtk }
812ec57cc7eSjtk /*
813ec57cc7eSjtk * Fix up the csum array. If shrinking, this involves freeing zero or
814ec57cc7eSjtk * more frags; if growing, it involves allocating them, or if the
815ec57cc7eSjtk * frags being grown into aren't free, finding space elsewhere for the
816ec57cc7eSjtk * csum info. (If the number of occupied frags doesn't change,
817ec57cc7eSjtk * nothing happens here.)
818ec57cc7eSjtk */
819ec57cc7eSjtk static void
csum_fixup(void)820ec57cc7eSjtk csum_fixup(void)
821ec57cc7eSjtk {
822ec57cc7eSjtk int nold; /* # frags in old csum info */
823ec57cc7eSjtk int ntot; /* # frags in new csum info */
824ec57cc7eSjtk int nnew; /* ntot-nold */
825ec57cc7eSjtk int newloc; /* new location for csum info, if necessary */
826ec57cc7eSjtk int i; /* generic loop index */
827ec57cc7eSjtk int j; /* generic loop index */
828ec57cc7eSjtk int f; /* "from" frag number, if moving */
829ec57cc7eSjtk int t; /* "to" frag number, if moving */
830ec57cc7eSjtk int cgn; /* cg number, used when shrinking */
831ec57cc7eSjtk
832ec57cc7eSjtk ntot = howmany(newsb->fs_cssize, newsb->fs_fsize);
833ec57cc7eSjtk nold = howmany(oldsb->fs_cssize, newsb->fs_fsize);
834ec57cc7eSjtk nnew = ntot - nold;
835ec57cc7eSjtk /* First, if there's no change in frag counts, it's easy. */
836ec57cc7eSjtk if (nnew == 0)
837ec57cc7eSjtk return;
838ec57cc7eSjtk /* Next, if we're shrinking, it's almost as easy. Just free up any
839ec57cc7eSjtk * frags in the old area we no longer need. */
840ec57cc7eSjtk if (nnew < 0) {
841ec57cc7eSjtk for ((i = newsb->fs_csaddr + ntot - 1), (j = nnew);
842ec57cc7eSjtk j < 0;
843ec57cc7eSjtk i--, j++) {
844ec57cc7eSjtk free_frag(i);
845ec57cc7eSjtk }
846ec57cc7eSjtk return;
847ec57cc7eSjtk }
848ec57cc7eSjtk /* We must be growing. Check to see that the new csum area fits
849ec57cc7eSjtk * within the file system. I think this can never happen, since for
850ec57cc7eSjtk * the csum area to grow, we must be adding at least one cg, so the
851ec57cc7eSjtk * old csum area can't be this close to the end of the new file system.
852ec57cc7eSjtk * But it's a cheap check. */
853ec57cc7eSjtk /* XXX what if csum info is at end of cg and grows into next cg, what
854ec57cc7eSjtk * if it spills over onto the next cg's backup superblock? Can this
855ec57cc7eSjtk * happen? */
856ec57cc7eSjtk if (newsb->fs_csaddr + ntot <= newsb->fs_size) {
857ec57cc7eSjtk /* Okay, it fits - now, see if the space we want is free. */
858ec57cc7eSjtk for ((i = newsb->fs_csaddr + nold), (j = nnew);
859ec57cc7eSjtk j > 0;
860ec57cc7eSjtk i++, j--) {
861ec57cc7eSjtk cgn = dtog(newsb, i);
862ec57cc7eSjtk if (bit_is_clr(cg_blksfree(cgs[cgn], 0),
863ec57cc7eSjtk dtogd(newsb, i)))
864ec57cc7eSjtk break;
865ec57cc7eSjtk }
866ec57cc7eSjtk if (j <= 0) {
867ec57cc7eSjtk /* Win win - all the frags we want are free. Allocate
868ec57cc7eSjtk * 'em and we're all done. */
869d765f2d2Sriz for ((i = newsb->fs_csaddr + ntot - nnew),
870d765f2d2Sriz (j = nnew); j > 0; i++, j--) {
871ec57cc7eSjtk alloc_frag(i);
872ec57cc7eSjtk }
873ec57cc7eSjtk return;
874ec57cc7eSjtk }
875ec57cc7eSjtk }
876ec57cc7eSjtk /* We have to move the csum info, sigh. Look for new space, free old
877ec57cc7eSjtk * space, and allocate new. Update fs_csaddr. We don't copy anything
878ec57cc7eSjtk * on disk at this point; the csum info will be written to the
879ec57cc7eSjtk * then-current fs_csaddr as part of the final flush. */
880ec57cc7eSjtk newloc = find_freespace(ntot);
88117187885Schristos if (newloc < 0)
88217187885Schristos errx(EXIT_FAILURE, "Sorry, no space available for new csums");
883ec57cc7eSjtk for (i = 0, f = newsb->fs_csaddr, t = newloc; i < ntot; i++, f++, t++) {
884ec57cc7eSjtk if (i < nold) {
885ec57cc7eSjtk free_frag(f);
886ec57cc7eSjtk }
887ec57cc7eSjtk alloc_frag(t);
888ec57cc7eSjtk }
889ec57cc7eSjtk newsb->fs_csaddr = newloc;
890ec57cc7eSjtk }
891ec57cc7eSjtk /*
892ec57cc7eSjtk * Recompute newsb->fs_dsize. Just scans all cgs, adding the number of
893ec57cc7eSjtk * data blocks in that cg to the total.
894ec57cc7eSjtk */
895ec57cc7eSjtk static void
recompute_fs_dsize(void)896ec57cc7eSjtk recompute_fs_dsize(void)
897ec57cc7eSjtk {
898*f298a94bSchs uint32_t i;
899ec57cc7eSjtk
900ec57cc7eSjtk newsb->fs_dsize = 0;
901ec57cc7eSjtk for (i = 0; i < newsb->fs_ncg; i++) {
9020e0ce305Ssborrill int64_t dlow; /* size of before-sb data area */
9030e0ce305Ssborrill int64_t dhigh; /* offset of post-inode data area */
9040e0ce305Ssborrill int64_t dmax; /* total size of cg */
9050e0ce305Ssborrill int64_t base; /* base of cg, since cgsblock() etc add it in */
906ec57cc7eSjtk base = cgbase(newsb, i);
907ec57cc7eSjtk dlow = cgsblock(newsb, i) - base;
908ec57cc7eSjtk dhigh = cgdmin(newsb, i) - base;
909ec57cc7eSjtk dmax = newsb->fs_size - base;
910ec57cc7eSjtk if (dmax > newsb->fs_fpg)
911ec57cc7eSjtk dmax = newsb->fs_fpg;
912ec57cc7eSjtk newsb->fs_dsize += dlow + dmax - dhigh;
913ec57cc7eSjtk }
914ec57cc7eSjtk /* Space in cg 0 before cgsblock is boot area, not free space! */
915ec57cc7eSjtk newsb->fs_dsize -= cgsblock(newsb, 0) - cgbase(newsb, 0);
916ec57cc7eSjtk /* And of course the csum info takes up space. */
917ec57cc7eSjtk newsb->fs_dsize -= howmany(newsb->fs_cssize, newsb->fs_fsize);
918ec57cc7eSjtk }
919ec57cc7eSjtk /*
920ec57cc7eSjtk * Return the current time. We call this and assign, rather than
921ec57cc7eSjtk * calling time() directly, as insulation against OSes where fs_time
922ec57cc7eSjtk * is not a time_t.
923ec57cc7eSjtk */
924ec57cc7eSjtk static time_t
timestamp(void)925ec57cc7eSjtk timestamp(void)
926ec57cc7eSjtk {
927ec57cc7eSjtk time_t t;
928ec57cc7eSjtk
929ec57cc7eSjtk time(&t);
930ec57cc7eSjtk return (t);
931ec57cc7eSjtk }
932ec57cc7eSjtk
933f3a44502Smlelstv /*
934f3a44502Smlelstv * Calculate new filesystem geometry
935f3a44502Smlelstv * return 0 if geometry actually changed
936f3a44502Smlelstv */
937f3a44502Smlelstv static int
makegeometry(int chatter)938f3a44502Smlelstv makegeometry(int chatter)
939f3a44502Smlelstv {
940f3a44502Smlelstv
941ec57cc7eSjtk /* Update the size. */
9422737439dSdholland newsb->fs_size = FFS_DBTOFSB(newsb, newsize);
943d765f2d2Sriz if (is_ufs2)
944d765f2d2Sriz newsb->fs_ncg = howmany(newsb->fs_size, newsb->fs_fpg);
945d765f2d2Sriz else {
946628c00a0Schristos /* Update fs_old_ncyl and fs_ncg. */
9479f2c8cccSriz newsb->fs_old_ncyl = howmany(newsb->fs_size * NSPF(newsb),
9489f2c8cccSriz newsb->fs_old_spc);
949628c00a0Schristos newsb->fs_ncg = howmany(newsb->fs_old_ncyl, newsb->fs_old_cpg);
950d765f2d2Sriz }
951d765f2d2Sriz
952ec57cc7eSjtk /* Does the last cg end before the end of its inode area? There is no
953ec57cc7eSjtk * reason why this couldn't be handled, but it would complicate a lot
954ec57cc7eSjtk * of code (in all file system code - fsck, kernel, etc) because of the
955ec57cc7eSjtk * potential partial inode area, and the gain in space would be
956ec57cc7eSjtk * minimal, at most the pre-sb data area. */
957ec57cc7eSjtk if (cgdmin(newsb, newsb->fs_ncg - 1) > newsb->fs_size) {
958ec57cc7eSjtk newsb->fs_ncg--;
9594962b364Smlelstv if (is_ufs2)
9604962b364Smlelstv newsb->fs_size = newsb->fs_ncg * newsb->fs_fpg;
9614962b364Smlelstv else {
962628c00a0Schristos newsb->fs_old_ncyl = newsb->fs_ncg * newsb->fs_old_cpg;
9634962b364Smlelstv newsb->fs_size = (newsb->fs_old_ncyl *
9644962b364Smlelstv newsb->fs_old_spc) / NSPF(newsb);
9654962b364Smlelstv }
966f3a44502Smlelstv if (chatter || verbose) {
967ec57cc7eSjtk printf("Warning: last cylinder group is too small;\n");
968ec57cc7eSjtk printf(" dropping it. New size = %lu.\n",
9692737439dSdholland (unsigned long int) FFS_FSBTODB(newsb, newsb->fs_size));
970ec57cc7eSjtk }
971f3a44502Smlelstv }
972f3a44502Smlelstv
973f3a44502Smlelstv /* Did we actually not grow? (This can happen if newsize is less than
974f3a44502Smlelstv * a frag larger than the old size - unlikely, but no excuse to
975f3a44502Smlelstv * misbehave if it happens.) */
976f3a44502Smlelstv if (newsb->fs_size == oldsb->fs_size)
977f3a44502Smlelstv return 1;
978f3a44502Smlelstv
979f3a44502Smlelstv return 0;
980f3a44502Smlelstv }
981f3a44502Smlelstv
982f3a44502Smlelstv
983f3a44502Smlelstv /*
984f3a44502Smlelstv * Grow the file system.
985f3a44502Smlelstv */
986f3a44502Smlelstv static void
grow(void)987f3a44502Smlelstv grow(void)
988f3a44502Smlelstv {
989*f298a94bSchs uint32_t i;
990f3a44502Smlelstv
991f3a44502Smlelstv if (makegeometry(1)) {
992f3a44502Smlelstv printf("New fs size %"PRIu64" = old fs size %"PRIu64
993f3a44502Smlelstv ", not growing.\n", newsb->fs_size, oldsb->fs_size);
994f3a44502Smlelstv return;
995f3a44502Smlelstv }
996f3a44502Smlelstv
997f3a44502Smlelstv if (verbose) {
998f3a44502Smlelstv printf("Growing fs from %"PRIu64" blocks to %"PRIu64
999f3a44502Smlelstv " blocks.\n", oldsb->fs_size, newsb->fs_size);
1000f3a44502Smlelstv }
1001f3a44502Smlelstv
1002f3a44502Smlelstv /* Update the timestamp. */
1003f3a44502Smlelstv newsb->fs_time = timestamp();
1004f3a44502Smlelstv /* Allocate and clear the new-inode area, in case we add any cgs. */
1005fbf36969Sdholland if (is_ufs2) {
1006fbf36969Sdholland zinodes2 = alloconce(newsb->fs_ipg * sizeof(*zinodes2),
1007fbf36969Sdholland "zeroed inodes");
1008fbf36969Sdholland memset(zinodes2, 0, newsb->fs_ipg * sizeof(*zinodes2));
1009fbf36969Sdholland } else {
1010fbf36969Sdholland zinodes1 = alloconce(newsb->fs_ipg * sizeof(*zinodes1),
1011fbf36969Sdholland "zeroed inodes");
1012fbf36969Sdholland memset(zinodes1, 0, newsb->fs_ipg * sizeof(*zinodes1));
1013fbf36969Sdholland }
1014f3a44502Smlelstv
1015f3a44502Smlelstv /* Check that the new last sector (frag, actually) is writable. Since
1016f3a44502Smlelstv * it's at least one frag larger than it used to be, we know we aren't
1017f3a44502Smlelstv * overwriting anything important by this. (The choice of sbbuf as
1018f3a44502Smlelstv * what to write is irrelevant; it's just something handy that's known
1019f3a44502Smlelstv * to be at least one frag in size.) */
1020f3a44502Smlelstv writeat(FFS_FSBTODB(newsb,newsb->fs_size - 1), &sbbuf, newsb->fs_fsize);
1021f3a44502Smlelstv
1022ec57cc7eSjtk /* Find out how big the csum area is, and realloc csums if bigger. */
1023e1610ba4Sdholland newsb->fs_cssize = ffs_fragroundup(newsb,
1024ec57cc7eSjtk newsb->fs_ncg * sizeof(struct csum));
1025ec57cc7eSjtk if (newsb->fs_cssize > oldsb->fs_cssize)
1026ec57cc7eSjtk csums = nfrealloc(csums, newsb->fs_cssize, "new cg summary");
1027d765f2d2Sriz /* If we're adding any cgs, realloc structures and set up the new
1028d765f2d2Sriz cgs. */
1029ec57cc7eSjtk if (newsb->fs_ncg > oldsb->fs_ncg) {
1030ec57cc7eSjtk char *cgp;
103117187885Schristos cgs = nfrealloc(cgs, newsb->fs_ncg * sizeof(*cgs),
1032ec57cc7eSjtk "cg pointers");
1033ec57cc7eSjtk cgflags = nfrealloc(cgflags, newsb->fs_ncg, "cg flags");
1034d765f2d2Sriz memset(cgflags + oldsb->fs_ncg, 0,
1035d765f2d2Sriz newsb->fs_ncg - oldsb->fs_ncg);
1036ec57cc7eSjtk cgp = alloconce((newsb->fs_ncg - oldsb->fs_ncg) * cgblksz,
1037ec57cc7eSjtk "cgs");
1038ec57cc7eSjtk for (i = oldsb->fs_ncg; i < newsb->fs_ncg; i++) {
1039ec57cc7eSjtk cgs[i] = (struct cg *) cgp;
1040d0b93bc8Sjmcneill progress_bar(special, "grow cg",
1041d0b93bc8Sjmcneill i - oldsb->fs_ncg, newsb->fs_ncg - oldsb->fs_ncg);
1042ec57cc7eSjtk initcg(i);
1043ec57cc7eSjtk cgp += cgblksz;
1044ec57cc7eSjtk }
1045628c00a0Schristos cgs[oldsb->fs_ncg - 1]->cg_old_ncyl = oldsb->fs_old_cpg;
1046ec57cc7eSjtk cgflags[oldsb->fs_ncg - 1] |= CGF_DIRTY;
1047ec57cc7eSjtk }
1048ec57cc7eSjtk /* If the old fs ended partway through a cg, we have to update the old
1049ec57cc7eSjtk * last cg (though possibly not to a full cg!). */
1050ec57cc7eSjtk if (oldsb->fs_size % oldsb->fs_fpg) {
1051ec57cc7eSjtk struct cg *cg;
1052fa4874d5Smlelstv int64_t newcgsize;
1053fa4874d5Smlelstv int64_t prevcgtop;
1054fa4874d5Smlelstv int64_t oldcgsize;
1055ec57cc7eSjtk cg = cgs[oldsb->fs_ncg - 1];
1056ec57cc7eSjtk cgflags[oldsb->fs_ncg - 1] |= CGF_DIRTY | CGF_BLKMAPS;
1057ec57cc7eSjtk prevcgtop = oldsb->fs_fpg * (oldsb->fs_ncg - 1);
1058ec57cc7eSjtk newcgsize = newsb->fs_size - prevcgtop;
1059ec57cc7eSjtk if (newcgsize > newsb->fs_fpg)
1060ec57cc7eSjtk newcgsize = newsb->fs_fpg;
1061ec57cc7eSjtk oldcgsize = oldsb->fs_size % oldsb->fs_fpg;
1062ec57cc7eSjtk set_bits(cg_blksfree(cg, 0), oldcgsize, newcgsize - oldcgsize);
1063a7e78491Smhitch cg->cg_old_ncyl = oldsb->fs_old_cpg;
1064ec57cc7eSjtk cg->cg_ndblk = newcgsize;
1065ec57cc7eSjtk }
1066ec57cc7eSjtk /* Fix up the csum info, if necessary. */
1067ec57cc7eSjtk csum_fixup();
1068ec57cc7eSjtk /* Make fs_dsize match the new reality. */
1069ec57cc7eSjtk recompute_fs_dsize();
1070d0b93bc8Sjmcneill
1071d0b93bc8Sjmcneill progress_done();
1072ec57cc7eSjtk }
1073ec57cc7eSjtk /*
1074ec57cc7eSjtk * Call (*fn)() for each inode, passing the inode and its inumber. The
10752fa7e141Sandvar * number of cylinder groups is passed in, so this can be used to map
1076ec57cc7eSjtk * over either the old or the new file system's set of inodes.
1077ec57cc7eSjtk */
1078ec57cc7eSjtk static void
map_inodes(void (* fn)(union dinode * di,unsigned int,void * arg),int ncg,void * cbarg)1079d765f2d2Sriz map_inodes(void (*fn) (union dinode * di, unsigned int, void *arg),
108018174be8Sriz int ncg, void *cbarg) {
1081ec57cc7eSjtk int i;
1082ec57cc7eSjtk int ni;
1083ec57cc7eSjtk
1084ec57cc7eSjtk ni = oldsb->fs_ipg * ncg;
1085ec57cc7eSjtk for (i = 0; i < ni; i++)
1086ec57cc7eSjtk (*fn) (inodes + i, i, cbarg);
1087ec57cc7eSjtk }
1088ec57cc7eSjtk /* Values for the third argument to the map function for
1089ec57cc7eSjtk * map_inode_data_blocks. MDB_DATA indicates the block is contains
1090ec57cc7eSjtk * file data; MDB_INDIR_PRE and MDB_INDIR_POST indicate that it's an
1091ec57cc7eSjtk * indirect block. The MDB_INDIR_PRE call is made before the indirect
1092ec57cc7eSjtk * block pointers are followed and the pointed-to blocks scanned,
1093ec57cc7eSjtk * MDB_INDIR_POST after.
1094ec57cc7eSjtk */
1095ec57cc7eSjtk #define MDB_DATA 1
1096ec57cc7eSjtk #define MDB_INDIR_PRE 2
1097ec57cc7eSjtk #define MDB_INDIR_POST 3
1098ec57cc7eSjtk
10997a8d9bb4Sdholland typedef void (*mark_callback_t) (off_t blocknum, unsigned int nfrags,
110018174be8Sriz unsigned int blksize, int opcode);
1101ec57cc7eSjtk
1102ec57cc7eSjtk /* Helper function - handles a data block. Calls the callback
1103ec57cc7eSjtk * function and returns number of bytes occupied in file (actually,
1104ec57cc7eSjtk * rounded up to a frag boundary). The name is historical. */
1105ec57cc7eSjtk static int
markblk(mark_callback_t fn,union dinode * di,off_t bn,off_t o)11067a8d9bb4Sdholland markblk(mark_callback_t fn, union dinode * di, off_t bn, off_t o)
1107ec57cc7eSjtk {
1108ec57cc7eSjtk int sz;
1109ec57cc7eSjtk int nb;
11107a8d9bb4Sdholland off_t filesize;
111146eb69afSdholland
11127a8d9bb4Sdholland filesize = DIP(di,di_size);
11137a8d9bb4Sdholland if (o >= filesize)
1114ec57cc7eSjtk return (0);
1115e1610ba4Sdholland sz = dblksize(newsb, di, ffs_lblkno(newsb, o), filesize);
11167a8d9bb4Sdholland nb = (sz > filesize - o) ? filesize - o : sz;
1117ec57cc7eSjtk if (bn)
1118e1610ba4Sdholland (*fn) (bn, ffs_numfrags(newsb, sz), nb, MDB_DATA);
1119ec57cc7eSjtk return (sz);
1120ec57cc7eSjtk }
1121ec57cc7eSjtk /* Helper function - handles an indirect block. Makes the
1122ec57cc7eSjtk * MDB_INDIR_PRE callback for the indirect block, loops over the
1123ec57cc7eSjtk * pointers and recurses, and makes the MDB_INDIR_POST callback.
1124ec57cc7eSjtk * Returns the number of bytes occupied in file, as does markblk().
1125ec57cc7eSjtk * For the sake of update_for_data_move(), we read the indirect block
1126ec57cc7eSjtk * _after_ making the _PRE callback. The name is historical. */
1127fa4874d5Smlelstv static off_t
markiblk(mark_callback_t fn,union dinode * di,off_t bn,off_t o,int lev)11287a8d9bb4Sdholland markiblk(mark_callback_t fn, union dinode * di, off_t bn, off_t o, int lev)
1129ec57cc7eSjtk {
1130ec57cc7eSjtk int i;
11317a8d9bb4Sdholland unsigned k;
1132fa4874d5Smlelstv off_t j, tot;
1133416cebb2Smartin static int32_t indirblk1[howmany(MAXBSIZE, sizeof(int32_t))];
1134416cebb2Smartin static int32_t indirblk2[howmany(MAXBSIZE, sizeof(int32_t))];
1135416cebb2Smartin static int32_t indirblk3[howmany(MAXBSIZE, sizeof(int32_t))];
1136416cebb2Smartin static int32_t *indirblks[3] = {
1137ec57cc7eSjtk &indirblk1[0], &indirblk2[0], &indirblk3[0]
1138ec57cc7eSjtk };
113946eb69afSdholland
1140ec57cc7eSjtk if (lev < 0)
1141ec57cc7eSjtk return (markblk(fn, di, bn, o));
1142ec57cc7eSjtk if (bn == 0) {
1143fa4874d5Smlelstv for (j = newsb->fs_bsize;
1144ec57cc7eSjtk lev >= 0;
1145fa4874d5Smlelstv j *= FFS_NINDIR(newsb), lev--);
1146fa4874d5Smlelstv return (j);
1147ec57cc7eSjtk }
1148ec57cc7eSjtk (*fn) (bn, newsb->fs_frag, newsb->fs_bsize, MDB_INDIR_PRE);
11492737439dSdholland readat(FFS_FSBTODB(newsb, bn), indirblks[lev], newsb->fs_bsize);
1150d765f2d2Sriz if (needswap)
11517a8d9bb4Sdholland for (k = 0; k < howmany(MAXBSIZE, sizeof(int32_t)); k++)
11527a8d9bb4Sdholland indirblks[lev][k] = bswap32(indirblks[lev][k]);
1153ec57cc7eSjtk tot = 0;
1154f1333577Sdholland for (i = 0; i < FFS_NINDIR(newsb); i++) {
1155ec57cc7eSjtk j = markiblk(fn, di, indirblks[lev][i], o, lev - 1);
1156ec57cc7eSjtk if (j == 0)
1157ec57cc7eSjtk break;
1158ec57cc7eSjtk o += j;
1159ec57cc7eSjtk tot += j;
1160ec57cc7eSjtk }
1161ec57cc7eSjtk (*fn) (bn, newsb->fs_frag, newsb->fs_bsize, MDB_INDIR_POST);
1162ec57cc7eSjtk return (tot);
1163ec57cc7eSjtk }
1164ec57cc7eSjtk
1165ec57cc7eSjtk
1166ec57cc7eSjtk /*
1167ec57cc7eSjtk * Call (*fn)() for each data block for an inode. This routine assumes
1168ec57cc7eSjtk * the inode is known to be of a type that has data blocks (file,
1169ec57cc7eSjtk * directory, or non-fast symlink). The called function is:
1170ec57cc7eSjtk *
1171ec57cc7eSjtk * (*fn)(unsigned int blkno, unsigned int nf, unsigned int nb, int op)
1172ec57cc7eSjtk *
1173ec57cc7eSjtk * where blkno is the frag number, nf is the number of frags starting
1174ec57cc7eSjtk * at blkno (always <= fs_frag), nb is the number of bytes that belong
1175ec57cc7eSjtk * to the file (usually nf*fs_frag, often less for the last block/frag
1176ec57cc7eSjtk * of a file).
1177ec57cc7eSjtk */
1178ec57cc7eSjtk static void
map_inode_data_blocks(union dinode * di,mark_callback_t fn)1179d765f2d2Sriz map_inode_data_blocks(union dinode * di, mark_callback_t fn)
1180ec57cc7eSjtk {
1181ec57cc7eSjtk off_t o; /* offset within inode */
1182fa4874d5Smlelstv off_t inc; /* increment for o */
1183ec57cc7eSjtk int b; /* index within di_db[] and di_ib[] arrays */
1184ec57cc7eSjtk
1185ec57cc7eSjtk /* Scan the direct blocks... */
1186ec57cc7eSjtk o = 0;
1187dcd34a91Sdholland for (b = 0; b < UFS_NDADDR; b++) {
1188d765f2d2Sriz inc = markblk(fn, di, DIP(di,di_db[b]), o);
1189ec57cc7eSjtk if (inc == 0)
1190ec57cc7eSjtk break;
1191ec57cc7eSjtk o += inc;
1192ec57cc7eSjtk }
1193ec57cc7eSjtk /* ...and the indirect blocks. */
1194ec57cc7eSjtk if (inc) {
1195dcd34a91Sdholland for (b = 0; b < UFS_NIADDR; b++) {
1196d765f2d2Sriz inc = markiblk(fn, di, DIP(di,di_ib[b]), o, b);
1197ec57cc7eSjtk if (inc == 0)
1198ec57cc7eSjtk return;
1199ec57cc7eSjtk o += inc;
1200ec57cc7eSjtk }
1201ec57cc7eSjtk }
1202ec57cc7eSjtk }
1203ec57cc7eSjtk
1204ec57cc7eSjtk static void
dblk_callback(union dinode * di,unsigned int inum,void * arg)1205d765f2d2Sriz dblk_callback(union dinode * di, unsigned int inum, void *arg)
1206ec57cc7eSjtk {
1207ec57cc7eSjtk mark_callback_t fn;
12087a8d9bb4Sdholland off_t filesize;
120946eb69afSdholland
12107a8d9bb4Sdholland filesize = DIP(di,di_size);
1211ec57cc7eSjtk fn = (mark_callback_t) arg;
1212d765f2d2Sriz switch (DIP(di,di_mode) & IFMT) {
1213ec57cc7eSjtk case IFLNK:
121464b87635Sdholland if (filesize <= newsb->fs_maxsymlinklen) {
121564b87635Sdholland break;
121664b87635Sdholland }
121764b87635Sdholland /* FALLTHROUGH */
1218ec57cc7eSjtk case IFDIR:
1219ec57cc7eSjtk case IFREG:
1220ec57cc7eSjtk map_inode_data_blocks(di, fn);
1221ec57cc7eSjtk break;
1222ec57cc7eSjtk }
1223ec57cc7eSjtk }
1224ec57cc7eSjtk /*
1225ec57cc7eSjtk * Make a callback call, a la map_inode_data_blocks, for all data
1226ec57cc7eSjtk * blocks in the entire fs. This is used only once, in
1227ec57cc7eSjtk * update_for_data_move, but it's out at top level because the complex
1228ec57cc7eSjtk * downward-funarg nesting that would otherwise result seems to give
1229ec57cc7eSjtk * gcc gastric distress.
1230ec57cc7eSjtk */
1231ec57cc7eSjtk static void
map_data_blocks(mark_callback_t fn,int ncg)1232ec57cc7eSjtk map_data_blocks(mark_callback_t fn, int ncg)
1233ec57cc7eSjtk {
1234ec57cc7eSjtk map_inodes(&dblk_callback, ncg, (void *) fn);
1235ec57cc7eSjtk }
1236ec57cc7eSjtk /*
1237ec57cc7eSjtk * Initialize the blkmove array.
1238ec57cc7eSjtk */
1239ec57cc7eSjtk static void
blkmove_init(void)1240ec57cc7eSjtk blkmove_init(void)
1241ec57cc7eSjtk {
1242ec57cc7eSjtk int i;
1243ec57cc7eSjtk
1244ec57cc7eSjtk blkmove = alloconce(oldsb->fs_size * sizeof(*blkmove), "blkmove");
1245ec57cc7eSjtk for (i = 0; i < oldsb->fs_size; i++)
1246ec57cc7eSjtk blkmove[i] = i;
1247ec57cc7eSjtk }
1248ec57cc7eSjtk /*
1249ec57cc7eSjtk * Load the inodes off disk. Allocates the structures and initializes
1250ec57cc7eSjtk * them - the inodes from disk, the flags to zero.
1251ec57cc7eSjtk */
1252ec57cc7eSjtk static void
loadinodes(void)1253ec57cc7eSjtk loadinodes(void)
1254ec57cc7eSjtk {
1255*f298a94bSchs int imax, ino, j;
1256*f298a94bSchs uint32_t i;
1257d765f2d2Sriz struct ufs1_dinode *dp1 = NULL;
1258d765f2d2Sriz struct ufs2_dinode *dp2 = NULL;
1259d765f2d2Sriz
1260d765f2d2Sriz /* read inodes one fs block at a time and copy them */
1261ec57cc7eSjtk
126218174be8Sriz inodes = alloconce(oldsb->fs_ncg * oldsb->fs_ipg *
1263d765f2d2Sriz sizeof(union dinode), "inodes");
1264ec57cc7eSjtk iflags = alloconce(oldsb->fs_ncg * oldsb->fs_ipg, "inode flags");
1265d765f2d2Sriz memset(iflags, 0, oldsb->fs_ncg * oldsb->fs_ipg);
1266d765f2d2Sriz
1267d765f2d2Sriz ibuf = nfmalloc(oldsb->fs_bsize,"inode block buf");
1268d765f2d2Sriz if (is_ufs2)
1269d765f2d2Sriz dp2 = (struct ufs2_dinode *)ibuf;
1270d765f2d2Sriz else
1271d765f2d2Sriz dp1 = (struct ufs1_dinode *)ibuf;
1272d765f2d2Sriz
1273d765f2d2Sriz for (ino = 0,imax = oldsb->fs_ipg * oldsb->fs_ncg; ino < imax; ) {
12742737439dSdholland readat(FFS_FSBTODB(oldsb, ino_to_fsba(oldsb, ino)), ibuf,
1275d765f2d2Sriz oldsb->fs_bsize);
1276d765f2d2Sriz
1277d765f2d2Sriz for (i = 0; i < oldsb->fs_inopb; i++) {
1278d765f2d2Sriz if (is_ufs2) {
1279d765f2d2Sriz if (needswap) {
1280d765f2d2Sriz ffs_dinode2_swap(&(dp2[i]), &(dp2[i]));
12819eac9537Schristos for (j = 0; j < UFS_NDADDR; j++)
1282d765f2d2Sriz dp2[i].di_db[j] =
1283d765f2d2Sriz bswap32(dp2[i].di_db[j]);
12849eac9537Schristos for (j = 0; j < UFS_NIADDR; j++)
12859eac9537Schristos dp2[i].di_ib[j] =
12869eac9537Schristos bswap32(dp2[i].di_ib[j]);
1287d765f2d2Sriz }
1288d765f2d2Sriz memcpy(&inodes[ino].dp2, &dp2[i],
128917187885Schristos sizeof(inodes[ino].dp2));
1290d765f2d2Sriz } else {
1291d765f2d2Sriz if (needswap) {
1292d765f2d2Sriz ffs_dinode1_swap(&(dp1[i]), &(dp1[i]));
1293c1673bd6Schristos for (j = 0; j < UFS_NDADDR; j++)
1294d765f2d2Sriz dp1[i].di_db[j] =
1295d765f2d2Sriz bswap32(dp1[i].di_db[j]);
12969eac9537Schristos for (j = 0; j < UFS_NIADDR; j++)
12979eac9537Schristos dp1[i].di_ib[j] =
12989eac9537Schristos bswap32(dp1[i].di_ib[j]);
1299d765f2d2Sriz }
1300d765f2d2Sriz memcpy(&inodes[ino].dp1, &dp1[i],
130117187885Schristos sizeof(inodes[ino].dp1));
1302d765f2d2Sriz }
1303d765f2d2Sriz if (++ino > imax)
1304d765f2d2Sriz errx(EXIT_FAILURE,
1305d765f2d2Sriz "Exceeded number of inodes");
1306d765f2d2Sriz }
1307d765f2d2Sriz
1308ec57cc7eSjtk }
1309ec57cc7eSjtk }
1310ec57cc7eSjtk /*
1311c93380ebSwiz * Report a file-system-too-full problem.
1312ec57cc7eSjtk */
131317187885Schristos __dead static void
toofull(void)1314ec57cc7eSjtk toofull(void)
1315ec57cc7eSjtk {
131617187885Schristos errx(EXIT_FAILURE, "Sorry, would run out of data blocks");
1317ec57cc7eSjtk }
1318ec57cc7eSjtk /*
1319ec57cc7eSjtk * Record a desire to move "n" frags from "from" to "to".
1320ec57cc7eSjtk */
1321ec57cc7eSjtk static void
mark_move(unsigned int from,unsigned int to,unsigned int n)1322ec57cc7eSjtk mark_move(unsigned int from, unsigned int to, unsigned int n)
1323ec57cc7eSjtk {
1324ec57cc7eSjtk for (; n > 0; n--)
1325ec57cc7eSjtk blkmove[from++] = to++;
1326ec57cc7eSjtk }
1327ec57cc7eSjtk /* Helper function - evict n frags, starting with start (cg-relative).
1328ec57cc7eSjtk * The free bitmap is scanned, unallocated frags are ignored, and
1329ec57cc7eSjtk * each block of consecutive allocated frags is moved as a unit.
1330ec57cc7eSjtk */
1331ec57cc7eSjtk static void
fragmove(struct cg * cg,int64_t base,unsigned int start,unsigned int n)1332fa4874d5Smlelstv fragmove(struct cg * cg, int64_t base, unsigned int start, unsigned int n)
1333ec57cc7eSjtk {
13347a8d9bb4Sdholland unsigned int i;
1335ec57cc7eSjtk int run;
133646eb69afSdholland
1337ec57cc7eSjtk run = 0;
1338ec57cc7eSjtk for (i = 0; i <= n; i++) {
1339ec57cc7eSjtk if ((i < n) && bit_is_clr(cg_blksfree(cg, 0), start + i)) {
1340ec57cc7eSjtk run++;
1341ec57cc7eSjtk } else {
1342ec57cc7eSjtk if (run > 0) {
1343ec57cc7eSjtk int off;
1344ec57cc7eSjtk off = find_freespace(run);
1345ec57cc7eSjtk if (off < 0)
1346ec57cc7eSjtk toofull();
1347ec57cc7eSjtk mark_move(base + start + i - run, off, run);
1348ec57cc7eSjtk set_bits(cg_blksfree(cg, 0), start + i - run,
1349ec57cc7eSjtk run);
1350ec57cc7eSjtk clr_bits(cg_blksfree(cgs[dtog(oldsb, off)], 0),
1351ec57cc7eSjtk dtogd(oldsb, off), run);
1352ec57cc7eSjtk }
1353ec57cc7eSjtk run = 0;
1354ec57cc7eSjtk }
1355ec57cc7eSjtk }
1356ec57cc7eSjtk }
1357ec57cc7eSjtk /*
1358ec57cc7eSjtk * Evict all data blocks from the given cg, starting at minfrag (based
1359ec57cc7eSjtk * at the beginning of the cg), for length nfrag. The eviction is
1360ec57cc7eSjtk * assumed to be entirely data-area; this should not be called with a
1361ec57cc7eSjtk * range overlapping the metadata structures in the cg. It also
1362ec57cc7eSjtk * assumes minfrag points into the given cg; it will misbehave if this
1363ec57cc7eSjtk * is not true.
1364ec57cc7eSjtk *
1365ec57cc7eSjtk * See the comment header on find_freespace() for one possible bug
1366ec57cc7eSjtk * lurking here.
1367ec57cc7eSjtk */
1368ec57cc7eSjtk static void
evict_data(struct cg * cg,unsigned int minfrag,int nfrag)13697a8d9bb4Sdholland evict_data(struct cg * cg, unsigned int minfrag, int nfrag)
1370ec57cc7eSjtk {
13710e0ce305Ssborrill int64_t base; /* base of cg (in frags from beginning of fs) */
1372ec57cc7eSjtk
1373ec57cc7eSjtk base = cgbase(oldsb, cg->cg_cgx);
1374d765f2d2Sriz /* Does the boundary fall in the middle of a block? To avoid
1375d765f2d2Sriz * breaking between frags allocated as consecutive, we always
1376d765f2d2Sriz * evict the whole block in this case, though one could argue
1377d765f2d2Sriz * we should check to see if the frag before or after the
1378d765f2d2Sriz * break is unallocated. */
1379ec57cc7eSjtk if (minfrag % oldsb->fs_frag) {
1380ec57cc7eSjtk int n;
1381ec57cc7eSjtk n = minfrag % oldsb->fs_frag;
1382ec57cc7eSjtk minfrag -= n;
1383ec57cc7eSjtk nfrag += n;
1384ec57cc7eSjtk }
1385d765f2d2Sriz /* Do whole blocks. If a block is wholly free, skip it; if
1386d765f2d2Sriz * wholly allocated, move it in toto. If neither, call
1387d765f2d2Sriz * fragmove() to move the frags to new locations. */
1388ec57cc7eSjtk while (nfrag >= oldsb->fs_frag) {
1389ec57cc7eSjtk if (!blk_is_set(cg_blksfree(cg, 0), minfrag, oldsb->fs_frag)) {
1390ec57cc7eSjtk if (blk_is_clr(cg_blksfree(cg, 0), minfrag,
1391ec57cc7eSjtk oldsb->fs_frag)) {
1392ec57cc7eSjtk int off;
1393ec57cc7eSjtk off = find_freeblock();
1394ec57cc7eSjtk if (off < 0)
1395ec57cc7eSjtk toofull();
1396ec57cc7eSjtk mark_move(base + minfrag, off, oldsb->fs_frag);
1397ec57cc7eSjtk set_bits(cg_blksfree(cg, 0), minfrag,
1398ec57cc7eSjtk oldsb->fs_frag);
1399ec57cc7eSjtk clr_bits(cg_blksfree(cgs[dtog(oldsb, off)], 0),
1400ec57cc7eSjtk dtogd(oldsb, off), oldsb->fs_frag);
1401ec57cc7eSjtk } else {
1402ec57cc7eSjtk fragmove(cg, base, minfrag, oldsb->fs_frag);
1403ec57cc7eSjtk }
1404ec57cc7eSjtk }
1405ec57cc7eSjtk minfrag += oldsb->fs_frag;
1406ec57cc7eSjtk nfrag -= oldsb->fs_frag;
1407ec57cc7eSjtk }
1408ec57cc7eSjtk /* Clean up any sub-block amount left over. */
1409ec57cc7eSjtk if (nfrag) {
1410ec57cc7eSjtk fragmove(cg, base, minfrag, nfrag);
1411ec57cc7eSjtk }
1412ec57cc7eSjtk }
1413ec57cc7eSjtk /*
1414ec57cc7eSjtk * Move all data blocks according to blkmove. We have to be careful,
1415ec57cc7eSjtk * because we may be updating indirect blocks that will themselves be
1416416cebb2Smartin * getting moved, or inode int32_t arrays that point to indirect
1417ec57cc7eSjtk * blocks that will be moved. We call this before
1418ec57cc7eSjtk * update_for_data_move, and update_for_data_move does inodes first,
1419ec57cc7eSjtk * then indirect blocks in preorder, so as to make sure that the
1420ec57cc7eSjtk * file system is self-consistent at all points, for better crash
1421ec57cc7eSjtk * tolerance. (We can get away with this only because all the writes
1422ec57cc7eSjtk * done by perform_data_move() are writing into space that's not used
1423ec57cc7eSjtk * by the old file system.) If we crash, some things may point to the
1424ec57cc7eSjtk * old data and some to the new, but both copies are the same. The
1425ec57cc7eSjtk * only wrong things should be csum info and free bitmaps, which fsck
1426ec57cc7eSjtk * is entirely capable of cleaning up.
1427ec57cc7eSjtk *
1428ec57cc7eSjtk * Since blkmove_init() initializes all blocks to move to their current
1429ec57cc7eSjtk * locations, we can have two blocks marked as wanting to move to the
1430ec57cc7eSjtk * same location, but only two and only when one of them is the one
1431ec57cc7eSjtk * that was already there. So if blkmove[i]==i, we ignore that entry
1432ec57cc7eSjtk * entirely - for unallocated blocks, we don't want it (and may be
1433ec57cc7eSjtk * putting something else there), and for allocated blocks, we don't
1434ec57cc7eSjtk * want to copy it anywhere.
1435ec57cc7eSjtk */
1436ec57cc7eSjtk static void
perform_data_move(void)1437ec57cc7eSjtk perform_data_move(void)
1438ec57cc7eSjtk {
1439ec57cc7eSjtk int i;
1440ec57cc7eSjtk int run;
1441ec57cc7eSjtk int maxrun;
1442ec57cc7eSjtk char buf[65536];
1443ec57cc7eSjtk
1444ec57cc7eSjtk maxrun = sizeof(buf) / newsb->fs_fsize;
1445ec57cc7eSjtk run = 0;
1446ec57cc7eSjtk for (i = 0; i < oldsb->fs_size; i++) {
14477a8d9bb4Sdholland if ((blkmove[i] == (unsigned)i /*XXX cast*/) ||
1448ec57cc7eSjtk (run >= maxrun) ||
1449ec57cc7eSjtk ((run > 0) &&
1450ec57cc7eSjtk (blkmove[i] != blkmove[i - 1] + 1))) {
1451ec57cc7eSjtk if (run > 0) {
14522737439dSdholland readat(FFS_FSBTODB(oldsb, i - run), &buf[0],
1453ec57cc7eSjtk run << oldsb->fs_fshift);
14542737439dSdholland writeat(FFS_FSBTODB(oldsb, blkmove[i - run]),
1455ec57cc7eSjtk &buf[0], run << oldsb->fs_fshift);
1456ec57cc7eSjtk }
1457ec57cc7eSjtk run = 0;
1458ec57cc7eSjtk }
14597a8d9bb4Sdholland if (blkmove[i] != (unsigned)i /*XXX cast*/)
1460ec57cc7eSjtk run++;
1461ec57cc7eSjtk }
1462ec57cc7eSjtk if (run > 0) {
14632737439dSdholland readat(FFS_FSBTODB(oldsb, i - run), &buf[0],
1464ec57cc7eSjtk run << oldsb->fs_fshift);
14652737439dSdholland writeat(FFS_FSBTODB(oldsb, blkmove[i - run]), &buf[0],
1466ec57cc7eSjtk run << oldsb->fs_fshift);
1467ec57cc7eSjtk }
1468ec57cc7eSjtk }
1469ec57cc7eSjtk /*
1470416cebb2Smartin * This modifies an array of int32_t, according to blkmove. This is
1471ec57cc7eSjtk * used to update inode block arrays and indirect blocks to point to
1472ec57cc7eSjtk * the new locations of data blocks.
1473ec57cc7eSjtk *
1474416cebb2Smartin * Return value is the number of int32_ts that needed updating; in
1475ec57cc7eSjtk * particular, the return value is zero iff nothing was modified.
1476ec57cc7eSjtk */
1477ec57cc7eSjtk static int
movemap_blocks(int32_t * vec,int n)1478416cebb2Smartin movemap_blocks(int32_t * vec, int n)
1479ec57cc7eSjtk {
1480ec57cc7eSjtk int rv;
148146eb69afSdholland
1482ec57cc7eSjtk rv = 0;
1483ec57cc7eSjtk for (; n > 0; n--, vec++) {
14847a8d9bb4Sdholland if (blkmove[*vec] != (unsigned)*vec /*XXX cast*/) {
1485ec57cc7eSjtk *vec = blkmove[*vec];
1486ec57cc7eSjtk rv++;
1487ec57cc7eSjtk }
1488ec57cc7eSjtk }
1489ec57cc7eSjtk return (rv);
1490ec57cc7eSjtk }
1491ec57cc7eSjtk static void
moveblocks_callback(union dinode * di,unsigned int inum,void * arg)1492d765f2d2Sriz moveblocks_callback(union dinode * di, unsigned int inum, void *arg)
1493ec57cc7eSjtk {
14947a8d9bb4Sdholland int32_t *dblkptr, *iblkptr;
149546eb69afSdholland
1496d765f2d2Sriz switch (DIP(di,di_mode) & IFMT) {
1497ec57cc7eSjtk case IFLNK:
14987a8d9bb4Sdholland if ((off_t)DIP(di,di_size) <= oldsb->fs_maxsymlinklen) {
14992bf41d92Sdholland break;
15002bf41d92Sdholland }
15012bf41d92Sdholland /* FALLTHROUGH */
1502ec57cc7eSjtk case IFDIR:
1503ec57cc7eSjtk case IFREG:
1504d765f2d2Sriz if (is_ufs2) {
15057a8d9bb4Sdholland /* XXX these are not int32_t and this is WRONG! */
15067a8d9bb4Sdholland dblkptr = (void *) &(di->dp2.di_db[0]);
15077a8d9bb4Sdholland iblkptr = (void *) &(di->dp2.di_ib[0]);
1508d765f2d2Sriz } else {
1509d765f2d2Sriz dblkptr = &(di->dp1.di_db[0]);
1510d765f2d2Sriz iblkptr = &(di->dp1.di_ib[0]);
1511d765f2d2Sriz }
151246eb69afSdholland /*
151346eb69afSdholland * Don't || these two calls; we need their
151446eb69afSdholland * side-effects.
151546eb69afSdholland */
1516dcd34a91Sdholland if (movemap_blocks(dblkptr, UFS_NDADDR)) {
1517ec57cc7eSjtk iflags[inum] |= IF_DIRTY;
1518ec57cc7eSjtk }
1519dcd34a91Sdholland if (movemap_blocks(iblkptr, UFS_NIADDR)) {
1520ec57cc7eSjtk iflags[inum] |= IF_DIRTY;
1521ec57cc7eSjtk }
1522ec57cc7eSjtk break;
1523ec57cc7eSjtk }
1524ec57cc7eSjtk }
1525ec57cc7eSjtk
1526ec57cc7eSjtk static void
moveindir_callback(off_t off,unsigned int nfrag,unsigned int nbytes,int kind)15277a8d9bb4Sdholland moveindir_callback(off_t off, unsigned int nfrag, unsigned int nbytes,
152818174be8Sriz int kind)
1529ec57cc7eSjtk {
15307a8d9bb4Sdholland unsigned int i;
153146eb69afSdholland
1532ec57cc7eSjtk if (kind == MDB_INDIR_PRE) {
1533416cebb2Smartin int32_t blk[howmany(MAXBSIZE, sizeof(int32_t))];
15342737439dSdholland readat(FFS_FSBTODB(oldsb, off), &blk[0], oldsb->fs_bsize);
1535d765f2d2Sriz if (needswap)
1536d765f2d2Sriz for (i = 0; i < howmany(MAXBSIZE, sizeof(int32_t)); i++)
1537d765f2d2Sriz blk[i] = bswap32(blk[i]);
1538f1333577Sdholland if (movemap_blocks(&blk[0], FFS_NINDIR(oldsb))) {
1539d765f2d2Sriz if (needswap)
1540d765f2d2Sriz for (i = 0; i < howmany(MAXBSIZE,
1541d765f2d2Sriz sizeof(int32_t)); i++)
1542d765f2d2Sriz blk[i] = bswap32(blk[i]);
15432737439dSdholland writeat(FFS_FSBTODB(oldsb, off), &blk[0], oldsb->fs_bsize);
1544ec57cc7eSjtk }
1545ec57cc7eSjtk }
1546ec57cc7eSjtk }
1547ec57cc7eSjtk /*
1548ec57cc7eSjtk * Update all inode data arrays and indirect blocks to point to the new
1549ec57cc7eSjtk * locations of data blocks. See the comment header on
1550ec57cc7eSjtk * perform_data_move for some ordering considerations.
1551ec57cc7eSjtk */
1552ec57cc7eSjtk static void
update_for_data_move(void)1553ec57cc7eSjtk update_for_data_move(void)
1554ec57cc7eSjtk {
1555ec57cc7eSjtk map_inodes(&moveblocks_callback, oldsb->fs_ncg, NULL);
1556ec57cc7eSjtk map_data_blocks(&moveindir_callback, oldsb->fs_ncg);
1557ec57cc7eSjtk }
1558ec57cc7eSjtk /*
1559ec57cc7eSjtk * Initialize the inomove array.
1560ec57cc7eSjtk */
1561ec57cc7eSjtk static void
inomove_init(void)1562ec57cc7eSjtk inomove_init(void)
1563ec57cc7eSjtk {
1564ec57cc7eSjtk int i;
1565ec57cc7eSjtk
1566ec57cc7eSjtk inomove = alloconce(oldsb->fs_ipg * oldsb->fs_ncg * sizeof(*inomove),
1567ec57cc7eSjtk "inomove");
1568ec57cc7eSjtk for (i = (oldsb->fs_ipg * oldsb->fs_ncg) - 1; i >= 0; i--)
1569ec57cc7eSjtk inomove[i] = i;
1570ec57cc7eSjtk }
1571ec57cc7eSjtk /*
1572ec57cc7eSjtk * Flush all dirtied inodes to disk. Scans the inode flags array; for
1573ec57cc7eSjtk * each dirty inode, it sets the BDIRTY bit on the first inode in the
1574ec57cc7eSjtk * block containing the dirty inode. Then it scans by blocks, and for
1575ec57cc7eSjtk * each marked block, writes it.
1576ec57cc7eSjtk */
1577ec57cc7eSjtk static void
flush_inodes(void)1578ec57cc7eSjtk flush_inodes(void)
1579ec57cc7eSjtk {
15809eac9537Schristos int i, j, k, ni, m;
1581d765f2d2Sriz struct ufs1_dinode *dp1 = NULL;
1582d765f2d2Sriz struct ufs2_dinode *dp2 = NULL;
1583ec57cc7eSjtk
1584ec57cc7eSjtk ni = newsb->fs_ipg * newsb->fs_ncg;
1585f1333577Sdholland m = FFS_INOPB(newsb) - 1;
1586ec57cc7eSjtk for (i = 0; i < ni; i++) {
1587ec57cc7eSjtk if (iflags[i] & IF_DIRTY) {
1588ec57cc7eSjtk iflags[i & ~m] |= IF_BDIRTY;
1589ec57cc7eSjtk }
1590ec57cc7eSjtk }
1591ec57cc7eSjtk m++;
1592d765f2d2Sriz
1593d765f2d2Sriz if (is_ufs2)
1594d765f2d2Sriz dp2 = (struct ufs2_dinode *)ibuf;
1595d765f2d2Sriz else
1596d765f2d2Sriz dp1 = (struct ufs1_dinode *)ibuf;
1597d765f2d2Sriz
1598ec57cc7eSjtk for (i = 0; i < ni; i += m) {
15999eac9537Schristos if ((iflags[i] & IF_BDIRTY) == 0)
16009eac9537Schristos continue;
1601d765f2d2Sriz if (is_ufs2)
1602d765f2d2Sriz for (j = 0; j < m; j++) {
1603d765f2d2Sriz dp2[j] = inodes[i + j].dp2;
1604d765f2d2Sriz if (needswap) {
16059eac9537Schristos for (k = 0; k < UFS_NDADDR; k++)
1606d765f2d2Sriz dp2[j].di_db[k] =
1607d765f2d2Sriz bswap32(dp2[j].di_db[k]);
16089eac9537Schristos for (k = 0; k < UFS_NIADDR; k++)
16099eac9537Schristos dp2[j].di_ib[k] =
16109eac9537Schristos bswap32(dp2[j].di_ib[k]);
1611d765f2d2Sriz ffs_dinode2_swap(&dp2[j],
1612d765f2d2Sriz &dp2[j]);
1613d765f2d2Sriz }
1614d765f2d2Sriz }
1615d765f2d2Sriz else
1616d765f2d2Sriz for (j = 0; j < m; j++) {
1617d765f2d2Sriz dp1[j] = inodes[i + j].dp1;
1618d765f2d2Sriz if (needswap) {
16199eac9537Schristos for (k = 0; k < UFS_NDADDR; k++)
1620d765f2d2Sriz dp1[j].di_db[k]=
1621d765f2d2Sriz bswap32(dp1[j].di_db[k]);
16229eac9537Schristos for (k = 0; k < UFS_NIADDR; k++)
16239eac9537Schristos dp1[j].di_ib[k]=
16249eac9537Schristos bswap32(dp1[j].di_ib[k]);
1625d765f2d2Sriz ffs_dinode1_swap(&dp1[j],
1626d765f2d2Sriz &dp1[j]);
1627d765f2d2Sriz }
1628d765f2d2Sriz }
1629d765f2d2Sriz
16302737439dSdholland writeat(FFS_FSBTODB(newsb, ino_to_fsba(newsb, i)),
1631d765f2d2Sriz ibuf, newsb->fs_bsize);
1632ec57cc7eSjtk }
1633ec57cc7eSjtk }
1634ec57cc7eSjtk /*
1635ec57cc7eSjtk * Evict all inodes from the specified cg. shrink() already checked
1636ec57cc7eSjtk * that there were enough free inodes, so the no-free-inodes check is
1637ec57cc7eSjtk * a can't-happen. If it does trip, the file system should be in good
1638ec57cc7eSjtk * enough shape for fsck to fix; see the comment on perform_data_move
1639ec57cc7eSjtk * for the considerations in question.
1640ec57cc7eSjtk */
1641ec57cc7eSjtk static void
evict_inodes(struct cg * cg)1642ec57cc7eSjtk evict_inodes(struct cg * cg)
1643ec57cc7eSjtk {
1644ec57cc7eSjtk int inum;
1645ec57cc7eSjtk int fi;
1646*f298a94bSchs uint32_t i;
1647ec57cc7eSjtk
1648ec57cc7eSjtk inum = newsb->fs_ipg * cg->cg_cgx;
1649ec57cc7eSjtk for (i = 0; i < newsb->fs_ipg; i++, inum++) {
1650d765f2d2Sriz if (DIP(inodes + inum,di_mode) != 0) {
1651ec57cc7eSjtk fi = find_freeinode();
165217187885Schristos if (fi < 0)
165317187885Schristos errx(EXIT_FAILURE, "Sorry, inodes evaporated - "
165417187885Schristos "file system probably needs fsck");
1655ec57cc7eSjtk inomove[inum] = fi;
1656ec57cc7eSjtk clr_bits(cg_inosused(cg, 0), i, 1);
1657ec57cc7eSjtk set_bits(cg_inosused(cgs[ino_to_cg(newsb, fi)], 0),
1658ec57cc7eSjtk fi % newsb->fs_ipg, 1);
1659ec57cc7eSjtk }
1660ec57cc7eSjtk }
1661ec57cc7eSjtk }
1662ec57cc7eSjtk /*
1663ec57cc7eSjtk * Move inodes from old locations to new. Does not actually write
1664ec57cc7eSjtk * anything to disk; just copies in-core and sets dirty bits.
1665ec57cc7eSjtk *
1666ec57cc7eSjtk * We have to be careful here for reasons similar to those mentioned in
1667ec57cc7eSjtk * the comment header on perform_data_move, above: for the sake of
1668ec57cc7eSjtk * crash tolerance, we want to make sure everything is present at both
1669ec57cc7eSjtk * old and new locations before we update pointers. So we call this
1670ec57cc7eSjtk * first, then flush_inodes() to get them out on disk, then update
1671ec57cc7eSjtk * directories to match.
1672ec57cc7eSjtk */
1673ec57cc7eSjtk static void
perform_inode_move(void)1674ec57cc7eSjtk perform_inode_move(void)
1675ec57cc7eSjtk {
16767a8d9bb4Sdholland unsigned int i;
16777a8d9bb4Sdholland unsigned int ni;
1678ec57cc7eSjtk
1679ec57cc7eSjtk ni = oldsb->fs_ipg * oldsb->fs_ncg;
1680ec57cc7eSjtk for (i = 0; i < ni; i++) {
1681ec57cc7eSjtk if (inomove[i] != i) {
1682ec57cc7eSjtk inodes[inomove[i]] = inodes[i];
1683ec57cc7eSjtk iflags[inomove[i]] = iflags[i] | IF_DIRTY;
1684ec57cc7eSjtk }
1685ec57cc7eSjtk }
1686ec57cc7eSjtk }
1687ec57cc7eSjtk /*
1688ec57cc7eSjtk * Update the directory contained in the nb bytes at buf, to point to
1689ec57cc7eSjtk * inodes' new locations.
1690ec57cc7eSjtk */
1691ec57cc7eSjtk static int
update_dirents(char * buf,int nb)1692ec57cc7eSjtk update_dirents(char *buf, int nb)
1693ec57cc7eSjtk {
1694ec57cc7eSjtk int rv;
1695ec57cc7eSjtk #define d ((struct direct *)buf)
1696d765f2d2Sriz #define s32(x) (needswap?bswap32((x)):(x))
1697d765f2d2Sriz #define s16(x) (needswap?bswap16((x)):(x))
1698ec57cc7eSjtk
1699ec57cc7eSjtk rv = 0;
1700ec57cc7eSjtk while (nb > 0) {
1701d765f2d2Sriz if (inomove[s32(d->d_ino)] != s32(d->d_ino)) {
1702ec57cc7eSjtk rv++;
1703d765f2d2Sriz d->d_ino = s32(inomove[s32(d->d_ino)]);
1704ec57cc7eSjtk }
1705d765f2d2Sriz nb -= s16(d->d_reclen);
1706d765f2d2Sriz buf += s16(d->d_reclen);
1707ec57cc7eSjtk }
1708ec57cc7eSjtk return (rv);
1709ec57cc7eSjtk #undef d
1710d765f2d2Sriz #undef s32
1711d765f2d2Sriz #undef s16
1712ec57cc7eSjtk }
1713ec57cc7eSjtk /*
1714ec57cc7eSjtk * Callback function for map_inode_data_blocks, for updating a
1715ec57cc7eSjtk * directory to point to new inode locations.
1716ec57cc7eSjtk */
1717ec57cc7eSjtk static void
update_dir_data(off_t bn,unsigned int size,unsigned int nb,int kind)17187a8d9bb4Sdholland update_dir_data(off_t bn, unsigned int size, unsigned int nb, int kind)
1719ec57cc7eSjtk {
1720ec57cc7eSjtk if (kind == MDB_DATA) {
1721ec57cc7eSjtk union {
1722ec57cc7eSjtk struct direct d;
1723ec57cc7eSjtk char ch[MAXBSIZE];
1724ec57cc7eSjtk } buf;
17252737439dSdholland readat(FFS_FSBTODB(oldsb, bn), &buf, size << oldsb->fs_fshift);
1726ec57cc7eSjtk if (update_dirents((char *) &buf, nb)) {
17272737439dSdholland writeat(FFS_FSBTODB(oldsb, bn), &buf,
1728ec57cc7eSjtk size << oldsb->fs_fshift);
1729ec57cc7eSjtk }
1730ec57cc7eSjtk }
1731ec57cc7eSjtk }
1732ec57cc7eSjtk static void
dirmove_callback(union dinode * di,unsigned int inum,void * arg)1733d765f2d2Sriz dirmove_callback(union dinode * di, unsigned int inum, void *arg)
1734ec57cc7eSjtk {
1735d765f2d2Sriz switch (DIP(di,di_mode) & IFMT) {
1736ec57cc7eSjtk case IFDIR:
1737ec57cc7eSjtk map_inode_data_blocks(di, &update_dir_data);
1738ec57cc7eSjtk break;
1739ec57cc7eSjtk }
1740ec57cc7eSjtk }
1741ec57cc7eSjtk /*
1742ec57cc7eSjtk * Update directory entries to point to new inode locations.
1743ec57cc7eSjtk */
1744ec57cc7eSjtk static void
update_for_inode_move(void)1745ec57cc7eSjtk update_for_inode_move(void)
1746ec57cc7eSjtk {
1747ec57cc7eSjtk map_inodes(&dirmove_callback, newsb->fs_ncg, NULL);
1748ec57cc7eSjtk }
1749ec57cc7eSjtk /*
1750ec57cc7eSjtk * Shrink the file system.
1751ec57cc7eSjtk */
1752ec57cc7eSjtk static void
shrink(void)1753ec57cc7eSjtk shrink(void)
1754ec57cc7eSjtk {
1755*f298a94bSchs uint32_t i;
1756ec57cc7eSjtk
1757f3a44502Smlelstv if (makegeometry(1)) {
1758f3a44502Smlelstv printf("New fs size %"PRIu64" = old fs size %"PRIu64
1759f3a44502Smlelstv ", not shrinking.\n", newsb->fs_size, oldsb->fs_size);
1760f3a44502Smlelstv return;
1761d765f2d2Sriz }
17624962b364Smlelstv
1763ec57cc7eSjtk /* Let's make sure we're not being shrunk into oblivion. */
176417187885Schristos if (newsb->fs_ncg < 1)
176517187885Schristos errx(EXIT_FAILURE, "Size too small - file system would "
176617187885Schristos "have no cylinders");
1767f3a44502Smlelstv
1768f3a44502Smlelstv if (verbose) {
1769f3a44502Smlelstv printf("Shrinking fs from %"PRIu64" blocks to %"PRIu64
1770f3a44502Smlelstv " blocks.\n", oldsb->fs_size, newsb->fs_size);
1771f3a44502Smlelstv }
1772f3a44502Smlelstv
1773f3a44502Smlelstv /* Load the inodes off disk - we'll need 'em. */
1774f3a44502Smlelstv loadinodes();
1775f3a44502Smlelstv
1776f3a44502Smlelstv /* Update the timestamp. */
1777f3a44502Smlelstv newsb->fs_time = timestamp();
1778f3a44502Smlelstv
1779ec57cc7eSjtk /* Initialize for block motion. */
1780ec57cc7eSjtk blkmove_init();
1781ec57cc7eSjtk /* Update csum size, then fix up for the new size */
1782e1610ba4Sdholland newsb->fs_cssize = ffs_fragroundup(newsb,
1783ec57cc7eSjtk newsb->fs_ncg * sizeof(struct csum));
1784ec57cc7eSjtk csum_fixup();
1785d3df836aSsnj /* Evict data from any cgs being wholly eliminated */
1786ec57cc7eSjtk for (i = newsb->fs_ncg; i < oldsb->fs_ncg; i++) {
17870e0ce305Ssborrill int64_t base;
17880e0ce305Ssborrill int64_t dlow;
17890e0ce305Ssborrill int64_t dhigh;
17900e0ce305Ssborrill int64_t dmax;
1791ec57cc7eSjtk base = cgbase(oldsb, i);
1792ec57cc7eSjtk dlow = cgsblock(oldsb, i) - base;
1793ec57cc7eSjtk dhigh = cgdmin(oldsb, i) - base;
1794ec57cc7eSjtk dmax = oldsb->fs_size - base;
1795ec57cc7eSjtk if (dmax > cgs[i]->cg_ndblk)
1796ec57cc7eSjtk dmax = cgs[i]->cg_ndblk;
1797ec57cc7eSjtk evict_data(cgs[i], 0, dlow);
1798ec57cc7eSjtk evict_data(cgs[i], dhigh, dmax - dhigh);
1799ec57cc7eSjtk newsb->fs_cstotal.cs_ndir -= cgs[i]->cg_cs.cs_ndir;
1800ec57cc7eSjtk newsb->fs_cstotal.cs_nifree -= cgs[i]->cg_cs.cs_nifree;
1801ec57cc7eSjtk newsb->fs_cstotal.cs_nffree -= cgs[i]->cg_cs.cs_nffree;
1802ec57cc7eSjtk newsb->fs_cstotal.cs_nbfree -= cgs[i]->cg_cs.cs_nbfree;
1803ec57cc7eSjtk }
1804ec57cc7eSjtk /* Update the new last cg. */
1805ec57cc7eSjtk cgs[newsb->fs_ncg - 1]->cg_ndblk = newsb->fs_size -
1806ec57cc7eSjtk ((newsb->fs_ncg - 1) * newsb->fs_fpg);
1807ec57cc7eSjtk /* Is the new last cg partial? If so, evict any data from the part
1808ec57cc7eSjtk * being shrunken away. */
1809ec57cc7eSjtk if (newsb->fs_size % newsb->fs_fpg) {
1810ec57cc7eSjtk struct cg *cg;
1811ec57cc7eSjtk int oldcgsize;
1812ec57cc7eSjtk int newcgsize;
1813ec57cc7eSjtk cg = cgs[newsb->fs_ncg - 1];
1814ec57cc7eSjtk newcgsize = newsb->fs_size % newsb->fs_fpg;
181518174be8Sriz oldcgsize = oldsb->fs_size - ((newsb->fs_ncg - 1) &
181618174be8Sriz oldsb->fs_fpg);
1817ec57cc7eSjtk if (oldcgsize > oldsb->fs_fpg)
1818ec57cc7eSjtk oldcgsize = oldsb->fs_fpg;
1819ec57cc7eSjtk evict_data(cg, newcgsize, oldcgsize - newcgsize);
1820ec57cc7eSjtk clr_bits(cg_blksfree(cg, 0), newcgsize, oldcgsize - newcgsize);
1821ec57cc7eSjtk }
1822d765f2d2Sriz /* Find out whether we would run out of inodes. (Note we
1823d765f2d2Sriz * haven't actually done anything to the file system yet; all
1824d765f2d2Sriz * those evict_data calls just update blkmove.) */
1825ec57cc7eSjtk {
1826ec57cc7eSjtk int slop;
1827ec57cc7eSjtk slop = 0;
1828ec57cc7eSjtk for (i = 0; i < newsb->fs_ncg; i++)
1829ec57cc7eSjtk slop += cgs[i]->cg_cs.cs_nifree;
1830ec57cc7eSjtk for (; i < oldsb->fs_ncg; i++)
1831ec57cc7eSjtk slop -= oldsb->fs_ipg - cgs[i]->cg_cs.cs_nifree;
183217187885Schristos if (slop < 0)
183317187885Schristos errx(EXIT_FAILURE, "Sorry, would run out of inodes");
1834ec57cc7eSjtk }
1835d765f2d2Sriz /* Copy data, then update pointers to data. See the comment
1836d765f2d2Sriz * header on perform_data_move for ordering considerations. */
1837ec57cc7eSjtk perform_data_move();
1838ec57cc7eSjtk update_for_data_move();
1839d765f2d2Sriz /* Now do inodes. Initialize, evict, move, update - see the
1840d765f2d2Sriz * comment header on perform_inode_move. */
1841ec57cc7eSjtk inomove_init();
1842ec57cc7eSjtk for (i = newsb->fs_ncg; i < oldsb->fs_ncg; i++)
1843ec57cc7eSjtk evict_inodes(cgs[i]);
1844ec57cc7eSjtk perform_inode_move();
1845ec57cc7eSjtk flush_inodes();
1846ec57cc7eSjtk update_for_inode_move();
1847ec57cc7eSjtk /* Recompute all the bitmaps; most of them probably need it anyway,
1848ec57cc7eSjtk * the rest are just paranoia and not wanting to have to bother
1849ec57cc7eSjtk * keeping track of exactly which ones require it. */
1850ec57cc7eSjtk for (i = 0; i < newsb->fs_ncg; i++)
1851ec57cc7eSjtk cgflags[i] |= CGF_DIRTY | CGF_BLKMAPS | CGF_INOMAPS;
18526f8b62aaSriz /* Update the cg_old_ncyl value for the last cylinder. */
1853a7e78491Smhitch if ((newsb->fs_old_flags & FS_FLAGS_UPDATED) == 0)
1854628c00a0Schristos cgs[newsb->fs_ncg - 1]->cg_old_ncyl =
1855628c00a0Schristos newsb->fs_old_ncyl % newsb->fs_old_cpg;
1856ec57cc7eSjtk /* Make fs_dsize match the new reality. */
1857ec57cc7eSjtk recompute_fs_dsize();
1858ec57cc7eSjtk }
1859ec57cc7eSjtk /*
1860ec57cc7eSjtk * Recompute the block totals, block cluster summaries, and rotational
1861ec57cc7eSjtk * position summaries, for a given cg (specified by number), based on
1862ec57cc7eSjtk * its free-frag bitmap (cg_blksfree()[]).
1863ec57cc7eSjtk */
1864ec57cc7eSjtk static void
rescan_blkmaps(int cgn)1865ec57cc7eSjtk rescan_blkmaps(int cgn)
1866ec57cc7eSjtk {
1867ec57cc7eSjtk struct cg *cg;
1868*f298a94bSchs uint32_t f;
1869ec57cc7eSjtk int b;
1870ec57cc7eSjtk int blkfree;
1871ec57cc7eSjtk int blkrun;
1872ec57cc7eSjtk int fragrun;
1873ec57cc7eSjtk int fwb;
1874ec57cc7eSjtk
1875ec57cc7eSjtk cg = cgs[cgn];
1876ec57cc7eSjtk /* Subtract off the current totals from the sb's summary info */
1877ec57cc7eSjtk newsb->fs_cstotal.cs_nffree -= cg->cg_cs.cs_nffree;
1878ec57cc7eSjtk newsb->fs_cstotal.cs_nbfree -= cg->cg_cs.cs_nbfree;
1879ec57cc7eSjtk /* Clear counters and bitmaps. */
1880ec57cc7eSjtk cg->cg_cs.cs_nffree = 0;
1881ec57cc7eSjtk cg->cg_cs.cs_nbfree = 0;
1882d765f2d2Sriz memset(&cg->cg_frsum[0], 0, MAXFRAG * sizeof(cg->cg_frsum[0]));
1883d765f2d2Sriz memset(&old_cg_blktot(cg, 0)[0], 0,
188418174be8Sriz newsb->fs_old_cpg * sizeof(old_cg_blktot(cg, 0)[0]));
1885d765f2d2Sriz memset(&old_cg_blks(newsb, cg, 0, 0)[0], 0,
1886628c00a0Schristos newsb->fs_old_cpg * newsb->fs_old_nrpos *
188718174be8Sriz sizeof(old_cg_blks(newsb, cg, 0, 0)[0]));
1888ec57cc7eSjtk if (newsb->fs_contigsumsize > 0) {
1889ec57cc7eSjtk cg->cg_nclusterblks = cg->cg_ndblk / newsb->fs_frag;
1890d765f2d2Sriz memset(&cg_clustersum(cg, 0)[1], 0,
1891ec57cc7eSjtk newsb->fs_contigsumsize *
1892ec57cc7eSjtk sizeof(cg_clustersum(cg, 0)[1]));
1893d765f2d2Sriz if (is_ufs2)
1894d765f2d2Sriz memset(&cg_clustersfree(cg, 0)[0], 0,
1895d765f2d2Sriz howmany(newsb->fs_fpg / NSPB(newsb), NBBY));
1896d765f2d2Sriz else
1897d765f2d2Sriz memset(&cg_clustersfree(cg, 0)[0], 0,
189818174be8Sriz howmany((newsb->fs_old_cpg * newsb->fs_old_spc) /
189918174be8Sriz NSPB(newsb), NBBY));
1900ec57cc7eSjtk }
1901d765f2d2Sriz /* Scan the free-frag bitmap. Runs of free frags are kept
1902d765f2d2Sriz * track of with fragrun, and recorded into cg_frsum[] and
1903d765f2d2Sriz * cg_cs.cs_nffree; on each block boundary, entire free blocks
1904d765f2d2Sriz * are recorded as well. */
1905ec57cc7eSjtk blkfree = 1;
1906ec57cc7eSjtk blkrun = 0;
1907ec57cc7eSjtk fragrun = 0;
1908ec57cc7eSjtk f = 0;
1909ec57cc7eSjtk b = 0;
1910ec57cc7eSjtk fwb = 0;
1911ec57cc7eSjtk while (f < cg->cg_ndblk) {
1912ec57cc7eSjtk if (bit_is_set(cg_blksfree(cg, 0), f)) {
1913ec57cc7eSjtk fragrun++;
1914ec57cc7eSjtk } else {
1915ec57cc7eSjtk blkfree = 0;
1916ec57cc7eSjtk if (fragrun > 0) {
1917ec57cc7eSjtk cg->cg_frsum[fragrun]++;
1918ec57cc7eSjtk cg->cg_cs.cs_nffree += fragrun;
1919ec57cc7eSjtk }
1920ec57cc7eSjtk fragrun = 0;
1921ec57cc7eSjtk }
1922ec57cc7eSjtk f++;
1923ec57cc7eSjtk fwb++;
1924ec57cc7eSjtk if (fwb >= newsb->fs_frag) {
1925ec57cc7eSjtk if (blkfree) {
1926ec57cc7eSjtk cg->cg_cs.cs_nbfree++;
1927ec57cc7eSjtk if (newsb->fs_contigsumsize > 0)
1928ec57cc7eSjtk set_bits(cg_clustersfree(cg, 0), b, 1);
1929d765f2d2Sriz if (is_ufs2 == 0) {
1930d765f2d2Sriz old_cg_blktot(cg, 0)[
1931d765f2d2Sriz old_cbtocylno(newsb,
193218174be8Sriz f - newsb->fs_frag)]++;
193318174be8Sriz old_cg_blks(newsb, cg,
1934d765f2d2Sriz old_cbtocylno(newsb,
1935d765f2d2Sriz f - newsb->fs_frag),
193618174be8Sriz 0)[old_cbtorpos(newsb,
193718174be8Sriz f - newsb->fs_frag)]++;
1938d765f2d2Sriz }
1939ec57cc7eSjtk blkrun++;
1940ec57cc7eSjtk } else {
1941ec57cc7eSjtk if (fragrun > 0) {
1942ec57cc7eSjtk cg->cg_frsum[fragrun]++;
1943ec57cc7eSjtk cg->cg_cs.cs_nffree += fragrun;
1944ec57cc7eSjtk }
1945ec57cc7eSjtk if (newsb->fs_contigsumsize > 0) {
1946ec57cc7eSjtk if (blkrun > 0) {
194718174be8Sriz cg_clustersum(cg, 0)[(blkrun
194818174be8Sriz > newsb->fs_contigsumsize)
194918174be8Sriz ? newsb->fs_contigsumsize
195018174be8Sriz : blkrun]++;
1951ec57cc7eSjtk }
1952ec57cc7eSjtk }
1953ec57cc7eSjtk blkrun = 0;
1954ec57cc7eSjtk }
1955ec57cc7eSjtk fwb = 0;
1956ec57cc7eSjtk b++;
1957ec57cc7eSjtk blkfree = 1;
1958ec57cc7eSjtk fragrun = 0;
1959ec57cc7eSjtk }
1960ec57cc7eSjtk }
1961ec57cc7eSjtk if (fragrun > 0) {
1962ec57cc7eSjtk cg->cg_frsum[fragrun]++;
1963ec57cc7eSjtk cg->cg_cs.cs_nffree += fragrun;
1964ec57cc7eSjtk }
1965ec57cc7eSjtk if ((blkrun > 0) && (newsb->fs_contigsumsize > 0)) {
1966ec57cc7eSjtk cg_clustersum(cg, 0)[(blkrun > newsb->fs_contigsumsize) ?
1967ec57cc7eSjtk newsb->fs_contigsumsize : blkrun]++;
1968ec57cc7eSjtk }
1969ec57cc7eSjtk /*
1970ec57cc7eSjtk * Put the updated summary info back into csums, and add it
1971ec57cc7eSjtk * back into the sb's summary info. Then mark the cg dirty.
1972ec57cc7eSjtk */
1973ec57cc7eSjtk csums[cgn] = cg->cg_cs;
1974ec57cc7eSjtk newsb->fs_cstotal.cs_nffree += cg->cg_cs.cs_nffree;
1975ec57cc7eSjtk newsb->fs_cstotal.cs_nbfree += cg->cg_cs.cs_nbfree;
1976ec57cc7eSjtk cgflags[cgn] |= CGF_DIRTY;
1977ec57cc7eSjtk }
1978ec57cc7eSjtk /*
1979ec57cc7eSjtk * Recompute the cg_inosused()[] bitmap, and the cs_nifree and cs_ndir
1980ec57cc7eSjtk * values, for a cg, based on the in-core inodes for that cg.
1981ec57cc7eSjtk */
1982ec57cc7eSjtk static void
rescan_inomaps(int cgn)1983ec57cc7eSjtk rescan_inomaps(int cgn)
1984ec57cc7eSjtk {
1985ec57cc7eSjtk struct cg *cg;
1986ec57cc7eSjtk int inum;
1987*f298a94bSchs uint32_t iwc;
1988ec57cc7eSjtk
1989ec57cc7eSjtk cg = cgs[cgn];
1990ec57cc7eSjtk newsb->fs_cstotal.cs_ndir -= cg->cg_cs.cs_ndir;
1991ec57cc7eSjtk newsb->fs_cstotal.cs_nifree -= cg->cg_cs.cs_nifree;
1992ec57cc7eSjtk cg->cg_cs.cs_ndir = 0;
1993ec57cc7eSjtk cg->cg_cs.cs_nifree = 0;
1994d765f2d2Sriz memset(&cg_inosused(cg, 0)[0], 0, howmany(newsb->fs_ipg, NBBY));
1995ec57cc7eSjtk inum = cgn * newsb->fs_ipg;
1996ec57cc7eSjtk if (cgn == 0) {
1997ec57cc7eSjtk set_bits(cg_inosused(cg, 0), 0, 2);
1998ec57cc7eSjtk iwc = 2;
1999ec57cc7eSjtk inum += 2;
2000ec57cc7eSjtk } else {
2001ec57cc7eSjtk iwc = 0;
2002ec57cc7eSjtk }
2003ec57cc7eSjtk for (; iwc < newsb->fs_ipg; iwc++, inum++) {
2004d765f2d2Sriz switch (DIP(inodes + inum, di_mode) & IFMT) {
2005ec57cc7eSjtk case 0:
2006ec57cc7eSjtk cg->cg_cs.cs_nifree++;
2007ec57cc7eSjtk break;
2008ec57cc7eSjtk case IFDIR:
2009ec57cc7eSjtk cg->cg_cs.cs_ndir++;
201064b87635Sdholland /* FALLTHROUGH */
2011ec57cc7eSjtk default:
2012ec57cc7eSjtk set_bits(cg_inosused(cg, 0), iwc, 1);
2013ec57cc7eSjtk break;
2014ec57cc7eSjtk }
2015ec57cc7eSjtk }
2016ec57cc7eSjtk csums[cgn] = cg->cg_cs;
2017ec57cc7eSjtk newsb->fs_cstotal.cs_ndir += cg->cg_cs.cs_ndir;
2018ec57cc7eSjtk newsb->fs_cstotal.cs_nifree += cg->cg_cs.cs_nifree;
2019ec57cc7eSjtk cgflags[cgn] |= CGF_DIRTY;
2020ec57cc7eSjtk }
2021ec57cc7eSjtk /*
2022ec57cc7eSjtk * Flush cgs to disk, recomputing anything they're marked as needing.
2023ec57cc7eSjtk */
2024ec57cc7eSjtk static void
flush_cgs(void)2025ec57cc7eSjtk flush_cgs(void)
2026ec57cc7eSjtk {
2027*f298a94bSchs uint32_t i;
2028ec57cc7eSjtk
2029ec57cc7eSjtk for (i = 0; i < newsb->fs_ncg; i++) {
2030d0b93bc8Sjmcneill progress_bar(special, "flush cg",
2031d0b93bc8Sjmcneill i, newsb->fs_ncg - 1);
2032ec57cc7eSjtk if (cgflags[i] & CGF_BLKMAPS) {
2033ec57cc7eSjtk rescan_blkmaps(i);
2034ec57cc7eSjtk }
2035ec57cc7eSjtk if (cgflags[i] & CGF_INOMAPS) {
2036ec57cc7eSjtk rescan_inomaps(i);
2037ec57cc7eSjtk }
2038ec57cc7eSjtk if (cgflags[i] & CGF_DIRTY) {
2039ec57cc7eSjtk cgs[i]->cg_rotor = 0;
2040ec57cc7eSjtk cgs[i]->cg_frotor = 0;
2041ec57cc7eSjtk cgs[i]->cg_irotor = 0;
2042d765f2d2Sriz if (needswap)
2043d765f2d2Sriz ffs_cg_swap(cgs[i],cgs[i],newsb);
20442737439dSdholland writeat(FFS_FSBTODB(newsb, cgtod(newsb, i)), cgs[i],
2045ec57cc7eSjtk cgblksz);
2046ec57cc7eSjtk }
2047ec57cc7eSjtk }
2048d765f2d2Sriz if (needswap)
2049d765f2d2Sriz ffs_csum_swap(csums,csums,newsb->fs_cssize);
20502737439dSdholland writeat(FFS_FSBTODB(newsb, newsb->fs_csaddr), csums, newsb->fs_cssize);
2051d0b93bc8Sjmcneill
2052d0b93bc8Sjmcneill progress_done();
2053ec57cc7eSjtk }
2054ec57cc7eSjtk /*
2055ec57cc7eSjtk * Write the superblock, both to the main superblock and to each cg's
2056ec57cc7eSjtk * alternative superblock.
2057ec57cc7eSjtk */
2058ec57cc7eSjtk static void
write_sbs(void)2059ec57cc7eSjtk write_sbs(void)
2060ec57cc7eSjtk {
2061*f298a94bSchs uint32_t i;
2062ec57cc7eSjtk
2063a7e78491Smhitch if (newsb->fs_magic == FS_UFS1_MAGIC &&
2064a7e78491Smhitch (newsb->fs_old_flags & FS_FLAGS_UPDATED) == 0) {
2065a7e78491Smhitch newsb->fs_old_time = newsb->fs_time;
2066a7e78491Smhitch newsb->fs_old_size = newsb->fs_size;
2067a7e78491Smhitch /* we don't update fs_csaddr */
2068a7e78491Smhitch newsb->fs_old_dsize = newsb->fs_dsize;
2069a7e78491Smhitch newsb->fs_old_cstotal.cs_ndir = newsb->fs_cstotal.cs_ndir;
2070a7e78491Smhitch newsb->fs_old_cstotal.cs_nbfree = newsb->fs_cstotal.cs_nbfree;
2071a7e78491Smhitch newsb->fs_old_cstotal.cs_nifree = newsb->fs_cstotal.cs_nifree;
2072a7e78491Smhitch newsb->fs_old_cstotal.cs_nffree = newsb->fs_cstotal.cs_nffree;
2073a7e78491Smhitch /* fill fs_old_postbl_start with 256 bytes of 0xff? */
2074a7e78491Smhitch }
2075d765f2d2Sriz /* copy newsb back to oldsb, so we can use it for offsets if
2076d765f2d2Sriz newsb has been swapped for writing to disk */
2077d765f2d2Sriz memcpy(oldsb, newsb, SBLOCKSIZE);
2078d765f2d2Sriz if (needswap)
2079d765f2d2Sriz ffs_sb_swap(newsb,newsb);
2080a59a9ef8Sbouyer writeat(where / DEV_BSIZE, newsb, SBLOCKSIZE);
2081d765f2d2Sriz for (i = 0; i < oldsb->fs_ncg; i++) {
2082d0b93bc8Sjmcneill progress_bar(special, "write sb",
2083d0b93bc8Sjmcneill i, oldsb->fs_ncg - 1);
20842737439dSdholland writeat(FFS_FSBTODB(oldsb, cgsblock(oldsb, i)), newsb, SBLOCKSIZE);
2085ec57cc7eSjtk }
2086d0b93bc8Sjmcneill
2087d0b93bc8Sjmcneill progress_done();
2088ec57cc7eSjtk }
20891e891081Shaad
2090f3a44502Smlelstv /*
2091f93d6526Smaya * Check to see whether new size changes the filesystem
2092f3a44502Smlelstv * return exit code
2093f3a44502Smlelstv */
2094f3a44502Smlelstv static int
checkonly(void)2095f3a44502Smlelstv checkonly(void)
2096f3a44502Smlelstv {
2097f3a44502Smlelstv if (makegeometry(0)) {
2098f3a44502Smlelstv if (verbose) {
2099f3a44502Smlelstv printf("Wouldn't change: already %" PRId64
2100f3a44502Smlelstv " blocks\n", (int64_t)oldsb->fs_size);
2101f3a44502Smlelstv }
2102f3a44502Smlelstv return 1;
2103f3a44502Smlelstv }
2104f3a44502Smlelstv
2105f3a44502Smlelstv if (verbose) {
2106f3a44502Smlelstv printf("Would change: newsize: %" PRId64 " oldsize: %"
2107f3a44502Smlelstv PRId64 " fsdb: %" PRId64 "\n", FFS_DBTOFSB(oldsb, newsize),
2108f3a44502Smlelstv (int64_t)oldsb->fs_size,
2109f3a44502Smlelstv (int64_t)oldsb->fs_fsbtodb);
2110f3a44502Smlelstv }
2111f3a44502Smlelstv return 0;
2112f3a44502Smlelstv }
2113f3a44502Smlelstv
21147a8d9bb4Sdholland static off_t
get_dev_size(const char * dev_name)21152cf653d8Sjmcneill get_dev_size(const char *dev_name)
21161e891081Shaad {
21171e891081Shaad struct dkwedge_info dkw;
21181e891081Shaad struct partition *pp;
21191e891081Shaad struct disklabel lp;
2120bb5cf371Sriastradh struct stat st;
21211e891081Shaad size_t ptn;
21221e891081Shaad
21231e891081Shaad /* Get info about partition/wedge */
21249ea98f45Sriastradh if (ioctl(fd, DIOCGWEDGEINFO, &dkw) != -1)
2125bb5cf371Sriastradh return dkw.dkw_size;
21269ea98f45Sriastradh if (ioctl(fd, DIOCGDINFO, &lp) != -1) {
21271e891081Shaad ptn = strchr(dev_name, '\0')[-1] - 'a';
21281e891081Shaad if (ptn >= lp.d_npartitions)
21291e891081Shaad return 0;
21301e891081Shaad pp = &lp.d_partitions[ptn];
21311e891081Shaad return pp->p_size;
21321e891081Shaad }
21339ea98f45Sriastradh if (fstat(fd, &st) != -1 && S_ISREG(st.st_mode))
2134ae3fcf09Schopps return st.st_size / DEV_BSIZE;
21351e891081Shaad
2136bb5cf371Sriastradh return 0;
21371e891081Shaad }
21381e891081Shaad
2139ec57cc7eSjtk /*
2140ec57cc7eSjtk * main().
2141ec57cc7eSjtk */
2142ec57cc7eSjtk int
main(int argc,char ** argv)21431e891081Shaad main(int argc, char **argv)
2144ec57cc7eSjtk {
21451e891081Shaad int ch;
2146ae3fcf09Schopps int CheckOnlyFlag;
21471e891081Shaad int ExpertFlag;
21481e891081Shaad int SFlag;
2149628c00a0Schristos size_t i;
21502cf653d8Sjmcneill char specname[MAXPATHLEN];
21512cf653d8Sjmcneill char rawname[MAXPATHLEN];
21522cf653d8Sjmcneill const char *raw;
21531e891081Shaad
21541e891081Shaad char reply[5];
21551e891081Shaad
21561e891081Shaad newsize = 0;
21571e891081Shaad ExpertFlag = 0;
21581e891081Shaad SFlag = 0;
2159ae3fcf09Schopps CheckOnlyFlag = 0;
21601e891081Shaad
2161d0b93bc8Sjmcneill while ((ch = getopt(argc, argv, "cps:vy")) != -1) {
21621e891081Shaad switch (ch) {
2163ae3fcf09Schopps case 'c':
2164ae3fcf09Schopps CheckOnlyFlag = 1;
2165ae3fcf09Schopps break;
2166d0b93bc8Sjmcneill case 'p':
2167d0b93bc8Sjmcneill progress = 1;
2168d0b93bc8Sjmcneill break;
21691e891081Shaad case 's':
21701e891081Shaad SFlag = 1;
21717a8d9bb4Sdholland newsize = strtoll(optarg, NULL, 10);
21721e891081Shaad if(newsize < 1) {
21731e891081Shaad usage();
2174ec57cc7eSjtk }
21751e891081Shaad break;
2176ae3fcf09Schopps case 'v':
2177ae3fcf09Schopps verbose = 1;
2178ae3fcf09Schopps break;
21791e891081Shaad case 'y':
21801e891081Shaad ExpertFlag = 1;
21811e891081Shaad break;
21821e891081Shaad case '?':
21831e891081Shaad /* FALLTHROUGH */
21841e891081Shaad default:
21851e891081Shaad usage();
21861e891081Shaad }
21871e891081Shaad }
21881e891081Shaad argc -= optind;
21891e891081Shaad argv += optind;
21901e891081Shaad
21911e891081Shaad if (argc != 1) {
21921e891081Shaad usage();
21931e891081Shaad }
21941e891081Shaad
21952cf653d8Sjmcneill special = getfsspecname(specname, sizeof(specname), argv[0]);
21962cf653d8Sjmcneill if (special == NULL)
21972cf653d8Sjmcneill err(EXIT_FAILURE, "%s: %s", argv[0], specname);
21982cf653d8Sjmcneill raw = getdiskrawname(rawname, sizeof(rawname), special);
21992cf653d8Sjmcneill if (raw != NULL)
22002cf653d8Sjmcneill special = raw;
22011e891081Shaad
2202ae3fcf09Schopps if (ExpertFlag == 0 && CheckOnlyFlag == 0) {
2203a3a3268fSriz printf("It's required to manually run fsck on file system "
22041e891081Shaad "before you can resize it\n\n"
22051e891081Shaad " Did you run fsck on your disk (Yes/No) ? ");
22061e891081Shaad fgets(reply, (int)sizeof(reply), stdin);
22071e891081Shaad if (strcasecmp(reply, "Yes\n")) {
22081e891081Shaad printf("\n Nothing done \n");
22091e891081Shaad exit(EXIT_SUCCESS);
22101e891081Shaad }
22111e891081Shaad }
22121e891081Shaad
2213a3a3268fSriz fd = open(special, O_RDWR, 0);
2214628c00a0Schristos if (fd < 0)
2215a3a3268fSriz err(EXIT_FAILURE, "Can't open `%s'", special);
2216ec57cc7eSjtk checksmallio();
22171e891081Shaad
22181e891081Shaad if (SFlag == 0) {
2219a3a3268fSriz newsize = get_dev_size(special);
22201e891081Shaad if (newsize == 0)
222118174be8Sriz err(EXIT_FAILURE,
222218174be8Sriz "Can't resize file system, newsize not known.");
22231e891081Shaad }
22241e891081Shaad
2225ec57cc7eSjtk oldsb = (struct fs *) & sbbuf;
2226628c00a0Schristos newsb = (struct fs *) (SBLOCKSIZE + (char *) &sbbuf);
2227628c00a0Schristos for (where = search[i = 0]; search[i] != -1; where = search[++i]) {
22282d9d4664Sbouyer readat(where / DEV_BSIZE, oldsb, SBLOCKSIZE);
2229a3a3268fSriz switch (oldsb->fs_magic) {
2230a3a3268fSriz case FS_UFS2_MAGIC:
223187ba0e2aSchs case FS_UFS2EA_MAGIC:
2232a3a3268fSriz is_ufs2 = 1;
223364b87635Sdholland /* FALLTHROUGH */
2234a3a3268fSriz case FS_UFS1_MAGIC:
2235a3a3268fSriz needswap = 0;
2236a3a3268fSriz break;
2237a3a3268fSriz case FS_UFS2_MAGIC_SWAPPED:
223887ba0e2aSchs case FS_UFS2EA_MAGIC_SWAPPED:
2239a3a3268fSriz is_ufs2 = 1;
2240a3a3268fSriz /* FALLTHROUGH */
2241a3a3268fSriz case FS_UFS1_MAGIC_SWAPPED:
2242a3a3268fSriz needswap = 1;
2243a3a3268fSriz break;
2244a3a3268fSriz default:
22456efa15a7Sriz continue;
2246a3a3268fSriz }
2247a3a3268fSriz if (!is_ufs2 && where == SBLOCK_UFS2)
2248a3a3268fSriz continue;
2249628c00a0Schristos break;
2250ec57cc7eSjtk }
2251628c00a0Schristos if (where == (off_t)-1)
22521e891081Shaad errx(EXIT_FAILURE, "Bad magic number");
2253d765f2d2Sriz if (needswap)
2254d765f2d2Sriz ffs_sb_swap(oldsb,oldsb);
2255a7e78491Smhitch if (oldsb->fs_magic == FS_UFS1_MAGIC &&
2256a7e78491Smhitch (oldsb->fs_old_flags & FS_FLAGS_UPDATED) == 0) {
2257a7e78491Smhitch oldsb->fs_csaddr = oldsb->fs_old_csaddr;
2258a7e78491Smhitch oldsb->fs_size = oldsb->fs_old_size;
2259a7e78491Smhitch oldsb->fs_dsize = oldsb->fs_old_dsize;
2260a7e78491Smhitch oldsb->fs_cstotal.cs_ndir = oldsb->fs_old_cstotal.cs_ndir;
2261a7e78491Smhitch oldsb->fs_cstotal.cs_nbfree = oldsb->fs_old_cstotal.cs_nbfree;
2262a7e78491Smhitch oldsb->fs_cstotal.cs_nifree = oldsb->fs_old_cstotal.cs_nifree;
2263a7e78491Smhitch oldsb->fs_cstotal.cs_nffree = oldsb->fs_old_cstotal.cs_nffree;
2264a7e78491Smhitch /* any others? */
2265a7e78491Smhitch printf("Resizing with ffsv1 superblock\n");
2266a7e78491Smhitch }
2267d765f2d2Sriz
2268ec57cc7eSjtk oldsb->fs_qbmask = ~(int64_t) oldsb->fs_bmask;
2269ec57cc7eSjtk oldsb->fs_qfmask = ~(int64_t) oldsb->fs_fmask;
2270f1333577Sdholland if (oldsb->fs_ipg % FFS_INOPB(oldsb))
2271f1333577Sdholland errx(EXIT_FAILURE, "ipg[%d] %% FFS_INOPB[%d] != 0",
2272f1333577Sdholland (int) oldsb->fs_ipg, (int) FFS_INOPB(oldsb));
2273d765f2d2Sriz /* The superblock is bigger than struct fs (there are trailing
2274d765f2d2Sriz * tables, of non-fixed size); make sure we copy the whole
2275d765f2d2Sriz * thing. SBLOCKSIZE may be an over-estimate, but we do this
2276d765f2d2Sriz * just once, so being generous is cheap. */
2277d765f2d2Sriz memcpy(newsb, oldsb, SBLOCKSIZE);
2278d0b93bc8Sjmcneill
2279d0b93bc8Sjmcneill if (progress) {
2280d0b93bc8Sjmcneill progress_ttywidth(0);
2281d0b93bc8Sjmcneill signal(SIGWINCH, progress_ttywidth);
2282d0b93bc8Sjmcneill }
2283d0b93bc8Sjmcneill
2284ec57cc7eSjtk loadcgs();
2285ae3fcf09Schopps
2286d0b93bc8Sjmcneill if (progress && !CheckOnlyFlag) {
2287d0b93bc8Sjmcneill progress_switch(progress);
2288d0b93bc8Sjmcneill progress_init();
2289d0b93bc8Sjmcneill }
2290d0b93bc8Sjmcneill
22912737439dSdholland if (newsize > FFS_FSBTODB(oldsb, oldsb->fs_size)) {
2292f3a44502Smlelstv if (CheckOnlyFlag)
2293f3a44502Smlelstv exit(checkonly());
2294ec57cc7eSjtk grow();
22952737439dSdholland } else if (newsize < FFS_FSBTODB(oldsb, oldsb->fs_size)) {
2296d765f2d2Sriz if (is_ufs2)
2297d765f2d2Sriz errx(EXIT_FAILURE,"shrinking not supported for ufs2");
2298f3a44502Smlelstv if (CheckOnlyFlag)
2299f3a44502Smlelstv exit(checkonly());
2300ec57cc7eSjtk shrink();
2301f3a44502Smlelstv } else {
2302f3a44502Smlelstv if (CheckOnlyFlag)
2303f3a44502Smlelstv exit(checkonly());
2304f3a44502Smlelstv if (verbose)
2305f3a44502Smlelstv printf("No change requested: already %" PRId64
2306f3a44502Smlelstv " blocks\n", (int64_t)oldsb->fs_size);
2307ec57cc7eSjtk }
2308f3a44502Smlelstv
2309ec57cc7eSjtk flush_cgs();
2310ec57cc7eSjtk write_sbs();
23118966ecedSriz if (isplainfile())
23128966ecedSriz ftruncate(fd,newsize * DEV_BSIZE);
23131e891081Shaad return 0;
23141e891081Shaad }
23151e891081Shaad
23161e891081Shaad static void
usage(void)23171e891081Shaad usage(void)
23181e891081Shaad {
23191e891081Shaad
23207ebfc10bSchristos (void)fprintf(stderr, "usage: %s [-cpvy] [-s size] special\n",
2321d765f2d2Sriz getprogname());
23221e891081Shaad exit(EXIT_FAILURE);
2323ec57cc7eSjtk }
2324