Coverage Report

Created: 2026-07-30 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/e2fsprogs/lib/ext2fs/imager.c
Line
Count
Source
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.24k
#define BUF_BLOCKS  32
61
62
errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags)
63
90
{
64
90
  dgrp_t    group;
65
90
  ssize_t   left, c, d;
66
90
  char    *buf, *cp;
67
90
  blk64_t   blk;
68
90
  ssize_t   actual;
69
90
  errcode_t retval;
70
90
  ext2_loff_t r;
71
72
90
  buf = malloc(fs->blocksize * BUF_BLOCKS);
73
90
  if (!buf)
74
0
    return ENOMEM;
75
76
2.17k
  for (group = 0; group < fs->group_desc_count; group++) {
77
2.16k
    blk = ext2fs_inode_table_loc(fs, group);
78
2.16k
    if (!blk) {
79
3
      retval = EXT2_ET_MISSING_INODE_TABLE;
80
3
      goto errout;
81
3
    }
82
2.15k
    left = fs->inode_blocks_per_group;
83
2.15k
    if ((blk < fs->super->s_first_data_block) ||
84
2.15k
        (blk + left - 1 >= ext2fs_blocks_count(fs->super))) {
85
48
      retval = EXT2_ET_GDESC_BAD_INODE_TABLE;
86
48
      goto errout;
87
48
    }
88
5.23k
    while (left) {
89
3.15k
      c = BUF_BLOCKS;
90
3.15k
      if (c > left)
91
1.92k
        c = left;
92
3.15k
      retval = io_channel_read_blk64(fs->io, blk, c, buf);
93
3.15k
      if (retval)
94
25
        goto errout;
95
3.12k
      cp = buf;
96
6.25k
      while (c) {
97
3.12k
        if (!(flags & IMAGER_FLAG_SPARSEWRITE)) {
98
3.12k
          d = c;
99
3.12k
          goto skip_sparse;
100
3.12k
        }
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.12k
      skip_sparse:
123
3.12k
        actual = write(fd, cp, d * fs->blocksize);
124
3.12k
        if (actual == -1) {
125
0
          retval = errno;
126
0
          goto errout;
127
0
        }
128
3.12k
        if (actual != d * fs->blocksize) {
129
0
          retval = EXT2_ET_SHORT_WRITE;
130
0
          goto errout;
131
0
        }
132
3.12k
        blk += d;
133
3.12k
        left -= d;
134
3.12k
        cp += d * fs->blocksize;
135
3.12k
        c -= d;
136
3.12k
      }
137
3.12k
    }
138
2.11k
  }
139
14
  retval = 0;
140
141
90
errout:
142
90
  free(buf);
143
90
  return retval;
144
14
}
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
1
{
152
1
  dgrp_t    group;
153
1
  ssize_t   c, left;
154
1
  char    *buf;
155
1
  blk64_t   blk;
156
1
  ssize_t   actual;
157
1
  errcode_t retval;
158
159
1
  buf = malloc(fs->blocksize * BUF_BLOCKS);
160
1
  if (!buf)
161
0
    return ENOMEM;
162
163
34
  for (group = 0; group < fs->group_desc_count; group++) {
164
34
    blk = ext2fs_inode_table_loc(fs, group);
165
34
    if (!blk) {
166
1
      retval = EXT2_ET_MISSING_INODE_TABLE;
167
1
      goto errout;
168
1
    }
169
33
    left = fs->inode_blocks_per_group;
170
33
    while (left) {
171
0
      c = BUF_BLOCKS;
172
0
      if (c > left)
173
0
        c = left;
174
0
      actual = read(fd, buf, fs->blocksize * c);
175
0
      if (actual == -1) {
176
0
        retval = errno;
177
0
        goto errout;
178
0
      }
179
0
      if (actual != fs->blocksize * c) {
180
0
        retval = EXT2_ET_SHORT_READ;
181
0
        goto errout;
182
0
      }
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
33
  }
191
0
  retval = ext2fs_flush_icache(fs);
192
193
1
errout:
194
1
  free(buf);
195
1
  return retval;
196
0
}
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
0
{
298
0
  char    *buf;
299
0
  ssize_t   actual, size;
300
0
  errcode_t retval;
301
302
0
  size = (ssize_t)fs->blocksize * (fs->desc_blocks + 1);
303
0
  buf = malloc(size);
304
0
  if (!buf)
305
0
    return ENOMEM;
306
307
  /*
308
   * Read it all in.
309
   */
310
0
  actual = read(fd, buf, size);
311
0
  if (actual == -1) {
312
0
    retval = errno;
313
0
    goto errout;
314
0
  }
315
0
  if (actual != size) {
316
0
    retval = EXT2_ET_SHORT_READ;
317
0
    goto errout;
318
0
  }
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->desc_blocks);
327
328
0
  retval = 0;
329
330
0
errout:
331
0
  free(buf);
332
0
  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
116
{
340
116
  ext2fs_generic_bitmap bmap;
341
116
  errcode_t   retval;
342
116
  ssize_t     actual;
343
116
  size_t      c;
344
116
  __u64     itr, cnt, size, total_size;
345
116
  char      buf[1024];
346
347
116
  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
116
  } else {
359
116
    if (!fs->block_map) {
360
116
      retval = ext2fs_read_block_bitmap(fs);
361
116
      if (retval)
362
22
        return retval;
363
116
    }
364
94
    bmap = fs->block_map;
365
94
    itr = fs->super->s_first_data_block;
366
94
    cnt = EXT2_GROUPS_TO_CLUSTERS(fs->super, fs->group_desc_count);
367
94
    size = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
368
94
  }
369
94
  total_size = size * fs->group_desc_count;
370
371
68.7k
  while (cnt > 0) {
372
68.6k
    size = sizeof(buf);
373
68.6k
    if (size > (cnt >> 3))
374
87
      size = (cnt >> 3);
375
68.6k
    if (size == 0)
376
22
      break;
377
378
68.6k
    retval = ext2fs_get_generic_bmap_range(bmap, itr,
379
68.6k
                   size << 3, buf);
380
68.6k
    if (retval)
381
0
      return retval;
382
383
68.6k
    actual = write(fd, buf, size);
384
68.6k
    if (actual == -1)
385
0
      return errno;
386
68.6k
    if (actual != (int) size)
387
0
      return EXT2_ET_SHORT_READ;
388
389
68.6k
    itr += size << 3;
390
68.6k
    cnt -= size << 3;
391
68.6k
  }
392
393
94
  size = total_size % fs->blocksize;
394
94
  memset(buf, 0, sizeof(buf));
395
94
  if (size) {
396
68
    size = fs->blocksize - size;
397
370
    while (size) {
398
302
      c = size;
399
302
      if (c > (int) sizeof(buf))
400
234
        c = sizeof(buf);
401
302
      actual = write(fd, buf, c);
402
302
      if (actual < 0)
403
0
        return errno;
404
302
      if ((size_t) actual != c)
405
0
        return EXT2_ET_SHORT_WRITE;
406
302
      size -= c;
407
302
    }
408
68
  }
409
94
  return 0;
410
94
}
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
76
{
418
76
  ext2fs_generic_bitmap bmap;
419
76
  errcode_t   retval;
420
76
  __u64     itr, cnt;
421
76
  char      buf[1024];
422
76
  unsigned int    size;
423
76
  ssize_t     actual;
424
425
76
  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
76
  } else {
437
76
    if (!fs->block_map) {
438
76
      retval = ext2fs_read_block_bitmap(fs);
439
76
      if (retval)
440
8
        return retval;
441
76
    }
442
68
    bmap = fs->block_map;
443
68
    itr = fs->super->s_first_data_block;
444
68
    cnt = EXT2_GROUPS_TO_BLOCKS(fs->super, fs->group_desc_count);
445
68
    size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
446
68
  }
447
448
68
  while (cnt > 0) {
449
68
    size = sizeof(buf);
450
68
    if (size > (cnt >> 3))
451
42
      size = (cnt >> 3);
452
68
    if (size == 0)
453
0
      break;
454
455
68
    actual = read(fd, buf, size);
456
68
    if (actual == -1)
457
0
      return errno;
458
68
    if (actual != (int) size)
459
68
      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
68
}