Plan 9 from Bell Labs’s /usr/web/sources/contrib/cinap_lenrek/old/linuxemu.old/sysfile.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <ureg.h>
#include "linuxsys.h"
#include "linux.h"

/* open/fcntl - O_SYNC is only implemented on blocks devices and on files
   located on an ext2 file system */

enum {
	O_ACCMODE	=0003,
	O_RDONLY	=00,
	O_WRONLY	=01,
	O_RDWR		=02,
	O_CREAT		=0100,
	O_EXCL		=0200,
	O_NOCTTY	=0400,
	O_TRUNC		=01000,
	O_APPEND	=02000,
	O_NONBLOCK	=04000,
	O_NDELAY	=04000,
	O_SYNC		=010000,
	FASYNC		=020000,
};

typedef struct FDFlags FDFlags;
struct FDFlags
{
	ulong	flags;
};

static void
destroyfdflagstag(void *tag)
{
	FDFlags *fdf;

	DPRINT("destroyfdflagstag()...");

	fdf = *fdtagp(tag);
	*fdtagp(tag) = nil;
	free(fdf);
}

SYSCALL(sys_access)
{
	char *name;
	int mode;
	int r;
	Dir *d;

	name = (char*)ARG1;
	mode = (int)ARG2;

	DPRINT("access(%s,0%o)...", name, mode);

	if((d = dirstat(name)) == nil){
		RETURN(mkerror());
	}
	if(d->mode & DMDIR){
		free(d);
		/* access() doesnt work with directories */
		RETURN(0);
	}
	free(d);
	r = access(name, mode & 7);
	if(r)
		r = mkerror();
	RETURN(r);
}

/* 
 * BUG: can't support O_EXCL (not in plan9 semantics)
 * O_NOTTY doesn't make sense for plan9.
 * BUG: supporting O_APPEND will be hard.
 * BUG: O_NONBLOCK will be hard.
 * O_SYNC doesn't make sense for plan9.
 *
 * To support O_APPEND and O_NONBLOCK,
 * we really need to keep our own fd table.
 * I'm not willing to do that quite yet.  --rsc
 */
SYSCALL(sys_open)
{
	char *path = (char*)ARG1;
	int mode = ARG2;
	int perm = ARG3;
	int mode9, perm9, fd;

	DPRINT("open(%s, %#o, %#o)...", path, mode, (mode&O_CREAT)?perm:0);
		mode9 = mode & 3;
	if(mode & O_TRUNC)
		mode9 |= OTRUNC;
	if(mode & O_CREAT) {
		perm9 = (perm & 0777) & ~threadp->umask;
		if(mode9 & OTRUNC){
			fd = create(path, mode9, perm9);
		} else {
			/*
			 * we try open first because in the case we want to reopen the file,
			 * create would truncates the file to 0 bytes and that is what we want
			 * to avoid. (opera bug)
			 */
			fd = open(path, mode9);
	
			if(fd < 0) {
				/* seems the file doesnt exist, so just create it */
				fd = create(path, mode9, perm9);
			}
		}
	} else
		fd = open(path, mode9);
	
	if(fd >= 0){
		void *tag;
		FDFlags *fdf;

		DPRINT("-> %d ...", fd);

		tag = openfdtag(fd, TAG_FDFLAGS, 1);
		if(*fdtagp(tag) == nil){
			fdf = malloc(sizeof(FDFlags));
			fdf->flags = mode;
			*fdtagp(tag) = fdf;
			atdestroyfdtag(tag, destroyfdflagstag);
		}
		closefdtag(tag);
		if(mode & O_NONBLOCK){
			Dir *d;

			/*
			 * O_NONBLOCK doent work with directories because
			 * we read it with dirreadall which uses the plan9-read.
			 */
			if(d = dirfstat(fd)){
				if(d->mode&DMDIR == 0)
					buffd(fd);
				free(d);
			}
		}

		RETURN(fd);
	}
	DPRINT("-> error...");
	RETURN(mkerror());
}

