Coverage Report

Created: 2023-09-25 06:05

/src/e2fsprogs/lib/ext2fs/imager.c
Line
Count
Source (jump to first uncovered line)
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
0
{
46
0
  char  *cp = buf;
47
0
  int left = blocksize;
48
49
0
  while (left > 0) {
50
0
    if (*cp++)
51
0
      return 0;
52
0
    left--;
53
0
  }
54
0
  return 1;
55
0
}
56
57
/*
58
 * Write the inode table out as a single block.
59
 */
60
3.48k
#define BUF_BLOCKS  32
61
62
errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags)
63
179
{
64
179
  dgrp_t    group;
65
179
  ssize_t   left, c, d;
66
179
  char    *buf, *cp;
67
179
  blk64_t   blk;
68
179
  ssize_t   actual;
69
179
  errcode_t retval;
70
179
  ext2_loff_t r;
71
72
179
  buf = malloc(fs->blocksize * BUF_BLOCKS);
73
179
  if (!buf)
74
0
    return ENOMEM;
75
76
2.72k
  for (group = 0; group < fs->group_desc_count; group++) {
77
2.71k
    blk = ext2fs_inode_table_loc(fs, group);
78
2.71k
    if (!blk) {
79
6
      retval = EXT2_ET_MISSING_INODE_TABLE;
80
6
      goto errout;
81
6
    }
82
2.70k
    left = fs->inode_blocks_per_group;
83
2.70k
    if ((blk < fs->super->s_first_data_block) ||
84
2.70k
        (blk + left - 1 >= ext2fs_blocks_count(fs->super))) {
85
113
      retval = EXT2_ET_GDESC_BAD_INODE_TABLE;
86
113
      goto errout;
87
113
    }
88
5.82k
    while (left) {
89
3.27k
      c = BUF_BLOCKS;
90
3.27k
      if (c > left)
91
2.17k
        c = left;
92
3.27k
      retval = io_channel_read_blk64(fs->io, blk, c, buf);
93
3.27k
      if (retval)
94
45
        goto errout;
95
3.23k
      cp = buf;
96
6.46k
      while (c) {
97
3.23k
        if (!(flags & IMAGER_FLAG_SPARSEWRITE)) {
98
3.23k
          d = c;
99
3.23k
          goto skip_sparse;
100
3.23k
        }
101
        /* Skip zero blocks */
102
0
        if (check_zero_block(cp, fs->blocksize)) {
103
0
          c--;
104
0
          blk++;
105
0
          left--;
106
0
          cp += fs->blocksize;
107
0
          r = ext2fs_llseek(fd, fs->blocksize,
108
0
                SEEK_CUR);
109
0
          if (r < 0) {
110
0
            retval = errno;
111
0
            goto errout;
112
0
          }
113
0
          continue;
114
0
        }
115
        /* Find non-zero blocks */
116
0
        for (d = 1; d < c; d++) {
117
0
          if (check_zero_block(cp +
118
0
                   d * fs->blocksize,
119
0
                   fs->blocksize))
120
0
            break;
121
0
        }
122
3.23k
      skip_sparse:
123
3.23k
        actual = write(fd, cp, d * fs->blocksize);
124
3.23k
        if (actual == -1) {
125
0
          retval = errno;
126
0
          goto errout;
127
0
        }
128
3.23k
        if (actual != d * fs->blocksize) {
129
0
          retval = EXT2_ET_SHORT_WRITE;
130
0
          goto errout;
131
0
        }
132
3.23k
        blk += d;
133
3.23k
        left -= d;
134
3.23k
        cp += d * fs->blocksize;
135
3.23k
        c -= d;
136
3.23k
      }
137
3.23k
    }
138
2.59k
  }
139
15
  retval = 0;
140
141
179
errout:
142
179
  free(buf);
143
179
  return retval;
144
15
}
145
146
/*
147
 * Read in the inode table and stuff it into place
148
 */
149
errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd,
150
          int flags EXT2FS_ATTR((unused)))
151
13
{
152
13
  dgrp_t    group;
153
13
  ssize_t   c, left;
154
13
  char    *buf;
155
13
  blk64_t   blk;
156
13
  ssize_t   actual;
157
13
  errcode_t retval;
158
159
13
  buf = malloc(fs->blocksize * BUF_BLOCKS);
160
13
  if (!buf)
161
0
    return ENOMEM;
162
163
106
  for (group = 0; group < fs->group_desc_count; group++) {
164
103
    blk = ext2fs_inode_table_loc(fs, group);
165
103
    if (!blk) {
166
1
      retval = EXT2_ET_MISSING_INODE_TABLE;
167
1
      goto errout;
168
1
    }
169
102
    left = fs->inode_blocks_per_group;
170
102
    while (left) {
171
9
      c = BUF_BLOCKS;
172
9
      if (c > left)
173
3
        c = left;
174
9
      actual = read(fd, buf, fs->blocksize * c);
175
9
      if (actual == -1) {
176
0
        retval = errno;
177
0
        goto errout;
178
0
      }
179
9
      if (actual != fs->blocksize * c) {
180
9
        retval = EXT2_ET_SHORT_READ;
181
9
        goto errout;
182
9
      }
183
0
      retval = io_channel_write_blk64(fs->io, blk, c, buf);
184
0
      if (retval)
185
0
        goto errout;
186
187
0
      blk += c;
188
0
      left -= c;
189
0
    }
190
102
  }
191
3
  retval = ext2fs_flush_icache(fs);
192
193
13
errout:
194
13
  free(buf);
195
13
  return retval;
196
3
}
197
198
/*
199
 * Write out superblock and group descriptors
200
 */
