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 <stdio.h> 17 #include <string.h> 18 #if HAVE_UNISTD_H 19 #include <unistd.h> 20 #endif 21 #if HAVE_ERRNO_H 22 #include <errno.h> 23 #endif 24 #include <fcntl.h> 25 #include <time.h> 26 #if HAVE_SYS_STAT_H 27 #include <sys/stat.h> 28 #endif 29 #if HAVE_SYS_TYPES_H 30 #include <sys/types.h> 31 #endif 32 33 #include "ext2_fs.h" 34 #include "ext2fs.h" 35 36 #ifndef HAVE_TYPE_SSIZE_T 37 typedef int ssize_t; 38 #endif 39 40 /* 41 * This function returns 1 if the specified block is all zeros 42 */ 43 static int check_zero_block(char *buf, int blocksize) 44 { 45 char *cp = buf; 46 int left = blocksize; 47 48 while (left > 0) { 49 if (*cp++) 50 return 0; 51 left--; 52 } 53 return 1; 54 } 55 56 /* 57 * Write the inode table out as a single block. 58 */ 59 #define BUF_BLOCKS 32 60 61 errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags) 62 { 63 unsigned int group, left, c, d; 64 char *buf, *cp; 65 blk64_t blk; 66 ssize_t actual; 67 errcode_t retval; 68 off_t r; 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 r = lseek(fd, fs->blocksize, SEEK_CUR); 101 if (r < 0) { 102 retval = errno; 103 goto errout; 104 } 105 continue; 106 } 107 /* Find non-zero blocks */ 108 for (d=1; d < c; d++) { 109 if (check_zero_block(cp + d*fs->blocksize, fs->blocksize)) 110 break; 111 } 112 skip_sparse: 113 actual = write(fd, cp, fs->blocksize * d); 114 if (actual == -1) { 115 retval = errno; 116 goto errout; 117 } 118 if (actual != (ssize_t) (fs->blocksize * d)) { 119 retval = EXT2_ET_SHORT_WRITE; 120 goto errout; 121 } 122 blk += d; 123 left -= d; 124 cp += fs->blocksize * d; 125 c -= d; 126 } 127 } 128 } 129 retval = 0; 130 131 errout: 132 free(buf); 133 return retval; 134 } 135 136 /* 137 * Read in the inode table and stuff it into place 138 */ 139 errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, 140 int flags EXT2FS_ATTR((unused))) 141 { 142 unsigned int group, c, left; 143 char *buf; 144 blk64_t blk; 145 ssize_t actual; 146 errcode_t retval; 147 148 buf = malloc(fs->blocksize * BUF_BLOCKS); 149 if (!buf) 150 return ENOMEM; 151 152 for (group = 0; group < fs->group_desc_count; group++) { 153 blk = ext2fs_inode_table_loc(fs, (unsigned)group); 154 if (!blk) { 155 retval = EXT2_ET_MISSING_INODE_TABLE; 156 goto errout; 157 } 158 left = fs->inode_blocks_per_group; 159 while (left) { 160 c = BUF_BLOCKS; 161 if (c > left) 162 c = left; 163 actual = read(fd, buf, fs->blocksize * c); 164 if (actual == -1) { 165 retval = errno; 166 goto errout; 167 } 168 if (actual != (ssize_t) (fs->blocksize * c)) { 169 retval = EXT2_ET_SHORT_READ; 170 goto errout; 171 } 172 retval = io_channel_write_blk64(fs->io, blk, c, buf); 173 if (retval) 174 goto errout; 175 176 blk += c; 177 left -= c; 178 } 179 } 180 retval = ext2fs_flush_icache(fs); 181 182 errout: 183 free(buf); 184 return retval; 185 } 186 187 /* 188 * Write out superblock and group descriptors 189 */ 190 errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, 191 int flags EXT2FS_ATTR((unused))) 192 { 193 char *buf, *cp; 194 ssize_t actual; 195 errcode_t retval; 196 197 buf = malloc(fs->blocksize); 198 if (!buf) 199 return ENOMEM; 200 201 /* 202 * Write out the superblock 203 */ 204 memset(buf, 0, fs->blocksize); 205 memcpy(buf, fs->super, SUPERBLOCK_SIZE); 206 actual = write(fd, buf, fs->blocksize); 207 if (actual == -1) { 208 retval = errno; 209 goto errout; 210 } 211 if (actual != (ssize_t) fs->blocksize) { 212 retval = EXT2_ET_SHORT_WRITE; 213 goto errout; 214 } 215 216 /* 217 * Now write out the block group descriptors 218 */ 219 cp = (char *) fs->group_desc; 220 actual = write(fd, cp, fs->blocksize * fs->desc_blocks); 221 if (actual == -1) { 222 retval = errno; 223 goto errout; 224 } 225 if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) { 226 retval = EXT2_ET_SHORT_WRITE; 227 goto errout; 228 } 229 230 retval = 0; 231 232 errout: 233 free(buf); 234 return retval; 235 } 236 237 /* 238 * Read the superblock and group descriptors and overwrite them. 239 */ 240 errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, 241 int flags EXT2FS_ATTR((unused))) 242 { 243 char *buf; 244 ssize_t actual, size; 245 errcode_t retval; 246 247 size = fs->blocksize * (fs->group_desc_count + 1); 248 buf = malloc(size); 249 if (!buf) 250 return ENOMEM; 251 252 /* 253 * Read it all in. 254 */ 255 actual = read(fd, buf, size); 256 if (actual == -1) { 257 retval = errno; 258 goto errout; 259 } 260 if (actual != size) { 261 retval = EXT2_ET_SHORT_READ; 262 goto errout; 263 } 264 265 /* 266 * Now copy in the superblock and group descriptors 267 */ 268 memcpy(fs->super, buf, SUPERBLOCK_SIZE); 269 270 memcpy(fs->group_desc, buf + fs->blocksize, 271 fs->blocksize * fs->group_desc_count); 272 273 retval = 0; 274 275 errout: 276 free(buf); 277 return retval; 278 } 279 280 /* 281 * Write the block/inode bitmaps. 282 */ 283 errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags) 284 { 285 ext2fs_generic_bitmap bmap; 286 errcode_t retval; 287 ssize_t actual; 288 __u32 itr, cnt, size; 289 int c, total_size; 290 char buf[1024]; 291 292 if (flags & IMAGER_FLAG_INODEMAP) { 293 if (!fs->inode_map) { 294 retval = ext2fs_read_inode_bitmap(fs); 295 if (retval) 296 return retval; 297 } 298 bmap = fs->inode_map; 299 itr = 1; 300 cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count; 301 size = (EXT2_INODES_PER_GROUP(fs->super) / 8); 302 } else { 303 if (!fs->block_map) { 304 retval = ext2fs_read_block_bitmap(fs); 305 if (retval) 306 return retval; 307 } 308 bmap = fs->block_map; 309 itr = fs->super->s_first_data_block; 310 cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count; 311 size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; 312 } 313 total_size = size * fs->group_desc_count; 314 315 while (cnt > 0) { 316 size = sizeof(buf); 317 if (size > (cnt >> 3)) 318 size = (cnt >> 3); 319 320 retval = ext2fs_get_generic_bmap_range(bmap, itr, 321 size << 3, buf); 322 if (retval) 323 return retval; 324 325 actual = write(fd, buf, size); 326 if (actual == -1) 327 return errno; 328 if (actual != (int) size) 329 return EXT2_ET_SHORT_READ; 330 331 itr += size << 3; 332 cnt -= size << 3; 333 } 334 335 size = total_size % fs->blocksize; 336 memset(buf, 0, sizeof(buf)); 337 if (size) { 338 size = fs->blocksize - size; 339 while (size) { 340 c = size; 341 if (c > (int) sizeof(buf)) 342 c = sizeof(buf); 343 actual = write(fd, buf, c); 344 if (actual == -1) 345 return errno; 346 if (actual != c) 347 return EXT2_ET_SHORT_WRITE; 348 size -= c; 349 } 350 } 351 return 0; 352 } 353 354 355 /* 356 * Read the block/inode bitmaps. 357 */ 358 errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags) 359 { 360 ext2fs_generic_bitmap bmap; 361 errcode_t retval; 362 __u32 itr, cnt; 363 char buf[1024]; 364 unsigned int size; 365 ssize_t actual; 366 367 if (flags & IMAGER_FLAG_INODEMAP) { 368 if (!fs->inode_map) { 369 retval = ext2fs_read_inode_bitmap(fs); 370 if (retval) 371 return retval; 372 } 373 bmap = fs->inode_map; 374 itr = 1; 375 cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count; 376 size = (EXT2_INODES_PER_GROUP(fs->super) / 8); 377 } else { 378 if (!fs->block_map) { 379 retval = ext2fs_read_block_bitmap(fs); 380 if (retval) 381 return retval; 382 } 383 bmap = fs->block_map; 384 itr = fs->super->s_first_data_block; 385 cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count; 386 size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; 387 } 388 389 while (cnt > 0) { 390 size = sizeof(buf); 391 if (size > (cnt >> 3)) 392 size = (cnt >> 3); 393 394 actual = read(fd, buf, size); 395 if (actual == -1) 396 return errno; 397 if (actual != (int) size) 398 return EXT2_ET_SHORT_READ; 399 400 retval = ext2fs_set_generic_bmap_range(bmap, itr, 401 size << 3, buf); 402 if (retval) 403 return retval; 404 405 itr += size << 3; 406 cnt -= size << 3; 407 } 408 return 0; 409 } 410