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