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