#include #include #include #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 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; iumask))) < 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; imode & 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); }