Home | History | Annotate | Download | only in ext2
      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  */
     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 *
     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  */
     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  */
     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 *
     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 *
     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 *
    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 
    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 
    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 
    177 static struct inode *ext2_iget_root(struct fs_info *fs)
    178 {
    179     return ext2_iget_by_inr(fs, EXT2_ROOT_INO);
    180 }
    181 
    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  */
    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 
    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  */
    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  */
    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)
    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