cynix

x86 UNIX-like OS
git clone git://git.2f30.org/cynix
Log | Files | Refs | README | LICENSE

ext2.c (19048B)


      1 /*
      2  *  core/ext2.c
      3  *
      4  *  Copyright (C) 2009 stateless
      5  */
      6 
      7 #include <ext2.h>
      8 #include <common.h>
      9 #include <errno.h>
     10 #include <string.h>
     11 #include <heap.h>
     12 #include <x86.h>
     13 #include <mm.h>
     14 #include <syscall.h>
     15 #include <elf.h>
     16 #include <tty.h>
     17 #include <unistd.h>
     18 #include <pipe.h>
     19 
     20 #define EXT2_SETERRNO_AND_RET(err, ret) SETERRNO_AND_RET(err, ret)
     21 
     22 spinlock_t flist_lock;
     23 struct list_head flist;
     24 
     25 static struct ext2_superblock_t *sblock = NULL;
     26 static struct ext2_inode_t *root_inode = NULL;
     27 static char *fs_base_addr = NULL;
     28 
     29 static void *readblock(uint32_t block_no);
     30 static struct ext2_inode_t *geti(ino_t ino);
     31 static struct ext2_gdt_t *readgdt(uint32_t gdt);
     32 static int read_inode_blocks(struct ext2_inode_t *inode, off_t offs, char *buffer, size_t nbytes);
     33 static int check_inode_status(ino_t inode);
     34 static struct ext2_dir_t *resolve_path_internal(struct ext2_inode_t *inode, const char *path);
     35 static struct ext2_dir_t *resolve_path(const char *path);
     36 static struct ext2_dir_t *find_dentry(struct ext2_inode_t *inode, const char *name, uint32_t is_root);
     37 static int filealloc(ino_t inode_nr);
     38 static blksize_t get_block_size(void);
     39 
     40 static blksize_t
     41 get_block_size(void)
     42 {
     43 	assert(sblock);
     44 	return 1024 << sblock->block_size;
     45 }
     46 
     47 static inline void *
     48 readblock(uint32_t block_no)
     49 {
     50 	assert(sblock);
     51 	return fs_base_addr + get_block_size() * block_no;
     52 }
     53 
     54 static inline struct ext2_gdt_t *
     55 readgdt(uint32_t gdt) {
     56 	char *block;
     57 
     58 	block = readblock(2);
     59 	return (struct ext2_gdt_t *)(block + (gdt * sizeof(struct ext2_gdt_t)));
     60 }
     61 
     62 static inline struct ext2_inode_t *
     63 geti(ino_t ino) {
     64 	struct ext2_gdt_t *gdt;
     65 
     66 	gdt = readgdt((ino - 1) / sblock->nr_inodes_per_group);
     67 	return (struct ext2_inode_t *)((char *)readblock(gdt->addr_inode_table) + (ino - 1) * sblock->inode_size);
     68 }
     69 
     70 /* should be called with flist_lock held */
     71 __attribute__ ((unused)) static void
     72 dump_flist(void)
     73 {
     74 	struct list_head *iter;
     75 	struct file_t *f;
     76 
     77 	list_for_each(iter, &flist) {
     78 		f = list_entry(iter, struct file_t, f_listopen);
     79 		printf("inode = 0x%08lx, off = %lu, state = %s, ref_count = %d\n",
     80 		       (void *)f->f_inode,
     81 		       f->f_off,
     82 		       (f->f_state == FILE_ALLOC) ? "FILE_ALLOC" : "FILE_NONE",
     83 		       f->f_refcount
     84 		      );
     85 	}
     86 }
     87 
     88 int
     89 ext2_mount(char *addr)
     90 {
     91 	int i;
     92 
     93 	if (!addr) return -EINVAL;
     94 	fs_base_addr = addr;
     95 	sblock = (struct ext2_superblock_t *)(fs_base_addr + 1024);
     96 	if (sblock->magic != 0xef53) {
     97 		info("invalid ext2 volume!\n");
     98 		sblock = NULL;
     99 		return -EILSEQ;
    100 	}
    101 	root_inode = geti(2);
    102 	if (!(root_inode->file_mode & DIRECTORY_T)) {
    103 		info("can't locate the root directory of the filesystem!\n");
    104 		return -ENOTDIR;
    105 	}
    106 	for (i = 1; i <= 10; ++i)
    107 		if (check_inode_status(i) < 0) {
    108 			info("inode validation failed!\n");
    109 			return -EIO;
    110 		}
    111 	INIT_LIST_HEAD(&flist);
    112 	initspinlock(&flist_lock);
    113 	return 0;
    114 }
    115 
    116 struct ext2_inode_t *
    117 get_root_inode(void) {
    118 	assert(root_inode);
    119 	return root_inode;
    120 }
    121 
    122 static int
    123 read_inode_blocks(struct ext2_inode_t *inode, off_t offs /* in blocks */, char *buffer, size_t nbytes)
    124 {
    125 	int ret = -EINVAL, off = 0;
    126 	uint32_t *block, i;
    127 	blkcnt_t nr_blocks;
    128 	blksize_t blksize;
    129 	char *tmp;
    130 
    131 	if (!inode || !buffer)
    132 		return ret;
    133 	if (!nbytes)
    134 		return 0;
    135 
    136 	/* TODO: make sure this code is bug-proof */
    137 	blksize = get_block_size();
    138 	nr_blocks = inode->size_low32 / blksize;
    139 	if (nr_blocks >= 12 + (blksize / 4)
    140 			|| (nbytes / blksize) + offs >= 12 + (blksize / 4)) {
    141 		info("can't handle double indirect blocks yet!\n");
    142 		return -EIO;
    143 	}
    144 	if (offs >= 12) {
    145 		offs -= 12;
    146 		goto read_indirect;
    147 	}
    148 	block = (uint32_t *)inode->direct_blocks;
    149 	block += offs;
    150 	for (i = offs / blksize; nbytes && i <= nr_blocks; ++i, ++block) {
    151 		if (!*block)
    152 			continue;
    153 		tmp = readblock(*block);
    154 		memcpy((char *)buffer + off, tmp, blksize);
    155 		nbytes -= blksize;
    156 		off += blksize;
    157 		if (off / blksize >= 12 && nbytes)
    158 			goto read_indirect;
    159 	}
    160 	if (!nbytes) return off;
    161 read_indirect:
    162 	block = (uint32_t *)readblock(inode->single_indir_block);
    163 	block += offs;
    164 	for (i = offs / blksize; nbytes && i <= nr_blocks; ++i, ++block) {
    165 		if (!*block)
    166 			continue;
    167 		tmp = readblock(*block);
    168 		memcpy((char *)buffer + off, tmp, blksize);
    169 		nbytes -= blksize;
    170 		off += blksize;
    171 	}
    172 	return off;
    173 }
    174 
    175 static struct ext2_dir_t *
    176 find_dentry(struct ext2_inode_t *inode, const char *name, uint32_t is_root) {
    177 	char *buffer;
    178 	struct ext2_dir_t *dir, *retdir;
    179 	int ret;
    180 	blkcnt_t nr_blocks;
    181 	blksize_t blksize;
    182 
    183 	if (!name)
    184 		return ERR_PTR(-EINVAL);
    185 
    186 	blksize = get_block_size();
    187 	buffer = kmalloc(blksize);
    188 	if (IS_ERR(buffer))
    189 		return (void *)buffer;
    190 	nr_blocks = inode->size_low32 / blksize;
    191 	for (uint32_t j = 0; j <= nr_blocks; ++j) {
    192 		if ((ret = read_inode_blocks(inode, j, buffer, blksize)) < 0)
    193 			kerror(-ret);
    194 		if (!ret) continue;
    195 		dir = (struct ext2_dir_t *)buffer;
    196 		if (is_root) goto found;
    197 		for (; dir->rec_length;
    198 				dir = (struct ext2_dir_t *)((char *)dir + dir->rec_length)) {
    199 			if (strlen(name) == dir->name_length) {
    200 				if (!strncmp(name, dir->filename, dir->name_length)) {
    201 found:
    202 					if (is_root)
    203 						assert(!strcmp(name, "/"));
    204 					retdir = kmalloc(sizeof(*retdir));
    205 					if (IS_ERR(retdir)) {
    206 						kfree(buffer);
    207 						return retdir;
    208 					}
    209 					memcpy(retdir, dir, sizeof(*retdir));
    210 					kfree(buffer);
    211 					return retdir;
    212 				}
    213 			}
    214 		}
    215 	}
    216 	kfree(buffer);
    217 	return ERR_PTR(-ENOENT);
    218 }
    219 
    220 static int
    221 filealloc(ino_t inode_nr)
    222 {
    223 	struct ext2_inode_t *inode;
    224 	struct file_t *f;
    225 	int i;
    226 
    227 	inode = geti(inode_nr);
    228 	acquire(&flist_lock);
    229 	for (i = 3; i < NR_MAX_OPEN_FILES; ++i) {
    230 		if (curr_proc->fdtable[i]
    231 				&& curr_proc->fdtable[i]->f_state != FILE_NONE)
    232 			continue;
    233 		if (!curr_proc->fdtable[i]) {
    234 			f = kmalloc(sizeof(struct file_t));
    235 			if (IS_ERR(f)) {
    236 				release(&flist_lock);
    237 				return PTR_ERR(f);
    238 			}
    239 			f->f_state = FILE_NONE;
    240 			list_add_tail(&f->f_listopen, &flist);
    241 			curr_proc->fdtable[i] = f;
    242 		}
    243 		f = curr_proc->fdtable[i];
    244 		assert(f->f_state == FILE_NONE);
    245 		f->f_refcount = 1;
    246 		f->f_off = 0;
    247 		f->f_state = FILE_ALLOC;
    248 		f->f_type = FILE_REG;
    249 		f->f_inode = inode;
    250 		f->f_inode_nr = inode_nr;
    251 		release(&flist_lock);
    252 		return i;
    253 	}
    254 	release(&flist_lock);
    255 	return -EMFILE;
    256 }
    257 
    258 int
    259 fileclose(int fd)
    260 {
    261 	struct file_t *f;
    262 	int j;
    263 
    264 	if (fd < 0 || fd >= NR_MAX_OPEN_FILES)
    265 		return -EINVAL;
    266 
    267 	acquire(&flist_lock);
    268 	f = curr_proc->fdtable[fd];
    269 	if (!f || f->f_state != FILE_ALLOC) {
    270 		release(&flist_lock);
    271 		return -EBADF;
    272 	}
    273 	if (!--f->f_refcount) {
    274 		curr_proc->fdtable[fd]->f_state = FILE_NONE;
    275 		if (f->f_type == FILE_PIPE) {
    276 			for (j = 3; j < NR_MAX_OPEN_FILES; ++j) {
    277 				if (fd == j || curr_proc->pipebufs[j] != curr_proc->pipebufs[fd])
    278 					continue;
    279 				if (curr_proc->fdtable[j]->f_state == FILE_ALLOC)
    280 					break;
    281 				/* no one is referencing this buffer */
    282 				kfree(curr_proc->pipebufs[fd]);
    283 				curr_proc->pipebufs[fd] = curr_proc->pipebufs[j] = NULL;
    284 				break;
    285 			}
    286 		}
    287 	}
    288 	release(&flist_lock);
    289 	return 0;
    290 }
    291 
    292 /* TODO: free pipebufs */
    293 void
    294 closefds(void)
    295 {
    296 	struct file_t *f;
    297 	int i;
    298 
    299 	acquire(&flist_lock);
    300 	for (i = 3; i < NR_MAX_OPEN_FILES; ++i) {
    301 		if (!curr_proc->fdtable[i])
    302 			continue;
    303 		f = curr_proc->fdtable[i];
    304 		if (f->f_state == FILE_NONE || f->f_state == FILE_ALLOC) {
    305 			if (f->f_state == FILE_NONE) {
    306 				assert(!f->f_refcount);
    307 				list_del(&f->f_listopen);
    308 				/* invalidate dup(2)ed close fds */
    309 				bzero(f, sizeof(*f));
    310 				kfree(f);
    311 			} else {
    312 				if (!--f->f_refcount) {
    313 					list_del(&f->f_listopen);
    314 					bzero(f, sizeof(*f));
    315 					kfree(f);
    316 				}
    317 			}
    318 		}
    319 	}
    320 	release(&flist_lock);
    321 }
    322 
    323 static struct ext2_dir_t *
    324 resolve_path_internal(struct ext2_inode_t *inode, const char *path) {
    325 	struct ext2_dir_t *dirent;
    326 	char *buf, *bufptr;
    327 	const char *ptr;
    328 	uint32_t ino;
    329 	uint32_t is_root_flag = 1;
    330 	int ret;
    331 
    332 	buf = kmalloc(EXT2_NAME_LEN + 1);
    333 	if (IS_ERR(buf)) {
    334 		ret = PTR_ERR(buf);
    335 		return ERR_PTR(ret);
    336 	}
    337 	memset(buf, 0, EXT2_NAME_LEN + 1);
    338 	ptr = path;
    339 	*buf = *ptr;
    340 	if (*ptr == '/') ++ptr;
    341 	if (*ptr) {
    342 		bufptr = buf;
    343 		while (*ptr && *ptr != '/') {
    344 			if (bufptr - buf >= EXT2_NAME_LEN) {
    345 				kfree(buf);
    346 				return ERR_PTR(-ENAMETOOLONG);
    347 			}
    348 			*bufptr++ = *ptr++;
    349 		}
    350 		if (*ptr == '/') ++ptr;
    351 		is_root_flag = 0;
    352 	}
    353 	if (IS_ERR(dirent = find_dentry(inode, buf, is_root_flag))) {
    354 		ret = PTR_ERR(dirent);
    355 		goto err;
    356 	}
    357 	kfree(buf);
    358 	if (!*ptr)
    359 		return dirent;
    360 	ino = dirent->inode;
    361 	kfree(dirent);
    362 	return resolve_path_internal(geti(ino), ptr);
    363 err:
    364 	kfree(buf);
    365 	return ERR_PTR(ret);
    366 }
    367 
    368 static struct ext2_dir_t *
    369 resolve_path(const char *path) {
    370 	if (!path)
    371 		return NULL;
    372 	if (*path == '/')
    373 		return resolve_path_internal(root_inode, path);
    374 	return resolve_path_internal(curr_proc->cdir, path);
    375 }
    376 
    377 static int
    378 check_inode_status(ino_t inode)
    379 {
    380 	struct ext2_gdt_t *gdt;
    381 	char *inode_bitmap;
    382 	blksize_t i;
    383 
    384 	gdt = readgdt((inode - 1) / sblock->nr_inodes_per_group);
    385 	inode_bitmap = readblock(gdt->addr_block_bitmap);
    386 	for (i = 0; i < get_block_size(); ++i)
    387 		if (inode_bitmap[i / 8] & (1 << (1 % 8)))
    388 			return 0;
    389 	return -ENOENT;
    390 }
    391 
    392 int
    393 dup(int oldfd)
    394 {
    395 	int i;
    396 	struct file_t *f, *tmp;
    397 
    398 	if (oldfd < 0 || oldfd >= NR_MAX_OPEN_FILES)
    399 		return -EINVAL;
    400 	acquire(&flist_lock);
    401 	f = curr_proc->fdtable[oldfd];
    402 	if (!f || f->f_state != FILE_ALLOC) {
    403 		release(&flist_lock);
    404 		return -EBADF;
    405 	}
    406 	for (i = 3; i < NR_MAX_OPEN_FILES; ++i) {
    407 		tmp = curr_proc->fdtable[i];
    408 		if (!tmp || tmp->f_state == FILE_NONE) {
    409 			if (tmp->f_state == FILE_NONE) {
    410 				assert(!tmp->f_refcount);
    411 				list_del(&tmp->f_listopen);
    412 				kfree(tmp);
    413 				tmp = NULL;
    414 			}
    415 			curr_proc->fdtable[i] = f;
    416 			++f->f_refcount;
    417 			release(&flist_lock);
    418 			return i;
    419 		}
    420 	}
    421 	release(&flist_lock);
    422 	return -EMFILE;
    423 }
    424 
    425 int
    426 dup2(int oldfd, int newfd)
    427 {
    428 	struct file_t *f, *tmp;
    429 
    430 	if (oldfd < 0 || oldfd >= NR_MAX_OPEN_FILES)
    431 		return -EINVAL;
    432 	acquire(&flist_lock);
    433 	f = curr_proc->fdtable[oldfd];
    434 	if (!f || f->f_state != FILE_ALLOC
    435 			|| newfd < 0 || newfd >= NR_MAX_OPEN_FILES) {
    436 		release(&flist_lock);
    437 		return -EBADF;
    438 	}
    439 	if (oldfd == newfd) {
    440 		release(&flist_lock);
    441 		return newfd;
    442 	}
    443 	tmp = curr_proc->fdtable[newfd];
    444 	if (tmp && tmp->f_state == FILE_ALLOC)
    445 		if (!--tmp->f_refcount)
    446 			tmp->f_state = FILE_NONE;
    447 	if (tmp)
    448 		assert(!tmp->f_refcount);
    449 	if (!tmp || !tmp->f_refcount) {
    450 		curr_proc->fdtable[newfd] = f;
    451 		++f->f_refcount;
    452 		release(&flist_lock);
    453 		return newfd;
    454 	}
    455 	release(&flist_lock);
    456 	return -EMFILE;
    457 }
    458 
    459 int
    460 open(const char *pathname,
    461      __attribute__ ((unused)) int flags,
    462      __attribute__ ((unused)) mode_t mode)
    463 {
    464 	struct ext2_dir_t *dirent;
    465 	int fd;
    466 
    467 	if (!pathname)
    468 		return -EINVAL;
    469 	dirent = resolve_path(pathname);
    470 	if (IS_ERR(dirent))
    471 		return PTR_ERR(dirent);
    472 	fd = filealloc(dirent->inode);
    473 	return fd;
    474 }
    475 
    476 int
    477 creat(__attribute__ ((unused)) const char *pathname,
    478       __attribute__ ((unused)) mode_t mode)
    479 {
    480 	return -ENOSYS;
    481 }
    482 
    483 int
    484 close(int fd)
    485 {
    486 	return fileclose(fd);
    487 }
    488 
    489 DIR *
    490 opendir(const char *name)
    491 {
    492 	int fd;
    493 	DIR *newdir = NULL;
    494 
    495 	fd = sys_open(name, 0, 0);
    496 	if (fd < 0)
    497 		EXT2_SETERRNO_AND_RET(-fd, NULL);
    498 	if (!(curr_proc->fdtable[fd]->f_inode->file_mode & DIRECTORY_T)) {
    499 		errno = ENOTDIR;
    500 		goto err;
    501 	}
    502 	newdir = kmalloc(sizeof(*newdir));
    503 	if (IS_ERR(newdir)) {
    504 		errno = -PTR_ERR(newdir);
    505 		goto err;
    506 	}
    507 	newdir->off = 0;
    508 	newdir->fd = fd;
    509 	return newdir;
    510 err:
    511 	sys_close(fd);
    512 	EXT2_SETERRNO_AND_RET(errno, NULL);
    513 }
    514 
    515 struct dirent_t *
    516 readdir(DIR *dirp) {
    517 	struct file_t *file;
    518 	static struct dirent_t dirent;
    519 	struct ext2_dir_t *dir;
    520 	struct ext2_inode_t *inode;
    521 	char *buffer;
    522 	blksize_t blksize;
    523 	int ret;
    524 
    525 	if (!dirp)
    526 		return ERR_PTR(-EINVAL);
    527 	file = curr_proc->fdtable[dirp->fd];
    528 	if (!file || !(file->f_inode->file_mode & DIRECTORY_T))
    529 		return ERR_PTR(-EINVAL);
    530 	inode = geti(file->f_inode_nr);
    531 	blksize = get_block_size();
    532 	buffer = kmalloc(blksize);
    533 	if (IS_ERR(buffer))
    534 		return (void *)buffer;
    535 	/* TODO: make it possible to have directory entries spanning many blocks */
    536 	ret = read_inode_blocks(inode, 0, buffer, blksize);
    537 	if (ret <= 0) {
    538 		kfree(buffer);
    539 		return ERR_PTR(ret);
    540 	}
    541 	dir = (struct ext2_dir_t *)(buffer + dirp->off);
    542 	if (dirp->off >= blksize) {
    543 		kfree(buffer);
    544 		return NULL;
    545 	}
    546 	dirp->off += dir->rec_length;
    547 	dirent.d_inode = dir->inode;
    548 	dirent.d_off = (uint32_t)((char *)dir - buffer);
    549 	dirent.d_namelen = dir->name_length;
    550 	memset(dirent.d_name, 0, EXT2_NAME_LEN + 1);
    551 	memcpy(dirent.d_name, dir->filename, dir->name_length);
    552 	kfree(buffer);
    553 	return &dirent;
    554 }
    555 
    556 int
    557 closedir(DIR *dirp)
    558 {
    559 	int ret;
    560 
    561 	if (!dirp)
    562 		return -EINVAL;
    563 	if ((ret = sys_close(dirp->fd)) < 0)
    564 		return ret;
    565 	kfree(dirp);
    566 	return 0;
    567 }
    568 
    569 ssize_t
    570 read(int fd, void *buf, size_t count)
    571 {
    572 	struct file_t *f;
    573 	int ret;
    574 	char *newbuf;
    575 	size_t newcnt;
    576 	blksize_t blksize;
    577 	off_t soff;
    578 	ssize_t r;
    579 
    580 
    581 	if (fd < 0 || fd >= NR_MAX_OPEN_FILES || !buf)
    582 		return -EINVAL;
    583 
    584 	if (fd == STDIN_FILENO)
    585 		return tty_read(buf, count);
    586 
    587 	acquire(&flist_lock);
    588 	f = curr_proc->fdtable[fd];
    589 	if (!f || f->f_state != FILE_ALLOC) {
    590 		release(&flist_lock);
    591 		return -EBADF;
    592 	}
    593 	if (f->f_type == FILE_PIPE) {
    594 		r = piperead(fd, buf, count);
    595 		release(&flist_lock);
    596 		return r;
    597 	}
    598 	assert(f->f_inode);
    599 	if (f->f_inode->file_mode & DIRECTORY_T) {
    600 		release(&flist_lock);
    601 		return -EISDIR;
    602 	}
    603 	blksize = get_block_size();
    604 	if (f->f_inode->size_low32 - f->f_off < count)
    605 		count = f->f_inode->size_low32 - f->f_off;
    606 	if (!count) goto out;
    607 	newcnt = count;
    608 	if (newcnt % blksize) newcnt = (newcnt + blksize) & ~(blksize - 1);
    609 	newbuf = kmalloc(newcnt);
    610 	if (IS_ERR(newbuf)) {
    611 		release(&flist_lock);
    612 		return PTR_ERR(newbuf);
    613 	}
    614 	soff = (f->f_off % blksize) ? (f->f_off & ~((off_t)blksize - 1)) : (off_t)f->f_off;
    615 	soff /= blksize;
    616 	if ((ret = read_inode_blocks(f->f_inode, soff, newbuf, newcnt)) <= 0) {
    617 		kfree(newbuf);
    618 		release(&flist_lock);
    619 		return ret;
    620 	}
    621 	soff = f->f_off % newcnt;
    622 	if (newcnt - soff < count) count = newcnt - soff;
    623 	memcpy(buf, newbuf + soff, count);
    624 	f->f_off += count;
    625 	kfree(newbuf);
    626 out:
    627 	release(&flist_lock);
    628 	return count;
    629 }
    630 
    631 ssize_t
    632 write(int fd, const void *buf, size_t count)
    633 {
    634 	const char *ptr;
    635 	size_t len = 0;
    636 	struct file_t *f;
    637 	ssize_t r;
    638 
    639 	if (fd < 0 || fd >= NR_MAX_OPEN_FILES || !buf)
    640 		return -EINVAL;
    641 
    642 	/* ignore STDIN, STDOUT and STDERR for now */
    643 	if (fd > 2) {
    644 		acquire(&flist_lock);
    645 		f = curr_proc->fdtable[fd];
    646 		if (!f || f->f_state != FILE_ALLOC) {
    647 			release(&flist_lock);
    648 			return -EBADF;
    649 		}
    650 		if (f->f_type == FILE_PIPE) {
    651 			r = pipewrite(fd, buf, count);
    652 			release(&flist_lock);
    653 			return r;
    654 		}
    655 		release(&flist_lock);
    656 	}
    657 	ptr = buf;
    658 	while (count--) {
    659 		if (putchar(*ptr++) < 0)
    660 			break;
    661 		++len;
    662 	}
    663 	return len;
    664 }
    665 
    666 int
    667 stat(const char *path, struct stat *buf)
    668 {
    669 	struct ext2_dir_t *dir;
    670 	struct ext2_inode_t *inode;
    671 
    672 	if (!path || !buf)
    673 		return -EINVAL;
    674 
    675 	dir = resolve_path(path);
    676 	if (IS_ERR(dir))
    677 		return PTR_ERR(dir);
    678 	inode = geti(dir->inode);
    679 	buf->st_dev = 0;
    680 	buf->st_ino = dir->inode;
    681 	buf->st_mode = inode->file_mode;
    682 	buf->st_nlink = inode->link_count;
    683 	buf->st_uid = inode->uid_low16 | (inode->uid_upper16 << 16);
    684 	buf->st_gid = inode->gid_low16 | (inode->gid_upper16 << 16);
    685 	buf->st_rdev = 0;
    686 	buf->st_size = inode->size_low32;
    687 	buf->st_blksize = get_block_size();
    688 	buf->st_blocks = (inode->size_low32 / buf->st_blksize) * (buf->st_blksize / 512);
    689 	buf->st_atime = inode->atime;
    690 	buf->st_mtime = inode->mtime;
    691 	buf->st_ctime = inode->ctime;
    692 	return 0;
    693 }
    694 
    695 int
    696 fstat(int fd, struct stat *buf)
    697 {
    698 	struct file_t *f;
    699 	struct ext2_inode_t *inode;
    700 
    701 	if (fd < 0 || fd >= NR_MAX_OPEN_FILES)
    702 		return -EBADF;
    703 	if (!buf)
    704 		return -EINVAL;
    705 	acquire(&flist_lock);
    706 	f = curr_proc->fdtable[fd];
    707 	if (!f || f->f_state != FILE_ALLOC) {
    708 		release(&flist_lock);
    709 		return -EBADF;
    710 	}
    711 	inode = geti(f->f_inode_nr);
    712 	buf->st_dev = 0;
    713 	buf->st_ino = f->f_inode_nr;
    714 	buf->st_mode = inode->file_mode;
    715 	buf->st_nlink = inode->link_count;
    716 	buf->st_uid = inode->uid_low16 | (inode->uid_upper16 << 16);
    717 	buf->st_gid = inode->gid_low16 | (inode->gid_upper16 << 16);
    718 	buf->st_rdev = 0;
    719 	buf->st_size = inode->size_low32;
    720 	buf->st_blksize = get_block_size();
    721 	buf->st_blocks = (inode->size_low32 / buf->st_blksize) * (buf->st_blksize / 512);
    722 	buf->st_atime = inode->atime;
    723 	buf->st_mtime = inode->mtime;
    724 	buf->st_ctime = inode->ctime;
    725 	release(&flist_lock);
    726 	return 0;
    727 }
    728 
    729 int
    730 execve(const char *filename,
    731        __attribute__ ((unused)) char *const argv[],
    732        __attribute__ ((unused)) char *const envp[])
    733 {
    734 	struct stat buf;
    735 	int fd, ret = 0, i;
    736 	char *elfimage;
    737 	ssize_t bytesleft;
    738 	ssize_t bytesread = 0;
    739 	Elf32_Ehdr *elfheader;
    740 	Elf32_Phdr *elfprog;
    741 	uint32_t sz;
    742 	struct mmap_region *reg;
    743 	struct list_head *iter, *tmpreg;
    744 	size_t len;
    745 	blksize_t blksize;
    746 
    747 	if ((fd = open(filename, O_RDONLY, 0)) < 0)
    748 		return fd;
    749 	if ((ret = fstat(fd, &buf)) < 0)
    750 		goto err;
    751 	if (IS_ERR(elfimage = kmalloc(buf.st_size))) {
    752 		ret = PTR_ERR(elfimage);
    753 		goto err;
    754 	}
    755 
    756 	bytesleft = buf.st_size;
    757 	blksize = get_block_size();
    758 	do {
    759 		ret = read(fd, elfimage + bytesread, bytesleft);
    760 		if (!ret) break;
    761 		if (ret < 0) goto err1;
    762 		bytesread += ret;
    763 		bytesleft -= ret;
    764 	} while (bytesleft > 0);
    765 	assert(!bytesleft && bytesread == buf.st_size);
    766 	elfheader = (Elf32_Ehdr *)elfimage;
    767 
    768 	if (elfheader->e_ident[EI_MAG0] != 0x7f
    769 			|| elfheader->e_ident[EI_MAG1] != 'E'
    770 			|| elfheader->e_ident[EI_MAG2] != 'L'
    771 			|| elfheader->e_ident[EI_MAG3] != 'F'
    772 			|| elfheader->e_type != ET_EXEC
    773 			|| elfheader->e_machine != EM_386
    774 			|| !elfheader->e_entry
    775 			|| elfheader->e_version != EV_CURRENT
    776 			|| elfheader->e_ident[EI_VERSION] != EV_CURRENT
    777 			|| !elfheader->e_phnum
    778 			|| elfheader->e_ident[EI_CLASS] != ELFCLASS32
    779 			|| elfheader->e_ident[EI_DATA] != ELFDATA2LSB) {
    780 		ret = -ENOEXEC;
    781 		goto err1;
    782 	}
    783 
    784 	elfprog = (Elf32_Phdr *)(elfimage + elfheader->e_phoff);
    785 	for (i = 0; i < elfheader->e_phnum; ++i, ++elfprog) {
    786 		if (elfprog->p_type != 0x1) continue;
    787 		if (elfprog->p_vaddr + elfprog->p_memsz < elfprog->p_vaddr)
    788 			goto err1;
    789 		sz = elfprog->p_filesz;
    790 		reg = kmalloc(sizeof(*reg));
    791 		if (IS_ERR(reg)) {
    792 			ret = PTR_ERR(reg);
    793 			goto err1;
    794 		}
    795 		if ((ret = mmap_range(elfprog->p_vaddr, elfprog->p_vaddr + sz)) < 0) {
    796 			for (--elfprog; i > 0; --i) {
    797 				ret = unmap_range(elfprog->p_vaddr, elfprog->p_vaddr + sz);
    798 				if (ret < 0)
    799 					panic("can't unmap mapped pages!");
    800 			}
    801 			kfree(reg);
    802 			goto err2;
    803 		}
    804 		bzero((void *)elfprog->p_vaddr, 0);
    805 		memcpy((void *)elfprog->p_vaddr,
    806 		       (const void *)(elfimage + elfprog->p_offset), sz);
    807 		reg->base_addr = elfprog->p_vaddr; reg->size = sz;
    808 		list_add_tail(&reg->l_region, &curr_proc->l_regions);
    809 	}
    810 
    811 	kfree(curr_proc->name);
    812 	len = strlen(filename) + 1;
    813 	assert(len);
    814 	curr_proc->name = kmalloc(len);
    815 	if (IS_ERR(curr_proc->name)) {
    816 		ret = PTR_ERR(curr_proc->name);
    817 		goto err2;
    818 	}
    819 	strncpy(curr_proc->name, filename, len);
    820 	curr_proc->name[len - 1] = '\0';
    821 	curr_proc->cf->eip = elfheader->e_entry;
    822 	goto err1;
    823 err2:
    824 	list_for_each_safe(iter, tmpreg, &curr_proc->l_regions) {
    825 		reg = list_entry(iter, struct mmap_region, l_region);
    826 		if (unmap_range(reg->base_addr, reg->base_addr + reg->size) < 0)
    827 			panic("failed to unmap pages!");
    828 		list_del(iter);
    829 		kfree(reg);
    830 	}
    831 err1:
    832 	kfree(elfimage);
    833 err:
    834 	close(fd);
    835 	return ret;
    836 }
    837