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