SYSCALL(sys_creat)
{
	char *path = (char*)ARG1;
	int perm = ARG2;

	DPRINT("creat(%s, 0%o)...", path, perm);

	/*
	 * open(2) says ``creat is equivalent to open with flags equal to
	 * O_CREAT|O_WRONLY|O_TRUNC''.
	 */

	ARG3 = ARG2;
	ARG2 = O_CREAT|O_WRONLY|O_TRUNC;
	sys_open(ureg);
}

SYSCALL(sys_close)
{
	int fd = ARG1;
	DPRINT("close(%d)...", fd);
	RETURN(_close(fd));
}

int
_close(int fd)
{
	void *tag;

	if(fd <= 2)
		return 0;
	while(tag = openfdtag(fd, TAG_ALL, 0)){
		destroyfdtag(tag);
		closefdtag(tag);
	}

	if(close(fd) < 0)
		return -EBADFD;
	return 0;
}

int
_read(int fd, void *data, int len)
{
	void *tag;
	FDFlags *fdf;
	int noblock;
	int r;

	noblock = 0;
	if(tag = openfdtag(fd, TAG_FDFLAGS, 0)){
		if((fdf = (FDFlags*)*fdtagp(tag)) != nil)
			if(fdf->flags & O_NONBLOCK)
				noblock = 1;
		closefdtag(tag);
	}
	r = readbuffd(fd, data, len, noblock);
	if(r == -EBADF){
		r = read(fd, data, len);
		if(r >= 0)
			return r;
	} else {
		return r;
	}
	return mkerror();
}

int
_write(int fd, void *data, int len)
{
	void *tag;
	FDFlags *fdf;
	int noblock;
	int append;
	int r;

	append = 0;
	noblock = 0;
	if(tag = openfdtag(fd, TAG_FDFLAGS, 0)){
		if((fdf = (FDFlags*)*fdtagp(tag)) != nil){
			if(fdf->flags & O_NONBLOCK)
				noblock = 1;
			if(fdf->flags & O_APPEND)
				append = 1;
		}
		closefdtag(tag);
	}
	r = writebuffd(fd, data, len, noblock);
	if(r == -EBADF){
		vlong x;

		if(append){
			vlong y;
			x = seek(fd, 0, 1);
			y = seek(fd, 0, 2);
			//fprint(2, "_write: append: fd %d, seek from %llux to %llux\n", fd, x, y);
		}
		r = write(fd, data, len);
		if(r >= 0 && append){
			//fprint(2, "_write: append: fd %d, wrote %d bytes, restore offset to %llux\n", fd, r, x);
			seek(fd, x, 0);
		}
		if(r >= 0)
			return r;
	} else {
		return r;
	}
	return mkerror();
}

SYSCALL(sys_read)
{
	int fd = ARG1;
	void *v = (void*) ARG2;
	ulong n = ARG3;

	DPRINT("read(%d, %p, %lud)...", fd, v, n);
	RETURN(_read(fd, v, n));
}

SYSCALL(sys_write)
{
	int fd = ARG1;
	void *v = (void*) ARG2;
	ulong n = ARG3;

	DPRINT("write(%d, %p, %lud)...", fd, v, n);
	RETURN(_write(fd, v, n));
}

SYSCALL(sys_pipe)
{
	int *fd = (int*)ARG1;
	int r;

	DPRINT("pipe(0x%p)...", fd);
	r = pipe(fd);
	RETURN(r);
}

SYSCALL(sys_dup)
{
	int oldfd;
	int r;

	oldfd = ARG1;
	DPRINT("dup(%d)...", oldfd);
	r = dup(oldfd, -1);
	RETURN(r);
}

SYSCALL(sys_dup2)
{
	int oldfd, newfd;
	int r;

	oldfd = ARG1;
	newfd = ARG2;
	DPRINT("dup2(%d, %d)...", oldfd, newfd);
	r = dup(oldfd, newfd);
	RETURN(r);
}

SYSCALL(sys_link)
{
	/*
	 * link(2) says EPERM means the file system
	 * doesn't support links.
	 */
	DPRINT("link(%s, %s)...", (char*)ARG1, (char*)ARG2);
	if(strstr((char*)ARG2, ".LCK")){
		DPRINT("fontcache LOCKFILE hack!\n");
		RETURN(0);
	}
	RETURN(-EPERM);
}

