blob: ec60edf7d972afe1a58b7400cb58e421a0f69c70 [file] [log] [blame]
Theodore Ts'oc762c8e1997-06-17 03:52:12 +00001/*
2 * ext2_block_move.c --- ext2resizer block mover
3 *
4 * Copyright (C) 1997 Theodore Ts'o
5 *
6 * %Begin-Header%
7 * All rights reserved.
8 * %End-Header%
9 */
10
11#include "resize2fs.h"
12
13struct process_block_struct {
14 ino_t ino;
15 struct ext2_inode * inode;
16 ext2_extent bmap;
17 errcode_t error;
18 int is_dir;
19 int flags;
20};
21
22static int process_block(ext2_filsys fs, blk_t *block_nr,
23 int blockcnt, blk_t ref_block,
24 int ref_offset, void *private)
25{
26 struct process_block_struct *pb = private;
27 errcode_t retval;
28 blk_t block, new;
29 int ret = 0;
30
31 block = *block_nr;
32
33 new = ext2fs_extent_translate(pb->bmap, block);
34 if (new) {
35 *block_nr = new;
36 ret |= BLOCK_CHANGED;
37 if (pb->flags & RESIZE_DEBUG_BMOVE)
38 printf("ino=%ld, blockcnt=%d, %u->%u\n", pb->ino,
39 blockcnt, block, new);
40 }
41
42 if (pb->is_dir) {
43 retval = ext2fs_add_dir_block(fs->dblist, pb->ino,
44 *block_nr, blockcnt);
45 if (retval) {
46 pb->error = retval;
47 ret |= BLOCK_ABORT;
48 }
49 }
50
51 return ret;
52}
53
54errcode_t ext2fs_block_move(ext2_resize_t rfs)
55{
56 ext2_extent bmap;
57 blk_t blk, old, new;
58 ext2_filsys fs = rfs->new_fs;
59 ext2_filsys old_fs = rfs->old_fs;
60 ino_t ino;
61 struct ext2_inode inode;
62 errcode_t retval;
63 struct process_block_struct pb;
64 ext2_inode_scan scan = 0;
65 char *block_buf;
66 int size, c;
67 int to_move, moved;
68 ext2_sim_progmeter progress = 0;
69
70 new = fs->super->s_first_data_block;
71 if (!rfs->itable_buf) {
72 rfs->itable_buf = malloc(fs->blocksize *
73 fs->inode_blocks_per_group);
74 if (!rfs->itable_buf)
75 return ENOMEM;
76 }
77 retval = ext2fs_create_extent_table(&bmap, 0);
78 if (retval)
79 return retval;
80
81 /*
82 * The first step is to figure out where all of the blocks
83 * will go.
84 */
85 to_move = moved = 0;
86 for (blk = old_fs->super->s_first_data_block;
87 blk < old_fs->super->s_blocks_count; blk++) {
88 if (!ext2fs_test_block_bitmap(old_fs->block_map, blk))
89 continue;
90 if (!ext2fs_test_block_bitmap(rfs->move_blocks, blk))
91 continue;
92
93 while (1) {
94 if (new >= fs->super->s_blocks_count) {
95 retval = ENOSPC;
96 goto errout;
97 }
98 if (!ext2fs_test_block_bitmap(fs->block_map, new) &&
99 !ext2fs_test_block_bitmap(rfs->reserve_blocks,
100 new))
101 break;
102 new++;
103 }
104 ext2fs_mark_block_bitmap(fs->block_map, new);
105 ext2fs_add_extent_entry(bmap, blk, new);
106 to_move++;
107 }
108 if (to_move == 0)
109 return 0;
110 /*
111 * Step two is to actually move the blocks
112 */
113 retval = ext2fs_iterate_extent(bmap, 0, 0, 0);
114 if (retval) goto errout;
115
116 if (rfs->flags & RESIZE_PERCENT_COMPLETE) {
117 retval = ext2fs_progress_init(&progress,
118 "Relocating blocks", 30, 40, to_move, 0);
119 if (retval)
120 return retval;
121 }
122
123 while (1) {
124 retval = ext2fs_iterate_extent(bmap, &old, &new, &size);
125 if (retval) goto errout;
126 if (!size)
127 break;
128 if (rfs->flags & RESIZE_DEBUG_BMOVE)
129 printf("Moving %d blocks %u->%u\n", size,
130 old, new);
131 do {
132 c = size;
133 if (c > fs->inode_blocks_per_group)
134 c = fs->inode_blocks_per_group;
135 retval = io_channel_read_blk(fs->io, old, c,
136 rfs->itable_buf);
137 if (retval) goto errout;
138 retval = io_channel_write_blk(fs->io, new, c,
139 rfs->itable_buf);
140 if (retval) goto errout;
141 size -= c;
142 new += c;
143 old += c;
144 moved += c;
145 io_channel_flush(fs->io);
146 if (progress)
147 ext2fs_progress_update(progress, moved);
148 } while (size > 0);
149 io_channel_flush(fs->io);
150 }
151 if (progress) {
152 ext2fs_progress_close(progress);
153 progress = 0;
154 }
155
156 /*
157 * Step 3 is where we update the block pointers
158 */
159 retval = ext2fs_open_inode_scan(old_fs, 0, &scan);
160 if (retval) goto errout;
161
162 pb.error = 0;
163 pb.bmap = bmap;
164 pb.flags = rfs->flags;
165
166 block_buf = malloc(old_fs->blocksize * 3);
167 if (!block_buf) {
168 retval = ENOMEM;
169 goto errout;
170 }
171
172 /*
173 * We're going to initialize the dblist while we're at it.
174 */
175 if (old_fs->dblist) {
176 ext2fs_free_dblist(old_fs->dblist);
177 old_fs->dblist = NULL;
178 }
179 retval = ext2fs_init_dblist(old_fs, 0);
180 if (retval)
181 return retval;
182
183 retval = ext2fs_get_next_inode(scan, &ino, &inode);
184 if (retval) goto errout;
185
186 if (rfs->flags & RESIZE_PERCENT_COMPLETE) {
187 retval = ext2fs_progress_init(&progress,
188 "Updating block references", 30, 40,
189 old_fs->super->s_inodes_count, 0);
190 if (retval)
191 return retval;
192 }
193
194 while (ino) {
195 if ((inode.i_links_count == 0) ||
196 !ext2fs_inode_has_valid_blocks(&inode))
197 goto next;
198
199 pb.ino = ino;
200 pb.inode = &inode;
201
202 pb.is_dir = LINUX_S_ISDIR(inode.i_mode);
203
204 retval = ext2fs_block_iterate2(old_fs, ino, 0, block_buf,
205 process_block, &pb);
206 if (retval)
207 goto errout;
208 if (pb.error) {
209 retval = pb.error;
210 goto errout;
211 }
212
213 next:
214 if (progress)
215 ext2fs_progress_update(progress, ino);
216 retval = ext2fs_get_next_inode(scan, &ino, &inode);
217 if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
218 goto next;
219 }
220 retval = 0;
221errout:
222 if (progress)
223 ext2fs_progress_close(progress);
224
225 ext2fs_free_extent_table(bmap);
226 if (scan)
227 ext2fs_close_inode_scan(scan);
228 return retval;
229}
230