Add memtest support.
[syslinux-debian/hramrach.git] / core / fs / ext2 / ext2.c
blob957c60b7aee998cdea4ba92afc6f1522d62909fe
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"
13 * Convert an ext2 file type to the global values
15 static enum dirent_type ext2_cvt_type(unsigned int d_file_type)
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,
22 if (d_file_type > sizeof inode_type / sizeof *inode_type)
23 return DT_UNKNOWN;
24 else
25 return inode_type[d_file_type];
29 * get the group's descriptor of group_num
31 static const struct ext2_group_desc *
32 ext2_get_group_desc(struct fs_info *fs, uint32_t group_num)
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;
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);
44 return NULL;
47 desc_block = group_num / sbi->s_desc_per_block;
48 desc_index = group_num % sbi->s_desc_per_block;
50 desc_block += sbi->s_first_data_block + 1;
52 desc_data_block = get_cache(fs->fs_dev, desc_block);
53 return &desc_data_block[desc_index];
57 * Unlike strncmp, ext2_match_entry returns 1 for success, 0 for failure.
59 static inline bool ext2_match_entry(const char *name, size_t len,
60 const struct ext2_dir_entry * de)
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);
71 * p is at least 6 bytes before the end of page
73 static inline struct ext2_dir_entry *ext2_next_entry(struct ext2_dir_entry *p)
75 return (struct ext2_dir_entry *)((char*)p + p->d_rec_len);
79 * Map a logical sector and load it into the cache
81 static const void *
82 ext2_get_cache(struct inode *inode, block_t lblock)
84 block_t pblock = ext2_bmap(inode, lblock, NULL);
85 return get_cache(inode->fs->fs_dev, pblock);
89 * find a dir entry, return it if found, or return NULL.
91 static const struct ext2_dir_entry *
92 ext2_find_entry(struct fs_info *fs, struct inode *inode, const char *dname)
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);
100 while (i < inode->size) {
101 data = ext2_get_cache(inode, index++);
102 offset = 0;
103 maxoffset = min(BLOCK_SIZE(fs), i-inode->size);
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;
111 if (ext2_match_entry(dname, dname_len, de))
112 return de;
114 offset += de->d_rec_len;
116 i += BLOCK_SIZE(fs);
119 return NULL;
122 static const struct ext2_inode *
123 ext2_get_inode(struct fs_info *fs, int inr)
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;
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;
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);
141 data = get_cache(fs->fs_dev, block_num);
143 return (const struct ext2_inode *)
144 (data + block_off * EXT2_SB(fs)->s_inode_size);
147 static void fill_inode(struct inode *inode, const struct ext2_inode *e_inode)
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);
161 static struct inode *ext2_iget_by_inr(struct fs_info *fs, uint32_t inr)
163 const struct ext2_inode *e_inode;
164 struct inode *inode;
166 e_inode = ext2_get_inode(fs, inr);
167 if (!e_inode)
168 return NULL;
170 if (!(inode = alloc_inode(fs, inr, sizeof(struct ext2_pvt_inode))))
171 return NULL;
172 fill_inode(inode, e_inode);
174 return inode;
177 static struct inode *ext2_iget_root(struct fs_info *fs)
179 return ext2_iget_by_inr(fs, EXT2_ROOT_INO);
182 static struct inode *ext2_iget(const char *dname, struct inode *parent)
184 const struct ext2_dir_entry *de;
185 struct fs_info *fs = parent->fs;
187 de = ext2_find_entry(fs, parent, dname);
188 if (!de)
189 return NULL;
191 return ext2_iget_by_inr(fs, de->d_inode);
195 * Read the entire contents of an inode into a memory buffer
197 static int cache_get_file(struct inode *inode, void *buf, size_t bytes)
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;
206 if (inode->size > bytes)
207 bytes = inode->size;
209 while (bytes) {
210 chunk = min(bytes, block_size);
211 data = ext2_get_cache(inode, index++);
212 memcpy(p, data, chunk);
214 bytes -= chunk;
215 p += chunk;
218 return 0;
221 static int ext2_readlink(struct inode *inode, char *buf)
223 struct fs_info *fs = inode->fs;
224 int sec_per_block = 1 << (fs->block_shift - fs->sector_shift);
225 bool fast_symlink;
227 if (inode->size > BLOCK_SIZE(fs))
228 return -1; /* Error! */
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);
236 return inode->size;
240 * Read one directory entry at a time
242 static int ext2_readdir(struct file *file, struct dirent *dirent)
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;
250 if (file->offset >= inode->size)
251 return -1; /* End of file */
253 data = ext2_get_cache(inode, index);
254 de = (const struct ext2_dir_entry *)
255 (data + (file->offset & (BLOCK_SIZE(fs) - 1)));
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';
264 file->offset += de->d_rec_len; /* Update for next reading */
266 return 0;
270 * init. the fs meta data, return the block size bits.
272 static int ext2_fs_init(struct fs_info *fs)
274 struct disk *disk = fs->fs_dev->disk;
275 struct ext2_sb_info *sbi;
276 struct ext2_super_block sb;
277 struct cache *cs;
279 /* read the super block */
280 disk->rdwr_sectors(disk, &sb, 2, 2, 0);
282 /* check if it is ext2, since we also support btrfs now */
283 if (sb.s_magic != EXT2_SUPER_MAGIC)
284 return -1;
286 sbi = malloc(sizeof(*sbi));
287 if (!sbi) {
288 malloc_error("ext2_sb_info structure");
289 return -1;
291 fs->fs_info = sbi;
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;
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;
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;
315 /* Initialize the cache, and force block zero to all zero */
316 cache_init(fs->fs_dev, fs->block_shift);
317 cs = _get_cache_block(fs->fs_dev, 0);
318 memset(cs->data, 0, fs->block_size);
319 cache_lock_block(cs);
321 return fs->block_shift;
324 const struct fs_ops ext2_fs_ops = {
325 .fs_name = "ext2",
326 .fs_flags = FS_THISIND | FS_USEMEM,
327 .fs_init = ext2_fs_init,
328 .searchdir = NULL,
329 .getfssec = generic_getfssec,
330 .close_file = generic_close_file,
331 .mangle_name = generic_mangle_name,
332 .chdir_start = generic_chdir_start,
333 .open_config = generic_open_config,
334 .iget_root = ext2_iget_root,
335 .iget = ext2_iget,
336 .readlink = ext2_readlink,
337 .readdir = ext2_readdir,
338 .next_extent = ext2_next_extent,