SYSCALL(sys_unlink)
{
	char *file = (char*) ARG1;

	DPRINT("unlink(%s)...", file);

	if(remove(file) == 0)
		RETURN(0);
	if(strstr(file, ".LCK")){
		RETURN(0);
	}
	RETURN(mkerror());
}

SYSCALL(sys_chdir)
{
	char *file = (char*) ARG1;

	DPRINT("chdir(%s)...", file);

	if(chdir(file) == 0)
		RETURN(0);
	RETURN(mkerror());
}

SYSCALL(sys_fchdir)
{
	int fd = ARG1;
	char path[1024];

	DPRINT("fchdir(%d)...", fd);

	if(fd2path(fd, path, sizeof(path))!=0)
		RETURN(mkerror());
	if(chdir(path) == 0)
		RETURN(0);
	RETURN(mkerror());
}

SYSCALL(sys_getcwd)
{
	char *buf = (char*)ARG1;
	int n = ARG2;

	DPRINT("getcwd(%p, %d)...", buf, n);

	buf = getwd(buf, n);
	RETURN(strlen(buf));
}

SYSCALL(sys_mknod)
{
	/*
	 * mknod(2) says EPERM means the file system
	 * doesn't support the requested node type,
	 * which is pretty true.
	 */
	DPRINT("mknod(%s, %#luo, %lud)...", (char*)ARG1, ARG2, ARG3);
	RETURN(-EPERM);
}

SYSCALL(sys_chmod)
{
	char *file = (char*) ARG1;
	int mode = ARG2;
	Dir *d, nd;

	DPRINT("chmod(%s, %#o)...", file, mode);

	mode &= 0777;
	if((d = dirstat(file)) == nil)
		RETURN(mkerror());
	nulldir(&nd);
	nd.mode = d->mode & ~0777;
	free(d);
	nd.mode |= mode;
	if(dirwstat(file, &nd) < 0)
		RETURN(mkerror());
	RETURN(0);
}

SYSCALL(sys_umask)
{
	int mask;

	mask = (int)ARG1;
	DPRINT("umask(%o)...", mask);
	mask = (threadp->umask = (mask & 0777));
	RETURN(mask);
}

SYSCALL(sys_lseek)
{
	int fd = ARG1;
	int offset = ARG2;	
	int whence = ARG3;
	int ret;

	DPRINT("lseek(%d, %d, %d)...", fd, offset, whence);
	ret = seek(fd, offset, whence);
	if(ret < 0)
		RETURN(mkerror());
	RETURN(ret);
}

SYSCALL(sys_llseek)
{
	int fd = ARG1;
	int hoffset = ARG2;	
	int loffset = ARG3;
	vlong *result = (vlong*)ARG4;
	int whence = ARG5;
	vlong r;

	DPRINT("lseek(%d, %d:%d, 0x%p, %d)...", fd, hoffset, loffset, result, whence);

	r = seek(fd, (((vlong)hoffset)<<32)|((vlong)loffset), whence);
	if(r < 0)
		RETURN(mkerror());
	if(result)
		*result = r;
	RETURN(0);
}

/*
 * sync() always returns zero, even
 * though we don't support it.
 */
SYSCALL(sys_sync)
{
	DPRINT("sync()...");
	RETURN(0);
}

SYSCALL(sys_rmdir)
{
	char *file = (char*) ARG1;

	DPRINT("rmdir(%s)...", file);

	if(remove(file) < 0)
		RETURN(mkerror());
	RETURN(0);
}

struct iovec
{
	void		*base;
	ulong	len;
};