201
errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd,
202
           int flags EXT2FS_ATTR((unused)))
203
8
{
204
8
  char    *buf, *cp;
205
8
  ssize_t   actual;
206
8
  errcode_t retval;
207
#ifdef WORDS_BIGENDIAN
208
  unsigned int  groups_per_block;
209
  struct    ext2_group_desc *gdp;
210
  int   j;
211
#endif
212
213
8
  if (fs->group_desc == NULL)
214
0
    return EXT2_ET_NO_GDESC;
215
216
8
  buf = malloc(fs->blocksize);
217
8
  if (!buf)
218
0
    return ENOMEM;
219
220
  /*
221
   * Write out the superblock
222
   */
223
8
  memset(buf, 0, fs->blocksize);
224
#ifdef WORDS_BIGENDIAN
225
  /*
226
   * We're writing out superblock so let's convert
227
   * it to little endian and then back if needed
228
   */
229
  ext2fs_swap_super(fs->super);
230
  memcpy(buf, fs->super, SUPERBLOCK_SIZE);
231
  ext2fs_swap_super(fs->super);
232
#else
233
8
  memcpy(buf, fs->super, SUPERBLOCK_SIZE);
234
8
#endif
235
8
  actual = write(fd, buf, fs->blocksize);
236
8
  if (actual == -1) {
237
0
    retval = errno;
238
0
    goto errout;
239
0
  }
240
8
  if (actual != (ssize_t) fs->blocksize) {
241
0
    retval = EXT2_ET_SHORT_WRITE;
242
0
    goto errout;
243
0
  }
244
245
  /*
246
   * Now write out the block group descriptors
247
   */
248
249
8
  cp = (char *) fs->group_desc;
250
251
#ifdef WORDS_BIGENDIAN
252
  /*
253
   * Convert group descriptors to little endian and back
254
   * if needed
255
   */
256
  groups_per_block = EXT2_DESC_PER_BLOCK(fs->super);
257
  for (j=0; j < groups_per_block*fs->desc_blocks; j++) {
258
    gdp = ext2fs_group_desc(fs, fs->group_desc, j);
259
    if (gdp)
260
      ext2fs_swap_group_desc2(fs, gdp);
261
  }
262
#endif
263
264
8
  actual = write(fd, cp, (ssize_t)fs->blocksize * fs->desc_blocks);
265
266
267
#ifdef WORDS_BIGENDIAN
268
  groups_per_block = EXT2_DESC_PER_BLOCK(fs->super);
269
  for (j=0; j < groups_per_block*fs->desc_blocks; j++) {
270
    gdp = ext2fs_group_desc(fs, fs->group_desc, j);
271
    if (gdp)
272
      ext2fs_swap_group_desc2(fs, gdp);
273
  }
274
#endif
275
276
8
  if (actual == -1) {
277
0
    retval = errno;
278
0
    goto errout;
279
0
  }
280
8
  if (actual != (ssize_t)(fs->blocksize * fs->desc_blocks)) {
281
0
    retval = EXT2_ET_SHORT_WRITE;
282
0
    goto errout;
283
0
  }
284
285
8
  retval = 0;
286
287
8
errout:
288
8
  free(buf);
289
8
  return retval;
290
8
}
291
292
/*
293
 * Read the superblock and group descriptors and overwrite them.
294
 */
295
errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd,
296
          int flags EXT2FS_ATTR((unused)))
297
3
{
298
3
  char    *buf;
299
3
  ssize_t   actual, size;
300
3
  errcode_t retval;
301
302
3
  size = (ssize_t)fs->blocksize * (fs->group_desc_count + 1);
303
3
  buf = malloc(size);
304
3
  if (!buf)
305
0
    return ENOMEM;
306
307
  /*
308
   * Read it all in.
309
   */
310
3
  actual = read(fd, buf, size);
311
3
  if (actual == -1) {
312
0
    retval = errno;
313
0
    goto errout;
314
0
  }
315
3
  if (actual != size) {
316
3
    retval = EXT2_ET_SHORT_READ;
317
3
    goto errout;
318
3
  }
319
320
  /*
321
   * Now copy in the superblock and group descriptors
322
   */
323
0
  memcpy(fs->super, buf, SUPERBLOCK_SIZE);
324
325
0
  memcpy(fs->group_desc, buf + fs->blocksize,
326
0
         (ssize_t)fs->blocksize * fs->group_desc_count);
327
328
0
  retval = 0;
329
330
3
errout:
331
3
  free(buf);
332
3
  return retval;
333
0
}
334
335
/*
336
 * Write the block/inode bitmaps.
337
 */
