| /* vi: set sw=4 ts=4: */ | 
 | /* | 
 |  * bmove.c --- Move blocks around to make way for a particular | 
 |  *	filesystem structure. | 
 |  * | 
 |  * Copyright (C) 1997 Theodore Ts'o.  This file may be redistributed | 
 |  * under the terms of the GNU Public License. | 
 |  */ | 
 |  | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 | #if HAVE_UNISTD_H | 
 | #include <unistd.h> | 
 | #endif | 
 | #if HAVE_SYS_TYPES_H | 
 | #include <sys/types.h> | 
 | #endif | 
 |  | 
 | #include "ext2_fs.h" | 
 | #include "ext2fsP.h" | 
 |  | 
 | struct process_block_struct { | 
 | 	ext2_ino_t		ino; | 
 | 	struct ext2_inode *	inode; | 
 | 	ext2fs_block_bitmap	reserve; | 
 | 	ext2fs_block_bitmap	alloc_map; | 
 | 	errcode_t		error; | 
 | 	char			*buf; | 
 | 	int			add_dir; | 
 | 	int			flags; | 
 | }; | 
 |  | 
 | static int process_block(ext2_filsys fs, blk_t	*block_nr, | 
 | 			 e2_blkcnt_t blockcnt, blk_t ref_block, | 
 | 			 int ref_offset, void *priv_data) | 
 | { | 
 | 	struct process_block_struct *pb; | 
 | 	errcode_t	retval; | 
 | 	int		ret; | 
 | 	blk_t		block, orig; | 
 |  | 
 | 	pb = (struct process_block_struct *) priv_data; | 
 | 	block = orig = *block_nr; | 
 | 	ret = 0; | 
 |  | 
 | 	/* | 
 | 	 * Let's see if this is one which we need to relocate | 
 | 	 */ | 
 | 	if (ext2fs_test_block_bitmap(pb->reserve, block)) { | 
 | 		do { | 
 | 			if (++block >= fs->super->s_blocks_count) | 
 | 				block = fs->super->s_first_data_block; | 
 | 			if (block == orig) { | 
 | 				pb->error = EXT2_ET_BLOCK_ALLOC_FAIL; | 
 | 				return BLOCK_ABORT; | 
 | 			} | 
 | 		} while (ext2fs_test_block_bitmap(pb->reserve, block) || | 
 | 			 ext2fs_test_block_bitmap(pb->alloc_map, block)); | 
 |  | 
 | 		retval = io_channel_read_blk(fs->io, orig, 1, pb->buf); | 
 | 		if (retval) { | 
 | 			pb->error = retval; | 
 | 			return BLOCK_ABORT; | 
 | 		} | 
 | 		retval = io_channel_write_blk(fs->io, block, 1, pb->buf); | 
 | 		if (retval) { | 
 | 			pb->error = retval; | 
 | 			return BLOCK_ABORT; | 
 | 		} | 
 | 		*block_nr = block; | 
 | 		ext2fs_mark_block_bitmap(pb->alloc_map, block); | 
 | 		ret = BLOCK_CHANGED; | 
 | 		if (pb->flags & EXT2_BMOVE_DEBUG) | 
 | 			printf("ino=%ld, blockcnt=%lld, %d->%d\n", pb->ino, | 
 | 			       blockcnt, orig, block); | 
 | 	} | 
 | 	if (pb->add_dir) { | 
 | 		retval = ext2fs_add_dir_block(fs->dblist, pb->ino, | 
 | 					      block, (int) blockcnt); | 
 | 		if (retval) { | 
 | 			pb->error = retval; | 
 | 			ret |= BLOCK_ABORT; | 
 | 		} | 
 | 	} | 
 | 	return ret; | 
 | } | 
 |  | 
 | errcode_t ext2fs_move_blocks(ext2_filsys fs, | 
 | 			     ext2fs_block_bitmap reserve, | 
 | 			     ext2fs_block_bitmap alloc_map, | 
 | 			     int flags) | 
 | { | 
 | 	ext2_ino_t	ino; | 
 | 	struct ext2_inode inode; | 
 | 	errcode_t	retval; | 
 | 	struct process_block_struct pb; | 
 | 	ext2_inode_scan	scan; | 
 | 	char		*block_buf; | 
 |  | 
 | 	retval = ext2fs_open_inode_scan(fs, 0, &scan); | 
 | 	if (retval) | 
 | 		return retval; | 
 |  | 
 | 	pb.reserve = reserve; | 
 | 	pb.error = 0; | 
 | 	pb.alloc_map = alloc_map ? alloc_map : fs->block_map; | 
 | 	pb.flags = flags; | 
 |  | 
 | 	retval = ext2fs_get_mem(fs->blocksize * 4, &block_buf); | 
 | 	if (retval) | 
 | 		return retval; | 
 | 	pb.buf = block_buf + fs->blocksize * 3; | 
 |  | 
 | 	/* | 
 | 	 * If GET_DBLIST is set in the flags field, then we should | 
 | 	 * gather directory block information while we're doing the | 
 | 	 * block move. | 
 | 	 */ | 
 | 	if (flags & EXT2_BMOVE_GET_DBLIST) { | 
 | 		ext2fs_free_dblist(fs->dblist); | 
 | 		fs->dblist = NULL; | 
 | 		retval = ext2fs_init_dblist(fs, 0); | 
 | 		if (retval) | 
 | 			return retval; | 
 | 	} | 
 |  | 
 | 	retval = ext2fs_get_next_inode(scan, &ino, &inode); | 
 | 	if (retval) | 
 | 		return retval; | 
 |  | 
 | 	while (ino) { | 
 | 		if ((inode.i_links_count == 0) || | 
 | 		    !ext2fs_inode_has_valid_blocks(&inode)) | 
 | 			goto next; | 
 |  | 
 | 		pb.ino = ino; | 
 | 		pb.inode = &inode; | 
 |  | 
 | 		pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) && | 
 | 			      flags & EXT2_BMOVE_GET_DBLIST); | 
 |  | 
 | 		retval = ext2fs_block_iterate2(fs, ino, 0, block_buf, | 
 | 					      process_block, &pb); | 
 | 		if (retval) | 
 | 			return retval; | 
 | 		if (pb.error) | 
 | 			return pb.error; | 
 |  | 
 | 	next: | 
 | 		retval = ext2fs_get_next_inode(scan, &ino, &inode); | 
 | 		if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) | 
 | 			goto next; | 
 | 	} | 
 | 	return 0; | 
 | } |