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 "config.h"
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  */
check_zero_block(char * buf,int blocksize)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 
ext2fs_image_inode_write(ext2_filsys fs,int fd,int flags)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 	blk64_t		blk;
67 	ssize_t		actual;
68 	errcode_t	retval;
69 	off_t		r;
70 
71 	buf = malloc(fs->blocksize * BUF_BLOCKS);
72 	if (!buf)
73 		return ENOMEM;
74 
75 	for (group = 0; group < fs->group_desc_count; group++) {
76 		blk = ext2fs_inode_table_loc(fs, (unsigned)group);
77 		if (!blk) {
78 			retval = EXT2_ET_MISSING_INODE_TABLE;
79 			goto errout;
80 		}
81 		left = fs->inode_blocks_per_group;
82 		while (left) {
83 			c = BUF_BLOCKS;
84 			if (c > left)
85 				c = left;
86 			retval = io_channel_read_blk64(fs->io, blk, c, buf);
87 			if (retval)
88 				goto errout;
89 			cp = buf;
90 			while (c) {
91 				if (!(flags & IMAGER_FLAG_SPARSEWRITE)) {
92 					d = c;
93 					goto skip_sparse;
94 				}
95 				/* Skip zero blocks */
96 				if (check_zero_block(cp, fs->blocksize)) {
97 					c--;
98 					blk++;
99 					left--;
100 					cp += fs->blocksize;
101 					r = lseek(fd, fs->blocksize, SEEK_CUR);
102 					if (r < 0) {
103 						retval = errno;
104 						goto errout;
105 					}
106 					continue;
107 				}
108 				/* Find non-zero blocks */
109 				for (d=1; d < c; d++) {
110 					if (check_zero_block(cp + d*fs->blocksize, fs->blocksize))
111 						break;
112 				}
113 			skip_sparse:
114 				actual = write(fd, cp, fs->blocksize * d);
115 				if (actual == -1) {
116 					retval = errno;
117 					goto errout;
118 				}
119 				if (actual != (ssize_t) (fs->blocksize * d)) {
120 					retval = EXT2_ET_SHORT_WRITE;
121 					goto errout;
122 				}
123 				blk += d;
124 				left -= d;
125 				cp += fs->blocksize * d;
126 				c -= d;
127 			}
128 		}
129 	}
130 	retval = 0;
131 
132 errout:
133 	free(buf);
134 	return retval;
135 }
136 
137 /*
138  * Read in the inode table and stuff it into place
139  */
ext2fs_image_inode_read(ext2_filsys fs,int fd,int flags EXT2FS_ATTR ((unused)))140 errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd,
141 				  int flags EXT2FS_ATTR((unused)))
142 {
143 	unsigned int	group, c, left;
144 	char		*buf;
145 	blk64_t		blk;
146 	ssize_t		actual;
147 	errcode_t	retval;
148 
149 	buf = malloc(fs->blocksize * BUF_BLOCKS);
150 	if (!buf)
151 		return ENOMEM;
152 
153 	for (group = 0; group < fs->group_desc_count; group++) {
154 		blk = ext2fs_inode_table_loc(fs, (unsigned)group);
155 		if (!blk) {
156 			retval = EXT2_ET_MISSING_INODE_TABLE;
157 			goto errout;
158 		}
159 		left = fs->inode_blocks_per_group;
160 		while (left) {
161 			c = BUF_BLOCKS;
162 			if (c > left)
163 				c = left;
164 			actual = read(fd, buf, fs->blocksize * c);
165 			if (actual == -1) {
166 				retval = errno;
167 				goto errout;
168 			}
169 			if (actual != (ssize_t) (fs->blocksize * c)) {
170 				retval = EXT2_ET_SHORT_READ;
171 				goto errout;
172 			}
173 			retval = io_channel_write_blk64(fs->io, blk, c, buf);
174 			if (retval)
175 				goto errout;
176 
177 			blk += c;
178 			left -= c;
179 		}
180 	}
181 	retval = ext2fs_flush_icache(fs);
182 
183 errout:
184 	free(buf);
185 	return retval;
186 }
187 
188 /*
189  * Write out superblock and group descriptors
190  */
ext2fs_image_super_write(ext2_filsys fs,int fd,int flags EXT2FS_ATTR ((unused)))191 errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd,
192 				   int flags EXT2FS_ATTR((unused)))
193 {
194 	char		*buf, *cp;
195 	ssize_t		actual;
196 	errcode_t	retval;
197 
198 	buf = malloc(fs->blocksize);
199 	if (!buf)
200 		return ENOMEM;
201 
202 	/*
203 	 * Write out the superblock
204 	 */
205 	memset(buf, 0, fs->blocksize);
206 	memcpy(buf, fs->super, SUPERBLOCK_SIZE);
207 	actual = write(fd, buf, fs->blocksize);
208 	if (actual == -1) {
209 		retval = errno;
210 		goto errout;
211 	}
212 	if (actual != (ssize_t) fs->blocksize) {
213 		retval = EXT2_ET_SHORT_WRITE;
214 		goto errout;
215 	}
216 
217 	/*
218 	 * Now write out the block group descriptors
219 	 */
220 	cp = (char *) fs->group_desc;
221 	actual = write(fd, cp, fs->blocksize * fs->desc_blocks);
222 	if (actual == -1) {
223 		retval = errno;
224 		goto errout;
225 	}
226 	if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) {
227 		retval = EXT2_ET_SHORT_WRITE;
228 		goto errout;
229 	}
230 
231 	retval = 0;
232 
233 errout:
234 	free(buf);
235 	return retval;
236 }
237 
238 /*
239  * Read the superblock and group descriptors and overwrite them.
240  */
ext2fs_image_super_read(ext2_filsys fs,int fd,int flags EXT2FS_ATTR ((unused)))241 errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd,
242 				  int flags EXT2FS_ATTR((unused)))
243 {
244 	char		*buf;
245 	ssize_t		actual, size;
246 	errcode_t	retval;
247 
248 	size = fs->blocksize * (fs->group_desc_count + 1);
249 	buf = malloc(size);
250 	if (!buf)
251 		return ENOMEM;
252 
253 	/*
254 	 * Read it all in.
255 	 */
256 	actual = read(fd, buf, size);
257 	if (actual == -1) {
258 		retval = errno;
259 		goto errout;
260 	}
261 	if (actual != size) {
262 		retval = EXT2_ET_SHORT_READ;
263 		goto errout;
264 	}
265 
266 	/*
267 	 * Now copy in the superblock and group descriptors
268 	 */
269 	memcpy(fs->super, buf, SUPERBLOCK_SIZE);
270 
271 	memcpy(fs->group_desc, buf + fs->blocksize,
272 	       fs->blocksize * fs->group_desc_count);
273 
274 	retval = 0;
275 
276 errout:
277 	free(buf);
278 	return retval;
279 }
280 
281 /*
282  * Write the block/inode bitmaps.
283  */
ext2fs_image_bitmap_write(ext2_filsys fs,int fd,int flags)284 errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
285 {
286 	ext2fs_generic_bitmap	bmap;
287 	errcode_t		retval;
288 	ssize_t			actual;
289 	size_t			c;
290 	__u64			itr, cnt, size, total_size;
291 	char			buf[1024];
292 
293 	if (flags & IMAGER_FLAG_INODEMAP) {
294 		if (!fs->inode_map) {
295 			retval = ext2fs_read_inode_bitmap(fs);
296 			if (retval)
297 				return retval;
298 		}
299 		bmap = fs->inode_map;
300 		itr = 1;
301 		cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
302 		size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
303 	} else {
304 		if (!fs->block_map) {
305 			retval = ext2fs_read_block_bitmap(fs);
306 			if (retval)
307 				return retval;
308 		}
309 		bmap = fs->block_map;
310 		itr = fs->super->s_first_data_block;
311 		cnt = EXT2_GROUPS_TO_BLOCKS(fs->super, fs->group_desc_count);
312 		size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
313 	}
314 	total_size = size * fs->group_desc_count;
315 
316 	while (cnt > 0) {
317 		size = sizeof(buf);
318 		if (size > (cnt >> 3))
319 			size = (cnt >> 3);
320 
321 		retval = ext2fs_get_generic_bmap_range(bmap, itr,
322 						       size << 3, buf);
323 		if (retval)
324 			return retval;
325 
326 		actual = write(fd, buf, size);
327 		if (actual == -1)
328 			return errno;
329 		if (actual != (int) size)
330 			return EXT2_ET_SHORT_READ;
331 
332 		itr += size << 3;
333 		cnt -= size << 3;
334 	}
335 
336 	size = total_size % fs->blocksize;
337 	memset(buf, 0, sizeof(buf));
338 	if (size) {
339 		size = fs->blocksize - size;
340 		while (size) {
341 			c = size;
342 			if (c > (int) sizeof(buf))
343 				c = sizeof(buf);
344 			actual = write(fd, buf, c);
345 			if (actual < 0)
346 				return errno;
347 			if ((size_t) actual != c)
348 				return EXT2_ET_SHORT_WRITE;
349 			size -= c;
350 		}
351 	}
352 	return 0;
353 }
354 
355 
356 /*
357  * Read the block/inode bitmaps.
358  */
ext2fs_image_bitmap_read(ext2_filsys fs,int fd,int flags)359 errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
360 {
361 	ext2fs_generic_bitmap	bmap;
362 	errcode_t		retval;
363 	__u64			itr, cnt;
364 	char			buf[1024];
365 	unsigned int		size;
366 	ssize_t			actual;
367 
368 	if (flags & IMAGER_FLAG_INODEMAP) {
369 		if (!fs->inode_map) {
370 			retval = ext2fs_read_inode_bitmap(fs);
371 			if (retval)
372 				return retval;
373 		}
374 		bmap = fs->inode_map;
375 		itr = 1;
376 		cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
377 		size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
378 	} else {
379 		if (!fs->block_map) {
380 			retval = ext2fs_read_block_bitmap(fs);
381 			if (retval)
382 				return retval;
383 		}
384 		bmap = fs->block_map;
385 		itr = fs->super->s_first_data_block;
386 		cnt = EXT2_GROUPS_TO_BLOCKS(fs->super, fs->group_desc_count);
387 		size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
388 	}
389 
390 	while (cnt > 0) {
391 		size = sizeof(buf);
392 		if (size > (cnt >> 3))
393 			size = (cnt >> 3);
394 
395 		actual = read(fd, buf, size);
396 		if (actual == -1)
397 			return errno;
398 		if (actual != (int) size)
399 			return EXT2_ET_SHORT_READ;
400 
401 		retval = ext2fs_set_generic_bmap_range(bmap, itr,
402 						       size << 3, buf);
403 		if (retval)
404 			return retval;
405 
406 		itr += size << 3;
407 		cnt -= size << 3;
408 	}
409 	return 0;
410 }
411