[BACK]Return to vfs_syscalls.c CVS log [TXT][DIR] Up to [local] / prex-old / usr / server / fs / vfs

File: [local] / prex-old / usr / server / fs / vfs / vfs_syscalls.c (download)

Revision 1.1, Tue Jun 3 09:38:51 2008 UTC (15 years, 11 months ago) by nbrk
Branch point for: MAIN

Initial revision

/*
 * Copyright (c) 2005-2007, Kohsuke Ohtani
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * syscalls.c - everything in this file is a routine implementing
 *		a VFS system call.
 */

#include <prex/prex.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/mount.h>
#include <sys/dirent.h>
#include <sys/list.h>
#include <sys/buf.h>

#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>

#include "vfs.h"

int
sys_open(char *path, int flags, mode_t mode, file_t *file)
{
	vnode_t vp, dvp;
	file_t fp;
	char *filename;
	int err;

	dprintf("sys_open: path=%s flags=%x mode=%x\n", path, flags, mode);

	flags = FFLAGS(flags);
	if (flags & O_CREAT) {
		err = namei(path, &vp);
		if (err == ENOENT) {
			/* Create new file. */
			if ((err = lookup(path, &dvp, &filename)) != 0)
				return err;
			if (dvp->v_mount->m_flags & MNT_RDONLY) {
				vput(dvp);
				return EROFS;
			}
			mode &= ~S_IFMT;
			mode |= S_IFREG;
			err = VOP_CREATE(dvp, filename, mode);
			vput(dvp);
			if (err)
				return err;
			if ((err = namei(path, &vp)) != 0)
				return err;
			flags &= ~O_TRUNC;
		} else if (err) {
			return err;
		} else {
			/* File already exits */
			if (flags & O_EXCL) {
				vput(vp);
				return EEXIST;
			}
			flags &= ~O_CREAT;
		}
	} else {
		/* Open */
		if ((err = namei(path, &vp)) != 0)
			return err;
	}
	if ((flags & O_CREAT) == 0) {
		if (flags & FWRITE || flags & O_TRUNC) {
			if (vp->v_mount->m_flags & MNT_RDONLY) {
				vput(vp);
				return EROFS;
			}
			if (vp->v_type == VDIR) {
				/* Openning directory with writable. */
				vput(vp);
				return EISDIR;
			}
		}
	}
	if (flags & O_TRUNC) {
		if (!(flags & FWRITE) || (vp->v_type != VREG)) {
			vput(vp);
			return EINVAL;
		}
	}
	/* Process truncate request */
	if (flags & O_TRUNC) {
		if ((err = VOP_TRUNCATE(vp)) != 0) {
			vput(vp);
			return err;
		}
	}
	/* Setup file structure */
	if (!(fp = malloc(sizeof(struct file)))) {
		vput(vp);
		return ENOMEM;
	}
	/* Request to file system */
	if ((err = VOP_OPEN(vp, mode)) != 0) {
		free(fp);
		vput(vp);
		return err;
	}
	memset(fp, 0, sizeof(struct file));
	fp->f_vnode = vp;
	fp->f_flags = flags;
	fp->f_offset = 0;
	fp->f_count = 1;
	*file = fp;
	vn_unlock(vp);
	return 0;
}

int
sys_close(file_t fp)
{
	vnode_t vp;
	int err;

	dprintf("sys_close: fp=%x\n", (u_int)fp);

	vp = fp->f_vnode;
	if (--fp->f_count > 0) {
		vrele(vp);
		return 0;
	}
	vn_lock(vp);
	if ((err = VOP_CLOSE(vp, fp)) != 0) {
		vn_unlock(vp);
		return err;
	}
	vput(vp);
	free(fp);
	return 0;
}

int
sys_read(file_t fp, void *buf, size_t size, size_t *count)
{
	vnode_t vp;
	int err;

	dprintf("sys_read: fp=%x buf=%x size=%d\n",
		(u_int)fp, (u_int)buf, size);

	if ((fp->f_flags & FREAD) == 0)
		return EPERM;
	if (size == 0) {
		*count = 0;
		return 0;
	}
	vp = fp->f_vnode;
	vn_lock(vp);
	err = VOP_READ(vp, fp, buf, size, count);
	vn_unlock(vp);
	return err;
}

