summaryrefslogtreecommitdiff
path: root/sys/kern/kern_descrip.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/kern_descrip.c')
-rw-r--r--sys/kern/kern_descrip.c147
1 files changed, 132 insertions, 15 deletions
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index d122e89..83845f6 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -41,6 +41,7 @@
/*
* Allocate a file descriptor.
*
+ * @td: Process to allocate from (null for CURRENT)
* @fd_out: Pointer to allocated file descriptor output.
*
* This routine will create a new file descriptor
@@ -49,10 +50,13 @@
* Returns 0 on success.
*/
int
-fd_alloc(struct filedesc **fd_out)
+fd_alloc(struct proc *td, struct filedesc **fd_out)
{
struct filedesc *fd;
- struct proc *td = this_td();
+
+ if (td == NULL) {
+ td = this_td();
+ }
/* Find free fd table entry */
for (size_t i = 3; i < PROC_MAX_FILEDES; ++i) {
@@ -85,12 +89,15 @@ fd_alloc(struct filedesc **fd_out)
* Fetch a file descriptor from a file descriptor
* number.
*
+ * @td: Process to get fd from (NULL for current)
* @fdno: File descriptor to fetch
*/
struct filedesc *
-fd_get(unsigned int fdno)
+fd_get(struct proc *td, unsigned int fdno)
{
- struct proc *td = this_td();
+ if (td == NULL) {
+ td = this_td();
+ }
if (fdno > PROC_MAX_FILEDES) {
return NULL;
@@ -111,7 +118,7 @@ fd_close(unsigned int fd)
struct filedesc *filedes;
struct proc *td;
- if ((filedes = fd_get(fd)) == NULL) {
+ if ((filedes = fd_get(NULL, fd)) == NULL) {
return -EBADF;
}
@@ -149,18 +156,32 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write)
{
char *kbuf = NULL;
ssize_t n;
+ uint32_t seal;
struct filedesc *filedes;
struct sio_txn sio;
scret_t retval = 0;
+ if (fd > PROC_MAX_FILEDES) {
+ return -EBADF;
+ }
+
if (count > SSIZE_MAX) {
retval = -EINVAL;
goto done;
}
- filedes = fd_get(fd);
- kbuf = dynalloc(count);
+ filedes = fd_get(NULL, fd);
+ seal = filedes->flags;
+ /* Check the seal */
+ if (write && !ISSET(seal, O_ALLOW_WR)) {
+ return -EPERM;
+ }
+ if (!write && ISSET(seal, O_WRONLY)) {
+ return -EPERM;
+ }
+
+ kbuf = dynalloc(count);
if (kbuf == NULL) {
retval = -ENOMEM;
goto done;
@@ -187,6 +208,7 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write)
sio.buf = kbuf;
sio.offset = filedes->offset;
+ spinlock_acquire(&filedes->lock);
if (write) {
/* Copy in user buffer */
if (copyin(buf, kbuf, count) < 0) {
@@ -205,19 +227,52 @@ fd_rw(unsigned int fd, void *buf, size_t count, uint8_t write)
goto done;
}
+ /* End of file? */
+ if (n == 0) {
+ retval = 0;
+ goto done;
+ }
+
if (copyout(kbuf, buf, count) < 0) {
retval = -EFAULT;
goto done;
}
}
- retval = count;
+
+ /* Increment the offset per read */
+ filedes->offset += n;
+ retval = n;
done:
if (kbuf != NULL) {
dynfree(kbuf);
}
+ spinlock_release(&filedes->lock);
return retval;
}
+static int
+fd_do_create(const char *path, struct nameidata *ndp)
+{
+ struct vop_create_args cargs;
+ struct vnode *dirvp = ndp->vp;
+ const struct vops *vops = dirvp->vops;
+ int error;
+
+ if (vops->create == NULL) {
+ return -EINVAL;
+ }
+
+ cargs.path = path;
+ cargs.ppath = ndp->path;
+ cargs.dirvp = dirvp;
+ cargs.vpp = &ndp->vp;
+ if ((error = vops->create(&cargs)) < 0) {
+ return error;
+ }
+
+ return 0;
+}
+
int
fd_read(unsigned int fd, void *buf, size_t count)
{
@@ -236,28 +291,35 @@ fd_write(unsigned int fd, void *buf, size_t count)
*
* @pathname: Path of file to open.
* @flags: Flags to use.
- *
- * TODO: Use of flags.
*/
int
fd_open(const char *pathname, int flags)
{
int error;
+ const struct vops *vops;
struct filedesc *filedes;
struct nameidata nd;
nd.path = pathname;
- nd.flags = 0;
+ nd.flags = ISSET(flags, O_CREAT) ? NAMEI_WANTPARENT : 0;
if ((error = namei(&nd)) < 0) {
return error;
}
- if ((error = fd_alloc(&filedes)) != 0) {
+ if ((error = fd_alloc(NULL, &filedes)) != 0) {
vfs_release_vnode(nd.vp);
return error;
}
+ vops = nd.vp->vops;
+ if (ISSET(flags, O_CREAT) && vops->create != NULL) {
+ error = fd_do_create(pathname, &nd);
+ }
+ if (error < 0) {
+ return error;
+ }
+
filedes->vp = nd.vp;
filedes->flags = flags;
return filedes->fdno;
@@ -266,18 +328,25 @@ fd_open(const char *pathname, int flags)
/*
* Duplicate a file descriptor. New file descriptor
* points to the same vnode.
+ *
+ * @td: Process of fd to dup (NULL for current)
+ * @fd: File descriptor to dup
*/
int
-fd_dup(int fd)
+fd_dup(struct proc *td, int fd)
{
int error;
struct filedesc *new_desc, *tmp;
- tmp = fd_get(fd);
+ if (td == NULL) {
+ td = this_td();
+ }
+
+ tmp = fd_get(td, fd);
if (tmp == NULL)
return -EBADF;
- if ((error = fd_alloc(&new_desc)) != 0)
+ if ((error = fd_alloc(td, &new_desc)) != 0)
return error;
/* Ref that vnode before we point to it */
@@ -285,3 +354,51 @@ fd_dup(int fd)
new_desc->vp = tmp->vp;
return new_desc->fdno;
}
+
+off_t
+fd_seek(int fildes, off_t offset, int whence)
+{
+ struct filedesc *tmp;
+ struct vattr attr;
+ struct vop_getattr_args getattr_args;
+
+ tmp = fd_get(NULL, fildes);
+ if (tmp == NULL) {
+ return -EBADF;
+ }
+
+ getattr_args.vp = tmp->vp;
+ getattr_args.res = &attr;
+ if ((vfs_vop_getattr(&getattr_args)) < 0) {
+ return -EPIPE;
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ tmp->offset = offset;
+ break;
+ case SEEK_CUR:
+ tmp->offset += offset;
+ break;
+ case SEEK_END:
+ tmp->offset = attr.size + offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return tmp->offset;
+}
+
+/*
+ * Update file offset
+ *
+ * arg0: `filedes'
+ * arg1: `offset'
+ * arg2: `whence'
+ */
+scret_t
+sys_lseek(struct syscall_args *scargs)
+{
+ return fd_seek(scargs->arg0, scargs->arg1, scargs->arg2);
+}