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