/* * swapfs.c --- byte-swap an ext2 filesystem * * Copyright 1996, 1997 by Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% * */ #ifdef HAVE_ERRNO_H #include <errno.h> #endif #include <et/com_err.h> #include "e2fsck.h" #ifdef ENABLE_SWAPFS struct swap_block_struct { ext2_ino_t ino; int isdir; errcode_t errcode; char *dir_buf; struct ext2_inode *inode; }; /* * This is a helper function for block_iterate. We mark all of the * indirect and direct blocks as changed, so that block_iterate will * write them out. */ static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt, void *priv_data) { errcode_t retval; struct swap_block_struct *sb = (struct swap_block_struct *) priv_data; if (sb->isdir && (blockcnt >= 0) && *block_nr) { retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf); if (retval) { sb->errcode = retval; return BLOCK_ABORT; } retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf); if (retval) { sb->errcode = retval; return BLOCK_ABORT; } } if (blockcnt >= 0) { if (blockcnt < EXT2_NDIR_BLOCKS) return 0; return BLOCK_CHANGED; } if (blockcnt == BLOCK_COUNT_IND) { if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK]) return 0; return BLOCK_CHANGED; } if (blockcnt == BLOCK_COUNT_DIND) { if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK]) return 0; return BLOCK_CHANGED; } if (blockcnt == BLOCK_COUNT_TIND) { if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK]) return 0; return BLOCK_CHANGED; } return BLOCK_CHANGED; } /* * This function is responsible for byte-swapping all of the indirect, * block pointers. It is also responsible for byte-swapping directories. */ static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf, struct ext2_inode *inode) { errcode_t retval; struct swap_block_struct sb; sb.ino = ino; sb.inode = inode; sb.dir_buf = block_buf + ctx->fs->blocksize*3; sb.errcode = 0; sb.isdir = 0; if (LINUX_S_ISDIR(inode->i_mode)) sb.isdir = 1; retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf, swap_block, &sb); if (retval) { com_err("swap_inode_blocks", retval, _("while calling ext2fs_block_iterate")); ctx->flags |= E2F_FLAG_ABORT; return; } if (sb.errcode) { com_err("swap_inode_blocks", sb.errcode, _("while calling iterator function")); ctx->flags |= E2F_FLAG_ABORT; return; } } static void swap_inodes(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; dgrp_t group; unsigned int i; ext2_ino_t ino = 1; char *buf = NULL, *block_buf = NULL; errcode_t retval; struct ext2_inode * inode; e2fsck_use_inode_shortcuts(ctx, 1); retval = ext2fs_get_array(fs->blocksize, fs->inode_blocks_per_group, &buf); if (retval) { com_err("swap_inodes", retval, _("while allocating inode buffer")); ctx->flags |= E2F_FLAG_ABORT; goto errout; } block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4, "block interate buffer"); for (group = 0; group < fs->group_desc_count; group++) { retval = io_channel_read_blk(fs->io, fs->group_desc[group].bg_inode_table, fs->inode_blocks_per_group, buf); if (retval) { com_err("swap_inodes", retval, _("while reading inode table (group %d)"), group); ctx->flags |= E2F_FLAG_ABORT; goto errout; } inode = (struct ext2_inode *) buf; for (i=0; i < fs->super->s_inodes_per_group; i++, ino++, inode++) { ctx->stashed_ino = ino; ctx->stashed_inode = inode; if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ) ext2fs_swap_inode(fs, inode, inode, 0); /* * Skip deleted files. */ if (inode->i_links_count == 0) continue; if (LINUX_S_ISDIR(inode->i_mode) || ((inode->i_block[EXT2_IND_BLOCK] || inode->i_block[EXT2_DIND_BLOCK] || inode->i_block[EXT2_TIND_BLOCK]) && ext2fs_inode_has_valid_blocks(inode))) swap_inode_blocks(ctx, ino, block_buf, inode); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) goto errout; if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE) ext2fs_swap_inode(fs, inode, inode, 1); } retval = io_channel_write_blk(fs->io, fs->group_desc[group].bg_inode_table, fs->inode_blocks_per_group, buf); if (retval) { com_err("swap_inodes", retval, _("while writing inode table (group %d)"), group); ctx->flags |= E2F_FLAG_ABORT; goto errout; } } errout: if (buf) ext2fs_free_mem(&buf); if (block_buf) ext2fs_free_mem(&block_buf); e2fsck_use_inode_shortcuts(ctx, 0); ext2fs_flush_icache(fs); } #if defined(__powerpc__) && defined(EXT2FS_ENABLE_SWAPFS) /* * On the PowerPC, the big-endian variant of the ext2 filesystem * has its bitmaps stored as 32-bit words with bit 0 as the LSB * of each word. Thus a bitmap with only bit 0 set would be, as * a string of bytes, 00 00 00 01 00 ... * To cope with this, we byte-reverse each word of a bitmap if * we have a big-endian filesystem, that is, if we are *not* * byte-swapping other word-sized numbers. */ #define EXT2_BIG_ENDIAN_BITMAPS #endif #ifdef EXT2_BIG_ENDIAN_BITMAPS static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap) { __u32 *p = (__u32 *) bmap->bitmap; int n, nbytes = (bmap->end - bmap->start + 7) / 8; for (n = nbytes / sizeof(__u32); n > 0; --n, ++p) *p = ext2fs_swab32(*p); } #endif void swap_filesys(e2fsck_t ctx) { ext2_filsys fs = ctx->fs; #ifdef RESOURCE_TRACK struct resource_track rtrack; init_resource_track(&rtrack); #endif if (!(ctx->options & E2F_OPT_PREEN)) printf(_("Pass 0: Doing byte-swap of filesystem\n")); #ifdef MTRACE mtrace_print("Byte swap"); #endif if (fs->super->s_mnt_count) { fprintf(stderr, _("%s: the filesystem must be freshly " "checked using fsck\n" "and not mounted before trying to " "byte-swap it.\n"), ctx->device_name); ctx->flags |= E2F_FLAG_ABORT; return; } if (fs->flags & EXT2_FLAG_SWAP_BYTES) { fs->flags &= ~(EXT2_FLAG_SWAP_BYTES| EXT2_FLAG_SWAP_BYTES_WRITE); fs->flags |= EXT2_FLAG_SWAP_BYTES_READ; } else { fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ; fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE; } swap_inodes(ctx); if (ctx->flags & E2F_FLAG_SIGNAL_MASK) return; if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE) fs->flags |= EXT2_FLAG_SWAP_BYTES; fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ| EXT2_FLAG_SWAP_BYTES_WRITE); #ifdef EXT2_BIG_ENDIAN_BITMAPS e2fsck_read_bitmaps(ctx); ext2fs_swap_bitmap(fs->inode_map); ext2fs_swap_bitmap(fs->block_map); fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY; #endif fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; ext2fs_flush(fs); fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; #ifdef RESOURCE_TRACK if (ctx->options & E2F_OPT_TIME2) print_resource_track(_("Byte swap"), &rtrack); #endif } #endif