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 	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