blob: 5c7003a6fcaed1a2720f5b317eb2414c8642df95 [file] [log] [blame]
Theodore Ts'o72ed1262000-11-12 19:32:20 +00001/*
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
Theodore Ts'oefc6f622008-08-27 23:07:54 -04008 * functions in this library. So sue me.
Theodore Ts'o72ed1262000-11-12 19:32:20 +00009 *
10 * %Begin-Header%
Theodore Ts'o543547a2010-05-17 21:31:56 -040011 * This file may be redistributed under the terms of the GNU Library
12 * General Public License, version 2.
Theodore Ts'o72ed1262000-11-12 19:32:20 +000013 * %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
Theodore Ts'o72ed1262000-11-12 19:32:20 +000033#include "ext2_fs.h"
Theodore Ts'o72ed1262000-11-12 19:32:20 +000034#include "ext2fs.h"
35
Theodore Ts'offf45482003-04-13 00:44:19 -040036#ifndef HAVE_TYPE_SSIZE_T
37typedef int ssize_t;
38#endif
39
Theodore Ts'o72ed1262000-11-12 19:32:20 +000040/*
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;
JP Abgralle0ed7402014-03-19 19:08:39 -070065 blk64_t blk;
Theodore Ts'o72ed1262000-11-12 19:32:20 +000066 ssize_t actual;
67 errcode_t retval;
JP Abgralle0ed7402014-03-19 19:08:39 -070068 off_t r;
Theodore Ts'o72ed1262000-11-12 19:32:20 +000069
70 buf = malloc(fs->blocksize * BUF_BLOCKS);
71 if (!buf)
72 return ENOMEM;
Theodore Ts'oefc6f622008-08-27 23:07:54 -040073
Theodore Ts'o72ed1262000-11-12 19:32:20 +000074 for (group = 0; group < fs->group_desc_count; group++) {
JP Abgralle0ed7402014-03-19 19:08:39 -070075 blk = ext2fs_inode_table_loc(fs, (unsigned)group);
Brian Behlendorff93625b2007-03-21 17:43:37 -040076 if (!blk) {
77 retval = EXT2_ET_MISSING_INODE_TABLE;
78 goto errout;
79 }
Theodore Ts'o72ed1262000-11-12 19:32:20 +000080 left = fs->inode_blocks_per_group;
81 while (left) {
82 c = BUF_BLOCKS;
83 if (c > left)
84 c = left;
JP Abgralle0ed7402014-03-19 19:08:39 -070085 retval = io_channel_read_blk64(fs->io, blk, c, buf);
Theodore Ts'o72ed1262000-11-12 19:32:20 +000086 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;
JP Abgralle0ed7402014-03-19 19:08:39 -0700100 r = lseek(fd, fs->blocksize, SEEK_CUR);
101 if (r < 0) {
102 retval = errno;
103 goto errout;
104 }
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000105 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) {
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000115 retval = errno;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000116 goto errout;
117 }
Theodore Ts'o54434922003-12-07 01:28:50 -0500118 if (actual != (ssize_t) (fs->blocksize * d)) {
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000119 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 */
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400139errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd,
Theodore Ts'o54434922003-12-07 01:28:50 -0500140 int flags EXT2FS_ATTR((unused)))
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000141{
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000142 unsigned int group, c, left;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000143 char *buf;
JP Abgralle0ed7402014-03-19 19:08:39 -0700144 blk64_t blk;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000145 ssize_t actual;
146 errcode_t retval;
147
148 buf = malloc(fs->blocksize * BUF_BLOCKS);
149 if (!buf)
150 return ENOMEM;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400151
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000152 for (group = 0; group < fs->group_desc_count; group++) {
JP Abgralle0ed7402014-03-19 19:08:39 -0700153 blk = ext2fs_inode_table_loc(fs, (unsigned)group);
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000154 if (!blk) {
155 retval = EXT2_ET_MISSING_INODE_TABLE;
156 goto errout;
157 }
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000158 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) {
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000165 retval = errno;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000166 goto errout;
167 }
Theodore Ts'o54434922003-12-07 01:28:50 -0500168 if (actual != (ssize_t) (fs->blocksize * c)) {
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000169 retval = EXT2_ET_SHORT_READ;
170 goto errout;
171 }
JP Abgralle0ed7402014-03-19 19:08:39 -0700172 retval = io_channel_write_blk64(fs->io, blk, c, buf);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000173 if (retval)
174 goto errout;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400175
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000176 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 */
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400190errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd,
Theodore Ts'o54434922003-12-07 01:28:50 -0500191 int flags EXT2FS_ATTR((unused)))
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000192{
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000193 char *buf, *cp;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000194 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) {
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000208 retval = errno;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000209 goto errout;
210 }
Theodore Ts'o54434922003-12-07 01:28:50 -0500211 if (actual != (ssize_t) fs->blocksize) {
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000212 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) {
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000222 retval = errno;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000223 goto errout;
224 }
Theodore Ts'o54434922003-12-07 01:28:50 -0500225 if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) {
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000226 retval = EXT2_ET_SHORT_WRITE;
227 goto errout;
228 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400229
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000230 retval = 0;
231
232errout:
233 free(buf);
234 return retval;
235}
236
237/*
238 * Read the superblock and group descriptors and overwrite them.
239 */
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400240errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd,
Theodore Ts'o54434922003-12-07 01:28:50 -0500241 int flags EXT2FS_ATTR((unused)))
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000242{
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000243 char *buf;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000244 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) {
Theodore Ts'ob94bd812001-01-12 17:26:05 +0000257 retval = errno;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000258 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{
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400285 ext2fs_generic_bitmap bmap;
JP Abgralle0ed7402014-03-19 19:08:39 -0700286 errcode_t retval;
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400287 ssize_t actual;
288 __u32 itr, cnt, size;
289 int c, total_size;
290 char buf[1024];
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000291
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 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400298 bmap = fs->inode_map;
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400299 itr = 1;
300 cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
Theodore Ts'oa78926e2001-05-03 04:02:29 +0000301 size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000302 } else {
303 if (!fs->block_map) {
304 retval = ext2fs_read_block_bitmap(fs);
305 if (retval)
306 return retval;
307 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400308 bmap = fs->block_map;
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400309 itr = fs->super->s_first_data_block;
310 cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000311 size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
312 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400313 total_size = size * fs->group_desc_count;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000314
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400315 while (cnt > 0) {
316 size = sizeof(buf);
317 if (size > (cnt >> 3))
318 size = (cnt >> 3);
319
JP Abgralle0ed7402014-03-19 19:08:39 -0700320 retval = ext2fs_get_generic_bmap_range(bmap, itr,
321 size << 3, buf);
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400322 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;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400330
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400331 itr += size << 3;
332 cnt -= size << 3;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000333 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400334
335 size = total_size % fs->blocksize;
336 memset(buf, 0, sizeof(buf));
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000337 if (size) {
338 size = fs->blocksize - size;
339 while (size) {
340 c = size;
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400341 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;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000348 size -= c;
349 }
350 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400351 return 0;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000352}
353
354
355/*
356 * Read the block/inode bitmaps.
357 */
358errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
359{
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400360 ext2fs_generic_bitmap bmap;
JP Abgralle0ed7402014-03-19 19:08:39 -0700361 errcode_t retval;
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400362 __u32 itr, cnt;
363 char buf[1024];
364 unsigned int size;
365 ssize_t actual;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000366
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 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400373 bmap = fs->inode_map;
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400374 itr = 1;
375 cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
Theodore Ts'oa78926e2001-05-03 04:02:29 +0000376 size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000377 } else {
378 if (!fs->block_map) {
379 retval = ext2fs_read_block_bitmap(fs);
380 if (retval)
381 return retval;
382 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400383 bmap = fs->block_map;
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400384 itr = fs->super->s_first_data_block;
385 cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000386 size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
387 }
388
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400389 while (cnt > 0) {
390 size = sizeof(buf);
391 if (size > (cnt >> 3))
392 size = (cnt >> 3);
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000393
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400394 actual = read(fd, buf, size);
395 if (actual == -1)
396 return errno;
397 if (actual != (int) size)
398 return EXT2_ET_SHORT_READ;
399
JP Abgralle0ed7402014-03-19 19:08:39 -0700400 retval = ext2fs_set_generic_bmap_range(bmap, itr,
401 size << 3, buf);
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400402 if (retval)
403 return retval;
404
405 itr += size << 3;
406 cnt -= size << 3;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000407 }
Theodore Ts'of1f115a2007-07-23 04:32:48 -0400408 return 0;
Theodore Ts'o72ed1262000-11-12 19:32:20 +0000409}