busybox/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * bmap.c --- logical to physical block mapping
   4 *
   5 * Copyright (C) 1997 Theodore Ts'o.
   6 *
   7 * %Begin-Header%
   8 * This file may be redistributed under the terms of the GNU Public
   9 * License.
  10 * %End-Header%
  11 */
  12
  13#include <stdio.h>
  14#include <string.h>
  15#if HAVE_UNISTD_H
  16#include <unistd.h>
  17#endif
  18
  19#include "ext2_fs.h"
  20#include "ext2fs.h"
  21
  22extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino,
  23                             struct ext2_inode *inode,
  24                             char *block_buf, int bmap_flags,
  25                             blk_t block, blk_t *phys_blk);
  26
  27#define inode_bmap(inode, nr) ((inode)->i_block[(nr)])
  28
  29static errcode_t block_ind_bmap(ext2_filsys fs, int flags,
  30                                              blk_t ind, char *block_buf,
  31                                              int *blocks_alloc,
  32                                              blk_t nr, blk_t *ret_blk)
  33{
  34        errcode_t       retval;
  35        blk_t           b;
  36
  37        if (!ind) {
  38                if (flags & BMAP_SET)
  39                        return EXT2_ET_SET_BMAP_NO_IND;
  40                *ret_blk = 0;
  41                return 0;
  42        }
  43        retval = io_channel_read_blk(fs->io, ind, 1, block_buf);
  44        if (retval)
  45                return retval;
  46
  47        if (flags & BMAP_SET) {
  48                b = *ret_blk;
  49#if BB_BIG_ENDIAN
  50                if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
  51                    (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
  52                        b = ext2fs_swab32(b);
  53#endif
  54                ((blk_t *) block_buf)[nr] = b;
  55                return io_channel_write_blk(fs->io, ind, 1, block_buf);
  56        }
  57
  58        b = ((blk_t *) block_buf)[nr];
  59
  60#if BB_BIG_ENDIAN
  61        if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
  62            (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
  63                b = ext2fs_swab32(b);
  64#endif
  65
  66        if (!b && (flags & BMAP_ALLOC)) {
  67                b = nr ? ((blk_t *) block_buf)[nr-1] : 0;
  68                retval = ext2fs_alloc_block(fs, b,
  69                                            block_buf + fs->blocksize, &b);
  70                if (retval)
  71                        return retval;
  72
  73#if BB_BIG_ENDIAN
  74                if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
  75                    (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
  76                        ((blk_t *) block_buf)[nr] = ext2fs_swab32(b);
  77                else
  78#endif
  79                        ((blk_t *) block_buf)[nr] = b;
  80
  81                retval = io_channel_write_blk(fs->io, ind, 1, block_buf);
  82                if (retval)
  83                        return retval;
  84
  85                (*blocks_alloc)++;
  86        }
  87
  88        *ret_blk = b;
  89        return 0;
  90}
  91
  92static errcode_t block_dind_bmap(ext2_filsys fs, int flags,
  93                                               blk_t dind, char *block_buf,
  94                                               int *blocks_alloc,
  95                                               blk_t nr, blk_t *ret_blk)
  96{
  97        blk_t           b;
  98        errcode_t       retval;
  99        blk_t           addr_per_block;
 100
 101        addr_per_block = (blk_t) fs->blocksize >> 2;
 102
 103        retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf,
 104                                blocks_alloc, nr / addr_per_block, &b);
 105        if (retval)
 106                return retval;
 107        retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
 108                                nr % addr_per_block, ret_blk);
 109        return retval;
 110}
 111
 112static errcode_t block_tind_bmap(ext2_filsys fs, int flags,
 113                                               blk_t tind, char *block_buf,
 114                                               int *blocks_alloc,
 115                                               blk_t nr, blk_t *ret_blk)
 116{
 117        blk_t           b;
 118        errcode_t       retval;
 119        blk_t           addr_per_block;
 120
 121        addr_per_block = (blk_t) fs->blocksize >> 2;
 122
 123        retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf,
 124                                 blocks_alloc, nr / addr_per_block, &b);
 125        if (retval)
 126                return retval;
 127        retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
 128                                nr % addr_per_block, ret_blk);
 129        return retval;
 130}
 131
 132errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
 133                      char *block_buf, int bmap_flags, blk_t block,
 134                      blk_t *phys_blk)
 135{
 136        struct ext2_inode inode_buf;
 137        blk_t addr_per_block;
 138        blk_t   b;
 139        char    *buf = NULL;
 140        errcode_t       retval = 0;
 141        int             blocks_alloc = 0, inode_dirty = 0;
 142
 143        if (!(bmap_flags & BMAP_SET))
 144                *phys_blk = 0;
 145
 146        /* Read inode structure if necessary */
 147        if (!inode) {
 148                retval = ext2fs_read_inode(fs, ino, &inode_buf);
 149                if (retval)
 150                        return retval;
 151                inode = &inode_buf;
 152        }
 153        addr_per_block = (blk_t) fs->blocksize >> 2;
 154
 155        if (!block_buf) {
 156                retval = ext2fs_get_mem(fs->blocksize * 2, &buf);
 157                if (retval)
 158                        return retval;
 159                block_buf = buf;
 160        }
 161
 162        if (block < EXT2_NDIR_BLOCKS) {
 163                if (bmap_flags & BMAP_SET) {
 164                        b = *phys_blk;
 165#if BB_BIG_ENDIAN
 166                        if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
 167                            (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
 168                                b = ext2fs_swab32(b);
 169#endif
 170                        inode_bmap(inode, block) = b;
 171                        inode_dirty++;
 172                        goto done;
 173                }
 174
 175                *phys_blk = inode_bmap(inode, block);
 176                b = block ? inode_bmap(inode, block-1) : 0;
 177
 178                if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) {
 179                        retval = ext2fs_alloc_block(fs, b, block_buf, &b);
 180                        if (retval)
 181                                goto done;
 182                        inode_bmap(inode, block) = b;
 183                        blocks_alloc++;
 184                        *phys_blk = b;
 185                }
 186                goto done;
 187        }
 188
 189        /* Indirect block */
 190        block -= EXT2_NDIR_BLOCKS;
 191        if (block < addr_per_block) {
 192                b = inode_bmap(inode, EXT2_IND_BLOCK);
 193                if (!b) {
 194                        if (!(bmap_flags & BMAP_ALLOC)) {
 195                                if (bmap_flags & BMAP_SET)
 196                                        retval = EXT2_ET_SET_BMAP_NO_IND;
 197                                goto done;
 198                        }
 199
 200                        b = inode_bmap(inode, EXT2_IND_BLOCK-1);
 201                        retval = ext2fs_alloc_block(fs, b, block_buf, &b);
 202                        if (retval)
 203                                goto done;
 204                        inode_bmap(inode, EXT2_IND_BLOCK) = b;
 205                        blocks_alloc++;
 206                }
 207                retval = block_ind_bmap(fs, bmap_flags, b, block_buf,
 208                                        &blocks_alloc, block, phys_blk);
 209                goto done;
 210        }
 211
 212        /* Doubly indirect block  */
 213        block -= addr_per_block;
 214        if (block < addr_per_block * addr_per_block) {
 215                b = inode_bmap(inode, EXT2_DIND_BLOCK);
 216                if (!b) {
 217                        if (!(bmap_flags & BMAP_ALLOC)) {
 218                                if (bmap_flags & BMAP_SET)
 219                                        retval = EXT2_ET_SET_BMAP_NO_IND;
 220                                goto done;
 221                        }
 222
 223                        b = inode_bmap(inode, EXT2_IND_BLOCK);
 224                        retval = ext2fs_alloc_block(fs, b, block_buf, &b);
 225                        if (retval)
 226                                goto done;
 227                        inode_bmap(inode, EXT2_DIND_BLOCK) = b;
 228                        blocks_alloc++;
 229                }
 230                retval = block_dind_bmap(fs, bmap_flags, b, block_buf,
 231                                         &blocks_alloc, block, phys_blk);
 232                goto done;
 233        }
 234
 235        /* Triply indirect block */
 236        block -= addr_per_block * addr_per_block;
 237        b = inode_bmap(inode, EXT2_TIND_BLOCK);
 238        if (!b) {
 239                if (!(bmap_flags & BMAP_ALLOC)) {
 240                        if (bmap_flags & BMAP_SET)
 241                                retval = EXT2_ET_SET_BMAP_NO_IND;
 242                        goto done;
 243                }
 244
 245                b = inode_bmap(inode, EXT2_DIND_BLOCK);
 246                retval = ext2fs_alloc_block(fs, b, block_buf, &b);
 247                if (retval)
 248                        goto done;
 249                inode_bmap(inode, EXT2_TIND_BLOCK) = b;
 250                blocks_alloc++;
 251        }
 252        retval = block_tind_bmap(fs, bmap_flags, b, block_buf,
 253                                 &blocks_alloc, block, phys_blk);
 254done:
 255        ext2fs_free_mem(&buf);
 256        if ((retval == 0) && (blocks_alloc || inode_dirty)) {
 257                inode->i_blocks += (blocks_alloc * fs->blocksize) / 512;
 258                retval = ext2fs_write_inode(fs, ino, inode);
 259        }
 260        return retval;
 261}
 262