SYSCALL(sys_readv)
{
	int t;
	int r;
	int i;
	int fd;
	struct iovec *vec;
	int n;
	uchar *buf;

	fd = ARG1;
	vec = (struct iovec*)ARG2;
	n = ARG3;

	DPRINT("readv(%d, ..., %d)...", fd, n);
	t = 0;
	for(i=0; i<n; i++)
		t += vec[i].len;

	if((buf = malloc(t)) == nil)
		RETURN(-ENOMEM);
	r = _read(fd, buf, t);
	if(r < 0){
		free(buf);
		RETURN(r);
	}
	t = 0;
	for(i=0; i<n && t<r; i++){
		int l;
		l = vec[i].len;
		if(l > r-t)
			l = r-t;
		memcpy(vec[i].base, buf+t, l);
		t += l;
	}
	free(buf);
	RETURN(t);
}

SYSCALL(sys_writev)
{
	int i;
	int fd;
	struct iovec *vec;
	int n, r, t;

	fd = ARG1;
	vec = (struct iovec*)ARG2;
	n = ARG3;

	DPRINT("writev(%d, ..., %d)...", fd, n);
	t = 0;
	for(i=0; i<n; i++){
		if((r = _write(fd, vec[i].base, vec[i].len)) < 0)
			RETURN(r);
		t += r;
	}
	RETURN(t);
}

SYSCALL(sys_mkdir)
{
	char *name;
	int mode;
	int fd;

	name = (char*)ARG1;
	mode = (int)ARG2;

	DPRINT("mkdir(%s,%#o)...", name, mode);
	/* FIXME: createmask */
	if((fd = create(name, OREAD, DMDIR|(0777 & ~threadp->umask))) < 0){
		RETURN(mkerror());
	}
	close(fd);
	RETURN(0);
}

static int
dorename(char *from, char *to)
{
	int ret;

	int fromfd;
	int tofd;
	Dir *fromdir;
	Dir *todir;

	ret = 0;
	fromfd = tofd = -1;
	fromdir = todir = nil;

	from = cleanname(strdup(from));
	to = cleanname(strdup(to));

	if(strcmp(from, to) == 0){
		ret = 0; goto out;
	}
	if((fromfd = open(from, OREAD)) < 0){
		ret = mkerror(); goto out;
	}
	if((fromdir = dirfstat(fromfd)) == nil){
		ret = mkerror(); goto out;
	}
	if(fromdir->mode & DMDIR){
		int n;
		Dir *dir;

		if(todir = dirstat(to)) {
			if((todir->mode & DMDIR)==0){
				ret = -ENOTDIR; goto out;
			}
			free(todir); todir = nil;
			if(remove(to) < 0){
				ret = -ENOTEMPTY; goto out;
			}
		}
		if((tofd = create(to, OREAD, fromdir->mode)) < 0){
			ret = mkerror(); goto out;
		}
		close(tofd); tofd = -1;

		dir = nil;
		while((n = dirread(fromfd, &dir)) > 0){
			int i;
			for(i=0; i<n; i++){
				char *nfrom;
				char *nto;
				nfrom = malloc(strlen(from) + 1 + strlen(dir[i].name) + 1);
				nto = malloc(strlen(to) + 1 + strlen(dir[i].name) + 1);
				sprint(nfrom, "%s/%s", from, dir[i].name);
				sprint(nto, "%s/%s", to, dir[i].name);
				if((ret = dorename(nfrom, nto)) < 0){
					free(nfrom);
					free(nto);
					free(dir);
					goto out;
				}
				free(nfrom);
				free(nto);
			}
			free(dir); dir = nil;
		}
	} else {
		uchar *buf;
		int n, n1, nbuf;

		if(todir = dirstat(to)){
			if(todir->mode & DMDIR){
				ret = -EISDIR; goto out;
			}
			free(todir); todir = nil;
			if(remove(to) < 0){
				ret = mkerror(); goto out;
			}
		}
		if((tofd = create(to, OWRITE, fromdir->mode)) < 0){
			ret = mkerror(); goto out;
		}
		nbuf = 1024 * 8;
		if((buf = malloc(nbuf)) == nil){
			ret = -ENOMEM; goto out;
		}
		while ((n = read(fromfd, buf, nbuf)) > 0) {
			n1 = write(tofd, buf, n);
			if(n1 != n){
				free(buf);
				ret = mkerror(); goto out;
			}
		}
		free(buf);
		close(tofd); tofd = -1;
	}
	if(fromfd >= 0){
		close(fromfd); fromfd = -1;
	}
	if(remove(from) < 0){
		ret = mkerror(); goto out;
	}
out:
	if(fromdir)
		free(fromdir);
	if(todir)
		free(todir);
	if(fromfd >= 0)
		close(fromfd);
	if(tofd >= 0)
		close(tofd);
	free(from);
	free(to);
	return ret;
}

