Home | History | Annotate | Download | only in ext4_utils
      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