blob: 0f9cfcfafb5a32d441d26e07212c30ebda65ed3b
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * image.c --- writes out the critical parts of the filesystem as a |
4 | * flat file. |
5 | * |
6 | * Copyright (C) 2000 Theodore Ts'o. |
7 | * |
8 | * Note: this uses the POSIX IO interfaces, unlike most of the other |
9 | * functions in this library. So sue me. |
10 | * |
11 | * %Begin-Header% |
12 | * This file may be redistributed under the terms of the GNU Public |
13 | * License. |
14 | * %End-Header% |
15 | */ |
16 | |
17 | #include <stdio.h> |
18 | #include <string.h> |
19 | #if HAVE_UNISTD_H |
20 | #include <unistd.h> |
21 | #endif |
22 | #if HAVE_ERRNO_H |
23 | #include <errno.h> |
24 | #endif |
25 | #include <fcntl.h> |
26 | #include <time.h> |
27 | #if HAVE_SYS_STAT_H |
28 | #include <sys/stat.h> |
29 | #endif |
30 | #if HAVE_SYS_TYPES_H |
31 | #include <sys/types.h> |
32 | #endif |
33 | |
34 | #include "ext2_fs.h" |
35 | #include "ext2fs.h" |
36 | |
37 | #ifndef HAVE_TYPE_SSIZE_T |
38 | typedef int ssize_t; |
39 | #endif |
40 | |
41 | /* |
42 | * This function returns 1 if the specified block is all zeros |
43 | */ |
44 | static int check_zero_block(char *buf, int blocksize) |
45 | { |
46 | char *cp = buf; |
47 | int left = blocksize; |
48 | |
49 | while (left > 0) { |
50 | if (*cp++) |
51 | return 0; |
52 | left--; |
53 | } |
54 | return 1; |
55 | } |
56 | |
57 | /* |
58 | * Write the inode table out as a single block. |
59 | */ |
60 | #define BUF_BLOCKS 32 |
61 | |
62 | errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags) |
63 | { |
64 | unsigned int group, left, c, d; |
65 | char *buf, *cp; |
66 | blk_t blk; |
67 | ssize_t actual; |
68 | errcode_t retval; |
69 | |
70 | buf = xmalloc(fs->blocksize * BUF_BLOCKS); |
71 | |
72 | for (group = 0; group < fs->group_desc_count; group++) { |
73 | blk = fs->group_desc[(unsigned)group].bg_inode_table; |
74 | if (!blk) |
75 | return EXT2_ET_MISSING_INODE_TABLE; |
76 | left = fs->inode_blocks_per_group; |
77 | while (left) { |
78 | c = BUF_BLOCKS; |
79 | if (c > left) |
80 | c = left; |
81 | retval = io_channel_read_blk(fs->io, blk, c, buf); |
82 | if (retval) |
83 | goto errout; |
84 | cp = buf; |
85 | while (c) { |
86 | if (!(flags & IMAGER_FLAG_SPARSEWRITE)) { |
87 | d = c; |
88 | goto skip_sparse; |
89 | } |
90 | /* Skip zero blocks */ |
91 | if (check_zero_block(cp, fs->blocksize)) { |
92 | c--; |
93 | blk++; |
94 | left--; |
95 | cp += fs->blocksize; |
96 | lseek(fd, fs->blocksize, SEEK_CUR); |
97 | continue; |
98 | } |
99 | /* Find non-zero blocks */ |
100 | for (d=1; d < c; d++) { |
101 | if (check_zero_block(cp + d*fs->blocksize, fs->blocksize)) |
102 | break; |
103 | } |
104 | skip_sparse: |
105 | actual = write(fd, cp, fs->blocksize * d); |
106 | if (actual == -1) { |
107 | retval = errno; |
108 | goto errout; |
109 | } |
110 | if (actual != (ssize_t) (fs->blocksize * d)) { |
111 | retval = EXT2_ET_SHORT_WRITE; |
112 | goto errout; |
113 | } |
114 | blk += d; |
115 | left -= d; |
116 | cp += fs->blocksize * d; |
117 | c -= d; |
118 | } |
119 | } |
120 | } |
121 | retval = 0; |
122 | |
123 | errout: |
124 | free(buf); |
125 | return retval; |
126 | } |
127 | |
128 | /* |
129 | * Read in the inode table and stuff it into place |
130 | */ |
131 | errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, |
132 | int flags EXT2FS_ATTR((unused))) |
133 | { |
134 | unsigned int group, c, left; |
135 | char *buf; |
136 | blk_t blk; |
137 | ssize_t actual; |
138 | errcode_t retval; |
139 | |
140 | buf = xmalloc(fs->blocksize * BUF_BLOCKS); |
141 | |
142 | for (group = 0; group < fs->group_desc_count; group++) { |
143 | blk = fs->group_desc[(unsigned)group].bg_inode_table; |
144 | if (!blk) { |
145 | retval = EXT2_ET_MISSING_INODE_TABLE; |
146 | goto errout; |
147 | } |
148 | left = fs->inode_blocks_per_group; |
149 | while (left) { |
150 | c = BUF_BLOCKS; |
151 | if (c > left) |
152 | c = left; |
153 | actual = read(fd, buf, fs->blocksize * c); |
154 | if (actual == -1) { |
155 | retval = errno; |
156 | goto errout; |
157 | } |
158 | if (actual != (ssize_t) (fs->blocksize * c)) { |
159 | retval = EXT2_ET_SHORT_READ; |
160 | goto errout; |
161 | } |
162 | retval = io_channel_write_blk(fs->io, blk, c, buf); |
163 | if (retval) |
164 | goto errout; |
165 | |
166 | blk += c; |
167 | left -= c; |
168 | } |
169 | } |
170 | retval = ext2fs_flush_icache(fs); |
171 | |
172 | errout: |
173 | free(buf); |
174 | return retval; |
175 | } |
176 | |
177 | /* |
178 | * Write out superblock and group descriptors |
179 | */ |
180 | errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, |
181 | int flags EXT2FS_ATTR((unused))) |
182 | { |
183 | char *buf, *cp; |
184 | ssize_t actual; |
185 | errcode_t retval; |
186 | |
187 | buf = xmalloc(fs->blocksize); |
188 | |
189 | /* |
190 | * Write out the superblock |
191 | */ |
192 | memset(buf, 0, fs->blocksize); |
193 | memcpy(buf, fs->super, SUPERBLOCK_SIZE); |
194 | actual = write(fd, buf, fs->blocksize); |
195 | if (actual == -1) { |
196 | retval = errno; |
197 | goto errout; |
198 | } |
199 | if (actual != (ssize_t) fs->blocksize) { |
200 | retval = EXT2_ET_SHORT_WRITE; |
201 | goto errout; |
202 | } |
203 | |
204 | /* |
205 | * Now write out the block group descriptors |
206 | */ |
207 | cp = (char *) fs->group_desc; |
208 | actual = write(fd, cp, fs->blocksize * fs->desc_blocks); |
209 | if (actual == -1) { |
210 | retval = errno; |
211 | goto errout; |
212 | } |
213 | if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) { |
214 | retval = EXT2_ET_SHORT_WRITE; |
215 | goto errout; |
216 | } |
217 | |
218 | retval = 0; |
219 | |
220 | errout: |
221 | free(buf); |
222 | return retval; |
223 | } |
224 | |
225 | /* |
226 | * Read the superblock and group descriptors and overwrite them. |
227 | */ |
228 | errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, |
229 | int flags EXT2FS_ATTR((unused))) |
230 | { |
231 | char *buf; |
232 | ssize_t actual, size; |
233 | errcode_t retval; |
234 | |
235 | size = fs->blocksize * (fs->group_desc_count + 1); |
236 | buf = xmalloc(size); |
237 | |
238 | /* |
239 | * Read it all in. |
240 | */ |
241 | actual = read(fd, buf, size); |
242 | if (actual == -1) { |
243 | retval = errno; |
244 | goto errout; |
245 | } |
246 | if (actual != size) { |
247 | retval = EXT2_ET_SHORT_READ; |
248 | goto errout; |
249 | } |
250 | |
251 | /* |
252 | * Now copy in the superblock and group descriptors |
253 | */ |
254 | memcpy(fs->super, buf, SUPERBLOCK_SIZE); |
255 | |
256 | memcpy(fs->group_desc, buf + fs->blocksize, |
257 | fs->blocksize * fs->group_desc_count); |
258 | |
259 | retval = 0; |
260 | |
261 | errout: |
262 | free(buf); |
263 | return retval; |
264 | } |
265 | |
266 | /* |
267 | * Write the block/inode bitmaps. |
268 | */ |
269 | errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags) |
270 | { |
271 | char *ptr; |
272 | int c, size; |
273 | char zero_buf[1024]; |
274 | ssize_t actual; |
275 | errcode_t retval; |
276 | |
277 | if (flags & IMAGER_FLAG_INODEMAP) { |
278 | if (!fs->inode_map) { |
279 | retval = ext2fs_read_inode_bitmap(fs); |
280 | if (retval) |
281 | return retval; |
282 | } |
283 | ptr = fs->inode_map->bitmap; |
284 | size = (EXT2_INODES_PER_GROUP(fs->super) / 8); |
285 | } else { |
286 | if (!fs->block_map) { |
287 | retval = ext2fs_read_block_bitmap(fs); |
288 | if (retval) |
289 | return retval; |
290 | } |
291 | ptr = fs->block_map->bitmap; |
292 | size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; |
293 | } |
294 | size = size * fs->group_desc_count; |
295 | |
296 | actual = write(fd, ptr, size); |
297 | if (actual == -1) { |
298 | retval = errno; |
299 | goto errout; |
300 | } |
301 | if (actual != size) { |
302 | retval = EXT2_ET_SHORT_WRITE; |
303 | goto errout; |
304 | } |
305 | size = size % fs->blocksize; |
306 | memset(zero_buf, 0, sizeof(zero_buf)); |
307 | if (size) { |
308 | size = fs->blocksize - size; |
309 | while (size) { |
310 | c = size; |
311 | if (c > (int) sizeof(zero_buf)) |
312 | c = sizeof(zero_buf); |
313 | actual = write(fd, zero_buf, c); |
314 | if (actual == -1) { |
315 | retval = errno; |
316 | goto errout; |
317 | } |
318 | if (actual != c) { |
319 | retval = EXT2_ET_SHORT_WRITE; |
320 | goto errout; |
321 | } |
322 | size -= c; |
323 | } |
324 | } |
325 | retval = 0; |
326 | errout: |
327 | return retval; |
328 | } |
329 | |
330 | |
331 | /* |
332 | * Read the block/inode bitmaps. |
333 | */ |
334 | errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags) |
335 | { |
336 | char *ptr, *buf = NULL; |
337 | int size; |
338 | ssize_t actual; |
339 | errcode_t retval; |
340 | |
341 | if (flags & IMAGER_FLAG_INODEMAP) { |
342 | if (!fs->inode_map) { |
343 | retval = ext2fs_read_inode_bitmap(fs); |
344 | if (retval) |
345 | return retval; |
346 | } |
347 | ptr = fs->inode_map->bitmap; |
348 | size = (EXT2_INODES_PER_GROUP(fs->super) / 8); |
349 | } else { |
350 | if (!fs->block_map) { |
351 | retval = ext2fs_read_block_bitmap(fs); |
352 | if (retval) |
353 | return retval; |
354 | } |
355 | ptr = fs->block_map->bitmap; |
356 | size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; |
357 | } |
358 | size = size * fs->group_desc_count; |
359 | |
360 | buf = xmalloc(size); |
361 | |
362 | actual = read(fd, buf, size); |
363 | if (actual == -1) { |
364 | retval = errno; |
365 | goto errout; |
366 | } |
367 | if (actual != size) { |
368 | retval = EXT2_ET_SHORT_WRITE; |
369 | goto errout; |
370 | } |
371 | memcpy(ptr, buf, size); |
372 | |
373 | retval = 0; |
374 | errout: |
375 | free(buf); |
376 | return retval; |
377 | } |
378 |