int
sys_write(file_t fp, void *buf, size_t size, size_t *count)
{
	vnode_t vp;
	int err;

	dprintf("sys_write: fp=%x buf=%x size=%d\n",
		(u_int)fp, (u_int)buf, size);

	if (size == 0) {
		*count = 0;
		return 0;
	}
	vp = fp->f_vnode;
	vn_lock(vp);
	err = VOP_WRITE(vp, fp, buf, size, count);
	vn_unlock(vp);
	return err;
}

int
sys_lseek(file_t fp, off_t off, int type, off_t *origin)
{
	vnode_t vp;

	dprintf("sys_seek: fp=%x off=%d type=%d\n", (u_int)fp,
		(u_int)off, type);

	vp = fp->f_vnode;
	vn_lock(vp);
	switch (type) {
	case SEEK_SET:
		if (off < 0)
			off = 0;
		if (off > (off_t)vp->v_size)
			off = vp->v_size;
		break;
	case SEEK_CUR:
		if (fp->f_offset + off > (off_t)vp->v_size)
			off = vp->v_size;
		else if (fp->f_offset + off < 0)
			off = 0;
		else
			off = fp->f_offset + off;
		break;
	case SEEK_END:
		if (off > 0)
			off = vp->v_size;
		else if ((int)vp->v_size + off < 0)
			off = 0;
		else
			off = vp->v_size + off;
		break;
	default:
		vn_unlock(vp);
		return EINVAL;
	}
	/* Request to check the file offset */
	if (VOP_SEEK(vp, fp, fp->f_offset, off) != 0) {
		vn_unlock(vp);
		return EINVAL;
	}
	*origin = off;
	fp->f_offset = off;
	vn_unlock(vp);
	return 0;
}

int
sys_ioctl(file_t fp, int request, char *buf)
{
	vnode_t vp;
	int err;

	dprintf("sys_ioctl: fp=%x request=%x\n", fp, request);

	if ((fp->f_flags & (FREAD | FWRITE)) == 0)
		return EBADF;

	vp = fp->f_vnode;
	vn_lock(vp);
	err = VOP_IOCTL(vp, fp, request, (u_long)buf);
	vn_unlock(vp);
	return err;
}

int
sys_fsync(file_t fp)
{
	vnode_t vp;
	int err;

	dprintf("sys_fsync: fp=%x\n", fp);

	if ((fp->f_flags & FREAD) == 0)
		return EBADF;

	vp = fp->f_vnode;
	vn_lock(vp);
	err = VOP_FSYNC(vp, fp);
	vn_unlock(vp);
	return err;
}

int
sys_fstat(file_t fp, struct stat *st)
{
	vnode_t vp;
	mode_t mode;

	dprintf("sys_fstat: fp=%x\n", fp);

	memset(st, 0, sizeof(struct stat));

	vp = fp->f_vnode;
	vn_lock(vp);
	st->st_ino = (ino_t)&vp;
	st->st_size = vp->v_size;
	mode = vp->v_mode;
	switch (vp->v_type) {
	case VREG:
		mode |= S_IFREG;
		break;
	case VDIR:
		mode |= S_IFDIR;
		break;
	case VBLK:
		mode |= S_IFBLK;
		break;
	case VCHR:
		mode |= S_IFCHR;
		break;
	case VLNK:
		mode |= S_IFLNK;
		break;
	case VSOCK:
		mode |= S_IFSOCK;
		break;
	case VFIFO:
		mode |= S_IFIFO;
		break;
	default:
		return EBADF;
	};
	st->st_mode = mode;
	st->st_blksize = BSIZE;
	st->st_blocks = vp->v_size / S_BLKSIZE;
	st->st_uid = 0;
	st->st_gid = 0;
	if (vp->v_type == VCHR || vp->v_type == VBLK)
		st->st_rdev = (dev_t)vp->v_data;
	vn_unlock(vp);
	return 0;
}

/*
 * Return 0 if directory is empty
 */
static int
check_dir_empty(char *path)
{
	int err;
	file_t fp;
	struct dirent dir;

	if ((err = sys_opendir(path, &fp)) != 0)
		return err;
	do {
		err = sys_readdir(fp, &dir);
		if (err)
			break;
	} while (!strcmp(dir.d_name, ".") || !strcmp(dir.d_name, ".."));

	sys_closedir(fp);

	if (err == ENOENT)
		return 0;
	else if (err == 0)
		return EEXIST;
	return err;
}