SYSCALL(sys_rename)
{
	char *from;
	char *to;

	from = (char*)ARG1;
	to = (char*)ARG2;
	DPRINT("rename(%s, %s)...\n", from, to);
	RETURN(dorename(from, to));
}


enum{
	F_DUPFD	= 0,
	F_GETFD,
	F_SETFD,
	F_GETFL,
	F_SETFL,
	F_GETLK,
	F_SETLK,
	F_SETLKW,
	F_SETOWN,
	F_GETOWN,
	F_GETSIG,
};

enum {
	FD_CLOEXEC = 1,
};

SYSCALL(sys_fcntl)
{
	int fd = ARG1;
	int cmd = ARG2;
	int arg = ARG3;
	int ret;
	void *tag;
	FDFlags *fdf;

	DPRINT("sys_fcntl(%d, %d, %d)...", fd, cmd, arg);

	switch(cmd){
	case F_DUPFD:
		if((ret = dup(fd, arg)) < 0)
			RETURN(mkerror());
		RETURN(ret);

	case F_GETFD:
		RETURN(FD_CLOEXEC);

	case F_SETFD:
		RETURN(0);

	case F_GETFL:
	case F_SETFL:
		ret = -EINVAL;
		if(tag = openfdtag(fd, TAG_FDFLAGS, 1)){
			if((fdf = (FDFlags*)*fdtagp(tag)) == nil){
				fdf = malloc(sizeof(FDFlags));
				fdf->flags = 0;
				*fdtagp(tag) = fdf;
			}

			if(cmd == F_SETFL){
				ulong oldflags;
				ulong newflags;
				ulong setmask;
				ulong x;

				setmask = 0;
				newflags = ARG3;
				oldflags = fdf->flags;
				x = oldflags^newflags;

				if(x&O_NONBLOCK){
					setmask |= O_NONBLOCK;
					if(newflags&O_NONBLOCK){
						Dir *d;

						/*
						 * O_NONBLOCK doent work with directories because
						 * we read it with dirreadall which uses the plan9-read.
						 */
						if(d = dirfstat(fd)){
							if(d->mode&DMDIR == 0)
								buffd(fd);
							free(d);
						}
					}
				}

				fdf->flags = (fdf->flags & ~setmask) | (newflags & setmask);
				ret = 0;
			} else {
				ret = fdf->flags;
			}
			closefdtag(tag);
		}
		RETURN(ret);

	case F_GETLK:
	case F_SETLK:
		RETURN(0);

	case F_SETLKW:
	case F_SETOWN:
	case F_GETOWN:
	case F_GETSIG:
		RETURN(-EINVAL);
	}
}

SYSCALL(sys_fcntl64)
{
	sys_fcntl(ureg);
}

SYSCALL(sys_ioctl)
{
	int fd;
	uint cmd;
	ulong arg;
	Ioctlname *in;
	Ioctl *i;

	char *name;

	fd = (int)ARG1;
	cmd = (uint)ARG2;
	arg = (ulong)ARG3;

	DPRINT("sys_ioctl(%d, 0x%ux, 0x%lux)...", fd, cmd, arg);

	name =  "???";
	for(in=ioctlname; in->num; in++){
		if(in->num == cmd){
			name = in->name;
			break;
		}
	}
	DPRINT("ioctl 0x%ux/%s...", cmd, name);

	for(i=ioctltab; i->num; i++){
		if(i->num == cmd && i->f){
			RETURN((i->f)(fd, cmd, arg));
		}
	}
	DPRINT("not implemente");
	RETURN(-EINVAL);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].