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