int
sys_opendir(char *path, file_t *file)
{
	vnode_t dvp;
	file_t fp;
	int err;

	dprintf("sys_opendir: path=%s\n", path);

	if ((err = sys_open(path, O_RDONLY, 0, &fp)) != 0)
		return err;

	dvp = fp->f_vnode;
	vn_lock(dvp);
	if (dvp->v_type != VDIR) {
		vn_unlock(dvp);
		sys_close(fp);
		return ENOTDIR;
	}
	vn_unlock(dvp);

	*file = fp;
	return 0;
}

int
sys_closedir(file_t fp)
{
	vnode_t dvp;
	int err;

	dprintf("sys_closedir: fp=%x\n", fp);

	dvp = fp->f_vnode;
	vn_lock(dvp);
	if (dvp->v_type != VDIR) {
		vn_unlock(dvp);
		return EBADF;
	}
	vn_unlock(dvp);
	err = sys_close(fp);
	return err;
}

int
sys_readdir(file_t fp, struct dirent *dir)
{
	vnode_t dvp;
	int err;

	dprintf("sys_readdir: fp=%x\n", fp);

	dvp = fp->f_vnode;
	vn_lock(dvp);
	if (dvp->v_type != VDIR) {
		vn_unlock(dvp);
		return ENOTDIR;
	}
	err = VOP_READDIR(dvp, fp, dir);
	vn_unlock(dvp);
	return err;
}

int
sys_rewinddir(file_t fp)
{
	vnode_t dvp;

	dvp = fp->f_vnode;
	vn_lock(dvp);
	if (dvp->v_type != VDIR) {
		vn_unlock(dvp);
		return EINVAL;
	}
	fp->f_offset = 0;
	vn_unlock(dvp);
	return 0;
}

int
sys_seekdir(file_t fp, long loc)
{
	vnode_t dvp;

	dvp = fp->f_vnode;
	vn_lock(dvp);
	if (dvp->v_type != VDIR) {
		vn_unlock(dvp);
		return EINVAL;
	}
	fp->f_offset = (off_t)loc;
	vn_unlock(dvp);
	return 0;
}

int
sys_telldir(file_t fp, long *loc)
{
	vnode_t dvp;

	dvp = fp->f_vnode;
	vn_lock(dvp);
	if (dvp->v_type != VDIR) {
		vn_unlock(dvp);
		return EINVAL;
	}
	*loc = (long)fp->f_offset;
	vn_unlock(dvp);
	return 0;
}

int
sys_mkdir(char *path, mode_t mode)
{
	char *name;
	vnode_t vp, dvp;
	int err;

	dprintf("sys_mkdir: path=%s mode=%d\n", path, mode);

	if ((err = namei(path, &vp)) == 0) {
		/* File already exists */
		vput(vp);
		return EEXIST;
	}
	/* Notice: vp is invalid here! */

	if ((err = lookup(path, &dvp, &name)) != 0) {
		/* Directory already exists */
		return err;
	}
	if (dvp->v_mount->m_flags & MNT_RDONLY) {
		err = EROFS;
		goto out;
	}
	mode &= ~S_IFMT;
	mode |= S_IFDIR;

	err = VOP_MKDIR(dvp, name, mode);
 out:
	vput(dvp);
	return err;
}

int
sys_rmdir(char *path)
{
	vnode_t vp, dvp;
	int err;
	char *name;

	dprintf("sys_rmdir: path=%s\n", path);

	if ((err = check_dir_empty(path)) != 0)
		return err;
	if ((err = namei(path, &vp)) != 0)
		return err;

	if (vp->v_mount->m_flags & MNT_RDONLY) {
		err = EROFS;
		goto out;
	}
	if (vp->v_type != VDIR) {
		err = ENOTDIR;
		goto out;
	}
	if (vp->v_flags & VROOT || vcount(vp) >= 2) {
		err = EBUSY;
		goto out;
	}
	if ((err = lookup(path, &dvp, &name)) != 0)
		goto out;

	err = VOP_RMDIR(dvp, vp, name);
	vn_unlock(vp);
	vgone(vp);
	vput(dvp);
	return err;

 out:
	vput(vp);
	return err;
}

