busybox/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * res_gdt.c --- reserve blocks for growing the group descriptor table
   4 *               during online resizing.
   5 *
   6 * Copyright (C) 2002 Andreas Dilger
   7 *
   8 * %Begin-Header%
   9 * This file may be redistributed under the terms of the GNU Public
  10 * License.
  11 * %End-Header%
  12 */
  13
  14#include <stdio.h>
  15#include <string.h>
  16#include <time.h>
  17#include "ext2_fs.h"
  18#include "ext2fs.h"
  19
  20/*
  21 * Iterate through the groups which hold BACKUP superblock/GDT copies in an
  22 * ext3 filesystem.  The counters should be initialized to 1, 5, and 7 before
  23 * calling this for the first time.  In a sparse filesystem it will be the
  24 * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
  25 * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
  26 */
  27static unsigned int list_backups(ext2_filsys fs, unsigned int *three,
  28                                 unsigned int *five, unsigned int *seven)
  29{
  30        unsigned int *min = three;
  31        int mult = 3;
  32        unsigned int ret;
  33
  34        if (!(fs->super->s_feature_ro_compat &
  35              EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
  36                ret = *min;
  37                *min += 1;
  38                return ret;
  39        }
  40
  41        if (*five < *min) {
  42                min = five;
  43                mult = 5;
  44        }
  45        if (*seven < *min) {
  46                min = seven;
  47                mult = 7;
  48        }
  49
  50        ret = *min;
  51        *min *= mult;
  52
  53        return ret;
  54}
  55
  56/*
  57 * This code assumes that the reserved blocks have already been marked in-use
  58 * during ext2fs_initialize(), so that they are not allocated for other
  59 * uses before we can add them to the resize inode (which has to come
  60 * after the creation of the inode table).
  61 */
  62errcode_t ext2fs_create_resize_inode(ext2_filsys fs)
  63{
  64        errcode_t               retval, retval2;
  65        struct ext2_super_block *sb;
  66        struct ext2_inode       inode;
  67        __u32                   *dindir_buf, *gdt_buf;
  68        int                     rsv_add;
  69        unsigned long long      apb, inode_size;
  70        blk_t                   dindir_blk, rsv_off, gdt_off, gdt_blk;
  71        int                     dindir_dirty = 0, inode_dirty = 0;
  72
  73        EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
  74
  75        sb = fs->super;
  76
  77        retval = ext2fs_get_mem(2 * fs->blocksize, (void *)&dindir_buf);
  78        if (retval)
  79                goto out_free;
  80        gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize);
  81
  82        retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
  83        if (retval)
  84                goto out_free;
  85
  86        /* Maximum possible file size (we donly use the dindirect blocks) */
  87        apb = EXT2_ADDR_PER_BLOCK(sb);
  88        rsv_add = fs->blocksize / 512;
  89        if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) {
  90#ifdef RES_GDT_DEBUG
  91                printf("reading GDT dindir %u\n", dindir_blk);
  92#endif
  93                retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf);
  94                if (retval)
  95                        goto out_inode;
  96        } else {
  97                blk_t goal = 3 + sb->s_reserved_gdt_blocks +
  98                        fs->desc_blocks + fs->inode_blocks_per_group;
  99
 100                retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk);
 101                if (retval)
 102                        goto out_free;
 103                inode.i_mode = LINUX_S_IFREG | 0600;
 104                inode.i_links_count = 1;
 105                inode.i_block[EXT2_DIND_BLOCK] = dindir_blk;
 106                inode.i_blocks = rsv_add;
 107                memset(dindir_buf, 0, fs->blocksize);
 108#ifdef RES_GDT_DEBUG
 109                printf("allocated GDT dindir %u\n", dindir_blk);
 110#endif
 111                dindir_dirty = inode_dirty = 1;
 112                inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS;
 113                inode_size *= fs->blocksize;
 114                inode.i_size = inode_size & 0xFFFFFFFF;
 115                inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF;
 116                if (inode.i_size_high) {
 117                        sb->s_feature_ro_compat |=
 118                                EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
 119                }
 120                inode.i_ctime = time(NULL);
 121        }
 122
 123        for (rsv_off = 0, gdt_off = fs->desc_blocks,
 124             gdt_blk = sb->s_first_data_block + 1 + fs->desc_blocks;
 125             rsv_off < sb->s_reserved_gdt_blocks;
 126             rsv_off++, gdt_off++, gdt_blk++) {
 127                unsigned int three = 1, five = 5, seven = 7;
 128                unsigned int grp, last = 0;
 129                int gdt_dirty = 0;
 130
 131                gdt_off %= apb;
 132                if (!dindir_buf[gdt_off]) {
 133                        /* FIXME XXX XXX
 134                        blk_t new_blk;
 135
 136                        retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk);
 137                        if (retval)
 138                                goto out_free;
 139                        if (new_blk != gdt_blk) {
 140                                // XXX free block
 141                                retval = -1; // XXX
 142                        }
 143                        */
 144                        gdt_dirty = dindir_dirty = inode_dirty = 1;
 145                        memset(gdt_buf, 0, fs->blocksize);
 146                        dindir_buf[gdt_off] = gdt_blk;
 147                        inode.i_blocks += rsv_add;
 148#ifdef RES_GDT_DEBUG
 149                        printf("added primary GDT block %u at %u[%u]\n",
 150                               gdt_blk, dindir_blk, gdt_off);
 151#endif
 152                } else if (dindir_buf[gdt_off] == gdt_blk) {
 153#ifdef RES_GDT_DEBUG
 154                        printf("reading primary GDT block %u\n", gdt_blk);
 155#endif
 156                        retval = ext2fs_read_ind_block(fs, gdt_blk, gdt_buf);
 157                        if (retval)
 158                                goto out_dindir;
 159                } else {
 160#ifdef RES_GDT_DEBUG
 161                        printf("bad primary GDT %u != %u at %u[%u]\n",
 162                               dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off);
 163#endif
 164                        retval = EXT2_ET_RESIZE_INODE_CORRUPT;
 165                        goto out_dindir;
 166                }
 167
 168                while ((grp = list_backups(fs, &three, &five, &seven)) <
 169                       fs->group_desc_count) {
 170                        blk_t expect = gdt_blk + grp * sb->s_blocks_per_group;
 171
 172                        if (!gdt_buf[last]) {
 173#ifdef RES_GDT_DEBUG
 174                                printf("added backup GDT %u grp %u@%u[%u]\n",
 175                                       expect, grp, gdt_blk, last);
 176#endif
 177                                gdt_buf[last] = expect;
 178                                inode.i_blocks += rsv_add;
 179                                gdt_dirty = inode_dirty = 1;
 180                        } else if (gdt_buf[last] != expect) {
 181#ifdef RES_GDT_DEBUG
 182                                printf("bad backup GDT %u != %u at %u[%u]\n",
 183                                       gdt_buf[last], expect, gdt_blk, last);
 184#endif
 185                                retval = EXT2_ET_RESIZE_INODE_CORRUPT;
 186                                goto out_dindir;
 187                        }
 188                        last++;
 189                }
 190                if (gdt_dirty) {
 191#ifdef RES_GDT_DEBUG
 192                        printf("writing primary GDT block %u\n", gdt_blk);
 193#endif
 194                        retval = ext2fs_write_ind_block(fs, gdt_blk, gdt_buf);
 195                        if (retval)
 196                                goto out_dindir;
 197                }
 198        }
 199
 200out_dindir:
 201        if (dindir_dirty) {
 202                retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf);
 203                if (!retval)
 204                        retval = retval2;
 205        }
 206out_inode:
 207#ifdef RES_GDT_DEBUG
 208        printf("inode.i_blocks = %u, i_size = %u\n", inode.i_blocks,
 209               inode.i_size);
 210#endif
 211        if (inode_dirty) {
 212                inode.i_atime = inode.i_mtime = time(NULL);
 213                retval2 = ext2fs_write_inode(fs, EXT2_RESIZE_INO, &inode);
 214                if (!retval)
 215                        retval = retval2;
 216        }
 217out_free:
 218        ext2fs_free_mem((void *)&dindir_buf);
 219        return retval;
 220}
 221
 222