make_ext4fs.c revision 68e3dfd81ddb9367a0c3e0c72148c23a3227ed48
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 "make_ext4fs.h" 18#include "output_file.h" 19#include "ext4_utils.h" 20#include "allocate.h" 21#include "contents.h" 22#include "uuid.h" 23#include "backed_block.h" 24 25#include <assert.h> 26#include <dirent.h> 27#include <fcntl.h> 28#include <libgen.h> 29#include <stdio.h> 30#include <stdlib.h> 31#include <string.h> 32#include <unistd.h> 33#include <sys/stat.h> 34#include <sys/types.h> 35 36#ifdef USE_MINGW 37 38#include <winsock2.h> 39 40/* These match the Linux definitions of these flags. 41 L_xx is defined to avoid conflicting with the win32 versions. 42*/ 43#define L_S_IRUSR 00400 44#define L_S_IWUSR 00200 45#define L_S_IXUSR 00100 46#define S_IRWXU (L_S_IRUSR | L_S_IWUSR | L_S_IXUSR) 47#define S_IRGRP 00040 48#define S_IWGRP 00020 49#define S_IXGRP 00010 50#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP) 51#define S_IROTH 00004 52#define S_IWOTH 00002 53#define S_IXOTH 00001 54#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH) 55#define S_ISUID 0004000 56#define S_ISGID 0002000 57#define S_ISVTX 0001000 58 59#else 60 61#define O_BINARY 0 62 63#endif 64 65/* TODO: Not implemented: 66 Allocating blocks in the same block group as the file inode 67 Hash or binary tree directories 68 Special files: sockets, devices, fifos 69 */ 70 71static int filter_dot(const struct dirent *d) 72{ 73 return (strcmp(d->d_name, "..") && strcmp(d->d_name, ".")); 74} 75 76static u32 build_default_directory_structure() 77{ 78 u32 inode; 79 u32 root_inode; 80 struct dentry dentries = { 81 .filename = "lost+found", 82 .file_type = EXT4_FT_DIR, 83 .mode = S_IRWXU, 84 .uid = 0, 85 .gid = 0, 86 .mtime = 0, 87 }; 88 root_inode = make_directory(0, 1, &dentries, 1); 89 inode = make_directory(root_inode, 0, NULL, 0); 90 *dentries.inode = inode; 91 inode_set_permissions(inode, dentries.mode, 92 dentries.uid, dentries.gid, dentries.mtime); 93 94 return root_inode; 95} 96 97#ifndef USE_MINGW 98/* Read a local directory and create the same tree in the generated filesystem. 99 Calls itself recursively with each directory in the given directory */ 100static u32 build_directory_structure(const char *full_path, const char *dir_path, 101 u32 dir_inode, fs_config_func_t fs_config_func) 102{ 103 int entries = 0; 104 struct dentry *dentries; 105 struct dirent **namelist; 106 struct stat stat; 107 int ret; 108 int i; 109 u32 inode; 110 u32 entry_inode; 111 u32 dirs = 0; 112 113 entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort); 114 if (entries < 0) { 115 error_errno("scandir"); 116 return EXT4_ALLOCATE_FAILED; 117 } 118 119 dentries = calloc(entries, sizeof(struct dentry)); 120 if (dentries == NULL) 121 critical_error_errno("malloc"); 122 123 for (i = 0; i < entries; i++) { 124 dentries[i].filename = strdup(namelist[i]->d_name); 125 if (dentries[i].filename == NULL) 126 critical_error_errno("strdup"); 127 128 asprintf(&dentries[i].path, "%s/%s", dir_path, namelist[i]->d_name); 129 asprintf(&dentries[i].full_path, "%s/%s", full_path, namelist[i]->d_name); 130 131 free(namelist[i]); 132 133 ret = lstat(dentries[i].full_path, &stat); 134 if (ret < 0) { 135 error_errno("lstat"); 136 i--; 137 entries--; 138 continue; 139 } 140 141 dentries[i].size = stat.st_size; 142 dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); 143 dentries[i].mtime = stat.st_mtime; 144 if (fs_config_func != NULL) { 145#ifdef ANDROID 146 unsigned int mode = 0; 147 unsigned int uid = 0; 148 unsigned int gid = 0; 149 int dir = S_ISDIR(stat.st_mode); 150 fs_config_func(dentries[i].path, dir, &uid, &gid, &mode); 151 dentries[i].mode = mode; 152 dentries[i].uid = uid; 153 dentries[i].gid = gid; 154#else 155 error("can't set android permissions - built without android support"); 156#endif 157 } 158 159 if (S_ISREG(stat.st_mode)) { 160 dentries[i].file_type = EXT4_FT_REG_FILE; 161 } else if (S_ISDIR(stat.st_mode)) { 162 dentries[i].file_type = EXT4_FT_DIR; 163 dirs++; 164 } else if (S_ISCHR(stat.st_mode)) { 165 dentries[i].file_type = EXT4_FT_CHRDEV; 166 } else if (S_ISBLK(stat.st_mode)) { 167 dentries[i].file_type = EXT4_FT_BLKDEV; 168 } else if (S_ISFIFO(stat.st_mode)) { 169 dentries[i].file_type = EXT4_FT_FIFO; 170 } else if (S_ISSOCK(stat.st_mode)) { 171 dentries[i].file_type = EXT4_FT_SOCK; 172 } else if (S_ISLNK(stat.st_mode)) { 173 dentries[i].file_type = EXT4_FT_SYMLINK; 174 dentries[i].link = calloc(info.block_size, 1); 175 readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1); 176 } else { 177 error("unknown file type on %s", dentries[i].path); 178 i--; 179 entries--; 180 } 181 } 182 free(namelist); 183 184 inode = make_directory(dir_inode, entries, dentries, dirs); 185 186 for (i = 0; i < entries; i++) { 187 if (dentries[i].file_type == EXT4_FT_REG_FILE) { 188 entry_inode = make_file(dentries[i].full_path, dentries[i].size); 189 } else if (dentries[i].file_type == EXT4_FT_DIR) { 190 entry_inode = build_directory_structure(dentries[i].full_path, 191 dentries[i].path, inode, fs_config_func); 192 } else if (dentries[i].file_type == EXT4_FT_SYMLINK) { 193 entry_inode = make_link(dentries[i].full_path, dentries[i].link); 194 } else { 195 error("unknown file type on %s", dentries[i].path); 196 entry_inode = 0; 197 } 198 *dentries[i].inode = entry_inode; 199 200 ret = inode_set_permissions(entry_inode, dentries[i].mode, 201 dentries[i].uid, dentries[i].gid, 202 dentries[i].mtime); 203 if (ret) 204 error("failed to set permissions on %s\n", dentries[i].path); 205 206 free(dentries[i].path); 207 free(dentries[i].full_path); 208 free(dentries[i].link); 209 free((void *)dentries[i].filename); 210 } 211 212 free(dentries); 213 return inode; 214} 215#endif 216 217static u32 compute_block_size() 218{ 219 return 4096; 220} 221 222static u32 compute_journal_blocks() 223{ 224 u32 journal_blocks = DIV_ROUND_UP(info.len, info.block_size) / 64; 225 if (journal_blocks < 1024) 226 journal_blocks = 1024; 227 if (journal_blocks > 32768) 228 journal_blocks = 32768; 229 return journal_blocks; 230} 231 232static u32 compute_blocks_per_group() 233{ 234 return info.block_size * 8; 235} 236 237static u32 compute_inodes() 238{ 239 return DIV_ROUND_UP(info.len, info.block_size) / 4; 240} 241 242static u32 compute_inodes_per_group() 243{ 244 u32 blocks = DIV_ROUND_UP(info.len, info.block_size); 245 u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group); 246 u32 inodes = DIV_ROUND_UP(info.inodes, block_groups); 247 inodes = ALIGN(inodes, (info.block_size / info.inode_size)); 248 249 /* After properly rounding up the number of inodes/group, 250 * make sure to update the total inodes field in the info struct. 251 */ 252 info.inodes = inodes * block_groups; 253 254 return inodes; 255} 256 257static u32 compute_bg_desc_reserve_blocks() 258{ 259 u32 blocks = DIV_ROUND_UP(info.len, info.block_size); 260 u32 block_groups = DIV_ROUND_UP(blocks, info.blocks_per_group); 261 u32 bg_desc_blocks = DIV_ROUND_UP(block_groups * sizeof(struct ext2_group_desc), 262 info.block_size); 263 264 u32 bg_desc_reserve_blocks = 265 DIV_ROUND_UP(block_groups * 1024 * sizeof(struct ext2_group_desc), 266 info.block_size) - bg_desc_blocks; 267 268 if (bg_desc_reserve_blocks > info.block_size / sizeof(u32)) 269 bg_desc_reserve_blocks = info.block_size / sizeof(u32); 270 271 return bg_desc_reserve_blocks; 272} 273 274void reset_ext4fs_info() { 275 // Reset all the global data structures used by make_ext4fs so it 276 // can be called again. 277 memset(&info, 0, sizeof(info)); 278 memset(&aux_info, 0, sizeof(aux_info)); 279 free_data_blocks(); 280} 281 282int make_ext4fs(const char *filename, s64 len) 283{ 284 int fd; 285 int status; 286 287 reset_ext4fs_info(); 288 info.len = len; 289 290 fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); 291 if (fd < 0) { 292 error_errno("open"); 293 return EXIT_FAILURE; 294 } 295 296 status = make_ext4fs_internal(fd, NULL, NULL, NULL, 0, 0, 0, 1, 0); 297 close(fd); 298 299 return status; 300} 301 302int make_ext4fs_internal(int fd, const char *directory, 303 char *mountpoint, fs_config_func_t fs_config_func, int gzip, int sparse, 304 int crc, int wipe, int init_itabs) 305{ 306 u32 root_inode_num; 307 u16 root_mode; 308 309 if (setjmp(setjmp_env)) 310 return EXIT_FAILURE; /* Handle a call to longjmp() */ 311 312 if (info.len <= 0) 313 info.len = get_file_size(fd); 314 315 if (info.len <= 0) { 316 fprintf(stderr, "Need size of filesystem\n"); 317 return EXIT_FAILURE; 318 } 319 320 if (info.block_size <= 0) 321 info.block_size = compute_block_size(); 322 323 /* Round down the filesystem length to be a multiple of the block size */ 324 info.len &= ~((u64)info.block_size - 1); 325 326 if (info.journal_blocks == 0) 327 info.journal_blocks = compute_journal_blocks(); 328 329 if (info.no_journal == 0) 330 info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL; 331 else 332 info.journal_blocks = 0; 333 334 if (info.blocks_per_group <= 0) 335 info.blocks_per_group = compute_blocks_per_group(); 336 337 if (info.inodes <= 0) 338 info.inodes = compute_inodes(); 339 340 if (info.inode_size <= 0) 341 info.inode_size = 256; 342 343 if (info.label == NULL) 344 info.label = ""; 345 346 info.inodes_per_group = compute_inodes_per_group(); 347 348 info.feat_compat |= 349 EXT4_FEATURE_COMPAT_RESIZE_INODE; 350 351 info.feat_ro_compat |= 352 EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER | 353 EXT4_FEATURE_RO_COMPAT_LARGE_FILE; 354 355 info.feat_incompat |= 356 EXT4_FEATURE_INCOMPAT_EXTENTS | 357 EXT4_FEATURE_INCOMPAT_FILETYPE; 358 359 360 info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks(); 361 362 printf("Creating filesystem with parameters:\n"); 363 printf(" Size: %llu\n", info.len); 364 printf(" Block size: %d\n", info.block_size); 365 printf(" Blocks per group: %d\n", info.blocks_per_group); 366 printf(" Inodes per group: %d\n", info.inodes_per_group); 367 printf(" Inode size: %d\n", info.inode_size); 368 printf(" Journal blocks: %d\n", info.journal_blocks); 369 printf(" Label: %s\n", info.label); 370 371 ext4_create_fs_aux_info(); 372 373 printf(" Blocks: %llu\n", aux_info.len_blocks); 374 printf(" Block groups: %d\n", aux_info.groups); 375 printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks); 376 377 block_allocator_init(); 378 379 ext4_fill_in_sb(); 380 381 if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED) 382 error("failed to reserve first 10 inodes"); 383 384 if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL) 385 ext4_create_journal_inode(); 386 387 if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE) 388 ext4_create_resize_inode(); 389 390#ifdef USE_MINGW 391 // Windows needs only 'create an empty fs image' functionality 392 assert(!directory); 393 root_inode_num = build_default_directory_structure(); 394#else 395 if (directory) 396 root_inode_num = build_directory_structure(directory, mountpoint, 0, fs_config_func); 397 else 398 root_inode_num = build_default_directory_structure(); 399#endif 400 401 root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; 402 inode_set_permissions(root_inode_num, root_mode, 0, 0, 0); 403 404 ext4_update_free(); 405 406 if (init_itabs) 407 init_unused_inode_tables(); 408 409 ext4_queue_sb(); 410 411 printf("Created filesystem with %d/%d inodes and %d/%d blocks\n", 412 aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, 413 aux_info.sb->s_inodes_count, 414 aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, 415 aux_info.sb->s_blocks_count_lo); 416 417 write_ext4_image(fd, gzip, sparse, crc, wipe); 418 419 return 0; 420} 421