Coverage Report

Created: 2026-10-23 07:00

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
2.95k
#define BUF_BLOCKS  32
61
62
errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags)
63
124
{
64
124
  dgrp_t    group;
65
124
  ssize_t   left, c, d;
66
124
  char    *buf, *cp;
67
124
  blk64_t   blk;
68
124
  ssize_t   actual;
69
124
  errcode_t retval;
70
124
  ext2_loff_t r;
71
72
124
  buf = malloc(fs->blocksize * BUF_BLOCKS);
73
124
  if (!buf)
74
0
    return ENOMEM;
75
76
2.10k
  for (group = 0; group < fs->group_desc_count; group++) {
77
2.08k
    blk = ext2fs_inode_table_loc(fs, group);
78
2.08k
    if (!blk) {
79
5
      retval = EXT2_ET_MISSING_INODE_TABLE;
80
5
      goto errout;
81
5
    }
82
2.08k
    left = fs->inode_blocks_per_group;
83
2.08k
    if ((blk < fs->super->s_first_data_block) ||
84
2.08k
        (blk + left - 1 >= ext2fs_blocks_count(fs->super))) {
85
76
      retval = EXT2_ET_GDESC_BAD_INODE_TABLE;
86
76
      goto errout;
87
76
    }
88
4.80k
    while (left) {
89
2.82k
      c = BUF_BLOCKS;
90
2.82k
      if (c > left)
91
1.86k
        c = left;
92
2.82k
      retval = io_channel_read_blk64(fs->io, blk, c, buf);
93
2.82k
      if (retval)
94
27
        goto errout;
95
2.79k
      cp = buf;
96
5.59k
      while (c) {
97
2.79k
        if (!(flags & IMAGER_FLAG_SPARSEWRITE)) {
98
2.79k
          d = c;
99
2.79k
          goto skip_sparse;
100
2.79k
        }
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
2.79k
      skip_sparse:
123
2.79k
        actual = write(fd, cp, d * fs->blocksize);
124
2.79k
        if (actual == -1) {
125
0
          retval = errno;
126
0
          goto errout;
127
0
        }
128
2.79k
        if (actual != d * fs->blocksize) {
129
0
          retval = EXT2_ET_SHORT_WRITE;
130
0
          goto errout;
131
0
        }
132
2.79k
        blk += d;
133
2.79k
        left -= d;
134
2.79k
        cp += d * fs->blocksize;
135
2.79k
        c -= d;
136
2.79k
      }
137
2.79k
    }
138
2.00k
  }
139
16
  retval = 0;
140
141
124
errout:
142
124
  free(buf);
143
124
  return retval;
144
16
}
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
3
{
152
3
  dgrp_t    group;
153
3
  ssize_t   c, left;
154
3
  char    *buf;
155
3
  blk64_t   blk;
156
3
  ssize_t   actual;
157
3
  errcode_t retval;
158
159
3
  buf = malloc(fs->blocksize * BUF_BLOCKS);
160
3
  if (!buf)
161
0
    return ENOMEM;
162
163
68
  for (group = 0; group < fs->group_desc_count; group++) {
164
67
    blk = ext2fs_inode_table_loc(fs, group);
165
67
    if (!blk) {
166
1
      retval = EXT2_ET_MISSING_INODE_TABLE;
167
1
      goto errout;
168
1
    }
169
66
    left = fs->inode_blocks_per_group;
170
66
    while (left) {
171
1
      c = BUF_BLOCKS;
172
1
      if (c > left)
173
0
        c = left;
174
1
      actual = read(fd, buf, fs->blocksize * c);
175
1
      if (actual == -1) {
176
0
        retval = errno;
177
0
        goto errout;
178
0
      }
179
1
      if (actual != fs->blocksize * c) {
180
1
        retval = EXT2_ET_SHORT_READ;
181
1
        goto errout;
182
1
      }
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
66
  }
191
1
  retval = ext2fs_flush_icache(fs);
192
193
3
errout:
194
3
  free(buf);
195
3
  return retval;
196
1
}
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
122
{
340
122
  ext2fs_generic_bitmap bmap;
341
122
  errcode_t   retval;
342
122
  ssize_t     actual;
343
122
  size_t      c;
344
122
  __u64     itr, cnt, size, total_size;
345
122
  char      buf[1024];
346
347
122
  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
122
  } else {
359
122
    if (!fs->block_map) {
360
122
      retval = ext2fs_read_block_bitmap(fs);
361
122
      if (retval)
362
24
        return retval;
363
122
    }
364
98
    bmap = fs->block_map;
365
98
    itr = fs->super->s_first_data_block;
366
98
    cnt = EXT2_GROUPS_TO_CLUSTERS(fs->super, fs->group_desc_count);
367
98
    size = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
368
98
  }
369
98
  total_size = size * fs->group_desc_count;
370
371
86.0k
  while (cnt > 0) {
372
85.9k
    size = sizeof(buf);
373
85.9k
    if (size > (cnt >> 3))
374
86
      size = (cnt >> 3);
375
85.9k
    if (size == 0)
376
21
      break;
377
378
85.9k
    retval = ext2fs_get_generic_bmap_range(bmap, itr,
379
85.9k
                   size << 3, buf);
380
85.9k
    if (retval)
381
0
      return retval;
382
383
85.9k
    actual = write(fd, buf, size);
384
85.9k
    if (actual == -1)
385
0
      return errno;
386
85.9k
    if (actual != (int) size)
387
0
      return EXT2_ET_SHORT_READ;
388
389
85.9k
    itr += size << 3;
390
85.9k
    cnt -= size << 3;
391
85.9k
  }
392
393
98
  size = total_size % fs->blocksize;
394
98
  memset(buf, 0, sizeof(buf));
395
98
  if (size) {
396
65
    size = fs->blocksize - size;
397
254
    while (size) {
398
189
      c = size;
399
189
      if (c > (int) sizeof(buf))
400
124
        c = sizeof(buf);
401
189
      actual = write(fd, buf, c);
402
189
      if (actual < 0)
403
0
        return errno;
404
189
      if ((size_t) actual != c)
405
0
        return EXT2_ET_SHORT_WRITE;
406
189
      size -= c;
407
189
    }
408
65
  }
409
98
  return 0;
410
98
}
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
88
{
418
88
  ext2fs_generic_bitmap bmap;
419
88
  errcode_t   retval;
420
88
  __u64     itr, cnt;
421
88
  char      buf[1024];
422
88
  unsigned int    size;
423
88
  ssize_t     actual;
424
425
88
  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
88
  } else {
437
88
    if (!fs->block_map) {
438
88
      retval = ext2fs_read_block_bitmap(fs);
439
88
      if (retval)
440
9
        return retval;
441
88
    }
442
79
    bmap = fs->block_map;
443
79
    itr = fs->super->s_first_data_block;
444
79
    cnt = EXT2_GROUPS_TO_BLOCKS(fs->super, fs->group_desc_count);
445
79
    size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
446
79
  }
447
448
79
  while (cnt > 0) {
449
79
    size = sizeof(buf);
450
79
    if (size > (cnt >> 3))
451
53
      size = (cnt >> 3);
452
79
    if (size == 0)
453
0
      break;
454
455
79
    actual = read(fd, buf, size);
456
79
    if (actual == -1)
457
0
      return errno;
458
79
    if (actual != (int) size)
459
79
      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
79
}