int
sys_mknod(char *path, mode_t mode)
{
	char *name;
	vnode_t vp, dvp;
	int err;

	dprintf("sys_mknod: path=%s mode=%d\n", path, mode);

	if ((err = namei(path, &vp)) == 0) {
		vput(vp);
		return EEXIST;
	}

	if ((err = lookup(path, &dvp, &name)) != 0)
		return err;

	if (S_ISDIR(mode))
		err = VOP_MKDIR(dvp, name, mode);
	else
		err = VOP_CREATE(dvp, name, mode);

	vput(dvp);
	return err;
}

int
sys_rename(char *src, char *dest)
{
	vnode_t vp1, vp2 = 0, dvp1, dvp2;
	char *sname, *dname;
	int err;
	size_t len;
	char root[] = "/";

	dprintf("sys_rename: src=%s dest=%s\n", src, dest);

	if ((err = namei(src, &vp1)) != 0)
		return err;
	if (vp1->v_mount->m_flags & MNT_RDONLY) {
		err = EROFS;
		goto err1;
	}
	/* If source and dest are the same, do nothing */
	if (!strncmp(src, dest, PATH_MAX))
		goto err1;

	/* Check if target is directory of source */
	len = strlen(dest);
	if (!strncmp(src, dest, len)) {
		err = EINVAL;
		goto err1;
	}
	/* Is the source busy ? */
	if (vcount(vp1) >= 2) {
		err = EBUSY;
		goto err1;
	}
	/* Check type of source & target */
	err = namei(dest, &vp2);
	if (err == 0) {
		/* target exists */
		if (vp1->v_type == VDIR && vp2->v_type != VDIR) {
			err = ENOTDIR;
			goto err2;
		} else if (vp1->v_type != VDIR && vp2->v_type == VDIR) {
			err = EISDIR;
			goto err2;
		}
		if (vp2->v_type == VDIR && check_dir_empty(dest)) {
			err = EEXIST;
			goto err2;
		}

		if (vcount(vp2) >= 2) {
			err = EBUSY;
			goto err2;
		}
	}

	dname = strrchr(dest, '/');
	if (dname == NULL) {
		err = ENOTDIR;
		goto err2;
	}
	if (dname == dest)
		dest = root;

	*dname = 0;
	dname++;

	if ((err = lookup(src, &dvp1, &sname)) != 0)
		goto err2;

	if ((err = namei(dest, &dvp2)) != 0)
		goto err3;

	/* The source and dest must be same file system */
	if (dvp1->v_mount != dvp2->v_mount) {
		err = EXDEV;
		goto err4;
	}
	err = VOP_RENAME(dvp1, vp1, sname, dvp2, vp2, dname);
 err4:
	vput(dvp2);
 err3:
	vput(dvp1);
 err2:
	if (vp2)
		vput(vp2);
 err1:
	vput(vp1);
	return err;
}

int
sys_unlink(char *path)
{
	char *name;
	vnode_t vp, dvp;
	int err;

	dprintf("sys_unlink: path=%s\n", path);

	if ((err = namei(path, &vp)) != 0)
		return err;

	if (vp->v_mount->m_flags & MNT_RDONLY) {
		err = EROFS;
		goto out;
	}
	if (vp->v_type == VDIR) {
		err = EPERM;
		goto out;
	}
	if (vp->v_flags & VROOT || vcount(vp) >= 2) {
		err = EBUSY;
		goto out;
	}
	if ((err = lookup(path, &dvp, &name)) != 0)
		goto out;

	err = VOP_REMOVE(dvp, vp, name);

	vn_unlock(vp);
	vgone(vp);
	vput(dvp);
	return 0;
 out:
	vput(vp);
	return err;
}

int
sys_access(char *path, int mode)
{
	vnode_t vp;
	int err;

	dprintf("sys_access: path=%s\n", path);

	if ((err = namei(path, &vp)) != 0)
		return err;

	err = EACCES;
	if ((mode & X_OK) && (vp->v_mode & 0111) == 0)
		goto out;
	if ((mode & W_OK) && (vp->v_mode & 0222) == 0)
		goto out;
	if ((mode & R_OK) && (vp->v_mode & 0444) == 0)
		goto out;
	err = 0;
 out:
	vput(vp);
	return err;
}