Home | History | Annotate | Download | only in ext4_utils
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "ext4_utils/ext4_utils.h"
     18 
     19 #include <fcntl.h>
     20 #include <inttypes.h>
     21 #include <stddef.h>
     22 #include <string.h>
     23 #include <sys/stat.h>
     24 #include <sys/types.h>
     25 
     26 #ifdef _WIN32
     27 #include <winsock2.h>
     28 #else
     29 #include <arpa/inet.h>
     30 #include <sys/ioctl.h>
     31 #endif
     32 
     33 #if defined(__linux__)
     34 #include <linux/fs.h>
     35 #elif defined(__APPLE__) && defined(__MACH__)
     36 #include <sys/disk.h>
     37 #endif
     38 
     39 int force = 0;
     40 struct fs_info info;
     41 struct fs_aux_info aux_info;
     42 
     43 jmp_buf setjmp_env;
     44 
     45 /* returns 1 if a is a power of b */
     46 static int is_power_of(int a, int b)
     47 {
     48 	while (a > b) {
     49 		if (a % b)
     50 			return 0;
     51 		a /= b;
     52 	}
     53 
     54 	return (a == b) ? 1 : 0;
     55 }
     56 
     57 int bitmap_get_bit(u8 *bitmap, u32 bit)
     58 {
     59 	if (bitmap[bit / 8] & (1 << (bit % 8)))
     60 		return 1;
     61 
     62 	return 0;
     63 }
     64 
     65 void bitmap_clear_bit(u8 *bitmap, u32 bit)
     66 {
     67 	bitmap[bit / 8] &= ~(1 << (bit % 8));
     68 
     69 	return;
     70 }
     71 
     72 /* Returns 1 if the bg contains a backup superblock.  On filesystems with
     73    the sparse_super feature, only block groups 0, 1, and powers of 3, 5,
     74    and 7 have backup superblocks.  Otherwise, all block groups have backup
     75    superblocks */
     76 int ext4_bg_has_super_block(int bg)
     77 {
     78 	/* Without sparse_super, every block group has a superblock */
     79 	if (!(info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER))
     80 		return 1;
     81 
     82 	if (bg == 0 || bg == 1)
     83 		return 1;
     84 
     85 	if (is_power_of(bg, 3) || is_power_of(bg, 5) || is_power_of(bg, 7))
     86 		return 1;
     87 
     88 	return 0;
     89 }
     90 
     91 /* Function to read the primary superblock */
     92 void read_sb(int fd, struct ext4_super_block *sb)
     93 {
     94 	off64_t ret;
     95 
     96 	ret = lseek64(fd, 1024, SEEK_SET);
     97 	if (ret < 0)
     98 		critical_error_errno("failed to seek to superblock");
     99 
    100 	ret = read(fd, sb, sizeof(*sb));
    101 	if (ret < 0)
    102 		critical_error_errno("failed to read superblock");
    103 	if (ret != sizeof(*sb))
    104 		critical_error("failed to read all of superblock");
    105 }
    106 
    107 /* Compute the rest of the parameters of the filesystem from the basic info */
    108 void ext4_create_fs_aux_info()
    109 {
    110 	aux_info.first_data_block = (info.block_size > 1024) ? 0 : 1;
    111 	aux_info.len_blocks = info.len / info.block_size;
    112 	aux_info.inode_table_blocks = DIV_ROUND_UP(info.inodes_per_group * info.inode_size,
    113 		info.block_size);
    114 	aux_info.groups = DIV_ROUND_UP(aux_info.len_blocks - aux_info.first_data_block,
    115 		info.blocks_per_group);
    116 	aux_info.blocks_per_ind = info.block_size / sizeof(u32);
    117 	aux_info.blocks_per_dind = aux_info.blocks_per_ind * aux_info.blocks_per_ind;
    118 	aux_info.blocks_per_tind = aux_info.blocks_per_dind * aux_info.blocks_per_dind;
    119 
    120 	aux_info.bg_desc_blocks =
    121 		DIV_ROUND_UP(aux_info.groups * sizeof(struct ext2_group_desc),
    122 			info.block_size);
    123 
    124 	aux_info.default_i_flags = EXT4_NOATIME_FL;
    125 
    126 	u32 last_group_size = aux_info.len_blocks % info.blocks_per_group;
    127 	u32 last_header_size = 2 + aux_info.inode_table_blocks;
    128 	if (ext4_bg_has_super_block((int)aux_info.groups - 1))
    129 		last_header_size += 1 + aux_info.bg_desc_blocks +
    130 			info.bg_desc_reserve_blocks;
    131 	if (aux_info.groups <= 1 && last_group_size < last_header_size) {
    132 		critical_error("filesystem size too small");
    133 	}
    134 	if (last_group_size > 0 && last_group_size < last_header_size) {
    135 		aux_info.groups--;
    136 		aux_info.len_blocks -= last_group_size;
    137 	}
    138 
    139 	/* A zero-filled superblock to be written firstly to the block
    140 	 * device to mark the file-system as invalid
    141 	 */
    142 	aux_info.sb_zero = calloc(1, info.block_size);
    143 	if (!aux_info.sb_zero)
    144 		critical_error_errno("calloc");
    145 
    146 	/* The write_data* functions expect only block aligned calls.
    147 	 * This is not an issue, except when we write out the super
    148 	 * block on a system with a block size > 1K.  So, we need to
    149 	 * deal with that here.
    150 	 */
    151 	aux_info.sb_block = calloc(1, info.block_size);
    152 	if (!aux_info.sb_block)
    153 		critical_error_errno("calloc");
    154 
    155 	if (info.block_size > 1024)
    156 		aux_info.sb = (struct ext4_super_block *)((char *)aux_info.sb_block + 1024);
    157 	else
    158 		aux_info.sb = aux_info.sb_block;
    159 
    160 	/* Alloc an array to hold the pointers to the backup superblocks */
    161 	aux_info.backup_sb = calloc(aux_info.groups, sizeof(char *));
    162 
    163 	if (!aux_info.sb)
    164 		critical_error_errno("calloc");
    165 
    166 	aux_info.bg_desc = calloc(info.block_size, aux_info.bg_desc_blocks);
    167 	if (!aux_info.bg_desc)
    168 		critical_error_errno("calloc");
    169 	aux_info.xattrs = NULL;
    170 }
    171 
    172 void ext4_free_fs_aux_info()
    173 {
    174 	unsigned int i;
    175 
    176 	for (i=0; i<aux_info.groups; i++) {
    177 		if (aux_info.backup_sb[i])
    178 			free(aux_info.backup_sb[i]);
    179 	}
    180 	free(aux_info.sb_block);
    181 	free(aux_info.sb_zero);
    182 	free(aux_info.bg_desc);
    183 }
    184 
    185 void ext4_parse_sb_info(struct ext4_super_block *sb)
    186 {
    187 	if (sb->s_magic != EXT4_SUPER_MAGIC)
    188 		error("superblock magic incorrect");
    189 
    190 	if ((sb->s_state & EXT4_VALID_FS) != EXT4_VALID_FS)
    191 		error("filesystem state not valid");
    192 
    193 	ext4_parse_sb(sb, &info);
    194 
    195 	ext4_create_fs_aux_info();
    196 
    197 	memcpy(aux_info.sb, sb, sizeof(*sb));
    198 
    199 	if (aux_info.first_data_block != sb->s_first_data_block)
    200 		critical_error("first data block does not match");
    201 }
    202 
    203 u64 get_block_device_size(int fd)
    204 {
    205 	u64 size = 0;
    206 	int ret;
    207 
    208 #if defined(__linux__)
    209 	ret = ioctl(fd, BLKGETSIZE64, &size);
    210 #elif defined(__APPLE__) && defined(__MACH__)
    211 	ret = ioctl(fd, DKIOCGETBLOCKCOUNT, &size);
    212 #else
    213 	close(fd);
    214 	return 0;
    215 #endif
    216 
    217 	if (ret)
    218 		return 0;
    219 
    220 	return size;
    221 }
    222 
    223 int is_block_device_fd(int fd)
    224 {
    225 #ifdef _WIN32
    226 	return 0;
    227 #else
    228 	struct stat st;
    229 	int ret = fstat(fd, &st);
    230 	if (ret < 0)
    231 		return 0;
    232 
    233 	return S_ISBLK(st.st_mode);
    234 #endif
    235 }
    236 
    237 u64 get_file_size(int fd)
    238 {
    239 	struct stat buf;
    240 	int ret;
    241 	u64 reserve_len = 0;
    242 	s64 computed_size;
    243 
    244 	ret = fstat(fd, &buf);
    245 	if (ret)
    246 		return 0;
    247 
    248 	if (info.len < 0)
    249 		reserve_len = -info.len;
    250 
    251 	if (S_ISREG(buf.st_mode))
    252 		computed_size = buf.st_size - reserve_len;
    253 	else if (S_ISBLK(buf.st_mode))
    254 		computed_size = get_block_device_size(fd) - reserve_len;
    255 	else
    256 		computed_size = 0;
    257 
    258 	if (computed_size < 0) {
    259 		warn("Computed filesystem size less than 0");
    260 		computed_size = 0;
    261 	}
    262 
    263 	return computed_size;
    264 }
    265 
    266 int read_ext(int fd, int verbose)
    267 {
    268 	off64_t ret;
    269 	struct ext4_super_block sb;
    270 
    271 	read_sb(fd, &sb);
    272 
    273 	ext4_parse_sb_info(&sb);
    274 
    275 	ret = lseek64(fd, info.len, SEEK_SET);
    276 	if (ret < 0)
    277 		critical_error_errno("failed to seek to end of input image");
    278 
    279 	ret = lseek64(fd, info.block_size * (aux_info.first_data_block + 1), SEEK_SET);
    280 	if (ret < 0)
    281 		critical_error_errno("failed to seek to block group descriptors");
    282 
    283 	ret = read(fd, aux_info.bg_desc, info.block_size * aux_info.bg_desc_blocks);
    284 	if (ret < 0)
    285 		critical_error_errno("failed to read block group descriptors");
    286 	if (ret != (int)info.block_size * (int)aux_info.bg_desc_blocks)
    287 		critical_error("failed to read all of block group descriptors");
    288 
    289 	if (verbose) {
    290 		printf("Found filesystem with parameters:\n");
    291 		printf("    Size: %"PRIu64"\n", info.len);
    292 		printf("    Block size: %d\n", info.block_size);
    293 		printf("    Blocks per group: %d\n", info.blocks_per_group);
    294 		printf("    Inodes per group: %d\n", info.inodes_per_group);
    295 		printf("    Inode size: %d\n", info.inode_size);
    296 		printf("    Label: %s\n", info.label);
    297 		printf("    Blocks: %"PRIext4u64"\n", aux_info.len_blocks);
    298 		printf("    Block groups: %d\n", aux_info.groups);
    299 		printf("    Reserved block group size: %d\n", info.bg_desc_reserve_blocks);
    300 		printf("    Used %d/%d inodes and %d/%d blocks\n",
    301 			aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
    302 			aux_info.sb->s_inodes_count,
    303 			aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
    304 			aux_info.sb->s_blocks_count_lo);
    305 	}
    306 
    307 	return 0;
    308 }
    309 
    310