Пример #1
0
int build_jindex(struct gfs2_sbd *sdp)
{
	struct gfs2_inode *jindex;
	unsigned int j;
	int ret;

	jindex = createi(sdp->master_dir, "jindex", S_IFDIR | 0700,
			 GFS2_DIF_SYSTEM);
	if (jindex == NULL) {
		return errno;
	}
	sdp->md.journal = malloc(sdp->md.journals *
				 sizeof(struct gfs2_inode *));
	for (j = 0; j < sdp->md.journals; j++) {
		ret = build_journal(sdp, j, jindex);
		if (ret)
			return ret;
		inode_put(&sdp->md.journal[j]);
	}
	if (cfg_debug) {
		printf("\nJindex:\n");
		gfs2_dinode_print(&jindex->i_di);
	}

	free(sdp->md.journal);
	inode_put(&jindex);
	return 0;
}
Пример #2
0
/*
 * Removes a directory entry.
 */
PUBLIC int sys_unlink(const char *path)
{
	int ret;              /* Return value.      */
	struct inode *dir;    /* Working directory. */
	const char *filename; /* Working file name. */
	char *pathname;       /* Path name.         */
	
	pathname = getname(path);
	
	dir = inode_dname(pathname, &filename);
	
	/* Failed to get directory. */
	if (dir == NULL)
	{
		putname(pathname);
		return (-ENOENT);
	}

	/* No write permissions on directory. */
	if (!permission(dir->mode, dir->uid, dir->gid, curr_proc, MAY_WRITE, 0))
	{
		inode_put(dir);
		putname(pathname);
		return (-EPERM);
	}

	ret = dir_remove(dir, filename);
	
	inode_put(dir);
	putname(pathname);
	
	return (ret);
}
Пример #3
0
/**
 * Write a jindex file given a list of journal inums.
 * master: Inode of the master directory
 * jnls: List of inum structures relating to previously created journals.
 * nmemb: The number of entries in the list (number of journals).
 * Returns 0 on success or non-zero on error with errno set.
 */
int lgfs2_build_jindex(struct gfs2_inode *master, struct gfs2_inum *jnls, size_t nmemb)
{
	char fname[GFS2_FNAMESIZE + 1];
	struct gfs2_inode *jindex;
	unsigned j;
	int ret;

	if (nmemb == 0 || jnls == NULL) {
		errno = EINVAL;
		return 1;
	}
	jindex = createi(master, "jindex", S_IFDIR | 0700, GFS2_DIF_SYSTEM);
	if (jindex == NULL)
		return 1;

	fname[GFS2_FNAMESIZE] = '\0';

	for (j = 0; j < nmemb; j++) {
		snprintf(fname, GFS2_FNAMESIZE, "journal%u", j);
		ret = dir_add(jindex, fname, strlen(fname), &jnls[j], IF2DT(S_IFREG | 0600));
		if (ret) {
			inode_put(&jindex);
			return 1;
		}
	}

	if (cfg_debug) {
		printf("\nJindex:\n");
		gfs2_dinode_print(&jindex->i_di);
	}

	inode_put(&jindex);
	return 0;
}
Пример #4
0
/*
 * Removes an entry from a directory.
 */
