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