338
errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
339
130
{
340
130
  ext2fs_generic_bitmap bmap;
341
130
  errcode_t   retval;
342
130
  ssize_t     actual;
343
130
  size_t      c;
344
130
  __u64     itr, cnt, size, total_size;
345
130
  char      buf[1024];
346
347
130
  if (flags & IMAGER_FLAG_INODEMAP) {
348
0
    if (!fs->inode_map) {
349
0
      retval = ext2fs_read_inode_bitmap(fs);
350
0
      if (retval)
351
0
        return retval;
352
0
    }
353
0
    bmap = fs->inode_map;
354
0
    itr = 1;
355
0
    cnt = (__u64)EXT2_INODES_PER_GROUP(fs->super) *
356
0
      fs->group_desc_count;
357
0
    size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
358
130
  } else {
359
130
    if (!fs->block_map) {
360
130
      retval = ext2fs_read_block_bitmap(fs);
361
130
      if (retval)
362
19
        return retval;
363
130
    }
364
111
    bmap = fs->block_map;
365
111
    itr = fs->super->s_first_data_block;
366
111
    cnt = EXT2_GROUPS_TO_CLUSTERS(fs->super, fs->group_desc_count);
367
111
    size = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
368
111
  }
369
111
  total_size = size * fs->group_desc_count;
370
371
59.1k
  while (cnt > 0) {
372
59.0k
    size = sizeof(buf);
373
59.0k
    if (size > (cnt >> 3))
374
84
      size = (cnt >> 3);
375
59.0k
    if (size == 0)
376
20
      break;
377
378
59.0k
    retval = ext2fs_get_generic_bmap_range(bmap, itr,
379
59.0k
                   size << 3, buf);
380
59.0k
    if (retval)
381
0
      return retval;
382
383
59.0k
    actual = write(fd, buf, size);
384
59.0k
    if (actual == -1)
385
0
      return errno;
386
59.0k
    if (actual != (int) size)
387
0
      return EXT2_ET_SHORT_READ;
388
389
59.0k
    itr += size << 3;
390
59.0k
    cnt -= size << 3;
391
59.0k
  }
392
393
111
  size = total_size % fs->blocksize;
394
111
  memset(buf, 0, sizeof(buf));
395
111
  if (size) {
396
62
    size = fs->blocksize - size;
397
248
    while (size) {
398
186
      c = size;
399
186
      if (c > (int) sizeof(buf))
400
124
        c = sizeof(buf);
401
186
      actual = write(fd, buf, c);
402
186
      if (actual < 0)
403
0
        return errno;
404
186
      if ((size_t) actual != c)
405
0
        return EXT2_ET_SHORT_WRITE;
406
186
      size -= c;
407
186
    }
408
62
  }
409
111
  return 0;
410
111
}
411
412
413
/*
414
 * Read the block/inode bitmaps.
415
 */
416
errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
417
97
{
418
97
  ext2fs_generic_bitmap bmap;
419
97
  errcode_t   retval;
420
97
  __u64     itr, cnt;
421
97
  char      buf[1024];
422
97
  unsigned int    size;
423
97
  ssize_t     actual;
424
425
97
  if (flags & IMAGER_FLAG_INODEMAP) {
426
0
    if (!fs->inode_map) {
427
0
      retval = ext2fs_read_inode_bitmap(fs);
428
0
      if (retval)
429
0
        return retval;
430
0
    }
431
0
    bmap = fs->inode_map;
432
0
    itr = 1;
433
0
    cnt = (__u64)EXT2_INODES_PER_GROUP(fs->super) *
434
0
      fs->group_desc_count;
435
0
    size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
436
97
  } else {
437
97
    if (!fs->block_map) {
438
97
      retval = ext2fs_read_block_bitmap(fs);
439
97
      if (retval)
440
9
        return retval;
441
97
    }
442
88
    bmap = fs->block_map;
443
88
    itr = fs->super->s_first_data_block;
444
88
    cnt = EXT2_GROUPS_TO_BLOCKS(fs->super, fs->group_desc_count);
445
88
    size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
446
88
  }
447
448
88
  while (cnt > 0) {
449
88
    size = sizeof(buf);
450
88
    if (size > (cnt >> 3))
451
53
      size = (cnt >> 3);
452
88
    if (size == 0)
453
0
      break;
454
455
88
    actual = read(fd, buf, size);
456
88
    if (actual == -1)
457
0
      return errno;
458
88
    if (actual != (int) size)
459
88
      return EXT2_ET_SHORT_READ;
460
461
0
    retval = ext2fs_set_generic_bmap_range(bmap, itr,
462
0
                   size << 3, buf);
463
0
    if (retval)
464
0
      return retval;
465
466
0
    itr += size << 3;
467
0
    cnt -= size << 3;
468
0
  }
469
0
  return 0;
470
88
}