PUBLIC int dir_remove(struct inode *dinode, const char *filename)
{
	struct buffer *buf; /* Block buffer.    */
	struct d_dirent *d; /* Directory entry. */
	struct inode *file; /* File inode.      */
	
	d = dirent_search(dinode, filename, &buf, 0);
	
	/* Not found. */
	if (d == NULL)
		return (-ENOENT);

	/* Cannot remove '.' */
	if (d->d_ino == dinode->num)
	{
		brelse(buf);
		return (-EBUSY);
	}
	
	file = inode_get(dinode->dev, d->d_ino);
	
	/* Failed to get file's inode. */
	if (file == NULL)
	{
		brelse(buf);
		return (-ENOENT);
	}
	
	/* Unlinking directory. */
	if (S_ISDIR(file->mode))
	{
		/* Not allowed. */
		if (!IS_SUPERUSER(curr_proc))
		{
			inode_put(file);
			brelse(buf);
			return (-EPERM);
		}
		
		/* Directory not empty. */
		if (dinode->size)
		{
			inode_put(file);
			brelse(buf);
			return (-EBUSY);			
		}
	}
	
	/* Remove directory entry. */
	d->d_ino = INODE_NULL;
	buf->flags |= BUFFER_DIRTY;
	inode_touch(dinode);
	file->nlinks--;
	inode_touch(file);
	inode_put(file);
	brelse(buf);
	
	return (0);
}
Пример #5
0
/**
 * Look up and return the inode for a path name.
 * If nameiparent is true, return the inode for the parent and copy the final
 * path element into name, which must have room for DIRSIZ bytes.
 * Returns 0 in the case of error.
 */
static struct inode*
namex(char *path, bool nameiparent, char *name)
{
  struct inode *ip;
  struct inode *next;
  // If path is a full path, get the pointer to the root inode. Otherwise get
  // the inode corresponding to the current working directory.
  if(*path == '/'){
    ip = inode_get(ROOTDEV, ROOTINO);
  }
  else {
    ip = inode_dup((struct inode*) tcb_get_cwd(get_curid()));
  }

  while((path = skipelem(path, name)) != 0){
	inode_lock(ip);
	if(ip -> type != T_DIR) {
		return 0;
	}
	if(nameiparent && *path == 0) {
		inode_unlock(ip);
		return ip;
	}
	next = dir_lookup(ip, name, 0);
	inode_unlockput(ip);
	ip = next;
  }
  if(nameiparent){
    inode_put(ip);
    return 0;
  }
  return ip;
}
Пример #6
0
int build_quota(struct gfs2_sbd *sdp)
{
	struct gfs2_inode *ip;
	struct gfs2_quota qu;
	char buf[sizeof(struct gfs2_quota)];
	int count;

	ip = createi(sdp->master_dir, "quota", S_IFREG | 0600,
		     GFS2_DIF_SYSTEM | GFS2_DIF_JDATA);
	if (ip == NULL) {
		return errno;
	}
	ip->i_di.di_payload_format = GFS2_FORMAT_QU;
	bmodified(ip->i_bh);

	memset(&qu, 0, sizeof(struct gfs2_quota));
	qu.qu_value = 1;
	gfs2_quota_out(&qu, buf);

	count = gfs2_writei(ip, buf, ip->i_di.di_size, sizeof(struct gfs2_quota));
	if (count != sizeof(struct gfs2_quota))
		return -1;
	count = gfs2_writei(ip, buf, ip->i_di.di_size, sizeof(struct gfs2_quota));
	if (count != sizeof(struct gfs2_quota))
		return -1;

	if (cfg_debug) {
		printf("\nRoot quota:\n");
		gfs2_quota_print(&qu);
	}

	inode_put(&ip);
	return 0;
}
Пример #7
0
int build_per_node(struct gfs2_sbd *sdp)
{
	struct gfs2_inode *per_node;
	unsigned int j;
	int err;

	per_node = createi(sdp->master_dir, "per_node", S_IFDIR | 0700,
			   GFS2_DIF_SYSTEM);
	if (per_node == NULL) {
		return errno;
	}

	for (j = 0; j < sdp->md.journals; j++) {
		err = build_inum_range(per_node, j);
		if (err) {
			return err;
		}
		err = build_statfs_change(per_node, j);
		if (err) {
			return err;
		}
		err = build_quota_change(per_node, j);
		if (err) {
			return err;
		}
	}

	if (cfg_debug) {
		printf("\nper_node:\n");
		gfs2_dinode_print(&per_node->i_di);
	}

	inode_put(&per_node);
	return 0;
}
Пример #8
0
/**
 * find_journal_block - figure out where a journal starts, given the name
 * Returns: journal block number, changes j_size to the journal size
 */
