tech-userlevel archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: resize_ffs improvements
On Sat, Dec 25, 2010 at 03:53:01AM +0100, Adam Hamsik wrote:
>
> On Dec,Friday 24 2010, at 5:32 PM, Christopher Berardi wrote:
>
> >> hi,
> >>
> >> I have done some small improvements to resize_ffs. With [1] patch it ask
> >> for
> >> confirmation if user has run fsck on filesystem (requested by agc@) and I
> >> have
> >> changed default behaviour of tool to grow filesystem to device size by
> >> default,
> >> shrinking is still possible with -s parameter. I would like to commit this
> >> patch and add resize_ffs to release build to have at least ffsv1
> >> grow/shrink
> >> working for users.
> >>
> >> [1] www.netbsd.org/~haad/resize_ffs.diff
> >>
> >> Regards
> >>
> >> Adam.
> >
> > I've done some more work on this, too. I attempted to recycle newfs(8)
> > code, though I wasn't entirely successful in the integration. I did,
> > however, make important improvements in the realm of user usability. For
> > example, I add options for entering the new size in different units, such
> > as KB, MB, GB, etc. as well as a percentage of the current filesystem size
> > (such as 150% of current file system size).
> >
> > Let me know if you'd be interested in seeing my code.
>
> Yeah I'm interested and if you can show it to me it would be good.
>
> Regards
>
> Adam.
>
>
I've attached a copy of the file. The code that deals with the usuability
features is found in lines 29-37; 98-158; 195-209; 662-754.
And just FYI, all code I borrowed from elsewhere was under the BSD license
(e.g., newfs(8)) and all code I wrote I place under the BSD license.
--
Christopher Berardi
http://www.natoufa.com/
Be still, and know that I am God (Psalms 46:10)
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/disklabel.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <ufs/ffs/ffs_extern.h>
#include <ufs/ffs/fs.h>
#include <ufs/ufs/inode.h>
#include <ufs/ufs/ufs_bswap.h>
/* filesystem types */
#define FFS1 01
#define FFS2 02
/* definitions for size flags */
#define CAPACITY 00
#define KILOBYTES 01
#define MEGABYTES 02
#define GIGABYTES 03
#define TERABYTES 04
#define PERCENTAGE 05
#define TIMES 06
#define BLOCKS 07
/* global variables */
static struct fs *o_sblk; /* old superblock, backup copy */
static struct fs *n_sblk; /* new superblock, working copy */
static daddr_t fs_sbloc; /* superblock base address */
static int64_t fs_csaddr; /* modifiable copy of the csum base address */
static int32_t fs_type; /* the file system type, i.e., FFSv1 or FFSv2 */
static int32_t fd; /* file descriptor for the disk device */
static uint64_t newsize; /* the new size ffs is to be grown/shrunk to */
static char s_val; /* the size value used in output to the user */
static struct ufs1_dinode *blnk1_inodes; /* we don't know which one we need */
static struct ufs2_dinode *blnk2_inodes; /* so create both */
static struct csum *n_csum; /* pointer to cylinder group summary */
static struct csum *csum_zero; /* first block of cylinder group summary */
static struct csum *csum_next; /* next summary */
static struct csum *csum_end; /* end of summary buffer */
static struct csum *csum_reset; /* place for next summary */
static struct cg **n_cgs; /* pointers to new cylinder groups */
static struct cg n_cg; /* a new cylinder group */
static int32_t cg_blksize; /* size of cg blocks from cg_load() */
static char *fsbuf[3]; /* buffer for sblk, cg, and inodes */
static int32_t fsbuf_size; /* size of the buffer to end of 2nd inode blk */
static int32_t fsbuf_memsize; /* actual size of buffer */
static uint32_t sec_size; /* hardware disk sector size */
static time_t cur_time; /* the current time */
static int32_t needswap; /* determines if endianess needs swapped */
struct { /* flags set via command line arguments */
unsigned int expert : 1;
unsigned int size : 3;
} flags;
/* function definitions */
static void cg_init(int32_t);
static void fs_grow(void);
static void fs_read(daddr_t, int32_t, void *);
static void fs_write(daddr_t, int32_t, void *);
static void get_newsize(double);
static void get_sec_size(void);
static void get_s_val(void);
static void print_output(char *, char *, double);
static void set_blk(unsigned char *, int32_t);
static void usage(void);
/*
* resize_ffs: This is a utility for the resizing of a Berkeley Fast File
* System (ffs). Currently, this will only grow an unmounted file system.
*/
int
main(int argc, char *argv[])
{
int ch, i;
double r_size = 0;
char *r_dev;
off_t sblk_search[] = SBLOCKSEARCH;
/* parse command line arguments */
setprogname(argv[0]);
while ((ch = getopt(argc, argv, "b:cg:k:m:P:p:t:y")) != -1) {
switch (ch) {
case 'b':
flags.size = BLOCKS;
r_size = strtod(optarg, NULL);
break;
case 'c':
flags.size = CAPACITY;
break;
case 'g':
flags.size = GIGABYTES;
r_size = strtod(optarg, NULL);
break;
case 'k':
flags.size = KILOBYTES;
r_size = strtod(optarg, NULL);
break;
case 'm':
flags.size = MEGABYTES;
r_size = strtod(optarg, NULL);
break;
case 'P':
flags.size = PERCENTAGE;
r_size = strtod(optarg, NULL);
break;
case 'p':
flags.size = TIMES;
r_size = strtod(optarg, NULL);
break;
case 't':
flags.size = TERABYTES;
r_size = strtod(optarg, NULL);
break;
case 'y':
flags.expert = true;
break;
case '?':
/* FALLTHROUGH */
default:
usage();
/* NOT REACHED */
}
}
argc -= optind;
argv += optind;
/* since we use this a few times, it's easier to remember this way */
r_dev = argv[0];
/* did the user follow the proper usage? */
if (argc != 1)
usage();
/* open the device */
if ((fd = open(r_dev, O_RDWR, 0)) < 0)
err(EXIT_FAILURE, "CANNOT OPEN '%s'", r_dev);
/* get the disk sector size */
get_sec_size();
/* get superblock */
o_sblk = (struct fs *)malloc(sizeof(*o_sblk));
n_sblk = (struct fs *)malloc(sizeof(*n_sblk));
for (i = 0; sblk_search[i] != -1; i++) {
fs_read((daddr_t)(sblk_search[i]), SBLOCKSIZE, (void *)o_sblk);
if (o_sblk->fs_magic == FS_UFS1_MAGIC) {
fs_type = FFS1;
fs_sbloc = (daddr_t)sblk_search[i];
break;
} else if (o_sblk->fs_magic == FS_UFS2_MAGIC) {
fs_type = FFS2;
fs_sbloc = (daddr_t)sblk_search[i];
break;
}
}
/* check if the superblock is in the current format */
/*
if (!(o_sblk->fs_old_flags & FS_FLAGS_UPDATED))
errx(EXIT_FAILURE,
"(main) superblock in old format - "
"update with fsck_ffs -c");
*/
/* did we fail to find the superblock? */
if (sblk_search[i] == -1)
errx(EXIT_FAILURE, "(main) superblock not found or recognized");
/*
* make a backup copy from the old superblock (now our backup copy)
* to the new superblock (now our working copy)
*/
*n_sblk = *o_sblk;
/* calculate the newsize */
get_newsize(r_size);
/* get size value to use */
get_s_val();
/* are we not changing size? */
if (newsize == n_sblk->fs_size) {
printf("The sizes are the same -- nothing to do\n");
return EXIT_SUCCESS;
}
/* make sure we're doing what we really want to */
if (!flags.expert)
print_output(r_dev, "confirm", r_size);
/* are we growing or shrinking? */
if (newsize > n_sblk->fs_size)
fs_grow();
else /* we must be shrinking */
errx(EXIT_FAILURE, "SHRINKING NOT AVAILABLE");
print_output(r_dev, "summary", 0);
return EXIT_SUCCESS;
}
/*
* This will initialize a single new cylinder group.
*/
static void
cg_init(int32_t cg_num)
{
daddr_t cg_base, d_lower, d_upper, d_max;
int32_t bit, blk_num, cyl_num, i, j, map, run, start;
int32_t *clustersum_ptr;
u_char *clustermap_ptr;
struct ufs1_dinode *dinode_ptr1;
struct ufs2_dinode *dinode_ptr2;
/*
* Determine cylinder group block boundaries
* cg_base: base offset address of cylinder group
* d_lower: offset address of superblock area
* d_upper: offset address of post-inode data area
* d_max: offset address of end of cylinder group
*/
cg_base = cgbase(n_sblk, cg_num);
d_max = cg_base + n_sblk->fs_fpg;
if (d_max > n_sblk->fs_size)
d_max = n_sblk->fs_size;
d_lower = cgsblock(n_sblk, cg_num) - cg_base;
d_upper = cgdmin(n_sblk, cg_num) - cg_base;
/*
* Clear out cg and fill in cg struct information
*/
memset(&n_cg, 0, n_sblk->fs_cgsize);
n_cg.cg_magic = CG_MAGIC;
n_cg.cg_cgx = cg_num;
n_cg.cg_ndblk = d_max - cg_base;
if (n_sblk->fs_contigsumsize > 0)
n_cg.cg_nclusterblks = n_cg.cg_ndblk >> n_sblk->fs_fragshift;
start = &n_cg.cg_space[0] - (u_char *)(&n_cg.cg_firstfield);
if (fs_type == FFS2) {
n_cg.cg_time = cur_time;
n_cg.cg_niblk = n_sblk->fs_ipg;
n_cg.cg_initediblk = MIN(n_sblk->fs_ipg, (2 * INOPB(n_sblk)));
n_cg.cg_iusedoff = start;
} else {
n_cg.cg_old_ncyl = n_sblk->fs_old_cpg;
/*
if ((n_sblk->fs_old_flags & FS_FLAGS_UPDATED) == 0 &&
(cg_num == n_sblk->fs_ncg - 1))
n_cg.cg_old_ncyl =
n_sblk->fs_old_ncyl % n_sblk->fs_old_cpg;
*/
n_cg.cg_old_time = cur_time;
n_cg.cg_old_niblk = n_sblk->fs_ipg;
n_cg.cg_old_btotoff = start;
n_cg.cg_old_boff = n_cg.cg_old_btotoff +
n_sblk->fs_old_cpg * sizeof(int32_t);
n_cg.cg_iusedoff = n_cg.cg_old_boff +
n_sblk->fs_old_cpg * sizeof(u_int16_t);
}
n_cg.cg_freeoff = n_cg.cg_iusedoff + howmany(n_sblk->fs_ipg, CHAR_BIT);
if (n_sblk -> fs_contigsumsize <= 0) {
n_cg.cg_nextfreeoff =
n_cg.cg_freeoff + howmany(n_sblk->fs_fpg, CHAR_BIT);
} else {
n_cg.cg_clustersumoff = roundup((n_cg.cg_freeoff +
howmany(n_sblk->fs_fpg, CHAR_BIT) - sizeof(int32_t)),
sizeof(int32_t));
n_cg.cg_clusteroff = n_cg.cg_clustersumoff +
(n_sblk->fs_contigsumsize + 1) * sizeof(int32_t);
n_cg.cg_nextfreeoff = n_cg.cg_clusteroff +
howmany(fragstoblks(n_sblk, n_sblk->fs_fpg), CHAR_BIT);
}
n_cg.cg_cs.cs_nifree += n_sblk->fs_ipg;
for (i = 0, blk_num = 0; i < d_lower; i += n_sblk->fs_frag, blk_num++) {
set_blk(cg_blksfree(&n_cg, 0), blk_num);
if (n_sblk->fs_contigsumsize > 0)
setbit(cg_clustersfree(&n_cg, 0), blk_num);
n_cg.cg_cs.cs_nbfree++;
if (fs_type == FFS1) {
cyl_num = old_cbtocylno(n_sblk, i);
old_cg_blktot(&n_cg, 0)[cyl_num]++;
old_cg_blks(n_sblk, &n_cg, cyl_num,
0)[old_cbtorpos(n_sblk, i)]++;
}
}
if ((i = (d_upper & (n_sblk->fs_frag - 1))) != 0) {
n_cg.cg_frsum[n_sblk->fs_frag - 1]++;
for (j = d_upper + (n_sblk->fs_frag - 1); d_upper < j;
d_upper++) {
setbit(cg_blksfree(&n_cg, 0), d_upper);
n_cg.cg_cs.cs_nffree++;
}
}
for (i = d_upper, blk_num = d_upper >> n_sblk->fs_fragshift;
i + n_sblk->fs_frag <= n_cg.cg_ndblk;
i += n_sblk->fs_frag, blk_num++) {
set_blk(cg_blksfree(&n_cg, 0), blk_num);
if (n_sblk->fs_contigsumsize > 0)
setbit(cg_clustersfree(&n_cg, 0), blk_num);
n_cg.cg_cs.cs_nbfree++;
if (fs_type == FFS1) {
cyl_num = old_cbtocylno(n_sblk, i);
old_cg_blktot(&n_cg, 0)[cyl_num]++;
old_cg_blks(n_sblk, &n_cg, cyl_num,
0)[old_cbtorpos(n_sblk, i)]++;
}
}
if (i < n_cg.cg_ndblk) {
n_cg.cg_frsum[n_cg.cg_ndblk - i]++;
for (j = i; j < n_cg.cg_ndblk; j++) {
setbit(cg_blksfree(&n_cg, 0), j);
n_cg.cg_cs.cs_nffree++;
}
}
if (n_sblk->fs_contigsumsize > 0) {
clustersum_ptr = cg_clustersum(&n_cg, 0);
clustermap_ptr = cg_clustersfree(&n_cg, 0);
map = *clustermap_ptr++;
bit = 1;
run = 0;
for (i = 0; i < n_cg.cg_nclusterblks; i++) {
if ((map & bit) != 0) {
run++;
} else if (run != 0) {
if (run > n_sblk->fs_contigsumsize)
run = n_sblk->fs_contigsumsize;
clustersum_ptr[run]++;
run = 0;
}
if ((i & (CHAR_BIT - 1)) != (CHAR_BIT - 1)) {
bit <<= 1;
} else {
map = *clustermap_ptr++;
bit = 1;
}
}
if (run != 0) {
if (run > n_sblk->fs_contigsumsize)
run = n_sblk->fs_contigsumsize;
clustersum_ptr[run]++;
}
}
*csum_next++ = n_cg.cg_cs;
/* update superblock csum totals */
n_sblk->fs_cstotal.cs_ndir += n_cg.cg_cs.cs_ndir;
n_sblk->fs_cstotal.cs_nbfree += n_cg.cg_cs.cs_nbfree;
n_sblk->fs_cstotal.cs_nffree += n_cg.cg_cs.cs_nffree;
n_sblk->fs_cstotal.cs_nifree += n_cg.cg_cs.cs_nifree;
/*
* Time to start writing information back to disk
*/
if (csum_next == csum_end) {
if (needswap)
ffs_csum_swap(csum_reset, csum_reset, n_sblk->fs_fsize);
fs_csaddr++;
fs_write(fsbtodb(n_sblk, fs_csaddr), n_sblk->fs_fsize,
csum_reset);
csum_next = csum_reset;
memset(csum_next, 0, n_sblk->fs_fsize);
}
/*
* Write this cylinder group's superblock copy, the cylinder group map,
* and two blocks worth of inodes. Make the write an atomic operation.
*/
start = MAX(n_sblk->fs_bsize, SBLOCKSIZE);
memcpy(&fsbuf[1], &n_cg, n_sblk->fs_cgsize);
start+= n_sblk->fs_bsize;
dinode_ptr1 = (struct ufs1_dinode *)(&fsbuf[2]);
dinode_ptr2 = (struct ufs2_dinode *)(&fsbuf[2]);
for (i = MIN(n_sblk->fs_ipg, 2) * INOPB(n_sblk); i != 0; i--) {
if (fs_type == FFS1) {
dinode_ptr1->di_gen = arc4random() & INT32_MAX;
dinode_ptr1++;
} else {
dinode_ptr2->di_gen = arc4random() & INT32_MAX;
dinode_ptr2++;
}
}
fs_write(fsbtodb(n_sblk, cgsblock(n_sblk, cg_num)), fsbuf_size, fsbuf);
/* the FFSv1 requires that we initialize all the nodes */
if (fs_type == FFS2) return;
/* write (i) [generally 16 * fs_frag] file system fragments at once */
i = (fsbuf_memsize - start) / n_sblk->fs_bsize * n_sblk->fs_frag;
d_upper = n_sblk->fs_ipg / INOPF(n_sblk);
dinode_ptr1 = (struct ufs1_dinode *)(&fsbuf[2]);
do
dinode_ptr1->di_gen = arc4random() & INT32_MAX;
while ((char *)++dinode_ptr1 < (char *)fsbuf[fsbuf_memsize]);
fs_write(fsbtodb(n_sblk, cgsblock(n_sblk, cg_num)), fsbuf_size, fsbuf);
/*
for (j = 2 * n_sblk->fs_frag; j < d_upper; j += i) {
if (i > d_upper - j)
i = d_upper - j;
dinode_ptr1 = (struct ufs1_dinode *)(&fsbuf[2]);
do
dinode_ptr1->di_gen = arc4random() & INT32_MAX;
while ((char *)++dinode_ptr1 < (char *)fsbuf[fsbuf_memsize]);
fs_write(fsbtodb(n_sblk, cgimin(n_sblk, cg_num)),
i * n_sblk->fs_bsize / n_sblk->fs_frag, &fsbuf[2]);
}
*/
}
/*
* This will do the work of growing the filesystem
*/
static void
fs_grow(void)
{
int32_t i, j;
needswap = UFS_FSNEEDSWAP(n_sblk);
time(&cur_time);
/*
* Update and calculate new superblock values:
* fs_time: this updates the last write time of the filesystem
* fs_size: calculated from user input,
* the (new) size of the filesystem
* fs_dsize: total number of data blocks in the filesystem
* fs_ncg: total number of cylinder groups in the filesystem
* fs_cssize: size of cylinder group summary area
*/
n_sblk->fs_time = cur_time;
n_sblk->fs_size = newsize;
n_sblk->fs_ncg = howmany(n_sblk->fs_size, n_sblk->fs_fpg);
n_sblk->fs_dsize = n_sblk->fs_size - n_sblk->fs_sblkno -
n_sblk->fs_ncg * (n_sblk->fs_dblkno - n_sblk->fs_sblkno);
n_sblk->fs_cssize =
fragroundup(n_sblk, n_sblk->fs_ncg * sizeof(struct csum));
/*
* Update values used in FFSv1 filesystems
*/
if (fs_type == FFS1) {
n_sblk->fs_old_time = n_sblk->fs_time;
n_sblk->fs_old_size = n_sblk->fs_size;
n_sblk->fs_old_dsize = n_sblk->fs_dsize;
n_sblk->fs_old_ncyl = n_sblk->fs_ncg;
}
/*
* Allocate buffer space for the superblock, cylinder group information
* and two blocks of inodes
*/
if (n_sblk->fs_bsize < SBLOCKSIZE) {
fsbuf_size = SBLOCKSIZE + 3 * n_sblk->fs_bsize;
if ((fsbuf[0] = mmap(0, SBLOCKSIZE, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
errx(EXIT_FAILURE,
"(fs_grow) buffer allocation failure 1");
}
memset(fsbuf[0], 0, SBLOCKSIZE);
} else {
fsbuf_size = 4 * n_sblk->fs_bsize;
if ((fsbuf[0] = mmap(0, n_sblk->fs_bsize, PROT_READ |
PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
errx(EXIT_FAILURE,
"(fs_grow) buffer allocation failure 2");
}
memset(fsbuf[0], 0, n_sblk->fs_bsize);
}
if ((fsbuf[1] = mmap(0, n_sblk->fs_bsize, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
errx(EXIT_FAILURE, "(fs_grow) buffer allocation failure 3");
}
memset(fsbuf[1], 0, n_sblk->fs_bsize);
fsbuf_memsize = fsbuf_size;
/* FFSv1 likes a larger buffer for writing multiple inode blocks */
if (fs_type == FFS1) {
fsbuf_memsize += 14 * n_sblk->fs_bsize;
if ((fsbuf[2] = mmap(0, 16 * n_sblk->fs_bsize, PROT_READ |
PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
/* try once more with the smaller buf size */
if ((fsbuf[2] = mmap(0, 2 * n_sblk->fs_bsize,
PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
-1, 0)) == MAP_FAILED) {
errx(EXIT_FAILURE,
"(fs_grow) buffer allocation failure 4");
}
}
} else {
if ((fsbuf[2] = mmap(0, 2 * n_sblk->fs_bsize, PROT_READ |
PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
errx(EXIT_FAILURE,
"(fs_grow) buffer allocation failure 6");
}
}
memset(fsbuf[2], 0, ((17 * n_sblk->fs_bsize) - sizeof(fsbuf[0])));
/* put a copy of the superblock into the buffer */
memcpy(fsbuf[0], n_sblk, sizeof(n_sblk));
if (needswap)
ffs_sb_swap(n_sblk, (struct fs *)fsbuf[0]);
/*
* Get the cylinder summary address information from disk
*/
n_sblk->fs_cssize =
fragroundup(n_sblk, n_sblk->fs_ncg * sizeof(struct csum));
/* this comes in handy so we don't accidently alter the sblk value */
fs_csaddr = n_sblk->fs_csaddr;
csum_zero = (struct csum *)malloc((size_t)n_sblk->fs_cssize);
for (i = 0; i < o_sblk->fs_cssize; i += o_sblk->fs_bsize) {
fs_read(fsbtodb(o_sblk, (o_sblk->fs_csaddr +
numfrags(o_sblk, i))), MIN((o_sblk->fs_cssize - 1),
o_sblk->fs_bsize), (void *)(((char *)csum_zero) + i));
}
csum_end = (void *)((char *)csum_zero + (2 * n_sblk->fs_fsize));
csum_reset = (void *)((char *)csum_zero + n_sblk->fs_fsize);
/* fast forward csum_next to the proper cg */
for (csum_next = csum_zero, i = 0; i < o_sblk->fs_ncg; i++) {
*csum_next++;
}
/* create new cylinder groups */
for (i = o_sblk->fs_ncg; i < n_sblk->fs_ncg; i++) {
cg_init(i);
}
/*
* Update cylinder summary information
*/
if (fs_type == FFS1) {
n_sblk->fs_old_cstotal.cs_ndir = n_sblk->fs_cstotal.cs_ndir;
n_sblk->fs_old_cstotal.cs_nbfree = n_sblk->fs_cstotal.cs_nbfree;
n_sblk->fs_old_cstotal.cs_nifree = n_sblk->fs_cstotal.cs_nbfree;
n_sblk->fs_old_cstotal.cs_nffree = n_sblk->fs_cstotal.cs_nbfree;
}
/*
* The last step is to write back the superblock and cylinder summary
* to ALL the cylinder groups.
*/
i = cgsblock(n_sblk, 0) * n_sblk->fs_fsize - n_sblk->fs_sblockloc;
memset(fsbuf, 0, i);
memcpy(&fsbuf[0], n_sblk, sizeof(n_sblk));
if (needswap)
ffs_sb_swap(n_sblk, (struct fs *)fsbuf[0]);
fs_write(fs_sbloc, SBLOCKSIZE, (void *)n_sblk);
for (j = 1; j < n_sblk->fs_ncg; j++) {
fs_write(fsbtodb(n_sblk, cgsblock(n_sblk, j)), SBLOCKSIZE,
(void *)n_sblk);
}
}
/*
* fs_read(): read (size) bytes of (buf) back to disk at location (blk_num)
*/
static void
fs_read(daddr_t blk_num, int32_t size, void *buf)
{
int32_t i = 0;
if (lseek(fd, blk_num, SEEK_SET) < 0)
errx(EXIT_FAILURE, "(fs_read) lseek failure");
if ((i = read(fd, buf, size)) < 0)
errx(EXIT_FAILURE, "(fs_read) read failure");
else if (i != size)
errx(EXIT_FAILURE, "(fs_read) read size error");
/* something seems to be awry with my usage of pread() */
/*
if ((i = pread(fd, buf, size, (off_t)(blk_num) * sec_size)) < 0)
errx(EXIT_FAILURE, "(fs_read) read failure");
if (i != size)
errx(EXIT_FAILURE, "(fs_read) read size error");
*/
}
/*
* fs_write(): write (size) bytes of (buf) back to disk at location (blk_num)
*/
static void
fs_write(daddr_t blk_num, int32_t size, void *buf)
{
int32_t i = 0;
if (lseek(fd, blk_num, SEEK_SET) < 0)
errx(EXIT_FAILURE, "(fs_write) lseek failure");
if ((i = write(fd, buf, size)) < 0)
errx(EXIT_FAILURE, "(fs_write) write failure 1");
else if (i != size)
errx(EXIT_FAILURE, "(fs_write) write size error");
/* something seems to be awry with my usage of pwrite() */
/*
if ((i = pwrite(fd, buf, size, (off_t)(blk_num) * sec_size)) < 0)
errx(EXIT_FAILURE, "(fs_write) write failure 2");
if (i != size)
errx(EXIT_FAILURE, "(fs_write) write size error");
*/
}
/*
* Here we convert from the value the user selected to a value that is readily
* usable by the system, such as frag blocks. This also calculates the newsize
* given a percentage or when asked to fill the partition to capacity.
*/
static void
get_newsize(double size)
{
struct disklabel *d_label;
struct stat *d_stat;
uint64_t d_size;
/* get partition size */
d_label = (struct disklabel *)malloc(sizeof(*d_label));
d_stat = (struct stat *)malloc(sizeof(*d_stat));
if (ioctl(fd, DIOCGDINFO, d_label) < 0)
err(EXIT_FAILURE, "(get_newsize) ioctl failure");
if (fstat(fd, d_stat) < 0)
err(EXIT_FAILURE, "(get_newsize) fstat failure");
d_size = (d_label->d_partitions[DISKPART(d_stat->st_rdev)].p_size
* d_label->d_secsize) / n_sblk->fs_fsize;
switch (flags.size) {
case CAPACITY: /* size to the size of the partition */
newsize = d_size;
break;
case PERCENTAGE:/* a percentage (i.e., 150%) of the current size */
newsize = (size / 100) * n_sblk->fs_size;
break;
case TIMES: /* a percentage (i.e., 1.5x) of the current size */
newsize = size * n_sblk->fs_size;
break;
case KILOBYTES: /* convert from kilobytes */
newsize = (size * 1024ll) / n_sblk->fs_fsize;
break;
case MEGABYTES: /* convert from megabytes */
newsize = (size * 1048576ll) / n_sblk->fs_fsize;
break;
case GIGABYTES: /* convert from gigabytes */
newsize = (size * 1073741824ll) / n_sblk->fs_fsize;
break;
case TERABYTES: /* convert from terabytes */
newsize = (size * 1099511627776ll) / n_sblk->fs_fsize;
break;
case BLOCKS: /* input is already in BLOCKS */
newsize = size;
break;
default: /* default is to size to CAPACITY */
newsize = d_size;
/* NOT REACHED */
}
/* check bounds */
if (newsize > d_size)
errx(EXIT_FAILURE, "There isn't that much space on the disk");
if (newsize < 0)
errx(EXIT_FAILURE, "Can't create a negative size filesystem");
/*
* XXX: when shrinking, we'll need to check to make sure that the
* newsize is large enough to hold all our data.
*/
/* free up allocated memory now that we don't need it anymore */
free(d_label);
free(d_stat);
}
/*
* We need the hardware disk sector size in calculating the offsets for
* reading and writing from disk.
*/
static void
get_sec_size(void)
{
struct disklabel *d_label;
d_label = (struct disklabel *)malloc(sizeof(*d_label));
if (ioctl(fd, DIOCGDINFO, d_label) < 0)
err(EXIT_FAILURE, "(get_sec_size) ioctl failure");
sec_size = d_label->d_secsize;
free(d_label);
}
/*
* In several places we need to know how best to present sizes to the user,
* such as when printing the confirmation questions. It is easier just to
* figure this once instead of everytime we need it.
*/
static void
get_s_val(void)
{
double size_c; /* represents the smaller of the two sizes */
/* if the user has told us what size value to use, use it */
if (flags.size == KILOBYTES)
s_val = 'K';
if (flags.size == MEGABYTES)
s_val = 'M';
if (flags.size == GIGABYTES)
s_val = 'G';
if (flags.size == TERABYTES)
s_val = 'T';
if (flags.size == BLOCKS)
s_val = 'B';
/*
* size must be a percentage or capacity
* use the best fit of the smallest size
*/
size_c = MIN(n_sblk->fs_size, newsize);
size_c *= n_sblk->fs_fsize;
if ((size_c /= 1024) < 1)
s_val = 'B';
else if ((size_c /= 1024) < 1)
s_val = 'K';
else if ((size_c /= 1024) < 1)
s_val = 'M';
else if ((size_c /= 1024) < 1)
s_val = 'G';
else
s_val = 'T';
}
/*
* This function presents the user with confirmation and verification output
* about the resize operation about to take place or that has just taken place.
*/
static void
print_output(char *r_dev, char *mode, double p_size)
{
char *oper, ch;
double o_size = o_sblk->fs_size * o_sblk->fs_fsize;
double n_size;
/* what is the correct n_szie to use? */
if (mode == "confirm")
n_size = newsize * n_sblk->fs_fsize;
else
n_size = n_sblk->fs_size * n_sblk->fs_fsize;
/* what is the operation to be or was performed? */
if (o_size < n_size)
oper = "GROW";
else
oper = "SHRINK";
/* what is the display value of the original and new sizes? */
switch(s_val) {
case 'B':
o_size /= o_sblk->fs_fsize;
n_size /= n_sblk->fs_fsize;
break;
case 'K':
o_size /= 1024;
n_size /= 1024;
break;
case 'M':
o_size /= 1048576;
n_size /= 1048576;
break;
case 'G':
o_size /= 1073741824;
n_size /= 1073741824;
break;
case 'T':
/*
o_size /= 1099511627776;
n_size /= 1099511627776;
*/
break;
}
if (mode == "confirm") {
/* confirm backup has been made */
printf("It is strongly recommended that you make a full data "
"backup before proceeding.\n");
printf("Have you made a data backup? [Y/N] ");
if (toupper(getchar()) != 'Y')
exit(EXIT_FAILURE);
/* to clear the input stream after the read */
while ((ch = getchar()) != '\n' && ch != EOF);
printf("\n");
/*
* This will confirm that we the sizes we are moving from and
* going to are actually what we think they are.
*/
printf("This will %s the filesystem %s ", oper, r_dev);
if (flags.size == PERCENTAGE)
printf("%.2f%% ", p_size);
if (flags.size == TIMES)
printf("%.2fx ", p_size);
printf("from %.2f%c to %.2f%c\n", o_size, s_val, n_size, s_val);
printf("Is this correct? [Y/N] ");
if (toupper(getchar()) != 'Y')
exit(EXIT_FAILURE);
/* to clear the input stream after the read */
while ((ch = getchar()) != '\n' && ch != EOF);
printf("\n");
/* Give the user one last chance to abort */
printf("This is your last chance to exit before any changes ");
printf("are made to your system.\n");
printf("Do you want to continue? [Y/N] ");
if (toupper(getchar()) != 'Y')
exit(EXIT_FAILURE);
/* to clear the input stream after the read */
while ((ch = getchar()) != '\n' && ch != EOF);
printf("\n");
}
if (mode == "summary") {
printf("\n****************************************");
printf("****************************************\n\n");
printf("RESIZE SUMMARY FOR %s CYCLE ON %s\n\n", oper, r_dev);
printf("OLD SIZE: %.2f%c\n", o_size, s_val);
printf("NEW SIZE: %.2f%c\n", n_size, s_val);
printf("Have a very nice day!\n");
/*
* XXX: we should probably add more information here, such as
* the changes in the number of cg's, inodes, any superblocks
* lost or gained, etc.
*/
printf("\n****************************************");
printf("****************************************\n");
}
}
/*
* This will put a complete block into the free block map
*/
static void
set_blk(unsigned char *cp, int32_t blk_num)
{
switch (n_sblk->fs_fragshift) {
case 3:
cp[blk_num] = 0xff;
return;
case 2:
cp[blk_num >> 1] = (0x0f << ((blk_num & 0x1) << 2));
return;
case 1:
cp[blk_num >> 2] = (0x03 << ((blk_num & 0x3) << 1));
return;
case 0:
cp[blk_num >> 3] = (0x01 << (blk_num & 0x7));
return;
default:
return;
}
}
/*
* Print usage statement
*/
static void
usage(void)
{
(void)fprintf(stderr,
"usage: %s [-y] [-c | -bgkmPpt new-size] filesystem \n",
getprogname());
exit(EXIT_FAILURE);
}
Home |
Main Index |
Thread Index |
Old Index