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