paul@181 | 1 | /* |
paul@181 | 2 | * rw_bitmaps.c --- routines to read and write the inode and block bitmaps. |
paul@181 | 3 | * |
paul@181 | 4 | * Copyright (C) 1993, 1994, 1994, 1996 Theodore Ts'o. |
paul@181 | 5 | * |
paul@181 | 6 | * %Begin-Header% |
paul@181 | 7 | * This file may be redistributed under the terms of the GNU Library |
paul@181 | 8 | * General Public License, version 2. |
paul@181 | 9 | * %End-Header% |
paul@181 | 10 | */ |
paul@181 | 11 | |
paul@181 | 12 | #include "config.h" |
paul@181 | 13 | #include <stdio.h> |
paul@181 | 14 | #include <string.h> |
paul@181 | 15 | #if HAVE_UNISTD_H |
paul@181 | 16 | #include <unistd.h> |
paul@181 | 17 | #endif |
paul@181 | 18 | #include <fcntl.h> |
paul@181 | 19 | #include <time.h> |
paul@181 | 20 | #ifdef HAVE_SYS_STAT_H |
paul@181 | 21 | #include <sys/stat.h> |
paul@181 | 22 | #endif |
paul@181 | 23 | #ifdef HAVE_SYS_TYPES_H |
paul@181 | 24 | #include <sys/types.h> |
paul@181 | 25 | #endif |
paul@212 | 26 | #ifdef HAVE_PTHREAD_H |
paul@212 | 27 | #include <pthread.h> |
paul@212 | 28 | #endif |
paul@181 | 29 | |
paul@181 | 30 | #include "ext2_fs.h" |
paul@181 | 31 | #include "ext2fs.h" |
paul@181 | 32 | #include "e2image.h" |
paul@181 | 33 | |
paul@212 | 34 | #ifdef HAVE_PTHREAD |
paul@212 | 35 | typedef pthread_mutex_t mutex_t; |
paul@212 | 36 | |
paul@212 | 37 | static void unix_pthread_mutex_lock(mutex_t *mutex) |
paul@212 | 38 | { |
paul@212 | 39 | if (mutex) |
paul@212 | 40 | pthread_mutex_lock(mutex); |
paul@212 | 41 | } |
paul@212 | 42 | static void unix_pthread_mutex_unlock(mutex_t *mutex) |
paul@212 | 43 | { |
paul@212 | 44 | if (mutex) |
paul@212 | 45 | pthread_mutex_unlock(mutex); |
paul@212 | 46 | } |
paul@212 | 47 | #else |
paul@212 | 48 | typedef int mutex_t; |
paul@212 | 49 | #define unix_pthread_mutex_lock(mutex_t) do {} while (0) |
paul@212 | 50 | #define unix_pthread_mutex_unlock(mutex_t) do {} while (0) |
paul@212 | 51 | #endif |
paul@212 | 52 | |
paul@181 | 53 | static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) |
paul@181 | 54 | { |
paul@181 | 55 | dgrp_t i; |
paul@181 | 56 | unsigned int j; |
paul@181 | 57 | int block_nbytes, inode_nbytes; |
paul@181 | 58 | unsigned int nbits; |
paul@181 | 59 | errcode_t retval; |
paul@181 | 60 | char *block_buf = NULL, *inode_buf = NULL; |
paul@181 | 61 | int csum_flag; |
paul@181 | 62 | blk64_t blk; |
paul@181 | 63 | blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block); |
paul@181 | 64 | ext2_ino_t ino_itr = 1; |
paul@181 | 65 | |
paul@181 | 66 | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
paul@181 | 67 | |
paul@181 | 68 | if (!(fs->flags & EXT2_FLAG_RW)) |
paul@181 | 69 | return EXT2_ET_RO_FILSYS; |
paul@181 | 70 | |
paul@181 | 71 | csum_flag = ext2fs_has_group_desc_csum(fs); |
paul@181 | 72 | |
paul@181 | 73 | inode_nbytes = block_nbytes = 0; |
paul@181 | 74 | if (do_block) { |
paul@181 | 75 | block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; |
paul@181 | 76 | retval = io_channel_alloc_buf(fs->io, 0, &block_buf); |
paul@181 | 77 | if (retval) |
paul@181 | 78 | goto errout; |
paul@181 | 79 | memset(block_buf, 0xff, fs->blocksize); |
paul@181 | 80 | } |
paul@181 | 81 | if (do_inode) { |
paul@181 | 82 | inode_nbytes = (size_t) |
paul@181 | 83 | ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8); |
paul@181 | 84 | retval = io_channel_alloc_buf(fs->io, 0, &inode_buf); |
paul@181 | 85 | if (retval) |
paul@181 | 86 | goto errout; |
paul@181 | 87 | memset(inode_buf, 0xff, fs->blocksize); |
paul@181 | 88 | } |
paul@181 | 89 | |
paul@181 | 90 | for (i = 0; i < fs->group_desc_count; i++) { |
paul@181 | 91 | if (!do_block) |
paul@181 | 92 | goto skip_block_bitmap; |
paul@181 | 93 | |
paul@181 | 94 | if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) |
paul@181 | 95 | ) |
paul@181 | 96 | goto skip_this_block_bitmap; |
paul@181 | 97 | |
paul@181 | 98 | retval = ext2fs_get_block_bitmap_range2(fs->block_map, |
paul@181 | 99 | blk_itr, block_nbytes << 3, block_buf); |
paul@181 | 100 | if (retval) |
paul@181 | 101 | goto errout; |
paul@181 | 102 | |
paul@181 | 103 | if (i == fs->group_desc_count - 1) { |
paul@181 | 104 | /* Force bitmap padding for the last group */ |
paul@181 | 105 | nbits = EXT2FS_NUM_B2C(fs, |
paul@181 | 106 | ((ext2fs_blocks_count(fs->super) |
paul@181 | 107 | - (__u64) fs->super->s_first_data_block) |
paul@181 | 108 | % (__u64) EXT2_BLOCKS_PER_GROUP(fs->super))); |
paul@181 | 109 | if (nbits) |
paul@181 | 110 | for (j = nbits; j < fs->blocksize * 8; j++) |
paul@181 | 111 | ext2fs_set_bit(j, block_buf); |
paul@181 | 112 | } |
paul@181 | 113 | |
paul@181 | 114 | retval = ext2fs_block_bitmap_csum_set(fs, i, block_buf, |
paul@181 | 115 | block_nbytes); |
paul@181 | 116 | if (retval) |
paul@181 | 117 | return retval; |
paul@181 | 118 | ext2fs_group_desc_csum_set(fs, i); |
paul@181 | 119 | fs->flags |= EXT2_FLAG_DIRTY; |
paul@181 | 120 | |
paul@181 | 121 | blk = ext2fs_block_bitmap_loc(fs, i); |
paul@181 | 122 | if (blk) { |
paul@181 | 123 | retval = io_channel_write_blk64(fs->io, blk, 1, |
paul@181 | 124 | block_buf); |
paul@181 | 125 | if (retval) { |
paul@181 | 126 | retval = EXT2_ET_BLOCK_BITMAP_WRITE; |
paul@181 | 127 | goto errout; |
paul@181 | 128 | } |
paul@181 | 129 | } |
paul@181 | 130 | skip_this_block_bitmap: |
paul@181 | 131 | blk_itr += block_nbytes << 3; |
paul@181 | 132 | skip_block_bitmap: |
paul@181 | 133 | |
paul@181 | 134 | if (!do_inode) |
paul@181 | 135 | continue; |
paul@181 | 136 | |
paul@181 | 137 | if (csum_flag && ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) |
paul@181 | 138 | ) |
paul@181 | 139 | goto skip_this_inode_bitmap; |
paul@181 | 140 | |
paul@181 | 141 | retval = ext2fs_get_inode_bitmap_range2(fs->inode_map, |
paul@181 | 142 | ino_itr, inode_nbytes << 3, inode_buf); |
paul@181 | 143 | if (retval) |
paul@181 | 144 | goto errout; |
paul@181 | 145 | |
paul@181 | 146 | retval = ext2fs_inode_bitmap_csum_set(fs, i, inode_buf, |
paul@181 | 147 | inode_nbytes); |
paul@181 | 148 | if (retval) |
paul@181 | 149 | goto errout; |
paul@181 | 150 | ext2fs_group_desc_csum_set(fs, i); |
paul@181 | 151 | fs->flags |= EXT2_FLAG_DIRTY; |
paul@181 | 152 | |
paul@181 | 153 | blk = ext2fs_inode_bitmap_loc(fs, i); |
paul@181 | 154 | if (blk) { |
paul@181 | 155 | retval = io_channel_write_blk64(fs->io, blk, 1, |
paul@181 | 156 | inode_buf); |
paul@181 | 157 | if (retval) { |
paul@181 | 158 | retval = EXT2_ET_INODE_BITMAP_WRITE; |
paul@181 | 159 | goto errout; |
paul@181 | 160 | } |
paul@181 | 161 | } |
paul@181 | 162 | skip_this_inode_bitmap: |
paul@181 | 163 | ino_itr += inode_nbytes << 3; |
paul@181 | 164 | |
paul@181 | 165 | } |
paul@181 | 166 | if (do_block) { |
paul@181 | 167 | fs->flags &= ~EXT2_FLAG_BB_DIRTY; |
paul@181 | 168 | ext2fs_free_mem(&block_buf); |
paul@181 | 169 | } |
paul@181 | 170 | if (do_inode) { |
paul@181 | 171 | fs->flags &= ~EXT2_FLAG_IB_DIRTY; |
paul@181 | 172 | ext2fs_free_mem(&inode_buf); |
paul@181 | 173 | } |
paul@181 | 174 | return 0; |
paul@181 | 175 | errout: |
paul@181 | 176 | if (inode_buf) |
paul@181 | 177 | ext2fs_free_mem(&inode_buf); |
paul@181 | 178 | if (block_buf) |
paul@181 | 179 | ext2fs_free_mem(&block_buf); |
paul@181 | 180 | return retval; |
paul@181 | 181 | } |
paul@181 | 182 | |
paul@181 | 183 | static errcode_t mark_uninit_bg_group_blocks(ext2_filsys fs) |
paul@181 | 184 | { |
paul@181 | 185 | dgrp_t i; |
paul@181 | 186 | blk64_t blk; |
paul@181 | 187 | ext2fs_block_bitmap bmap = fs->block_map; |
paul@181 | 188 | |
paul@181 | 189 | for (i = 0; i < fs->group_desc_count; i++) { |
paul@181 | 190 | if (!ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT)) |
paul@181 | 191 | continue; |
paul@181 | 192 | |
paul@181 | 193 | ext2fs_reserve_super_and_bgd(fs, i, bmap); |
paul@181 | 194 | |
paul@181 | 195 | /* |
paul@181 | 196 | * Mark the blocks used for the inode table |
paul@181 | 197 | */ |
paul@181 | 198 | blk = ext2fs_inode_table_loc(fs, i); |
paul@181 | 199 | if (blk) |
paul@181 | 200 | ext2fs_mark_block_bitmap_range2(bmap, blk, |
paul@181 | 201 | fs->inode_blocks_per_group); |
paul@181 | 202 | |
paul@181 | 203 | /* |
paul@181 | 204 | * Mark block used for the block bitmap |
paul@181 | 205 | */ |
paul@181 | 206 | blk = ext2fs_block_bitmap_loc(fs, i); |
paul@181 | 207 | if (blk) |
paul@181 | 208 | ext2fs_mark_block_bitmap2(bmap, blk); |
paul@181 | 209 | |
paul@181 | 210 | /* |
paul@181 | 211 | * Mark block used for the inode bitmap |
paul@181 | 212 | */ |
paul@181 | 213 | blk = ext2fs_inode_bitmap_loc(fs, i); |
paul@181 | 214 | if (blk) |
paul@181 | 215 | ext2fs_mark_block_bitmap2(bmap, blk); |
paul@181 | 216 | } |
paul@181 | 217 | return 0; |
paul@181 | 218 | } |
paul@181 | 219 | |
paul@212 | 220 | static int bitmap_tail_verify(unsigned char *bitmap, int first, int last) |
paul@212 | 221 | { |
paul@212 | 222 | int i; |
paul@212 | 223 | |
paul@212 | 224 | for (i = first; i <= last; i++) |
paul@212 | 225 | if (bitmap[i] != 0xff) |
paul@212 | 226 | return 0; |
paul@212 | 227 | return 1; |
paul@212 | 228 | } |
paul@212 | 229 | |
paul@212 | 230 | static errcode_t read_bitmaps_range_prepare(ext2_filsys fs, int flags) |
paul@212 | 231 | { |
paul@212 | 232 | errcode_t retval; |
paul@212 | 233 | int block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; |
paul@212 | 234 | int inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8; |
paul@212 | 235 | char *buf; |
paul@212 | 236 | |
paul@212 | 237 | EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); |
paul@212 | 238 | |
paul@212 | 239 | if ((block_nbytes > (int) fs->blocksize) || |
paul@212 | 240 | (inode_nbytes > (int) fs->blocksize)) |
paul@212 | 241 | return EXT2_ET_CORRUPT_SUPERBLOCK; |
paul@212 | 242 | |
paul@212 | 243 | fs->write_bitmaps = ext2fs_write_bitmaps; |
paul@212 | 244 | |
paul@212 | 245 | retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); |
paul@212 | 246 | if (retval) |
paul@212 | 247 | return retval; |
paul@212 | 248 | |
paul@212 | 249 | if (flags & EXT2FS_BITMAPS_BLOCK) { |
paul@212 | 250 | if (fs->block_map) |
paul@212 | 251 | ext2fs_free_block_bitmap(fs->block_map); |
paul@212 | 252 | strcpy(buf, "block bitmap for "); |
paul@212 | 253 | strcat(buf, fs->device_name); |
paul@212 | 254 | retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); |
paul@212 | 255 | if (retval) |
paul@212 | 256 | goto cleanup; |
paul@212 | 257 | } |
paul@212 | 258 | |
paul@212 | 259 | if (flags & EXT2FS_BITMAPS_INODE) { |
paul@212 | 260 | if (fs->inode_map) |
paul@212 | 261 | ext2fs_free_inode_bitmap(fs->inode_map); |
paul@212 | 262 | strcpy(buf, "inode bitmap for "); |
paul@212 | 263 | strcat(buf, fs->device_name); |
paul@212 | 264 | retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); |
paul@212 | 265 | if (retval) |
paul@212 | 266 | goto cleanup; |
paul@212 | 267 | } |
paul@212 | 268 | ext2fs_free_mem(&buf); |
paul@212 | 269 | return retval; |
paul@212 | 270 | |
paul@212 | 271 | cleanup: |
paul@212 | 272 | if (flags & EXT2FS_BITMAPS_BLOCK) { |
paul@212 | 273 | ext2fs_free_block_bitmap(fs->block_map); |
paul@212 | 274 | fs->block_map = 0; |
paul@212 | 275 | } |
paul@212 | 276 | if (flags & EXT2FS_BITMAPS_INODE) { |
paul@212 | 277 | ext2fs_free_inode_bitmap(fs->inode_map); |
paul@212 | 278 | fs->inode_map = 0; |
paul@212 | 279 | } |
paul@212 | 280 | ext2fs_free_mem(&buf); |
paul@212 | 281 | return retval; |
paul@212 | 282 | } |
paul@212 | 283 | |
paul@212 | 284 | static errcode_t read_bitmaps_range_start(ext2_filsys fs, int flags, |
paul@212 | 285 | dgrp_t start, dgrp_t end, |
paul@212 | 286 | mutex_t *mutex, |
paul@212 | 287 | int *tail_flags) |
paul@181 | 288 | { |
paul@181 | 289 | dgrp_t i; |
paul@181 | 290 | char *block_bitmap = 0, *inode_bitmap = 0; |
paul@212 | 291 | errcode_t retval = 0; |
paul@181 | 292 | int block_nbytes = EXT2_CLUSTERS_PER_GROUP(fs->super) / 8; |
paul@181 | 293 | int inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8; |
paul@181 | 294 | int csum_flag; |
paul@181 | 295 | unsigned int cnt; |
paul@181 | 296 | blk64_t blk; |
paul@181 | 297 | blk64_t blk_itr = EXT2FS_B2C(fs, fs->super->s_first_data_block); |
paul@181 | 298 | blk64_t blk_cnt; |
paul@181 | 299 | ext2_ino_t ino_itr = 1; |
paul@181 | 300 | ext2_ino_t ino_cnt; |
paul@181 | 301 | |
paul@181 | 302 | csum_flag = ext2fs_has_group_desc_csum(fs); |
paul@181 | 303 | |
paul@212 | 304 | if (flags & EXT2FS_BITMAPS_BLOCK) { |
paul@181 | 305 | retval = io_channel_alloc_buf(fs->io, 0, &block_bitmap); |
paul@181 | 306 | if (retval) |
paul@181 | 307 | goto cleanup; |
paul@212 | 308 | } else { |
paul@181 | 309 | block_nbytes = 0; |
paul@212 | 310 | } |
paul@212 | 311 | |
paul@212 | 312 | if (flags & EXT2FS_BITMAPS_INODE) { |
paul@181 | 313 | retval = io_channel_alloc_buf(fs->io, 0, &inode_bitmap); |
paul@181 | 314 | if (retval) |
paul@181 | 315 | goto cleanup; |
paul@212 | 316 | } else { |
paul@181 | 317 | inode_nbytes = 0; |
paul@212 | 318 | } |
paul@181 | 319 | |
paul@212 | 320 | /* io should be null */ |
paul@181 | 321 | if (fs->flags & EXT2_FLAG_IMAGE_FILE) { |
paul@181 | 322 | blk = (ext2fs_le32_to_cpu(fs->image_header->offset_inodemap) / fs->blocksize); |
paul@181 | 323 | ino_cnt = fs->super->s_inodes_count; |
paul@181 | 324 | while (inode_bitmap && ino_cnt > 0) { |
paul@181 | 325 | retval = io_channel_read_blk64(fs->image_io, blk++, |
paul@181 | 326 | 1, inode_bitmap); |
paul@181 | 327 | if (retval) |
paul@181 | 328 | goto cleanup; |
paul@181 | 329 | cnt = fs->blocksize << 3; |
paul@181 | 330 | if (cnt > ino_cnt) |
paul@181 | 331 | cnt = ino_cnt; |
paul@181 | 332 | retval = ext2fs_set_inode_bitmap_range2(fs->inode_map, |
paul@181 | 333 | ino_itr, cnt, inode_bitmap); |
paul@181 | 334 | if (retval) |
paul@181 | 335 | goto cleanup; |
paul@181 | 336 | ino_itr += cnt; |
paul@181 | 337 | ino_cnt -= cnt; |
paul@181 | 338 | } |
paul@181 | 339 | blk = (ext2fs_le32_to_cpu(fs->image_header->offset_blockmap) / |
paul@181 | 340 | fs->blocksize); |
paul@181 | 341 | blk_cnt = EXT2_GROUPS_TO_CLUSTERS(fs->super, |
paul@181 | 342 | fs->group_desc_count); |
paul@181 | 343 | while (block_bitmap && blk_cnt > 0) { |
paul@181 | 344 | retval = io_channel_read_blk64(fs->image_io, blk++, |
paul@181 | 345 | 1, block_bitmap); |
paul@181 | 346 | if (retval) |
paul@181 | 347 | goto cleanup; |
paul@181 | 348 | cnt = fs->blocksize << 3; |
paul@181 | 349 | if (cnt > blk_cnt) |
paul@181 | 350 | cnt = blk_cnt; |
paul@181 | 351 | retval = ext2fs_set_block_bitmap_range2(fs->block_map, |
paul@181 | 352 | blk_itr, cnt, block_bitmap); |
paul@181 | 353 | if (retval) |
paul@181 | 354 | goto cleanup; |
paul@181 | 355 | blk_itr += cnt; |
paul@181 | 356 | blk_cnt -= cnt; |
paul@181 | 357 | } |
paul@212 | 358 | goto cleanup; |
paul@181 | 359 | } |
paul@181 | 360 | |
paul@212 | 361 | blk_itr += ((blk64_t)start * (block_nbytes << 3)); |
paul@212 | 362 | ino_itr += ((blk64_t)start * (inode_nbytes << 3)); |
paul@212 | 363 | for (i = start; i <= end; i++) { |
paul@181 | 364 | if (block_bitmap) { |
paul@181 | 365 | blk = ext2fs_block_bitmap_loc(fs, i); |
paul@212 | 366 | if ((csum_flag && |
paul@212 | 367 | ext2fs_bg_flags_test(fs, i, EXT2_BG_BLOCK_UNINIT) && |
paul@212 | 368 | ext2fs_group_desc_csum_verify(fs, i)) || |
paul@212 | 369 | (blk >= ext2fs_blocks_count(fs->super))) |
paul@181 | 370 | blk = 0; |
paul@181 | 371 | if (blk) { |
paul@181 | 372 | retval = io_channel_read_blk64(fs->io, blk, |
paul@181 | 373 | 1, block_bitmap); |
paul@181 | 374 | if (retval) { |
paul@181 | 375 | retval = EXT2_ET_BLOCK_BITMAP_READ; |
paul@181 | 376 | goto cleanup; |
paul@181 | 377 | } |
paul@181 | 378 | /* verify block bitmap checksum */ |
paul@181 | 379 | if (!(fs->flags & |
paul@181 | 380 | EXT2_FLAG_IGNORE_CSUM_ERRORS) && |
paul@181 | 381 | !ext2fs_block_bitmap_csum_verify(fs, i, |
paul@181 | 382 | block_bitmap, block_nbytes)) { |
paul@181 | 383 | retval = |
paul@181 | 384 | EXT2_ET_BLOCK_BITMAP_CSUM_INVALID; |
paul@181 | 385 | goto cleanup; |
paul@181 | 386 | } |
paul@212 | 387 | if (!bitmap_tail_verify((unsigned char *) block_bitmap, |
paul@212 | 388 | block_nbytes, fs->blocksize - 1)) |
paul@212 | 389 | *tail_flags |= EXT2_FLAG_BBITMAP_TAIL_PROBLEM; |
paul@181 | 390 | } else |
paul@181 | 391 | memset(block_bitmap, 0, block_nbytes); |
paul@181 | 392 | cnt = block_nbytes << 3; |
paul@212 | 393 | unix_pthread_mutex_lock(mutex); |
paul@181 | 394 | retval = ext2fs_set_block_bitmap_range2(fs->block_map, |
paul@181 | 395 | blk_itr, cnt, block_bitmap); |
paul@212 | 396 | unix_pthread_mutex_unlock(mutex); |
paul@181 | 397 | if (retval) |
paul@181 | 398 | goto cleanup; |
paul@181 | 399 | blk_itr += block_nbytes << 3; |
paul@181 | 400 | } |
paul@181 | 401 | if (inode_bitmap) { |
paul@181 | 402 | blk = ext2fs_inode_bitmap_loc(fs, i); |
paul@212 | 403 | if ((csum_flag && |
paul@212 | 404 | ext2fs_bg_flags_test(fs, i, EXT2_BG_INODE_UNINIT) && |
paul@212 | 405 | ext2fs_group_desc_csum_verify(fs, i)) || |
paul@212 | 406 | (blk >= ext2fs_blocks_count(fs->super))) |
paul@181 | 407 | blk = 0; |
paul@181 | 408 | if (blk) { |
paul@181 | 409 | retval = io_channel_read_blk64(fs->io, blk, |
paul@181 | 410 | 1, inode_bitmap); |
paul@181 | 411 | if (retval) { |
paul@181 | 412 | retval = EXT2_ET_INODE_BITMAP_READ; |
paul@181 | 413 | goto cleanup; |
paul@181 | 414 | } |
paul@181 | 415 | |
paul@181 | 416 | /* verify inode bitmap checksum */ |
paul@181 | 417 | if (!(fs->flags & |
paul@181 | 418 | EXT2_FLAG_IGNORE_CSUM_ERRORS) && |
paul@181 | 419 | !ext2fs_inode_bitmap_csum_verify(fs, i, |
paul@181 | 420 | inode_bitmap, inode_nbytes)) { |
paul@181 | 421 | retval = |
paul@181 | 422 | EXT2_ET_INODE_BITMAP_CSUM_INVALID; |
paul@181 | 423 | goto cleanup; |
paul@181 | 424 | } |
paul@212 | 425 | if (!bitmap_tail_verify((unsigned char *) inode_bitmap, |
paul@212 | 426 | inode_nbytes, fs->blocksize - 1)) |
paul@212 | 427 | *tail_flags |= EXT2_FLAG_IBITMAP_TAIL_PROBLEM; |
paul@181 | 428 | } else |
paul@181 | 429 | memset(inode_bitmap, 0, inode_nbytes); |
paul@181 | 430 | cnt = inode_nbytes << 3; |
paul@212 | 431 | unix_pthread_mutex_lock(mutex); |
paul@181 | 432 | retval = ext2fs_set_inode_bitmap_range2(fs->inode_map, |
paul@181 | 433 | ino_itr, cnt, inode_bitmap); |
paul@212 | 434 | unix_pthread_mutex_unlock(mutex); |
paul@181 | 435 | if (retval) |
paul@181 | 436 | goto cleanup; |
paul@181 | 437 | ino_itr += inode_nbytes << 3; |
paul@181 | 438 | } |
paul@181 | 439 | } |
paul@181 | 440 | |
paul@212 | 441 | cleanup: |
paul@181 | 442 | if (inode_bitmap) |
paul@181 | 443 | ext2fs_free_mem(&inode_bitmap); |
paul@181 | 444 | if (block_bitmap) |
paul@181 | 445 | ext2fs_free_mem(&block_bitmap); |
paul@212 | 446 | return retval; |
paul@212 | 447 | } |
paul@212 | 448 | |
paul@212 | 449 | static errcode_t read_bitmaps_range_end(ext2_filsys fs, int flags, |
paul@212 | 450 | int tail_flags) |
paul@212 | 451 | { |
paul@212 | 452 | errcode_t retval; |
paul@181 | 453 | |
paul@212 | 454 | /* Mark group blocks for any BLOCK_UNINIT groups */ |
paul@212 | 455 | if (flags & EXT2FS_BITMAPS_BLOCK) { |
paul@212 | 456 | retval = mark_uninit_bg_group_blocks(fs); |
paul@212 | 457 | if (retval) |
paul@212 | 458 | return retval; |
paul@212 | 459 | fs->flags &= ~EXT2_FLAG_BBITMAP_TAIL_PROBLEM; |
paul@212 | 460 | } |
paul@212 | 461 | if (flags & EXT2FS_BITMAPS_INODE) |
paul@212 | 462 | fs->flags &= ~EXT2_FLAG_IBITMAP_TAIL_PROBLEM; |
paul@212 | 463 | fs->flags |= tail_flags; |
paul@212 | 464 | |
paul@212 | 465 | return 0; |
paul@212 | 466 | } |
paul@212 | 467 | |
paul@212 | 468 | static void read_bitmaps_cleanup_on_error(ext2_filsys fs, int flags) |
paul@212 | 469 | { |
paul@212 | 470 | if (flags & EXT2FS_BITMAPS_BLOCK) { |
paul@212 | 471 | ext2fs_free_block_bitmap(fs->block_map); |
paul@181 | 472 | fs->block_map = 0; |
paul@181 | 473 | } |
paul@212 | 474 | if (flags & EXT2FS_BITMAPS_INODE) { |
paul@212 | 475 | ext2fs_free_inode_bitmap(fs->inode_map); |
paul@181 | 476 | fs->inode_map = 0; |
paul@181 | 477 | } |
paul@212 | 478 | } |
paul@212 | 479 | |
paul@212 | 480 | static errcode_t read_bitmaps_range(ext2_filsys fs, int flags, |
paul@212 | 481 | dgrp_t start, dgrp_t end) |
paul@212 | 482 | { |
paul@212 | 483 | errcode_t retval; |
paul@212 | 484 | int tail_flags = 0; |
paul@212 | 485 | |
paul@212 | 486 | retval = read_bitmaps_range_prepare(fs, flags); |
paul@212 | 487 | if (retval) |
paul@212 | 488 | return retval; |
paul@212 | 489 | |
paul@212 | 490 | retval = read_bitmaps_range_start(fs, flags, start, end, |
paul@212 | 491 | NULL, &tail_flags); |
paul@212 | 492 | if (retval == 0) |
paul@212 | 493 | retval = read_bitmaps_range_end(fs, flags, tail_flags); |
paul@212 | 494 | if (retval) |
paul@212 | 495 | read_bitmaps_cleanup_on_error(fs, flags); |
paul@181 | 496 | return retval; |
paul@181 | 497 | } |
paul@181 | 498 | |
paul@212 | 499 | #ifdef HAVE_PTHREAD |
paul@212 | 500 | struct read_bitmaps_thread_info { |
paul@212 | 501 | ext2_filsys rbt_fs; |
paul@212 | 502 | int rbt_flags; |
paul@212 | 503 | dgrp_t rbt_grp_start; |
paul@212 | 504 | dgrp_t rbt_grp_end; |
paul@212 | 505 | errcode_t rbt_retval; |
paul@212 | 506 | pthread_mutex_t *rbt_mutex; |
paul@212 | 507 | int rbt_tail_flags; |
paul@212 | 508 | }; |
paul@212 | 509 | |
paul@212 | 510 | static void *read_bitmaps_thread(void *data) |
paul@212 | 511 | { |
paul@212 | 512 | struct read_bitmaps_thread_info *rbt = data; |
paul@212 | 513 | |
paul@212 | 514 | rbt->rbt_retval = read_bitmaps_range_start(rbt->rbt_fs, rbt->rbt_flags, |
paul@212 | 515 | rbt->rbt_grp_start, rbt->rbt_grp_end, |
paul@212 | 516 | rbt->rbt_mutex, &rbt->rbt_tail_flags); |
paul@212 | 517 | return NULL; |
paul@212 | 518 | } |
paul@212 | 519 | #endif |
paul@212 | 520 | |
paul@212 | 521 | errcode_t ext2fs_rw_bitmaps(ext2_filsys fs, int flags, int num_threads) |
paul@212 | 522 | { |
paul@212 | 523 | #ifdef HAVE_PTHREAD |
paul@212 | 524 | pthread_attr_t attr; |
paul@212 | 525 | pthread_t *thread_ids = NULL; |
paul@212 | 526 | struct read_bitmaps_thread_info *thread_infos = NULL; |
paul@212 | 527 | pthread_mutex_t rbt_mutex = PTHREAD_MUTEX_INITIALIZER; |
paul@212 | 528 | errcode_t retval; |
paul@212 | 529 | errcode_t rc; |
paul@212 | 530 | unsigned flexbg_size = 1 << fs->super->s_log_groups_per_flex; |
paul@212 | 531 | dgrp_t average_group; |
paul@212 | 532 | int i, tail_flags = 0; |
paul@212 | 533 | #endif |
paul@212 | 534 | |
paul@212 | 535 | if (flags & ~EXT2FS_BITMAPS_VALID_FLAGS) |
paul@212 | 536 | return EXT2_ET_INVALID_ARGUMENT; |
paul@212 | 537 | |
paul@212 | 538 | if (flags & EXT2FS_BITMAPS_WRITE) |
paul@212 | 539 | return write_bitmaps(fs, flags & EXT2FS_BITMAPS_INODE, |
paul@212 | 540 | flags & EXT2FS_BITMAPS_BLOCK); |
paul@212 | 541 | |
paul@212 | 542 | #ifdef HAVE_PTHREAD |
paul@212 | 543 | if (((fs->io->flags & CHANNEL_FLAGS_THREADS) == 0) || |
paul@212 | 544 | (num_threads == 1) || (fs->flags & EXT2_FLAG_IMAGE_FILE)) |
paul@212 | 545 | goto fallback; |
paul@212 | 546 | |
paul@212 | 547 | #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF) |
paul@212 | 548 | if (num_threads < 0) |
paul@212 | 549 | num_threads = sysconf(_SC_NPROCESSORS_CONF); |
paul@212 | 550 | #endif |
paul@212 | 551 | /* |
paul@212 | 552 | * Guess for now; eventually we should probably define |
paul@212 | 553 | * ext2fs_get_num_cpus() and teach it how to get this info on |
paul@212 | 554 | * MacOS, FreeBSD, etc. |
paul@212 | 555 | * ref: https://stackoverflow.com/questions/150355 |
paul@212 | 556 | */ |
paul@212 | 557 | if (num_threads < 0) |
paul@212 | 558 | num_threads = 4; |
paul@212 | 559 | |
paul@212 | 560 | if (num_threads > fs->group_desc_count) |
paul@212 | 561 | num_threads = fs->group_desc_count; |
paul@212 | 562 | average_group = fs->group_desc_count / num_threads; |
paul@212 | 563 | if (ext2fs_has_feature_flex_bg(fs->super)) { |
paul@212 | 564 | average_group = (average_group / flexbg_size) * flexbg_size; |
paul@212 | 565 | } |
paul@212 | 566 | if (average_group == 0) |
paul@212 | 567 | goto fallback; |
paul@212 | 568 | |
paul@212 | 569 | io_channel_set_options(fs->io, "cache=off"); |
paul@212 | 570 | retval = pthread_attr_init(&attr); |
paul@212 | 571 | if (retval) |
paul@212 | 572 | return retval; |
paul@212 | 573 | |
paul@212 | 574 | thread_ids = calloc(sizeof(pthread_t), num_threads); |
paul@212 | 575 | if (!thread_ids) |
paul@212 | 576 | return ENOMEM; |
paul@212 | 577 | |
paul@212 | 578 | thread_infos = calloc(sizeof(struct read_bitmaps_thread_info), |
paul@212 | 579 | num_threads); |
paul@212 | 580 | if (!thread_infos) |
paul@212 | 581 | goto out; |
paul@212 | 582 | |
paul@212 | 583 | retval = read_bitmaps_range_prepare(fs, flags); |
paul@212 | 584 | if (retval) |
paul@212 | 585 | goto out; |
paul@212 | 586 | |
paul@212 | 587 | // fprintf(stdout, "Multiple threads triggered to read bitmaps\n"); |
paul@212 | 588 | for (i = 0; i < num_threads; i++) { |
paul@212 | 589 | thread_infos[i].rbt_fs = fs; |
paul@212 | 590 | thread_infos[i].rbt_flags = flags; |
paul@212 | 591 | thread_infos[i].rbt_mutex = &rbt_mutex; |
paul@212 | 592 | thread_infos[i].rbt_tail_flags = 0; |
paul@212 | 593 | if (i == 0) |
paul@212 | 594 | thread_infos[i].rbt_grp_start = 0; |
paul@212 | 595 | else |
paul@212 | 596 | thread_infos[i].rbt_grp_start = average_group * i + 1; |
paul@212 | 597 | |
paul@212 | 598 | if (i == num_threads - 1) |
paul@212 | 599 | thread_infos[i].rbt_grp_end = fs->group_desc_count - 1; |
paul@212 | 600 | else |
paul@212 | 601 | thread_infos[i].rbt_grp_end = average_group * (i + 1); |
paul@212 | 602 | retval = pthread_create(&thread_ids[i], &attr, |
paul@212 | 603 | &read_bitmaps_thread, &thread_infos[i]); |
paul@212 | 604 | if (retval) |
paul@212 | 605 | break; |
paul@212 | 606 | } |
paul@212 | 607 | for (i = 0; i < num_threads; i++) { |
paul@212 | 608 | if (!thread_ids[i]) |
paul@212 | 609 | break; |
paul@212 | 610 | rc = pthread_join(thread_ids[i], NULL); |
paul@212 | 611 | if (rc && !retval) |
paul@212 | 612 | retval = rc; |
paul@212 | 613 | rc = thread_infos[i].rbt_retval; |
paul@212 | 614 | if (rc && !retval) |
paul@212 | 615 | retval = rc; |
paul@212 | 616 | tail_flags |= thread_infos[i].rbt_tail_flags; |
paul@212 | 617 | } |
paul@212 | 618 | out: |
paul@212 | 619 | rc = pthread_attr_destroy(&attr); |
paul@212 | 620 | if (rc && !retval) |
paul@212 | 621 | retval = rc; |
paul@212 | 622 | free(thread_infos); |
paul@212 | 623 | free(thread_ids); |
paul@212 | 624 | |
paul@212 | 625 | if (retval == 0) |
paul@212 | 626 | retval = read_bitmaps_range_end(fs, flags, tail_flags); |
paul@212 | 627 | if (retval) |
paul@212 | 628 | read_bitmaps_cleanup_on_error(fs, flags); |
paul@212 | 629 | /* XXX should save and restore cache setting */ |
paul@212 | 630 | io_channel_set_options(fs->io, "cache=on"); |
paul@212 | 631 | return retval; |
paul@212 | 632 | fallback: |
paul@212 | 633 | #endif /* HAVE_PTHREAD */ |
paul@212 | 634 | return read_bitmaps_range(fs, flags, 0, fs->group_desc_count - 1); |
paul@212 | 635 | } |
paul@212 | 636 | |
paul@181 | 637 | errcode_t ext2fs_read_inode_bitmap(ext2_filsys fs) |
paul@181 | 638 | { |
paul@212 | 639 | return ext2fs_rw_bitmaps(fs, EXT2FS_BITMAPS_INODE, -1); |
paul@181 | 640 | } |
paul@181 | 641 | |
paul@181 | 642 | errcode_t ext2fs_read_block_bitmap(ext2_filsys fs) |
paul@181 | 643 | { |
paul@212 | 644 | return ext2fs_rw_bitmaps(fs, EXT2FS_BITMAPS_BLOCK, -1); |
paul@181 | 645 | } |
paul@181 | 646 | |
paul@181 | 647 | errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs) |
paul@181 | 648 | { |
paul@181 | 649 | return write_bitmaps(fs, 1, 0); |
paul@181 | 650 | } |
paul@181 | 651 | |
paul@181 | 652 | errcode_t ext2fs_write_block_bitmap (ext2_filsys fs) |
paul@181 | 653 | { |
paul@181 | 654 | return write_bitmaps(fs, 0, 1); |
paul@181 | 655 | } |
paul@181 | 656 | |
paul@181 | 657 | errcode_t ext2fs_read_bitmaps(ext2_filsys fs) |
paul@181 | 658 | { |
paul@212 | 659 | int flags = 0; |
paul@212 | 660 | |
paul@212 | 661 | if (!fs->inode_map) |
paul@212 | 662 | flags |= EXT2FS_BITMAPS_INODE; |
paul@212 | 663 | if (!fs->block_map) |
paul@212 | 664 | flags |= EXT2FS_BITMAPS_BLOCK; |
paul@212 | 665 | if (flags == 0) |
paul@181 | 666 | return 0; |
paul@212 | 667 | return ext2fs_rw_bitmaps(fs, flags, -1); |
paul@181 | 668 | } |
paul@181 | 669 | |
paul@181 | 670 | errcode_t ext2fs_write_bitmaps(ext2_filsys fs) |
paul@181 | 671 | { |
paul@181 | 672 | int do_inode = fs->inode_map && ext2fs_test_ib_dirty(fs); |
paul@181 | 673 | int do_block = fs->block_map && ext2fs_test_bb_dirty(fs); |
paul@181 | 674 | |
paul@181 | 675 | if (!do_inode && !do_block) |
paul@181 | 676 | return 0; |
paul@181 | 677 | |
paul@181 | 678 | return write_bitmaps(fs, do_inode, do_block); |
paul@181 | 679 | } |