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	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
131errout:
132	free(buf);
133	return retval;
134}
135
136/*
137 * Read in the inode table and stuff it into place
138 */
139errcode_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
182errout:
183	free(buf);
184	return retval;
185}
186
187/*
188 * Write out superblock and group descriptors
189 */
190errcode_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
232errout:
233	free(buf);
234	return retval;
235}
236
237/*
238 * Read the superblock and group descriptors and overwrite them.
239 */
240errcode_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
275errout:
276	free(buf);
277	return retval;
278}
279
280/*
281 * Write the block/inode bitmaps.
282 */
283errcode_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 */
358errcode_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