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
21struct 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 */
34static 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 */
80static 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
110static 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 = NULL, *block_buf = NULL;
117	errcode_t		retval;
118	struct ext2_inode *	inode;
119
120	e2fsck_use_inode_shortcuts(ctx, 1);
121
122	retval = ext2fs_get_array(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		goto errout;
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			goto errout;
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				goto errout;
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			goto errout;
180		}
181	}
182errout:
183	if (buf)
184		ext2fs_free_mem(&buf);
185	if (block_buf)
186		ext2fs_free_mem(&block_buf);
187	e2fsck_use_inode_shortcuts(ctx, 0);
188	ext2fs_flush_icache(fs);
189}
190
191#if defined(__powerpc__) && defined(EXT2FS_ENABLE_SWAPFS)
192/*
193 * On the PowerPC, the big-endian variant of the ext2 filesystem
194 * has its bitmaps stored as 32-bit words with bit 0 as the LSB
195 * of each word.  Thus a bitmap with only bit 0 set would be, as
196 * a string of bytes, 00 00 00 01 00 ...
197 * To cope with this, we byte-reverse each word of a bitmap if
198 * we have a big-endian filesystem, that is, if we are *not*
199 * byte-swapping other word-sized numbers.
200 */
201#define EXT2_BIG_ENDIAN_BITMAPS
202#endif
203
204#ifdef EXT2_BIG_ENDIAN_BITMAPS
205static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap)
206{
207	__u32 *p = (__u32 *) bmap->bitmap;
208	int n, nbytes = (bmap->end - bmap->start + 7) / 8;
209
210	for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
211		*p = ext2fs_swab32(*p);
212}
213#endif
214
215
216void swap_filesys(e2fsck_t ctx)
217{
218	ext2_filsys fs = ctx->fs;
219#ifdef RESOURCE_TRACK
220	struct resource_track	rtrack;
221
222	init_resource_track(&rtrack);
223#endif
224
225	if (!(ctx->options & E2F_OPT_PREEN))
226		printf(_("Pass 0: Doing byte-swap of filesystem\n"));
227
228#ifdef MTRACE
229	mtrace_print("Byte swap");
230#endif
231
232	if (fs->super->s_mnt_count) {
233		fprintf(stderr, _("%s: the filesystem must be freshly "
234			"checked using fsck\n"
235			"and not mounted before trying to "
236			"byte-swap it.\n"), ctx->device_name);
237		ctx->flags |= E2F_FLAG_ABORT;
238		return;
239	}
240	if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
241		fs->flags &= ~(EXT2_FLAG_SWAP_BYTES|
242			       EXT2_FLAG_SWAP_BYTES_WRITE);
243		fs->flags |= EXT2_FLAG_SWAP_BYTES_READ;
244	} else {
245		fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ;
246		fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE;
247	}
248	swap_inodes(ctx);
249	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
250		return;
251	if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
252		fs->flags |= EXT2_FLAG_SWAP_BYTES;
253	fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ|
254		       EXT2_FLAG_SWAP_BYTES_WRITE);
255
256#ifdef EXT2_BIG_ENDIAN_BITMAPS
257	e2fsck_read_bitmaps(ctx);
258	ext2fs_swap_bitmap(fs->inode_map);
259	ext2fs_swap_bitmap(fs->block_map);
260	fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
261#endif
262	fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
263	ext2fs_flush(fs);
264	fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
265
266#ifdef RESOURCE_TRACK
267	if (ctx->options & E2F_OPT_TIME2)
268		print_resource_track(_("Byte swap"), &rtrack);
269#endif
270}
271
272#endif
273