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#include "xattr.h" 29 30#ifdef USE_MINGW 31#define S_IFLNK 0 /* used by make_link, not needed under mingw */ 32#endif 33 34static u32 dentry_size(u32 entries, struct dentry *dentries) 35{ 36 u32 len = 24; 37 unsigned int i; 38 unsigned int dentry_len; 39 40 for (i = 0; i < entries; i++) { 41 dentry_len = 8 + ALIGN(strlen(dentries[i].filename), 4); 42 if (len % info.block_size + dentry_len > info.block_size) 43 len += info.block_size - (len % info.block_size); 44 len += dentry_len; 45 } 46 47 return len; 48} 49 50static 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 */ 86u32 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 /* pad the last dentry out to the end of the block */ 159 dentry->rec_len += len - offset; 160 161 return inode_num; 162} 163 164/* Creates a file on disk. Returns the inode number of the new file */ 165u32 make_file(const char *filename, u64 len) 166{ 167 struct ext4_inode *inode; 168 u32 inode_num; 169 170 inode_num = allocate_inode(info); 171 if (inode_num == EXT4_ALLOCATE_FAILED) { 172 error("failed to allocate inode\n"); 173 return EXT4_ALLOCATE_FAILED; 174 } 175 176 inode = get_inode(inode_num); 177 if (inode == NULL) { 178 error("failed to get inode %u", inode_num); 179 return EXT4_ALLOCATE_FAILED; 180 } 181 182 if (len > 0) 183 inode_allocate_file_extents(inode, len, filename); 184 185 inode->i_mode = S_IFREG; 186 inode->i_links_count = 1; 187 inode->i_flags |= aux_info.default_i_flags; 188 189 return inode_num; 190} 191 192/* Creates a file on disk. Returns the inode number of the new file */ 193u32 make_link(const char *filename, const char *link) 194{ 195 struct ext4_inode *inode; 196 u32 inode_num; 197 u32 len = strlen(link); 198 199 inode_num = allocate_inode(info); 200 if (inode_num == EXT4_ALLOCATE_FAILED) { 201 error("failed to allocate inode\n"); 202 return EXT4_ALLOCATE_FAILED; 203 } 204 205 inode = get_inode(inode_num); 206 if (inode == NULL) { 207 error("failed to get inode %u", inode_num); 208 return EXT4_ALLOCATE_FAILED; 209 } 210 211 inode->i_mode = S_IFLNK; 212 inode->i_links_count = 1; 213 inode->i_flags |= aux_info.default_i_flags; 214 inode->i_size_lo = len; 215 216 if (len + 1 <= sizeof(inode->i_block)) { 217 /* Fast symlink */ 218 memcpy((char*)inode->i_block, link, len); 219 } else { 220 u8 *data = inode_allocate_data_indirect(inode, info.block_size, info.block_size); 221 memcpy(data, link, len); 222 inode->i_blocks_lo = info.block_size / 512; 223 } 224 225 return inode_num; 226} 227 228int inode_set_permissions(u32 inode_num, u16 mode, u16 uid, u16 gid, u32 mtime) 229{ 230 struct ext4_inode *inode = get_inode(inode_num); 231 232 if (!inode) 233 return -1; 234 235 inode->i_mode |= mode; 236 inode->i_uid = uid; 237 inode->i_gid = gid; 238 inode->i_mtime = mtime; 239 inode->i_atime = mtime; 240 inode->i_ctime = mtime; 241 242 return 0; 243} 244 245#ifdef HAVE_SELINUX 246#define XATTR_SELINUX_SUFFIX "selinux" 247 248/* XXX */ 249#define cpu_to_le32(x) (x) 250#define cpu_to_le16(x) (x) 251 252int inode_set_selinux(u32 inode_num, const char *secon) 253{ 254 struct ext4_inode *inode = get_inode(inode_num); 255 u32 *hdr; 256 struct ext4_xattr_entry *entry; 257 size_t name_len = strlen(XATTR_SELINUX_SUFFIX); 258 size_t value_len; 259 size_t size, min_offs; 260 char *val; 261 262 if (!secon) 263 return 0; 264 265 if (!inode) 266 return -1; 267 268 hdr = (u32 *) (inode + 1); 269 *hdr = cpu_to_le32(EXT4_XATTR_MAGIC); 270 entry = (struct ext4_xattr_entry *) (hdr+1); 271 memset(entry, 0, EXT4_XATTR_LEN(name_len)); 272 entry->e_name_index = EXT4_XATTR_INDEX_SECURITY; 273 entry->e_name_len = name_len; 274 memcpy(entry->e_name, XATTR_SELINUX_SUFFIX, name_len); 275 value_len = strlen(secon)+1; 276 entry->e_value_size = cpu_to_le32(value_len); 277 min_offs = (char *)inode + info.inode_size - (char*) entry; 278 size = EXT4_XATTR_SIZE(value_len); 279 val = (char *)entry + min_offs - size; 280 entry->e_value_offs = cpu_to_le16(min_offs - size); 281 memset(val + size - EXT4_XATTR_PAD, 0, EXT4_XATTR_PAD); 282 memcpy(val, secon, value_len); 283 inode->i_extra_isize = cpu_to_le16(sizeof(struct ext4_inode) - EXT4_GOOD_OLD_INODE_SIZE); 284 285 return 0; 286} 287#else 288int inode_set_selinux(u32 inode_num, const char *secon) 289{ 290 return 0; 291} 292#endif 293