• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <dprintf.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <sys/dirent.h>
5 #include <minmax.h>
6 #include "cache.h"
7 #include "core.h"
8 #include "disk.h"
9 #include "fs.h"
10 #include "ext2_fs.h"
11 
12 /*
13  * Convert an ext2 file type to the global values
14  */
ext2_cvt_type(unsigned int d_file_type)15 static enum dirent_type ext2_cvt_type(unsigned int d_file_type)
16 {
17     static const enum dirent_type inode_type[] = {
18 	DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR,
19 	DT_BLK, DT_FIFO, DT_SOCK, DT_LNK,
20     };
21 
22     if (d_file_type > sizeof inode_type / sizeof *inode_type)
23 	return DT_UNKNOWN;
24     else
25 	return inode_type[d_file_type];
26 }
27 
28 /*
29  * get the group's descriptor of group_num
30  */
31 static const struct ext2_group_desc *
ext2_get_group_desc(struct fs_info * fs,uint32_t group_num)32 ext2_get_group_desc(struct fs_info *fs, uint32_t group_num)
33 {
34     struct ext2_sb_info *sbi = EXT2_SB(fs);
35     uint32_t desc_block, desc_index;
36     const struct ext2_group_desc *desc_data_block;
37 
38     if (group_num >= sbi->s_groups_count) {
39 	printf ("ext2_get_group_desc"
40 		"block_group >= groups_count - "
41 		"block_group = %d, groups_count = %d",
42 		group_num, sbi->s_groups_count);
43 
44 	return NULL;
45     }
46 
47     desc_block = group_num / sbi->s_desc_per_block;
48     desc_index = group_num % sbi->s_desc_per_block;
49 
50     desc_block += sbi->s_first_data_block + 1;
51 
52     desc_data_block = get_cache(fs->fs_dev, desc_block);
53     return &desc_data_block[desc_index];
54 }
55 
56 /*
57  * Unlike strncmp, ext2_match_entry returns 1 for success, 0 for failure.
58  */
ext2_match_entry(const char * name,size_t len,const struct ext2_dir_entry * de)59 static inline bool ext2_match_entry(const char *name, size_t len,
60                                     const struct ext2_dir_entry * de)
61 {
62     if (!de->d_inode)
63         return false;
64     if (len != de->d_name_len)
65 	return false;
66     return !memcmp(name, de->d_name, len);
67 }
68 
69 
70 /*
71  * p is at least 6 bytes before the end of page
72  */
ext2_next_entry(struct ext2_dir_entry * p)73 static inline struct ext2_dir_entry *ext2_next_entry(struct ext2_dir_entry *p)
74 {
75     return (struct ext2_dir_entry *)((char*)p + p->d_rec_len);
76 }
77 
78 /*
79  * Map a logical sector and load it into the cache
80  */
81 static const void *
ext2_get_cache(struct inode * inode,block_t lblock)82 ext2_get_cache(struct inode *inode, block_t lblock)
83 {
84     block_t pblock = ext2_bmap(inode, lblock, NULL);
85     return get_cache(inode->fs->fs_dev, pblock);
86 }
87 
88 /*
89  * find a dir entry, return it if found, or return NULL.
90  */
91 static const struct ext2_dir_entry *
ext2_find_entry(struct fs_info * fs,struct inode * inode,const char * dname)92 ext2_find_entry(struct fs_info *fs, struct inode *inode, const char *dname)
93 {
94     block_t index = 0;
95     uint32_t i = 0, offset, maxoffset;
96     const struct ext2_dir_entry *de;
97     const char *data;
98     size_t dname_len = strlen(dname);
99 
100     while (i < inode->size) {
101 	data = ext2_get_cache(inode, index++);
102 	offset = 0;
103 	maxoffset =  min(BLOCK_SIZE(fs), i-inode->size);
104 
105 	/* The smallest possible size is 9 bytes */
106 	while (offset < maxoffset-8) {
107 	    de = (const struct ext2_dir_entry *)(data + offset);
108 	    if (de->d_rec_len > maxoffset - offset)
109 		break;
110 
111 	    if (ext2_match_entry(dname, dname_len, de))
112 		return de;
113 
114 	    offset += de->d_rec_len;
115 	}
116 	i += BLOCK_SIZE(fs);
117     }
118 
119     return NULL;
120 }
121 
122 static const struct ext2_inode *
ext2_get_inode(struct fs_info * fs,int inr)123 ext2_get_inode(struct fs_info *fs, int inr)
124 {
125     const struct ext2_group_desc *desc;
126     const char *data;
127     uint32_t inode_group, inode_offset;
128     uint32_t block_num, block_off;
129 
130     inr--;
131     inode_group  = inr / EXT2_INODES_PER_GROUP(fs);
132     inode_offset = inr % EXT2_INODES_PER_GROUP(fs);
133     desc = ext2_get_group_desc(fs, inode_group);
134     if (!desc)
135 	return NULL;
136 
137     block_num = desc->bg_inode_table +
138 	inode_offset / EXT2_INODES_PER_BLOCK(fs);
139     block_off = inode_offset % EXT2_INODES_PER_BLOCK(fs);
140 
141     data = get_cache(fs->fs_dev, block_num);
142 
143     return (const struct ext2_inode *)
144 	(data + block_off * EXT2_SB(fs)->s_inode_size);
145 }
146 
fill_inode(struct inode * inode,const struct ext2_inode * e_inode)147 static void fill_inode(struct inode *inode, const struct ext2_inode *e_inode)
148 {
149     inode->mode    = IFTODT(e_inode->i_mode);
150     inode->size    = e_inode->i_size;
151     inode->atime   = e_inode->i_atime;
152     inode->ctime   = e_inode->i_ctime;
153     inode->mtime   = e_inode->i_mtime;
154     inode->dtime   = e_inode->i_dtime;
155     inode->blocks  = e_inode->i_blocks;
156     inode->flags   = e_inode->i_flags;
157     inode->file_acl = e_inode->i_file_acl;
158     memcpy(PVT(inode)->i_block, e_inode->i_block, sizeof PVT(inode)->i_block);
159 }
160 
ext2_iget_by_inr(struct fs_info * fs,uint32_t inr)161 static struct inode *ext2_iget_by_inr(struct fs_info *fs, uint32_t inr)
162 {
163     const struct ext2_inode *e_inode;
164     struct inode *inode;
165 
166     e_inode = ext2_get_inode(fs, inr);
167     if (!e_inode)
168 	return NULL;
169 
170     if (!(inode = alloc_inode(fs, inr, sizeof(struct ext2_pvt_inode))))
171 	return NULL;
172     fill_inode(inode, e_inode);
173 
174     return inode;
175 }
176 
ext2_iget_root(struct fs_info * fs)177 static struct inode *ext2_iget_root(struct fs_info *fs)
178 {
179     return ext2_iget_by_inr(fs, EXT2_ROOT_INO);
180 }
181 
ext2_iget(const char * dname,struct inode * parent)182 static struct inode *ext2_iget(const char *dname, struct inode *parent)
183 {
184     const struct ext2_dir_entry *de;
185     struct fs_info *fs = parent->fs;
186 
187     de = ext2_find_entry(fs, parent, dname);
188     if (!de)
189 	return NULL;
190 
191     return ext2_iget_by_inr(fs, de->d_inode);
192 }
193 
194 /*
195  * Read the entire contents of an inode into a memory buffer
196  */
cache_get_file(struct inode * inode,void * buf,size_t bytes)197 static int cache_get_file(struct inode *inode, void *buf, size_t bytes)
198 {
199     struct fs_info *fs = inode->fs;
200     size_t block_size = BLOCK_SIZE(fs);
201     uint32_t index = 0;		/* Logical block number */
202     size_t chunk;
203     const char *data;
204     char *p = buf;
205 
206     if (inode->size > bytes)
207 	bytes = inode->size;
208 
209     while (bytes) {
210 	chunk = min(bytes, block_size);
211 	data = ext2_get_cache(inode, index++);
212 	memcpy(p, data, chunk);
213 
214 	bytes -= chunk;
215 	p += chunk;
216     }
217 
218     return 0;
219 }
220 
ext2_readlink(struct inode * inode,char * buf)221 static int ext2_readlink(struct inode *inode, char *buf)
222 {
223     struct fs_info *fs = inode->fs;
224     int sec_per_block = 1 << (fs->block_shift - fs->sector_shift);
225     bool fast_symlink;
226 
227     if (inode->size > BLOCK_SIZE(fs))
228 	return -1;		/* Error! */
229 
230     fast_symlink = (inode->file_acl ? sec_per_block : 0) == inode->blocks;
231     if (fast_symlink)
232 	memcpy(buf, PVT(inode)->i_block, inode->size);
233     else
234 	cache_get_file(inode, buf, inode->size);
235 
236     return inode->size;
237 }
238 
239 /*
240  * Read one directory entry at a time
241  */
ext2_readdir(struct file * file,struct dirent * dirent)242 static int ext2_readdir(struct file *file, struct dirent *dirent)
243 {
244     struct fs_info *fs = file->fs;
245     struct inode *inode = file->inode;
246     const struct ext2_dir_entry *de;
247     const char *data;
248     block_t index = file->offset >> fs->block_shift;
249 
250     if (file->offset >= inode->size)
251 	return -1;		/* End of file */
252 
253     data = ext2_get_cache(inode, index);
254     de = (const struct ext2_dir_entry *)
255 	(data + (file->offset & (BLOCK_SIZE(fs) - 1)));
256 
257     dirent->d_ino = de->d_inode;
258     dirent->d_off = file->offset;
259     dirent->d_reclen = offsetof(struct dirent, d_name) + de->d_name_len + 1;
260     dirent->d_type = ext2_cvt_type(de->d_file_type);
261     memcpy(dirent->d_name, de->d_name, de->d_name_len);
262     dirent->d_name[de->d_name_len] = '\0';
263 
264     file->offset += de->d_rec_len;  /* Update for next reading */
265 
266     return 0;
267 }
268 
269 /*
270  * init. the fs meta data, return the block size bits.
271  */
ext2_fs_init(struct fs_info * fs)272 static int ext2_fs_init(struct fs_info *fs)
273 {
274     struct disk *disk = fs->fs_dev->disk;
275     struct ext2_sb_info *sbi;
276     struct ext2_super_block sb;
277     struct cache *cs;
278 
279     /* read the super block */
280     disk->rdwr_sectors(disk, &sb, 2, 2, 0);
281 
282     /* check if it is ext2, since we also support btrfs now */
283     if (sb.s_magic != EXT2_SUPER_MAGIC)
284 	return -1;
285 
286     sbi = malloc(sizeof(*sbi));
287     if (!sbi) {
288 	malloc_error("ext2_sb_info structure");
289 	return -1;
290     }
291     fs->fs_info = sbi;
292 
293     if (sb.s_magic != EXT2_SUPER_MAGIC) {
294 	printf("ext2 mount error: it's not a EXT2/3/4 file system!\n");
295 	return 0;
296     }
297 
298     fs->sector_shift = disk->sector_shift;
299     fs->block_shift  = sb.s_log_block_size + 10;
300     fs->sector_size  = 1 << fs->sector_shift;
301     fs->block_size   = 1 << fs->block_shift;
302 
303     sbi->s_inodes_per_group = sb.s_inodes_per_group;
304     sbi->s_blocks_per_group = sb.s_blocks_per_group;
305     sbi->s_inodes_per_block = BLOCK_SIZE(fs) / sb.s_inode_size;
306     if (sb.s_desc_size < sizeof(struct ext2_group_desc))
307 	sb.s_desc_size = sizeof(struct ext2_group_desc);
308     sbi->s_desc_per_block   = BLOCK_SIZE(fs) / sb.s_desc_size;
309     sbi->s_groups_count     = (sb.s_blocks_count - sb.s_first_data_block
310 			       + EXT2_BLOCKS_PER_GROUP(fs) - 1)
311 	                      / EXT2_BLOCKS_PER_GROUP(fs);
312     sbi->s_first_data_block = sb.s_first_data_block;
313     sbi->s_inode_size = sb.s_inode_size;
314 
315     /* Volume UUID */
316     memcpy(sbi->s_uuid, sb.s_uuid, sizeof(sbi->s_uuid));
317 
318     /* Initialize the cache, and force block zero to all zero */
319     cache_init(fs->fs_dev, fs->block_shift);
320     cs = _get_cache_block(fs->fs_dev, 0);
321     memset(cs->data, 0, fs->block_size);
322     cache_lock_block(cs);
323 
324     return fs->block_shift;
325 }
326 
327 #define EXT2_UUID_LEN (4 + 4 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 4 + 4 + 4 + 1)
ext2_fs_uuid(struct fs_info * fs)328 static char *ext2_fs_uuid(struct fs_info *fs)
329 {
330     char *uuid = NULL;
331 
332     uuid = malloc(EXT2_UUID_LEN);
333     if (!uuid)
334 	return NULL;
335 
336     if (snprintf(uuid, EXT2_UUID_LEN,
337                   "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
338 	          EXT2_SB(fs)->s_uuid[0],
339 	          EXT2_SB(fs)->s_uuid[1],
340 	          EXT2_SB(fs)->s_uuid[2],
341 	          EXT2_SB(fs)->s_uuid[3],
342 	          EXT2_SB(fs)->s_uuid[4],
343 	          EXT2_SB(fs)->s_uuid[5],
344 	          EXT2_SB(fs)->s_uuid[6],
345 	          EXT2_SB(fs)->s_uuid[7],
346 	          EXT2_SB(fs)->s_uuid[8],
347 	          EXT2_SB(fs)->s_uuid[9],
348 	          EXT2_SB(fs)->s_uuid[10],
349 	          EXT2_SB(fs)->s_uuid[11],
350 	          EXT2_SB(fs)->s_uuid[12],
351 	          EXT2_SB(fs)->s_uuid[13],
352 	          EXT2_SB(fs)->s_uuid[14],
353 	          EXT2_SB(fs)->s_uuid[15]
354 	          ) < 0) {
355 	free(uuid);
356 	return NULL;
357     }
358 
359     return uuid;
360 }
361 
362 const struct fs_ops ext2_fs_ops = {
363     .fs_name       = "ext2",
364     .fs_flags      = FS_THISIND | FS_USEMEM,
365     .fs_init       = ext2_fs_init,
366     .searchdir     = NULL,
367     .getfssec      = generic_getfssec,
368     .close_file    = generic_close_file,
369     .mangle_name   = generic_mangle_name,
370     .chdir_start   = generic_chdir_start,
371     .open_config   = generic_open_config,
372     .iget_root     = ext2_iget_root,
373     .iget          = ext2_iget,
374     .readlink      = ext2_readlink,
375     .readdir       = ext2_readdir,
376     .next_extent   = ext2_next_extent,
377     .fs_uuid       = ext2_fs_uuid,
378 };
379