jos/lib/fd.c

321 lines
6.9 KiB
C
Raw Permalink Normal View History

2018-10-24 17:44:45 -07:00
#include <inc/lib.h>
#define debug 0
// Maximum number of file descriptors a program may hold open concurrently
#define MAXFD 32
// Bottom of file descriptor area
#define FDTABLE 0xD0000000
// Bottom of file data area. We reserve one data page for each FD,
// which devices can use if they choose.
#define FILEDATA (FDTABLE + MAXFD*PGSIZE)
// Return the 'struct Fd*' for file descriptor index i
#define INDEX2FD(i) ((struct Fd*) (FDTABLE + (i)*PGSIZE))
// Return the file data page for file descriptor index i
#define INDEX2DATA(i) ((char*) (FILEDATA + (i)*PGSIZE))
// --------------------------------------------------------------
// File descriptor manipulators
// --------------------------------------------------------------
int
fd2num(struct Fd *fd)
{
return ((uintptr_t) fd - FDTABLE) / PGSIZE;
}
char*
fd2data(struct Fd *fd)
{
return INDEX2DATA(fd2num(fd));
}
// Finds the smallest i from 0 to MAXFD-1 that doesn't have
// its fd page mapped.
// Sets *fd_store to the corresponding fd page virtual address.
//
// fd_alloc does NOT actually allocate an fd page.
// It is up to the caller to allocate the page somehow.
// This means that if someone calls fd_alloc twice in a row
// without allocating the first page we return, we'll return the same
// page the second time.
//
// Hint: Use INDEX2FD.
//
// Returns 0 on success, < 0 on error. Errors are:
// -E_MAX_FD: no more file descriptors
// On error, *fd_store is set to 0.
int
fd_alloc(struct Fd **fd_store)
{
int i;
struct Fd *fd;
for (i = 0; i < MAXFD; i++) {
fd = INDEX2FD(i);
if ((uvpd[PDX(fd)] & PTE_P) == 0 || (uvpt[PGNUM(fd)] & PTE_P) == 0) {
*fd_store = fd;
return 0;
}
}
*fd_store = 0;
return -E_MAX_OPEN;
}
// Check that fdnum is in range and mapped.
// If it is, set *fd_store to the fd page virtual address.
//
// Returns 0 on success (the page is in range and mapped), < 0 on error.
// Errors are:
// -E_INVAL: fdnum was either not in range or not mapped.
int
fd_lookup(int fdnum, struct Fd **fd_store)
{
struct Fd *fd;
if (fdnum < 0 || fdnum >= MAXFD) {
if (debug)
cprintf("[%08x] bad fd %d\n", thisenv->env_id, fdnum);
return -E_INVAL;
}
fd = INDEX2FD(fdnum);
if (!(uvpd[PDX(fd)] & PTE_P) || !(uvpt[PGNUM(fd)] & PTE_P)) {
if (debug)
cprintf("[%08x] closed fd %d\n", thisenv->env_id, fdnum);
return -E_INVAL;
}
*fd_store = fd;
return 0;
}
// Frees file descriptor 'fd' by closing the corresponding file
// and unmapping the file descriptor page.
// If 'must_exist' is 0, then fd can be a closed or nonexistent file
// descriptor; the function will return 0 and have no other effect.
// If 'must_exist' is 1, then fd_close returns -E_INVAL when passed a
// closed or nonexistent file descriptor.
// Returns 0 on success, < 0 on error.
int
fd_close(struct Fd *fd, bool must_exist)
{
struct Fd *fd2;
struct Dev *dev;
int r;
if ((r = fd_lookup(fd2num(fd), &fd2)) < 0
|| fd != fd2)
return (must_exist ? r : 0);
if ((r = dev_lookup(fd->fd_dev_id, &dev)) >= 0) {
if (dev->dev_close)
r = (*dev->dev_close)(fd);
else
r = 0;
}
// Make sure fd is unmapped. Might be a no-op if
// (*dev->dev_close)(fd) already unmapped it.
(void) sys_page_unmap(0, fd);
return r;
}
// --------------------------------------------------------------
// File functions
// --------------------------------------------------------------
static struct Dev *devtab[] =
{
&devfile,
&devpipe,
&devcons,
0
};
int
dev_lookup(int dev_id, struct Dev **dev)
{
int i;
for (i = 0; devtab[i]; i++)
if (devtab[i]->dev_id == dev_id) {
*dev = devtab[i];
return 0;
}
cprintf("[%08x] unknown device type %d\n", thisenv->env_id, dev_id);
*dev = 0;
return -E_INVAL;
}
int
close(int fdnum)
{
struct Fd *fd;
int r;
if ((r = fd_lookup(fdnum, &fd)) < 0)
return r;
else
return fd_close(fd, 1);
}
void
close_all(void)
{
int i;
for (i = 0; i < MAXFD; i++)
close(i);
}
// Make file descriptor 'newfdnum' a duplicate of file descriptor 'oldfdnum'.
// For instance, writing onto either file descriptor will affect the
// file and the file offset of the other.
// Closes any previously open file descriptor at 'newfdnum'.
// This is implemented using virtual memory tricks (of course!).
int
dup(int oldfdnum, int newfdnum)
{
int r;
char *ova, *nva;
pte_t pte;
struct Fd *oldfd, *newfd;
if ((r = fd_lookup(oldfdnum, &oldfd)) < 0)
return r;
close(newfdnum);
newfd = INDEX2FD(newfdnum);
ova = fd2data(oldfd);
nva = fd2data(newfd);
if ((uvpd[PDX(ova)] & PTE_P) && (uvpt[PGNUM(ova)] & PTE_P))
if ((r = sys_page_map(0, ova, 0, nva, uvpt[PGNUM(ova)] & PTE_SYSCALL)) < 0)
goto err;
if ((r = sys_page_map(0, oldfd, 0, newfd, uvpt[PGNUM(oldfd)] & PTE_SYSCALL)) < 0)
goto err;
return newfdnum;
err:
sys_page_unmap(0, newfd);
sys_page_unmap(0, nva);
return r;
}
ssize_t
read(int fdnum, void *buf, size_t n)
{
int r;
struct Dev *dev;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
return r;
if ((fd->fd_omode & O_ACCMODE) == O_WRONLY) {
cprintf("[%08x] read %d -- bad mode\n", thisenv->env_id, fdnum);
return -E_INVAL;
}
if (!dev->dev_read)
return -E_NOT_SUPP;
return (*dev->dev_read)(fd, buf, n);
}
ssize_t
readn(int fdnum, void *buf, size_t n)
{
int m, tot;
for (tot = 0; tot < n; tot += m) {
m = read(fdnum, (char*)buf + tot, n - tot);
if (m < 0)
return m;
if (m == 0)
break;
}
return tot;
}
ssize_t
write(int fdnum, const void *buf, size_t n)
{
int r;
struct Dev *dev;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
return r;
if ((fd->fd_omode & O_ACCMODE) == O_RDONLY) {
cprintf("[%08x] write %d -- bad mode\n", thisenv->env_id, fdnum);
return -E_INVAL;
}
if (debug)
cprintf("write %d %p %d via dev %s\n",
fdnum, buf, n, dev->dev_name);
if (!dev->dev_write)
return -E_NOT_SUPP;
return (*dev->dev_write)(fd, buf, n);
}
int
seek(int fdnum, off_t offset)
{
int r;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0)
return r;
fd->fd_offset = offset;
return 0;
}
int
ftruncate(int fdnum, off_t newsize)
{
int r;
struct Dev *dev;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
return r;
if ((fd->fd_omode & O_ACCMODE) == O_RDONLY) {
cprintf("[%08x] ftruncate %d -- bad mode\n",
thisenv->env_id, fdnum);
return -E_INVAL;
}
if (!dev->dev_trunc)
return -E_NOT_SUPP;
return (*dev->dev_trunc)(fd, newsize);
}
int
fstat(int fdnum, struct Stat *stat)
{
int r;
struct Dev *dev;
struct Fd *fd;
if ((r = fd_lookup(fdnum, &fd)) < 0
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
return r;
if (!dev->dev_stat)
return -E_NOT_SUPP;
stat->st_name[0] = 0;
stat->st_size = 0;
stat->st_isdir = 0;
stat->st_dev = dev;
return (*dev->dev_stat)(fd, stat);
}
int
stat(const char *path, struct Stat *stat)
{
int fd, r;
if ((fd = open(path, O_RDONLY)) < 0)
return fd;
r = fstat(fd, stat);
close(fd);
return r;
}