Mike Frysinger | 51a43b4 | 2005-09-24 07:11:16 +0000 | [diff] [blame] | 1 | /* |
| 2 | * swapfs.c --- byte-swap an ext2 filesystem |
| 3 | * |
| 4 | * Copyright 1996, 1997 by Theodore Ts'o |
| 5 | * |
| 6 | * %Begin-Header% |
| 7 | * This file may be redistributed under the terms of the GNU Public |
| 8 | * License. |
| 9 | * %End-Header% |
| 10 | * |
| 11 | */ |
| 12 | |
| 13 | #ifdef HAVE_ERRNO_H |
| 14 | #include <errno.h> |
| 15 | #endif |
| 16 | #include <et/com_err.h> |
| 17 | #include "e2fsck.h" |
| 18 | |
| 19 | #ifdef ENABLE_SWAPFS |
| 20 | |
| 21 | struct swap_block_struct { |
| 22 | ext2_ino_t ino; |
| 23 | int isdir; |
| 24 | errcode_t errcode; |
| 25 | char *dir_buf; |
| 26 | struct ext2_inode *inode; |
| 27 | }; |
| 28 | |
| 29 | /* |
| 30 | * This is a helper function for block_iterate. We mark all of the |
| 31 | * indirect and direct blocks as changed, so that block_iterate will |
| 32 | * write them out. |
| 33 | */ |
| 34 | static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt, |
| 35 | void *priv_data) |
| 36 | { |
| 37 | errcode_t retval; |
| 38 | |
| 39 | struct swap_block_struct *sb = (struct swap_block_struct *) priv_data; |
| 40 | |
| 41 | if (sb->isdir && (blockcnt >= 0) && *block_nr) { |
| 42 | retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf); |
| 43 | if (retval) { |
| 44 | sb->errcode = retval; |
| 45 | return BLOCK_ABORT; |
| 46 | } |
| 47 | retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf); |
| 48 | if (retval) { |
| 49 | sb->errcode = retval; |
| 50 | return BLOCK_ABORT; |
| 51 | } |
| 52 | } |
| 53 | if (blockcnt >= 0) { |
| 54 | if (blockcnt < EXT2_NDIR_BLOCKS) |
| 55 | return 0; |
| 56 | return BLOCK_CHANGED; |
| 57 | } |
| 58 | if (blockcnt == BLOCK_COUNT_IND) { |
| 59 | if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK]) |
| 60 | return 0; |
| 61 | return BLOCK_CHANGED; |
| 62 | } |
| 63 | if (blockcnt == BLOCK_COUNT_DIND) { |
| 64 | if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK]) |
| 65 | return 0; |
| 66 | return BLOCK_CHANGED; |
| 67 | } |
| 68 | if (blockcnt == BLOCK_COUNT_TIND) { |
| 69 | if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK]) |
| 70 | return 0; |
| 71 | return BLOCK_CHANGED; |
| 72 | } |
| 73 | return BLOCK_CHANGED; |
| 74 | } |
| 75 | |
| 76 | /* |
| 77 | * This function is responsible for byte-swapping all of the indirect, |
| 78 | * block pointers. It is also responsible for byte-swapping directories. |
| 79 | */ |
| 80 | static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf, |
| 81 | struct ext2_inode *inode) |
| 82 | { |
| 83 | errcode_t retval; |
| 84 | struct swap_block_struct sb; |
| 85 | |
| 86 | sb.ino = ino; |
| 87 | sb.inode = inode; |
| 88 | sb.dir_buf = block_buf + ctx->fs->blocksize*3; |
| 89 | sb.errcode = 0; |
| 90 | sb.isdir = 0; |
| 91 | if (LINUX_S_ISDIR(inode->i_mode)) |
| 92 | sb.isdir = 1; |
| 93 | |
| 94 | retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf, |
| 95 | swap_block, &sb); |
| 96 | if (retval) { |
| 97 | com_err("swap_inode_blocks", retval, |
| 98 | _("while calling ext2fs_block_iterate")); |
| 99 | ctx->flags |= E2F_FLAG_ABORT; |
| 100 | return; |
| 101 | } |
| 102 | if (sb.errcode) { |
| 103 | com_err("swap_inode_blocks", sb.errcode, |
| 104 | _("while calling iterator function")); |
| 105 | ctx->flags |= E2F_FLAG_ABORT; |
| 106 | return; |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | static void swap_inodes(e2fsck_t ctx) |
| 111 | { |
| 112 | ext2_filsys fs = ctx->fs; |
| 113 | dgrp_t group; |
| 114 | unsigned int i; |
| 115 | ext2_ino_t ino = 1; |
| 116 | char *buf, *block_buf; |
| 117 | errcode_t retval; |
| 118 | struct ext2_inode * inode; |
| 119 | |
| 120 | e2fsck_use_inode_shortcuts(ctx, 1); |
| 121 | |
| 122 | retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group, |
| 123 | &buf); |
| 124 | if (retval) { |
| 125 | com_err("swap_inodes", retval, |
| 126 | _("while allocating inode buffer")); |
| 127 | ctx->flags |= E2F_FLAG_ABORT; |
| 128 | return; |
| 129 | } |
| 130 | block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4, |
| 131 | "block interate buffer"); |
| 132 | for (group = 0; group < fs->group_desc_count; group++) { |
| 133 | retval = io_channel_read_blk(fs->io, |
| 134 | fs->group_desc[group].bg_inode_table, |
| 135 | fs->inode_blocks_per_group, buf); |
| 136 | if (retval) { |
| 137 | com_err("swap_inodes", retval, |
| 138 | _("while reading inode table (group %d)"), |
| 139 | group); |
| 140 | ctx->flags |= E2F_FLAG_ABORT; |
| 141 | return; |
| 142 | } |
| 143 | inode = (struct ext2_inode *) buf; |
| 144 | for (i=0; i < fs->super->s_inodes_per_group; |
| 145 | i++, ino++, inode++) { |
| 146 | ctx->stashed_ino = ino; |
| 147 | ctx->stashed_inode = inode; |
| 148 | |
| 149 | if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ) |
| 150 | ext2fs_swap_inode(fs, inode, inode, 0); |
| 151 | |
| 152 | /* |
| 153 | * Skip deleted files. |
| 154 | */ |
| 155 | if (inode->i_links_count == 0) |
| 156 | continue; |
| 157 | |
| 158 | if (LINUX_S_ISDIR(inode->i_mode) || |
| 159 | ((inode->i_block[EXT2_IND_BLOCK] || |
| 160 | inode->i_block[EXT2_DIND_BLOCK] || |
| 161 | inode->i_block[EXT2_TIND_BLOCK]) && |
| 162 | ext2fs_inode_has_valid_blocks(inode))) |
| 163 | swap_inode_blocks(ctx, ino, block_buf, inode); |
| 164 | |
| 165 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
| 166 | return; |
| 167 | |
| 168 | if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE) |
| 169 | ext2fs_swap_inode(fs, inode, inode, 1); |
| 170 | } |
| 171 | retval = io_channel_write_blk(fs->io, |
| 172 | fs->group_desc[group].bg_inode_table, |
| 173 | fs->inode_blocks_per_group, buf); |
| 174 | if (retval) { |
| 175 | com_err("swap_inodes", retval, |
| 176 | _("while writing inode table (group %d)"), |
| 177 | group); |
| 178 | ctx->flags |= E2F_FLAG_ABORT; |
| 179 | return; |
| 180 | } |
| 181 | } |
| 182 | ext2fs_free_mem(&buf); |
| 183 | ext2fs_free_mem(&block_buf); |
| 184 | e2fsck_use_inode_shortcuts(ctx, 0); |
| 185 | ext2fs_flush_icache(fs); |
| 186 | } |
| 187 | |
| 188 | #if defined(__powerpc__) && defined(EXT2FS_ENABLE_SWAPFS) |
| 189 | /* |
| 190 | * On the PowerPC, the big-endian variant of the ext2 filesystem |
| 191 | * has its bitmaps stored as 32-bit words with bit 0 as the LSB |
| 192 | * of each word. Thus a bitmap with only bit 0 set would be, as |
| 193 | * a string of bytes, 00 00 00 01 00 ... |
| 194 | * To cope with this, we byte-reverse each word of a bitmap if |
| 195 | * we have a big-endian filesystem, that is, if we are *not* |
| 196 | * byte-swapping other word-sized numbers. |
| 197 | */ |
| 198 | #define EXT2_BIG_ENDIAN_BITMAPS |
| 199 | #endif |
| 200 | |
| 201 | #ifdef EXT2_BIG_ENDIAN_BITMAPS |
| 202 | static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap) |
| 203 | { |
| 204 | __u32 *p = (__u32 *) bmap->bitmap; |
| 205 | int n, nbytes = (bmap->end - bmap->start + 7) / 8; |
| 206 | |
| 207 | for (n = nbytes / sizeof(__u32); n > 0; --n, ++p) |
| 208 | *p = ext2fs_swab32(*p); |
| 209 | } |
| 210 | #endif |
| 211 | |
| 212 | |
| 213 | void swap_filesys(e2fsck_t ctx) |
| 214 | { |
| 215 | ext2_filsys fs = ctx->fs; |
| 216 | #ifdef RESOURCE_TRACK |
| 217 | struct resource_track rtrack; |
| 218 | |
| 219 | init_resource_track(&rtrack); |
| 220 | #endif |
| 221 | |
| 222 | if (!(ctx->options & E2F_OPT_PREEN)) |
| 223 | printf(_("Pass 0: Doing byte-swap of filesystem\n")); |
| 224 | |
| 225 | #ifdef MTRACE |
| 226 | mtrace_print("Byte swap"); |
| 227 | #endif |
| 228 | |
| 229 | if (fs->super->s_mnt_count) { |
| 230 | fprintf(stderr, _("%s: the filesystem must be freshly " |
| 231 | "checked using fsck\n" |
| 232 | "and not mounted before trying to " |
| 233 | "byte-swap it.\n"), ctx->device_name); |
| 234 | ctx->flags |= E2F_FLAG_ABORT; |
| 235 | return; |
| 236 | } |
| 237 | if (fs->flags & EXT2_FLAG_SWAP_BYTES) { |
| 238 | fs->flags &= ~(EXT2_FLAG_SWAP_BYTES| |
| 239 | EXT2_FLAG_SWAP_BYTES_WRITE); |
| 240 | fs->flags |= EXT2_FLAG_SWAP_BYTES_READ; |
| 241 | } else { |
| 242 | fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ; |
| 243 | fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE; |
| 244 | } |
| 245 | swap_inodes(ctx); |
| 246 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) |
| 247 | return; |
| 248 | if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE) |
| 249 | fs->flags |= EXT2_FLAG_SWAP_BYTES; |
| 250 | fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ| |
| 251 | EXT2_FLAG_SWAP_BYTES_WRITE); |
| 252 | |
| 253 | #ifdef EXT2_BIG_ENDIAN_BITMAPS |
| 254 | e2fsck_read_bitmaps(ctx); |
| 255 | ext2fs_swap_bitmap(fs->inode_map); |
| 256 | ext2fs_swap_bitmap(fs->block_map); |
| 257 | fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY; |
| 258 | #endif |
| 259 | fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; |
| 260 | ext2fs_flush(fs); |
| 261 | fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; |
| 262 | |
| 263 | #ifdef RESOURCE_TRACK |
| 264 | if (ctx->options & E2F_OPT_TIME2) |
| 265 | print_resource_track(_("Byte swap"), &rtrack); |
| 266 | #endif |
| 267 | } |
| 268 | |
| 269 | #endif |