uint64_t find_journal_block(const char *journal, uint64_t *j_size)
{
	int journal_num;
	uint64_t jindex_block, jblock = 0;
	int amtread;
	struct gfs2_buffer_head *jindex_bh, *j_bh;
	char jbuf[sbd.bsize];

	journal_num = atoi(journal + 7);
	if (journal_num < 0)
		return 0;

	/* Figure out the block of the jindex file */
	if (sbd.gfs1)
		jindex_block = sbd1->sb_jindex_di.no_addr;
	else
		jindex_block = masterblock("jindex");
	/* read in the block */
	jindex_bh = bread(&sbd, jindex_block);
	/* get the dinode data from it. */
	gfs2_dinode_in(&di, jindex_bh); /* parse disk inode to struct*/

	if (!sbd.gfs1)
		do_dinode_extended(&di, jindex_bh); /* parse dir. */

	if (sbd.gfs1) {
		struct gfs2_inode *jiinode;
		struct gfs_jindex ji;

		jiinode = lgfs2_inode_get(&sbd, jindex_bh);
		if (jiinode == NULL)
			return 0;
		amtread = gfs2_readi(jiinode, (void *)&jbuf,
				   journal_num * sizeof(struct gfs_jindex),
				   sizeof(struct gfs_jindex));
		if (amtread) {
			gfs_jindex_in(&ji, jbuf);
			jblock = ji.ji_addr;
			*j_size = ji.ji_nsegment * 0x10;
		}
		inode_put(&jiinode);
	} else {
		struct gfs2_dinode jdi;

		if (journal_num > indirect->ii[0].dirents - 2)
			return 0;
		jblock = indirect->ii[0].dirent[journal_num + 2].block;
		j_bh = bread(&sbd, jblock);
		gfs2_dinode_in(&jdi, j_bh);/* parse dinode to struct */
		*j_size = jdi.di_size;
		brelse(j_bh);
	}
	brelse(jindex_bh);
	return jblock;
}
Пример #9
0
/*
 * Changes working directory.
 */
PUBLIC int sys_chroot(const char *path)
{
	struct inode *inode;
	
	inode = inode_name(path);
	
	/* Failed to get inode. */
	if (inode == NULL)
		return (curr_proc->errno);
	
	/* Not a directory. */
	if (!S_ISDIR(inode->mode))
	{
		inode_put(inode);
		return (-ENOTDIR);
	}
	
	inode_put(curr_proc->root);
	
	curr_proc->root = inode;
	inode_unlock(inode);
	
	return (0);
}
Пример #10
0
int build_statfs(struct gfs2_sbd *sdp)
{
	struct gfs2_inode *ip;

	ip = createi(sdp->master_dir, "statfs", S_IFREG | 0600,
		     GFS2_DIF_SYSTEM | GFS2_DIF_JDATA);
	if (ip == NULL) {
		return errno;
	}

	if (cfg_debug) {
		printf("\nStatFS Inode:\n");
		gfs2_dinode_print(&ip->i_di);
	}

	inode_put(&ip);
	return 0;
}
Пример #11
0
int build_quota_change(struct gfs2_inode *per_node, unsigned int j)
{
	struct gfs2_sbd *sdp = per_node->i_sbd;
	struct gfs2_meta_header mh;
	char name[256];
	struct gfs2_inode *ip;
	unsigned int blocks = sdp->qcsize << (20 - sdp->sd_sb.sb_bsize_shift);
	unsigned int x;
	unsigned int hgt;
	struct gfs2_buffer_head *bh;

	memset(&mh, 0, sizeof(struct gfs2_meta_header));
	mh.mh_magic = GFS2_MAGIC;
	mh.mh_type = GFS2_METATYPE_QC;
	mh.mh_format = GFS2_FORMAT_QC;

	sprintf(name, "quota_change%u", j);
	ip = createi(per_node, name, S_IFREG | 0600, GFS2_DIF_SYSTEM);
	if (ip == NULL) {
		return errno;
	}

	hgt = calc_tree_height(ip, (blocks + 1) * sdp->bsize);
	build_height(ip, hgt);

	for (x = 0; x < blocks; x++) {
		bh = get_file_buf(ip, x, FALSE);
		if (!bh)
			return -1;

		memset(bh->b_data, 0, sdp->bsize);
		gfs2_meta_header_out(&mh, bh->b_data);
		bmodified(bh);
		brelse(bh);
	}

	if (cfg_debug) {
		printf("\nQuota Change %u:\n", j);
		gfs2_dinode_print(&ip->i_di);
	}

	inode_put(&ip);
	return 0;
}
Пример #12
0
/*
 * Closes a file.
 */
