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 <sys/stat.h> 18 #include <string.h> 19 #include <stdio.h> 20 21 #include "ext4_utils.h" 22 #include "ext4.h" 23 #include "make_ext4fs.h" 24 #include "allocate.h" 25 #include "contents.h" 26 #include "extent.h" 27 #include "indirect.h" 28 29 static u32 dentry_size(u32 entries, struct dentry *dentries) 30 { 31 u32 len = 24; 32 unsigned int i; 33 unsigned int dentry_len; 34 35 for (i = 0; i < entries; i++) { 36 dentry_len = 8 + ALIGN(strlen(dentries[i].filename), 4); 37 if (len % info.block_size + dentry_len > info.block_size) 38 len += info.block_size - (len % info.block_size); 39 len += dentry_len; 40 } 41 42 return len; 43 } 44 45 static struct ext4_dir_entry_2 *add_dentry(u8 *data, u32 *offset, 46 struct ext4_dir_entry_2 *prev, u32 inode, const char *name, 47 u8 file_type) 48 { 49 u8 name_len = strlen(name); 50 u16 rec_len = 8 + ALIGN(name_len, 4); 51 struct ext4_dir_entry_2 *dentry; 52 53 u32 start_block = *offset / info.block_size; 54 u32 end_block = (*offset + rec_len) / info.block_size; 55 if (start_block != end_block) { 56 /* Adding this dentry will cross a block boundary, so pad the previous 57 dentry to the block boundary */ 58 if (!prev) 59 critical_error("no prev"); 60 prev->rec_len += end_block * info.block_size - *offset; 61 *offset = end_block * info.block_size; 62 } 63 64 dentry = (struct ext4_dir_entry_2 *)(data + *offset); 65 dentry->inode = inode; 66 dentry->rec_len = rec_len; 67 dentry->name_len = name_len; 68 dentry->file_type = file_type; 69 memcpy(dentry->name, name, name_len); 70 71 *offset += rec_len; 72 return dentry; 73 } 74 75 /* Creates a directory structure for an array of directory entries, dentries, 76 and stores the location of the structure in an inode. The new inode's 77 .. link is set to dir_inode_num. Stores the location of the inode number 78 of each directory entry into dentries[i].inode, to be filled in later 79 when the inode for the entry is allocated. Returns the inode number of the 80 new directory */ 81 u32 make_directory(u32 dir_inode_num, u32 entries, struct dentry *dentries, 82 u32 dirs) 83 { 84 struct ext4_inode *inode; 85 u32 blocks; 86 u32 len; 87 u32 offset = 0; 88 u32 inode_num; 89 u8 *data; 90 unsigned int i; 91 struct ext4_dir_entry_2 *dentry; 92 93 blocks = DIV_ROUND_UP(dentry_size(entries, dentries), info.block_size); 94 len = blocks * info.block_size; 95 96 if (dir_inode_num) { 97 inode_num = allocate_inode(info); 98 } else { 99 dir_inode_num = EXT4_ROOT_INO; 100 inode_num = EXT4_ROOT_INO; 101 } 102 103 if (inode_num == EXT4_ALLOCATE_FAILED) { 104 error("failed to allocate inode\n"); 105 return EXT4_ALLOCATE_FAILED; 106 } 107 108 add_directory(inode_num); 109 110 inode = get_inode(inode_num); 111 if (inode == NULL) { 112 error("failed to get inode %u", inode_num); 113 return EXT4_ALLOCATE_FAILED; 114 } 115 116 data = inode_allocate_data_extents(inode, len, len); 117 if (data == NULL) { 118 error("failed to allocate %llu extents", len); 119 return EXT4_ALLOCATE_FAILED; 120 } 121 122 inode->i_mode = S_IFDIR; 123 inode->i_links_count = dirs + 2; 124 inode->i_flags |= aux_info.default_i_flags; 125 126 dentry = NULL; 127 128 dentry = add_dentry(data, &offset, NULL, inode_num, ".", EXT4_FT_DIR); 129 if (!dentry) { 130 error("failed to add . directory"); 131 return EXT4_ALLOCATE_FAILED; 132 } 133 134 dentry = add_dentry(data, &offset, dentry, dir_inode_num, "..", EXT4_FT_DIR); 135 if (!dentry) { 136 error("failed to add .. directory"); 137 return EXT4_ALLOCATE_FAILED; 138 } 139 140 for (i = 0; i < entries; i++) { 141 dentry = add_dentry(data, &offset, dentry, 0, 142 dentries[i].filename, dentries[i].file_type); 143 if (offset > len || (offset == len && i != entries - 1)) 144 critical_error("internal error: dentry for %s ends at %d, past %d\n", 145 dentries[i].filename, offset, len); 146 dentries[i].inode = &dentry->inode; 147 if (!dentry) { 148 error("failed to add directory"); 149 return EXT4_ALLOCATE_FAILED; 150 } 151 } 152 153 dentry = (struct ext4_dir_entry_2 *)(data + offset); 154 dentry->inode = 0; 155 dentry->rec_len = len - offset; 156 dentry->name_len = 0; 157 dentry->file_type = EXT4_FT_UNKNOWN; 158 159 return inode_num; 160 } 161 162 /* Creates a file on disk. Returns the inode number of the new file */ 163 u32 make_file(const char *filename, u64 len) 164 { 165 struct ext4_inode *inode; 166 u32 inode_num; 167 168 inode_num = allocate_inode(info); 169 if (inode_num == EXT4_ALLOCATE_FAILED) { 170 error("failed to allocate inode\n"); 171 return EXT4_ALLOCATE_FAILED; 172 } 173 174 inode = get_inode(inode_num); 175 if (inode == NULL) { 176 error("failed to get inode %u", inode_num); 177 return EXT4_ALLOCATE_FAILED; 178 } 179 180 if (len > 0) 181 inode_allocate_file_extents(inode, len, filename); 182 183 inode->i_mode = S_IFREG; 184 inode->i_links_count = 1; 185 inode->i_flags |= aux_info.default_i_flags; 186 187 return inode_num; 188 } 189 190 /* Creates a file on disk. Returns the inode number of the new file */ 191 u32 make_link(const char *filename, const char *link) 192 { 193 struct ext4_inode *inode; 194 u32 inode_num; 195 u32 len = strlen(link); 196 197 inode_num = allocate_inode(info); 198 if (inode_num == EXT4_ALLOCATE_FAILED) { 199 error("failed to allocate inode\n"); 200 return EXT4_ALLOCATE_FAILED; 201 } 202 203 inode = get_inode(inode_num); 204 if (inode == NULL) { 205 error("failed to get inode %u", inode_num); 206 return EXT4_ALLOCATE_FAILED; 207 } 208 209 inode->i_mode = S_IFLNK; 210 inode->i_links_count = 1; 211 inode->i_flags |= aux_info.default_i_flags; 212 inode->i_size_lo = len; 213 214 if (len + 1 <= sizeof(inode->i_block)) { 215 /* Fast symlink */ 216 memcpy((char*)inode->i_block, link, len); 217 } else { 218 u8 *data = inode_allocate_data_indirect(inode, info.block_size, info.block_size); 219 memcpy(data, link, len); 220 inode->i_blocks_lo = info.block_size / 512; 221 } 222 223 return inode_num; 224 } 225 226 int inode_set_permissions(u32 inode_num, u16 mode, u16 uid, u16 gid, u32 mtime) 227 { 228 struct ext4_inode *inode = get_inode(inode_num); 229 230 if (!inode) 231 return -1; 232 233 inode->i_mode |= mode; 234 inode->i_uid = uid; 235 inode->i_gid = gid; 236 inode->i_mtime = mtime; 237 inode->i_atime = mtime; 238 inode->i_ctime = mtime; 239 240 return 0; 241 } 242