1 /*
2  *
3  * (c) 2008-2009 Laurent Vivier <Laurent@lvivier.info>
4  *
5  * This file has been copied from EMILE, http://emile.sf.net
6  *
7  */
8 
9 #include "libext2.h"
10 #include "ext2_utils.h"
11 #include "libopenbios/bindings.h"
12 #include "libc/diskio.h"
13 #include "libc/byteorder.h"
14 
ext2_probe(int fd,long long offset)15 int ext2_probe(int fd, long long offset)
16 {
17 	struct ext2_super_block *super;
18 
19 	super = (struct ext2_super_block*)malloc(sizeof(struct ext2_super_block));
20 	seek_io(fd, 2 * 512 + offset);
21 	read_io(fd, super, sizeof (*super));
22 
23 	if (__le16_to_cpu(super->s_magic) != EXT2_SUPER_MAGIC) {
24 		free(super);
25 		return 0;
26 	}
27 
28 	free(super);
29 	return -1;
30 }
31 
ext2_get_super(int fd,struct ext2_super_block * super)32 void ext2_get_super(int fd, struct ext2_super_block *super)
33 {
34 	seek_io(fd, 2 * 512);
35 	read_io(fd, super, sizeof (*super));
36 
37 	super->s_inodes_count = __le32_to_cpu(super->s_inodes_count);
38 	super->s_blocks_count = __le32_to_cpu(super->s_blocks_count);
39 	super->s_r_blocks_count = __le32_to_cpu(super->s_r_blocks_count);
40 	super->s_free_blocks_count = __le32_to_cpu(super->s_free_blocks_count);
41 	super->s_free_inodes_count = __le32_to_cpu(super->s_free_inodes_count);
42 	super->s_first_data_block = __le32_to_cpu(super->s_first_data_block);
43 	super->s_log_block_size = __le32_to_cpu(super->s_log_block_size);
44 	super->s_log_frag_size = __le32_to_cpu(super->s_log_frag_size);
45 	super->s_blocks_per_group = __le32_to_cpu(super->s_blocks_per_group);
46 	super->s_frags_per_group = __le32_to_cpu(super->s_frags_per_group);
47 	super->s_inodes_per_group = __le32_to_cpu(super->s_inodes_per_group);
48 	super->s_mtime = __le32_to_cpu(super->s_mtime);
49 	super->s_wtime = __le32_to_cpu(super->s_wtime);
50 	super->s_mnt_count = __le16_to_cpu(super->s_mnt_count);
51 	super->s_max_mnt_count = __le16_to_cpu(super->s_max_mnt_count);
52 	super->s_magic = __le16_to_cpu(super->s_magic);
53 	super->s_state = __le16_to_cpu(super->s_state);
54 	super->s_errors = __le16_to_cpu(super->s_errors);
55 	super->s_minor_rev_level = __le16_to_cpu(super->s_minor_rev_level);
56 	super->s_lastcheck = __le32_to_cpu(super->s_lastcheck);
57 	super->s_checkinterval = __le32_to_cpu(super->s_checkinterval);
58 	super->s_creator_os = __le32_to_cpu(super->s_creator_os);
59 	super->s_rev_level = __le32_to_cpu(super->s_rev_level);
60 	super->s_def_resuid = __le16_to_cpu(super->s_def_resuid);
61 	super->s_def_resgid = __le16_to_cpu(super->s_def_resgid);
62 	super->s_first_ino = __le32_to_cpu(super->s_first_ino);
63 	super->s_inode_size = __le16_to_cpu(super->s_inode_size);
64 	super->s_block_group_nr = __le16_to_cpu(super->s_block_group_nr);
65 	super->s_feature_compat = __le32_to_cpu(super->s_feature_compat);
66 	super->s_feature_incompat = __le32_to_cpu(super->s_feature_incompat);
67 	super->s_feature_ro_compat = __le32_to_cpu(super->s_feature_ro_compat);
68 	super->s_algorithm_usage_bitmap =
69 				__le32_to_cpu(super->s_algorithm_usage_bitmap);
70 	super->s_journal_inum = __le32_to_cpu(super->s_journal_inum);
71 	super->s_journal_dev = __le32_to_cpu(super->s_journal_dev);
72 	super->s_last_orphan = __le32_to_cpu(super->s_last_orphan);
73 	super->s_hash_seed[0] = __le32_to_cpu(super->s_hash_seed[0]);
74 	super->s_hash_seed[1] = __le32_to_cpu(super->s_hash_seed[1]);
75 	super->s_hash_seed[2] = __le32_to_cpu(super->s_hash_seed[2]);
76 	super->s_hash_seed[3] = __le32_to_cpu(super->s_hash_seed[3]);
77 	super->s_default_mount_opts =
78 				__le32_to_cpu(super->s_default_mount_opts);
79 	super->s_first_meta_bg = __le32_to_cpu(super->s_first_meta_bg);
80 }
81 
ext2_read_block(ext2_VOLUME * volume,unsigned int fsblock)82 void ext2_read_block(ext2_VOLUME* volume, unsigned int fsblock)
83 {
84 	long long offset;
85 
86 	if (fsblock == volume->current)
87 		return;
88 
89 	volume->current = fsblock;
90 	offset = fsblock * EXT2_BLOCK_SIZE(volume->super);
91 
92 	seek_io(volume->fd, offset);
93 	read_io(volume->fd, volume->buffer, EXT2_BLOCK_SIZE(volume->super));
94 }
95 
ext2_get_group_desc(ext2_VOLUME * volume,int group_id,struct ext2_group_desc * gdp)96 void ext2_get_group_desc(ext2_VOLUME* volume,
97 		   int group_id, struct ext2_group_desc *gdp)
98 {
99 	unsigned int block, offset;
100 	struct ext2_group_desc *le_gdp;
101 
102 	block = 1 + volume->super->s_first_data_block;
103 	block += group_id / EXT2_DESC_PER_BLOCK(volume->super);
104 	ext2_read_block(volume,  block);
105 
106 	offset = group_id % EXT2_DESC_PER_BLOCK(volume->super);
107 	offset *= sizeof(*gdp);
108 
109 	le_gdp = (struct ext2_group_desc *)(volume->buffer + offset);
110 
111 	gdp->bg_block_bitmap = __le32_to_cpu(le_gdp->bg_block_bitmap);
112 	gdp->bg_inode_bitmap = __le32_to_cpu(le_gdp->bg_inode_bitmap);
113 	gdp->bg_inode_table = __le32_to_cpu(le_gdp->bg_inode_table);
114 	gdp->bg_free_blocks_count = __le16_to_cpu(le_gdp->bg_free_blocks_count);
115 	gdp->bg_free_inodes_count = __le16_to_cpu(le_gdp->bg_free_inodes_count);
116 	gdp->bg_used_dirs_count = __le16_to_cpu(le_gdp->bg_used_dirs_count);
117 }
118 
ext2_get_inode(ext2_VOLUME * volume,unsigned int ino,struct ext2_inode * inode)119 int ext2_get_inode(ext2_VOLUME* volume,
120 		    unsigned int ino, struct ext2_inode *inode)
121 {
122 	struct ext2_group_desc desc;
123 	unsigned int block;
124 	unsigned int group_id;
125 	unsigned int offset;
126 	struct ext2_inode *le_inode;
127 	int i;
128 
129 	ino--;
130 
131 	group_id = ino / EXT2_INODES_PER_GROUP(volume->super);
132 	ext2_get_group_desc(volume, group_id, &desc);
133 
134 	ino %= EXT2_INODES_PER_GROUP(volume->super);
135 
136 	block = desc.bg_inode_table;
137 	block += ino / (EXT2_BLOCK_SIZE(volume->super) /
138 			EXT2_INODE_SIZE(volume->super));
139 	ext2_read_block(volume, block);
140 
141 	offset = ino % (EXT2_BLOCK_SIZE(volume->super) /
142 			EXT2_INODE_SIZE(volume->super));
143 	offset *= EXT2_INODE_SIZE(volume->super);
144 
145 	le_inode = (struct ext2_inode *)(volume->buffer + offset);
146 
147 	inode->i_mode = __le16_to_cpu(le_inode->i_mode);
148 	inode->i_uid = __le16_to_cpu(le_inode->i_uid);
149 	inode->i_size = __le32_to_cpu(le_inode->i_size);
150 	inode->i_atime = __le32_to_cpu(le_inode->i_atime);
151 	inode->i_ctime = __le32_to_cpu(le_inode->i_ctime);
152 	inode->i_mtime = __le32_to_cpu(le_inode->i_mtime);
153 	inode->i_dtime = __le32_to_cpu(le_inode->i_dtime);
154 	inode->i_gid = __le16_to_cpu(le_inode->i_gid);
155 	inode->i_links_count = __le16_to_cpu(le_inode->i_links_count);
156 	inode->i_blocks = __le32_to_cpu(le_inode->i_blocks);
157 	inode->i_flags = __le32_to_cpu(le_inode->i_flags);
158 	if (S_ISLNK(inode->i_mode)) {
159 		memcpy(inode->i_block, le_inode->i_block, EXT2_N_BLOCKS * 4);
160 	} else {
161 		for (i = 0; i < EXT2_N_BLOCKS; i++)
162 			inode->i_block[i] = __le32_to_cpu(le_inode->i_block[i]);
163         }
164 	inode->i_generation = __le32_to_cpu(le_inode->i_generation);
165 	inode->i_file_acl = __le32_to_cpu(le_inode->i_file_acl);
166 	inode->i_dir_acl = __le32_to_cpu(le_inode->i_dir_acl);
167 	inode->i_faddr = __le32_to_cpu(le_inode->i_faddr);
168 	inode->osd2.linux2.l_i_frag = le_inode->osd2.linux2.l_i_frag;
169 	inode->osd2.linux2.l_i_fsize = le_inode->osd2.linux2.l_i_fsize;
170 	inode->osd2.linux2.l_i_uid_high =
171 			__le16_to_cpu(le_inode->osd2.linux2.l_i_uid_high);
172 	inode->osd2.linux2.l_i_gid_high =
173 			__le16_to_cpu(le_inode->osd2.linux2.l_i_gid_high);
174 	return 0;
175 }
176 
ext2_get_block_addr(ext2_VOLUME * volume,struct ext2_inode * inode,unsigned int logical)177 unsigned int ext2_get_block_addr(ext2_VOLUME* volume, struct ext2_inode *inode,
178 				 unsigned int logical)
179 {
180 	unsigned int physical;
181 	unsigned int addr_per_block;
182 
183 	/* direct */
184 
185 	if (logical < EXT2_NDIR_BLOCKS) {
186 		physical = inode->i_block[logical];
187 		return physical;
188 	}
189 
190 	/* indirect */
191 
192 	logical -= EXT2_NDIR_BLOCKS;
193 
194 	addr_per_block = EXT2_ADDR_PER_BLOCK (volume->super);
195 	if (logical < addr_per_block) {
196 		ext2_read_block(volume, inode->i_block[EXT2_IND_BLOCK]);
197 		physical = __le32_to_cpu(((unsigned int *)volume->buffer)[logical]);
198 		return physical;
199 	}
200 
201 	/* double indirect */
202 
203 	logical -=  addr_per_block;
204 
205 	if (logical < addr_per_block * addr_per_block) {
206 		ext2_read_block(volume, inode->i_block[EXT2_DIND_BLOCK]);
207 		physical = __le32_to_cpu(((unsigned int *)volume->buffer)
208 						[logical / addr_per_block]);
209 		ext2_read_block(volume, physical);
210 		physical = __le32_to_cpu(((unsigned int *)volume->buffer)
211 						[logical % addr_per_block]);
212 		return physical;
213 	}
214 
215 	/* triple indirect */
216 
217 	logical -= addr_per_block * addr_per_block;
218 	ext2_read_block(volume, inode->i_block[EXT2_DIND_BLOCK]);
219 	physical = __le32_to_cpu(((unsigned int *)volume->buffer)
220 				[logical / (addr_per_block * addr_per_block)]);
221 	ext2_read_block(volume, physical);
222 	logical = logical % (addr_per_block * addr_per_block);
223 	physical = __le32_to_cpu(((unsigned int *)volume->buffer)[logical / addr_per_block]);
224 	ext2_read_block(volume, physical);
225 	physical = __le32_to_cpu(((unsigned int *)volume->buffer)[logical % addr_per_block]);
226 	return physical;
227 }
228 
ext2_read_data(ext2_VOLUME * volume,struct ext2_inode * inode,off_t offset,char * buffer,size_t length)229 int ext2_read_data(ext2_VOLUME* volume, struct ext2_inode *inode,
230 		   off_t offset, char *buffer, size_t length)
231 {
232 	unsigned int logical, physical;
233 	int blocksize = EXT2_BLOCK_SIZE(volume->super);
234 	int shift;
235 	size_t read;
236 
237 	if (offset >= inode->i_size)
238 		return -1;
239 
240 	if (offset + length >= inode->i_size)
241 		length = inode->i_size - offset;
242 
243 	read = 0;
244 	logical = offset / blocksize;
245 	shift = offset % blocksize;
246 
247 	if (shift) {
248 		physical = ext2_get_block_addr(volume, inode, logical);
249 		ext2_read_block(volume, physical);
250 
251 		if (length < blocksize - shift) {
252 			memcpy(buffer, volume->buffer + shift, length);
253 			return length;
254 		}
255 		read += blocksize - shift;
256 		memcpy(buffer, volume->buffer + shift, read);
257 
258 		buffer += read;
259 		length -= read;
260 		logical++;
261 	}
262 
263 	while (length) {
264 		physical = ext2_get_block_addr(volume, inode, logical);
265 		ext2_read_block(volume, physical);
266 
267 		if (length < blocksize) {
268 			memcpy(buffer, volume->buffer, length);
269 			read += length;
270 			return read;
271 		}
272 		memcpy(buffer, volume->buffer, blocksize);
273 
274 		buffer += blocksize;
275 		length -= blocksize;
276 		read += blocksize;
277 		logical++;
278 	}
279 
280 	return read;
281 }
282 
ext2_dir_entry(ext2_VOLUME * volume,struct ext2_inode * inode,off_t index,struct ext2_dir_entry_2 * entry)283 off_t ext2_dir_entry(ext2_VOLUME *volume, struct ext2_inode *inode,
284 		     off_t index, struct ext2_dir_entry_2 *entry)
285 {
286 	int ret;
287 
288 	ret = ext2_read_data(volume, inode, index,
289 			     (char*)entry, sizeof(*entry));
290 	if (ret == -1)
291 		return -1;
292 
293         entry->inode = __le32_to_cpu(entry->inode);
294         entry->rec_len = __le16_to_cpu(entry->rec_len);
295 	return index + entry->rec_len;
296 }
297 
ext2_seek_name(ext2_VOLUME * volume,const char * name)298 unsigned int ext2_seek_name(ext2_VOLUME *volume, const char *name)
299 {
300 	struct ext2_inode inode;
301 	int ret;
302 	unsigned int ino;
303 	off_t index;
304 	struct ext2_dir_entry_2 entry;
305 
306 	ino = EXT2_ROOT_INO;
307 	while(1) {
308 		while (*name == '\\')
309 			name++;
310 		if (!*name)
311 		    break;
312 		ret = ext2_get_inode(volume, ino, &inode);
313 		if (ret == -1)
314 			return 0;
315 		index = 0;
316 		while (1) {
317 			index = ext2_dir_entry(volume, &inode, index, &entry);
318 			if (index == -1)
319 				return 0;
320 			ret = strncmp(name, entry.name, entry.name_len);
321 			if (ret == 0  &&
322 			    (name[entry.name_len] == 0 ||
323 			     name[entry.name_len] == '\\')) {
324 			     	ino = entry.inode;
325 				break;
326 			}
327 		}
328 		name += entry.name_len;
329 	}
330 
331 	return ino;
332 }
333