PUBLIC void do_close(int fd)
{
	struct inode *i; /* Inode. */
	struct file *f;  /* File.  */
	
	f = curr_proc->ofiles[fd];
	
	/* Not opened. */
	if (f == NULL)
		return;
	
	curr_proc->close &= ~(1 << fd);
	curr_proc->ofiles[fd] = NULL;	
	
	if (--f->count)
		return;
	
	inode_lock(i = f->inode);
	inode_put(i);
}
Пример #13
0
int build_rindex(struct gfs2_sbd *sdp)
{
	struct gfs2_inode *ip;
	struct osi_node *n, *next = NULL;
	struct rgrp_tree *rl;
	char buf[sizeof(struct gfs2_rindex)];
	int count;

	ip = createi(sdp->master_dir, "rindex", S_IFREG | 0600,
		     GFS2_DIF_SYSTEM | GFS2_DIF_JDATA);
	if (ip == NULL) {
		return errno;
	}
	ip->i_di.di_payload_format = GFS2_FORMAT_RI;
	bmodified(ip->i_bh);

	for (n = osi_first(&sdp->rgtree); n; n = next) {
		next = osi_next(n);
		rl = (struct rgrp_tree *)n;

		gfs2_rindex_out(&rl->ri, buf);

		count = gfs2_writei(ip, buf, ip->i_di.di_size,
				    sizeof(struct gfs2_rindex));
		if (count != sizeof(struct gfs2_rindex))
			return -1;
	}
	memset(buf, 0, sizeof(struct gfs2_rindex));
	count = __gfs2_writei(ip, buf, ip->i_di.di_size,
			      sizeof(struct gfs2_rindex), 0);
	if (count != sizeof(struct gfs2_rindex))
		return -1;

	if (cfg_debug) {
		printf("\nResource Index:\n");
		gfs2_dinode_print(&ip->i_di);
	}

	inode_put(&ip);
	return 0;
}
Пример #14
0
int build_statfs_change(struct gfs2_inode *per_node, unsigned int j)
{
	char name[256];
	struct gfs2_inode *ip;

	sprintf(name, "statfs_change%u", j);
	ip = createi(per_node, name, S_IFREG | 0600,
		     GFS2_DIF_SYSTEM | GFS2_DIF_JDATA);
	if (ip == NULL) {
		return errno;
	}
	ip->i_di.di_size = sizeof(struct gfs2_statfs_change);
	gfs2_dinode_out(&ip->i_di, ip->i_bh->b_data);
	bmodified(ip->i_bh);
	if (cfg_debug) {
		printf("\nStatFS Change %u:\n", j);
		gfs2_dinode_print(&ip->i_di);
	}

	inode_put(&ip);
	return 0;
}
Пример #15
0
int op_getattr(const char *path, struct stat *stbuf)
{
    struct ext4_inode raw_inode;
    struct inode *inode;
    int ret = 0;
    uint64_t size;

    DEBUG("getattr(%s)", path);

    memset(stbuf, 0, sizeof(struct stat));
    ret = inode_get_by_path(path, &raw_inode);

    if (ret < 0) {
        return ret;
    }

    inode = inode_get(0, &raw_inode);
    if (!inode)
        return -ENOMEM;

    size = inode_get_size(inode);
    inode_put(inode);


    DEBUG("getattr done");

    stbuf->st_mode = raw_inode.i_mode;
    stbuf->st_nlink = raw_inode.i_links_count;
    stbuf->st_size = size;
    stbuf->st_blocks = raw_inode.i_blocks_lo;
    stbuf->st_uid = raw_inode.i_uid;
    stbuf->st_gid = raw_inode.i_gid;
    stbuf->st_atime = raw_inode.i_atime;
    stbuf->st_mtime = raw_inode.i_mtime;
    stbuf->st_ctime = raw_inode.i_ctime;

    return 0;
}
Пример #16
0
/**
 * Close file f.  (Decrement ref count, close when reaches 0.)
 */
