busybox/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * bb_inode.c --- routines to update the bad block inode.
   4 *
   5 * WARNING: This routine modifies a lot of state in the filesystem; if
   6 * this routine returns an error, the bad block inode may be in an
   7 * inconsistent state.
   8 *
   9 * Copyright (C) 1994, 1995 Theodore Ts'o.
  10 *
  11 * %Begin-Header%
  12 * This file may be redistributed under the terms of the GNU Public
  13 * License.
  14 * %End-Header%
  15 */
  16
  17#include <stdio.h>
  18#include <string.h>
  19#if HAVE_UNISTD_H
  20#include <unistd.h>
  21#endif
  22#include <fcntl.h>
  23#include <time.h>
  24#if HAVE_SYS_STAT_H
  25#include <sys/stat.h>
  26#endif
  27#if HAVE_SYS_TYPES_H
  28#include <sys/types.h>
  29#endif
  30
  31#include "ext2_fs.h"
  32#include "ext2fs.h"
  33
  34struct set_badblock_record {
  35        ext2_badblocks_iterate  bb_iter;
  36        int             bad_block_count;
  37        blk_t           *ind_blocks;
  38        int             max_ind_blocks;
  39        int             ind_blocks_size;
  40        int             ind_blocks_ptr;
  41        char            *block_buf;
  42        errcode_t       err;
  43};
  44
  45static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
  46                              e2_blkcnt_t blockcnt,
  47                              blk_t ref_block, int ref_offset,
  48                              void *priv_data);
  49static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
  50                                e2_blkcnt_t blockcnt,
  51                                blk_t ref_block, int ref_offset,
  52                                void *priv_data);
  53
  54/*
  55 * Given a bad blocks bitmap, update the bad blocks inode to reflect
  56 * the map.
  57 */
  58errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list)
  59{
  60        errcode_t                       retval;
  61        struct set_badblock_record      rec;
  62        struct ext2_inode               inode;
  63
  64        EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
  65
  66        if (!fs->block_map)
  67                return EXT2_ET_NO_BLOCK_BITMAP;
  68
  69        rec.bad_block_count = 0;
  70        rec.ind_blocks_size = rec.ind_blocks_ptr = 0;
  71        rec.max_ind_blocks = 10;
  72        retval = ext2fs_get_mem(rec.max_ind_blocks * sizeof(blk_t),
  73                                &rec.ind_blocks);
  74        if (retval)
  75                return retval;
  76        memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t));
  77        retval = ext2fs_get_mem(fs->blocksize, &rec.block_buf);
  78        if (retval)
  79                goto cleanup;
  80        memset(rec.block_buf, 0, fs->blocksize);
  81        rec.err = 0;
  82
  83        /*
  84         * First clear the old bad blocks (while saving the indirect blocks)
  85         */
  86        retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
  87                                       BLOCK_FLAG_DEPTH_TRAVERSE, 0,
  88                                       clear_bad_block_proc, &rec);
  89        if (retval)
  90                goto cleanup;
  91        if (rec.err) {
  92                retval = rec.err;
  93                goto cleanup;
  94        }
  95
  96        /*
  97         * Now set the bad blocks!
  98         *
  99         * First, mark the bad blocks as used.  This prevents a bad
 100         * block from being used as an indirecto block for the bad
 101         * block inode (!).
 102         */
 103        if (bb_list) {
 104                retval = ext2fs_badblocks_list_iterate_begin(bb_list,
 105                                                             &rec.bb_iter);
 106                if (retval)
 107                        goto cleanup;
 108                retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
 109                                               BLOCK_FLAG_APPEND, 0,
 110                                               set_bad_block_proc, &rec);
 111                ext2fs_badblocks_list_iterate_end(rec.bb_iter);
 112                if (retval)
 113                        goto cleanup;
 114                if (rec.err) {
 115                        retval = rec.err;
 116                        goto cleanup;
 117                }
 118        }
 119
 120        /*
 121         * Update the bad block inode's mod time and block count
 122         * field.
 123         */
 124        retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode);
 125        if (retval)
 126                goto cleanup;
 127
 128        inode.i_atime = inode.i_mtime = time(NULL);
 129        if (!inode.i_ctime)
 130                inode.i_ctime = time(NULL);
 131        inode.i_blocks = rec.bad_block_count * (fs->blocksize / 512);
 132        inode.i_size = rec.bad_block_count * fs->blocksize;
 133
 134        retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode);
 135        if (retval)
 136                goto cleanup;
 137
 138cleanup:
 139        ext2fs_free_mem(&rec.ind_blocks);
 140        ext2fs_free_mem(&rec.block_buf);
 141        return retval;
 142}
 143
 144/*
 145 * Helper function for update_bb_inode()
 146 *
 147 * Clear the bad blocks in the bad block inode, while saving the
 148 * indirect blocks.
 149 */
 150#ifdef __TURBOC__
 151# pragma argsused
 152#endif
 153static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
 154                                e2_blkcnt_t blockcnt,
 155                                blk_t ref_block EXT2FS_ATTR((unused)),
 156                                int ref_offset EXT2FS_ATTR((unused)),
 157                                void *priv_data)
 158{
 159        struct set_badblock_record *rec = (struct set_badblock_record *)
 160                priv_data;
 161        errcode_t       retval;
 162        unsigned long   old_size;
 163
 164        if (!*block_nr)
 165                return 0;
 166
 167        /*
 168         * If the block number is outrageous, clear it and ignore it.
 169         */
 170        if (*block_nr >= fs->super->s_blocks_count ||
 171            *block_nr < fs->super->s_first_data_block) {
 172                *block_nr = 0;
 173                return BLOCK_CHANGED;
 174        }
 175
 176        if (blockcnt < 0) {
 177                if (rec->ind_blocks_size >= rec->max_ind_blocks) {
 178                        old_size = rec->max_ind_blocks * sizeof(blk_t);
 179                        rec->max_ind_blocks += 10;
 180                        retval = ext2fs_resize_mem(old_size,
 181                                   rec->max_ind_blocks * sizeof(blk_t),
 182                                   &rec->ind_blocks);
 183                        if (retval) {
 184                                rec->max_ind_blocks -= 10;
 185                                rec->err = retval;
 186                                return BLOCK_ABORT;
 187                        }
 188                }
 189                rec->ind_blocks[rec->ind_blocks_size++] = *block_nr;
 190        }
 191
 192        /*
 193         * Mark the block as unused, and update accounting information
 194         */
 195        ext2fs_block_alloc_stats(fs, *block_nr, -1);
 196
 197        *block_nr = 0;
 198        return BLOCK_CHANGED;
 199}
 200
 201
 202/*
 203 * Helper function for update_bb_inode()
 204 *
 205 * Set the block list in the bad block inode, using the supplied bitmap.
 206 */
 207#ifdef __TURBOC__
 208 #pragma argsused
 209#endif
 210static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
 211                              e2_blkcnt_t blockcnt,
 212                              blk_t ref_block EXT2FS_ATTR((unused)),
 213                              int ref_offset EXT2FS_ATTR((unused)),
 214                              void *priv_data)
 215{
 216        struct set_badblock_record *rec = (struct set_badblock_record *)
 217                priv_data;
 218        errcode_t       retval;
 219        blk_t           blk;
 220
 221        if (blockcnt >= 0) {
 222                /*
 223                 * Get the next bad block.
 224                 */
 225                if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk))
 226                        return BLOCK_ABORT;
 227                rec->bad_block_count++;
 228        } else {
 229                /*
 230                 * An indirect block; fetch a block from the
 231                 * previously used indirect block list.  The block
 232                 * most be not marked as used; if so, get another one.
 233                 * If we run out of reserved indirect blocks, allocate
 234                 * a new one.
 235                 */
 236        retry:
 237                if (rec->ind_blocks_ptr < rec->ind_blocks_size) {
 238                        blk = rec->ind_blocks[rec->ind_blocks_ptr++];
 239                        if (ext2fs_test_block_bitmap(fs->block_map, blk))
 240                                goto retry;
 241                } else {
 242                        retval = ext2fs_new_block(fs, 0, 0, &blk);
 243                        if (retval) {
 244                                rec->err = retval;
 245                                return BLOCK_ABORT;
 246                        }
 247                }
 248                retval = io_channel_write_blk(fs->io, blk, 1, rec->block_buf);
 249                if (retval) {
 250                        rec->err = retval;
 251                        return BLOCK_ABORT;
 252                }
 253        }
 254
 255        /*
 256         * Update block counts
 257         */
 258        ext2fs_block_alloc_stats(fs, blk, +1);
 259
 260        *block_nr = blk;
 261        return BLOCK_CHANGED;
 262}
 263