/* ??? Copyright 2002 Johan Rydberg, jrydberg@rtmk.org. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include "nova-intern.h" #include "proc.h" #include "pager.h" #include "io-vfs.h" #include "io-vnode.h" /* Try to allocate a new file object and a file descriptor for it. */ int alloc_file_and_fd (struct proc *p, struct file **filep, int *fdp, int any_p) { struct file **fpp = 0, *file; int i = 0; scan_fdset: if (any_p == 0) { if (*fdp >= p->p_nfds) goto extend_fdset; if (p->p_fdset [*fdp]) return EBADF; fpp = & p->p_fdset [*fdp]; goto alloc_descr; } for (i = 0; i < p->p_nfds; i++) { fpp = &p->p_fdset [i]; if (! p->p_fdset [i]) break; } /* The file descriptor table is dynamic allocated. If we have reached the end of the table we extend it with realloc. */ if (i == p->p_nfds) { struct file **fdset; int new_max; extend_fdset: new_max = p->p_nfds + 128; fdset = (struct file **) realloc (p->p_fdset, sizeof (struct file *) * new_max); if (fdset) return ENOMEM; memset (&fdset [p->p_nfds], 0, sizeof (struct file *) * (new_max - p->p_nfds)); p->p_fdset = fdset; p->p_nfds = new_max; goto scan_fdset; } /* When we get there; FPP is a pointer to the location where we can store the file structure, and I is the file descriptor number. */ alloc_descr: file = (struct file *) malloc (sizeof (struct file)); if (! file) return ENOMEM; *fpp = *filep = file; *fdp = i; return 0; } /* Try to open FILENAME. FLAGS is one of O_RDONLY, O_WRONLY or O_RDWR which request opening the file read-only, write-only or read/write, respectively, bitwise-or'd with zero or more of the following: O_CREAT, O_EXCL, ... MODE specifies the permissions to use in case a new file is created. Return the new file descriptor in *FDP. */ int nova_S_io_open (struct proc *p, char *filename, int filename_length, int flags, int mode, int *fdp) { struct file *file; struct vnode *vp; int err, fd; trace_printf ("open file %s", filename); /* First we try to look up the file as if we where just opening it. */ err = lookup (VLA_LOOKUP, p, filename, &vp); if (err == ENOENT && (flags & O_CREAT)) goto create_file; else if (err) return err; err = VOP_OPEN (vp, flags, 0); if (err) return err; /* ??? VOP_ACCESS? */ /* Okey, we have found the file. Vnode is in VP. Let us allocate a file descriptor for it. If we fail, return. */ err = alloc_file_and_fd (p, &file, &fd, 1); if (err) { /* VRELE (vp); */ return err; } /* Initialize file descriptor. */ file->f_vnode = vp; file->f_offset = 0; file->f_mode = flags; /* if flags & O_TRUNC -> vsetattr SIZE=0 */ *fdp = fd; return 0; create_file: panic ("not yet!"); return 0; } /* Write LENGTH bytes from BUF into file FD. Return number of bytes actual written in *ACTUAL. */ int nova_S_io_write (struct proc *p, int fd, char *buf, size_t length, int *actual) { struct io_iovec iov; struct io_uio uio; struct file *fp; int err; if (fd < 0 || fd > p->p_nfds || !p->p_fdset [fd]) return EBADF; fp = p->p_fdset [fd]; io_iov_init (&iov, buf, length); io_uio_init (&uio, IO_UIO_READ, IO_UIO_SYSSPACE, fp->f_offset, length, &iov, 1, p); err = VOP_WRITE (fp->f_vnode, &uio, 0); if (! err) *actual = length - uio.uio_resid; return err; } /* Read LENGTH bytes into BUF from file FD. Return numerb of bytes actual read in *ACTUALP. ??? use RPC for copy buffer */ int nova_S_io_read (struct proc *p, int fd, char *buf, size_t *length, int *actualp) { struct io_iovec iov; struct io_uio uio; struct file *fp; int err; if (fd < 0 || fd > p->p_nfds || !p->p_fdset [fd]) return EBADF; fp = p->p_fdset [fd]; io_iov_init (&iov, (void *) buf, *length); io_uio_init (&uio, IO_UIO_WRITE, IO_UIO_SYSSPACE, fp->f_offset, *length, &iov, 1, p); err = VOP_READ (fp->f_vnode, &uio, 0); if (! err) *actualp = *length - uio.uio_resid; fp->f_offset = fp->f_offset + (*length - uio.uio_resid); return err; } /* Duplicate file descriptor FD. If ANY_P is true we can select any new file descriptor. If not, *FDP is the file descriptor we want. New file descriptor is returned in *FDP. */ /* ??? this is not correctly implemented. FD and new FD should share file positions, file locks. all except close-on-exec flag. */ int nova_S_io_dup (struct proc *p, int fd, int any_p, int *fdp) { struct file *fp, *new_fp; int err; if (fd < 0 || fd > p->p_nfds || !p->p_fdset [fd]) return EBADF; fp = p->p_fdset [fd]; err = alloc_file_and_fd (p, &new_fp, fdp, any_p); if (err) return err; new_fp->f_vnode = fp->f_vnode; /* VHOLD */ new_fp->f_offset = 0; new_fp->f_mode = fp->f_mode; return 0; } /* Try to map file FD into proc P's address space. Return object for reading in *READOBJ, and object for writing on *WRITEOBJ. They may be the same. */ int nova_S_io_map (struct proc *p, int fd, rtmk_port_t *readobj, rtmk_port_t *writeobj) { struct pager *pager; rtmk_port_t memobj; struct file *fp; *readobj = *writeobj = RTMK_PORT_NULL; if (fd < 0 || fd > p->p_nfds || !p->p_fdset [fd]) return EBADF; fp = p->p_fdset [fd]; pager = pager_create (fp->f_vnode, VM_PROT_ALL, 0); if (! pager) return ENOMEM; memobj = pager_get_right (pager); #if 0 if ((fp->f_mode & 0xf) == O_RDWR) *writeobj = *readobj = memobj; else if ((fp->f_mode & 0xf) == O_RDONLY) *readobj = memobj; else if ((fp->f_mode & 0xf) == O_WRONLY) *writeobj = memobj; #else *readobj = *writeobj = memobj; #endif return 0; } /* Reposition read/write file offset for file FD. to the argument OFF according to the directive WHENCE. */ int nova_S_io_seek (struct proc *p, int fd, int off, int whence) { struct file *fp; int err; if (fd < 0 || fd > p->p_nfds || !p->p_fdset [fd]) return EBADF; fp = p->p_fdset [fd]; if (whence == SEEK_SET) fp->f_offset = off; else if (whence == SEEK_CUR) fp->f_offset = fp->f_offset + off; else if (whence == SEEK_END) { struct vattr vattr; err = VOP_GETATTR (fp->f_vnode, &vattr); if (err) return err; fp->f_offset = vattr.va_size; } return 0; } /* Read directory entries from directory FD. Entries will be put into buffer BUF. BUFSIZE is maximum size of entries. */ int nova_S_io_readdir (struct proc *p, int fd, void *buf, int *bufsize, int *eofp) { struct io_iovec iov; struct io_uio uio; struct file *fp; int err; if (fd < 0 || fd > p->p_nfds || !p->p_fdset [fd]) return EBADF; fp = p->p_fdset [fd]; trace_printf ("readdir fd %d size %d, offset %d", fd, *bufsize, fp->f_offset); io_iov_init (&iov, (void *) buf, *bufsize); io_uio_init (&uio, IO_UIO_WRITE, IO_UIO_SYSSPACE, fp->f_offset, *bufsize, &iov, 1, p); err = VOP_READDIR (fp->f_vnode, &uio, &p->p_ucred); if (! err) { *eofp = uio.uio_resid == *bufsize ? 1 : 0; trace_printf ("we set eof to %d", *eofp); fp->f_offset = fp->f_offset + (*bufsize - uio.uio_resid); *bufsize = *bufsize - uio.uio_resid; } return err; } /* Get information about FILENAME. Put info in BUF. */ int nova_S_io_stat (struct proc *p, char *filename, int filename_length, void *buf, int *buf_length) { struct vattr vattr; struct stat stat; struct vnode *vp; int err; trace_printf ("stat file '%s' %d", filename, strlen (filename)); /* First we try to look up the file as if we where just opening it. */ err = lookup (VLA_LOOKUP, p, filename, &vp); if (err) { trace_printf ("stat error %d", err); return err; } err = VOP_GETATTR (vp, &vattr); if (err) { trace_printf ("getattr error %d", err); return err; } /* VRELE (vp); */ stat.st_mode = vattr.va_mode; stat.st_size = vattr.va_size; stat.st_uid = vattr.va_uid; stat.st_gid = vattr.va_gid; stat.st_atime = vattr.va_atime.tv_sec; stat.st_mtime = vattr.va_mtime.tv_sec; stat.st_ctime = vattr.va_ctime.tv_sec; stat.st_uid = vattr.va_uid; stat.st_gid = vattr.va_gid; stat.st_nlink = vattr.va_nlink; memcpy (buf, &stat, *buf_length); /* VRELE (vp); */ return 0; }