source: bootcd/isolinux/syslinux-6.03/core/fs/ext2/ext2.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 9.9 KB
Line 
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 */
15static 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 */
31static const struct ext2_group_desc *
32ext2_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 */
59static 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 */
73static 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 */
81static const void *
82ext2_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 */
91static const struct ext2_dir_entry *
92ext2_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
122static const struct ext2_inode *
123ext2_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
147static 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
161static 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
177static struct inode *ext2_iget_root(struct fs_info *fs)
178{
179    return ext2_iget_by_inr(fs, EXT2_ROOT_INO);
180}
181
182static 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 */
197static 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       
221static 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 */
242static 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 */
272static 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)
328static 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
362const 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};
Note: See TracBrowser for help on using the repository browser.