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