#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);
}
|