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