void
file_close(struct file *f)
{
  struct file ff;

  spinlock_acquire(&ftable.lock);
  if(f->ref < 1)
    KERN_PANIC("file_close");
  if(--f->ref > 0){
    spinlock_release(&ftable.lock);
    return;
  }
  ff = *f;
  f->ref = 0;
  f->type = FD_NONE;
  spinlock_release(&ftable.lock);
  
  if(ff.type == FD_INODE){
    begin_trans();
    inode_put(ff.ip);
    commit_trans();
  }
}
Пример #17
0
/**
 * dump_journal - dump a journal file's contents.
 * @journal: name of the journal to dump
 * @tblk: block number to trace in the journals
 *
 * This function dumps the contents of a journal. If a trace block is specified
 * then only information printed is: (1) log descriptors that reference that
 * block, (2) metadata in the journal that references the block, or (3)
 * rgrp bitmaps that reference that block's allocation bit status.
 */
void dump_journal(const char *journal, int tblk)
{
	struct gfs2_buffer_head *j_bh = NULL, dummy_bh;
	uint64_t jblock, j_size, jb, abs_block, saveblk, wrappt = 0;
	int start_line, journal_num;
	struct gfs2_inode *j_inode = NULL;
	int ld_blocks = 0, offset_from_ld = 0;
	uint64_t tblk_off = 0, bblk_off = 0, bitblk = 0;
	uint64_t highest_seq = 0;
	char *jbuf = NULL;
	struct rgrp_tree *rgd = NULL;
	uint64_t abs_ld = 0;

	start_line = line;
	lines_per_row[dmode] = 1;
	journal_num = atoi(journal + 7);
	print_gfs2("Dumping journal #%d.", journal_num);
	if (tblk) {
		dmode = HEX_MODE;
		print_gfs2(" Tracing block 0x%llx", (unsigned long long)tblk);
	}
	eol(0);
	jblock = find_journal_block(journal, &j_size);
	if (!jblock)
		return;

	if (!sbd.gfs1) {
		j_bh = bread(&sbd, jblock);
		j_inode = lgfs2_inode_get(&sbd, j_bh);
		if (j_inode == NULL) {
			fprintf(stderr, "Out of memory\n");
			exit(-1);
		}
		jbuf = malloc(sbd.bsize);
		if (jbuf == NULL) {
			fprintf(stderr, "Out of memory\n");
			exit(-1);
		}
	}

	if (tblk) {
		uint64_t wp;

		rgd = gfs2_blk2rgrpd(&sbd, tblk);
		if (!rgd) {
			print_gfs2("Can't locate the rgrp for block 0x%x",
				   tblk);
			eol(0);
		} else {
			uint64_t o;
			int bmap = 0;

			print_gfs2("rgd: 0x%llx for 0x%x, ", rgd->ri.ri_addr,
				   rgd->ri.ri_length);
			o = tblk - rgd->ri.ri_data0;
			if (o >= (rgd->bits->bi_start +
				  rgd->bits->bi_len) * GFS2_NBBY)
				o += (sizeof(struct gfs2_rgrp) -
				      sizeof(struct gfs2_meta_header))
					* GFS2_NBBY;
			bmap = o / sbd.sd_blocks_per_bitmap;
			bitblk = rgd->ri.ri_addr + bmap;
			print_gfs2("bitmap: %d, bitblk: 0x%llx", bmap,
				   (unsigned long long)bitblk);
			eol(0);
		}

		wrappt = find_wrap_pt(j_inode, jbuf, jblock, j_size);
		wp = wrappt / (sbd.gfs1 ? 1 : sbd.bsize);
		print_gfs2("Starting at journal wrap block: 0x%llx "
			   "(j + 0x%llx)",
			   (unsigned long long)jblock + wp,
			   (unsigned long long)wp);
		eol(0);
	}

	for (jb = 0; jb < j_size; jb += (sbd.gfs1 ? 1 : sbd.bsize)) {
		int is_pertinent = 1;
		uint32_t block_type = 0;

		if (sbd.gfs1) {
			if (j_bh)
				brelse(j_bh);
			abs_block = jblock + ((jb + wrappt) % j_size);
			j_bh = bread(&sbd, abs_block);
			dummy_bh.b_data = j_bh->b_data;
		} else {
			int error = fsck_readi(j_inode, (void *)jbuf,
					   ((jb + wrappt) % j_size),
					   sbd.bsize, &abs_block);
			if (!error) /* end of file */
				break;
			dummy_bh.b_data = jbuf;
		}
		offset_from_ld++;
		block_type = get_block_type(&dummy_bh, NULL);
		if (block_type == GFS2_METATYPE_LD) {
			ld_blocks = process_ld(abs_block, wrappt, j_size, jb,
					       &dummy_bh, tblk, &tblk_off,
					       bitblk, rgd, &is_pertinent,
					       &bblk_off);
			offset_from_ld = 0;
			abs_ld = abs_block;
		} else if (!tblk && block_type == GFS2_METATYPE_LH) {
			struct gfs2_log_header lh;
			struct gfs_log_header lh1;

			if (sbd.gfs1) {
				gfs_log_header_in(&lh1, &dummy_bh);
				check_journal_wrap(lh1.lh_sequence,
						   &highest_seq);
				print_gfs2("0x%"PRIx64" (j+%4"PRIx64"): Log header: "
					   "Flags:%x, Seq: 0x%llx, 1st: 0x%llx, "
					   "tail: 0x%llx, last: 0x%llx",
					   abs_block, jb + wrappt,
					   lh1.lh_flags, lh1.lh_sequence,
					   lh1.lh_first, lh1.lh_tail,
					   lh1.lh_last_dump);
			} else {
				gfs2_log_header_in(&lh, &dummy_bh);
				check_journal_wrap(lh.lh_sequence,
						   &highest_seq);
				print_gfs2("0x%"PRIx64" (j+%4"PRIx64"): Log header: Seq"
					   ": 0x%llx, tail: 0x%x, blk: 0x%x%s",
					   abs_block, ((jb + wrappt) % j_size)
					   / sbd.bsize, lh.lh_sequence,
					   lh.lh_tail, lh.lh_blkno,
					   lh.lh_flags ==
					   GFS2_LOG_HEAD_UNMOUNT ?
					   " [UNMOUNTED]" : "");
			}
			eol(0);
		} else if ((ld_blocks > 0) &&
			   (sbd.gfs1 || block_type == GFS2_METATYPE_LB)) {
			print_gfs2("0x%"PRIx64" (j+%4"PRIx64"): Log descriptor"
				   " continuation block", abs_block,
				   ((jb + wrappt) % j_size) / sbd.bsize);
			eol(0);
			print_gfs2("                    ");
			ld_blocks -= print_ld_blks((uint64_t *)dummy_bh.b_data +
						   (sbd.gfs1 ? 0 :
						    sizeof(struct gfs2_meta_header)),
						   (dummy_bh.b_data +
						    sbd.bsize), start_line,
						   tblk, &tblk_off, 0, rgd,
						   0, 1, NULL, 0);
		} else if (block_type == 0) {
			continue;
		}
		/* Check if this metadata block references the block we're
		   trying to trace. */
		if (details || (tblk && ((is_pertinent &&
		     ((tblk_off && offset_from_ld == tblk_off) ||
		      (bblk_off && offset_from_ld == bblk_off))) ||
		     meta_has_ref(abs_block, tblk)))) {
			uint64_t ref_blk = 0;

			saveblk = block;
			block = abs_block;
			if (tblk && !details) {
				ref_blk = get_ldref(abs_ld, offset_from_ld);
				display(0, 1, tblk, ref_blk);
			} else {
				display(0, 0, 0, 0);
			}
			block = saveblk;
		}
	}
	inode_put(&j_inode);
	brelse(j_bh);
	blockhist = -1; /* So we don't print anything else */
	free(jbuf);
	if (!termlines)
		fflush(stdout);
}
Пример #18
0
/*
 * Executes a program.
 */
PUBLIC int sys_execve(const char *filename, const char **argv, const char **envp)
{
	int i;                /* Loop index.          */
	struct inode *inode;  /* File inode.          */
	struct region *reg;   /* Process region.      */
	addr_t entry;         /* Program entry point. */
	addr_t sp;            /* User stack pointer.  */
	char *name;           /* File name.           */
	char stack[ARG_MAX];  /* Stack size.          */

	/* Get file name. */
	if ((name = getname(filename)) == NULL)
		return (curr_proc->errno);

	/* Build arguments before freeing user memory. */
	kmemset(stack, 0, ARG_MAX);
	if (!(sp = buildargs(stack, ARG_MAX, argv, envp)))
	{
		putname(name);
		return (curr_proc->errno);
	}

	/* Get file's inode. */
	if ((inode = inode_name(name)) == NULL)
	{
		putname(name);
		return (curr_proc->errno);
	}

	/* Not a regular file. */
	if (!S_ISREG(inode->mode))
	{
		putname(name);
		inode_put(inode);
		return (-EACCES);
	}

	/* Not allowed. */
	if (!permission(inode->mode, inode->uid, inode->gid, curr_proc, MAY_EXEC, 0))
	{
		putname(name);
		inode_put(inode);
		return (-EACCES);
	}

	/* Close file descriptors. */
	for (i = 0; i < OPEN_MAX; i++)
	{
		if (curr_proc->close & (1 << i))
			do_close(i);
	}

	/* Detach process memory regions. */
	for (i = 0; i < NR_PREGIONS; i++)
		detachreg(curr_proc, &curr_proc->pregs[i]);
	
	/* Reset signal handlers. */
	curr_proc->restorer = NULL;
	for (i = 0; i < NR_SIGNALS; i++)
	{
		if (curr_proc->handlers[i] != SIG_DFL)
		{
			if (curr_proc->handlers[i] != SIG_IGN)
				curr_proc->handlers[i] = SIG_DFL;
		}
	}
	
	/* Load executable. */
	if (!(entry = load_elf32(inode)))
		goto die0;

	/* Attach stack region. */
	if ((reg = allocreg(S_IRUSR | S_IWUSR, PAGE_SIZE, REGION_DOWNWARDS)) == NULL)
		goto die0;
	if (attachreg(curr_proc, STACK(curr_proc), USTACK_ADDR - 1, reg))
		goto die1;
	unlockreg(reg);

	/* Attach heap region. */
	if ((reg = allocreg(S_IRUSR | S_IWUSR, PAGE_SIZE, REGION_UPWARDS)) == NULL)
		goto die0;
	if (attachreg(curr_proc, HEAP(curr_proc), UHEAP_ADDR, reg))
		goto die1;
	unlockreg(reg);
	
	inode_put(inode);
	putname(name);

	kmemcpy((void *)(USTACK_ADDR - ARG_MAX), stack, ARG_MAX);
	
	user_mode(entry, sp);
	
	/* Will not return. */
	return (0);

die1:
	unlockreg(reg);
	freereg(reg);
die0:
	inode_put(inode);
	putname(name);
	die(((SIGSEGV & 0xff) << 16) | (1 << 9));
	return (-1);
}