/* * This file implements the Files object, which is the namespace for a task's * files. It stores all the opened files for a task. It also implements many * of the file-based system calls that need to use a file descriptor. */ #include #include #include #include #include #include #include #include /* Structure for a Files namespace */ struct Files { Object obj; File *fd[NFILES]; }; static void files_delete(Object *); static void files_copy(Object *, Object *); /* Files object type */ ObjectType filesType = { .name = "FILES", .size = sizeof(Files), .delete = files_delete, .copy = files_copy, }; /* Destroy a Files object */ static void files_delete(Object *obj) { Files *files = (void *) obj; int fd; for (fd = 0; fd < NFILES; fd++) if (files->fd[fd]) put(files->fd[fd]); } /* Copy a Files object */ static void files_copy(Object *a, Object *b) { Files *parent = (void *) a, *child = (void *) b; File *file; int fd; for (fd = 0; fd < NFILES; fd++) { file = parent->fd[fd]; if (!file) continue; child->fd[fd] = get(file); } } /* Get the file described by fd */ File * get_file_by_fd(int fd) { if (fd < 0 || fd >= NFILES) return NULL; return current->files->fd[fd]; } /* Allocate file descriptor */ int allocate_fd(void) { int fd; for (fd = 0; fd < NFILES; fd++) if (!current->files->fd[fd]) break; if (fd == NFILES) fd = -1; return fd; } /* Install a file in a file descriptor */ void install_fd(int fd, File *file) { current->files->fd[fd] = file; } /* Duplicate a file descriptor */ int dup(int oldfd) { int newfd = allocate_fd(); if (newfd == -1) return -EMFILE; return dup2(oldfd, newfd); } /* Duplicate a file descriptor to a new file descriptor */ int dup2(int oldfd, int newfd) { /* Check file descriptors */ File *oldfile = get_file_by_fd(oldfd); File *newfile = get_file_by_fd(newfd); if (!oldfile || newfd < 0 || newfd >= NFILES) return -EBADF; if (newfd != oldfd) { if (newfile) close(newfd); install_fd(newfd, get(oldfile)); } return newfd; } /* Close a file */ int close(int fd) { if (!get_file_by_fd(fd)) return -EBADF; put(current->files->fd[fd]); current->files->fd[fd] = NULL; return 0; } /* Read a file */ int read(int fd, void *buf, size_t count) { if (!buf) return -EFAULT; if (!verify_access(buf, count, PROT_WRITE)) return -EFAULT; File *file = get_file_by_fd(fd); if (!file) return -EBADF; if (file->inode && S_ISDIR(file->inode->mode)) return -EISDIR; return file_read(file, buf, count); } /* Write a file */ int write(int fd, void *buf, size_t count) { if (!buf) return -EFAULT; if (!verify_access(buf, count, PROT_READ)) return -EFAULT; File *file = get_file_by_fd(fd); if (!file) return -EBADF; if (file->inode && S_ISDIR(file->inode->mode)) return -EISDIR; return file_write(file, buf, count); } /* Move a file's position */ off_t lseek(int fd, off_t offset, int whence) { File *file = get_file_by_fd(fd); if (!file) return -EBADF; lock(file); switch (whence) { case SEEK_SET: file->pos = offset; break; case SEEK_CUR: file->pos += offset; break; case SEEK_END: file->pos = file->inode->size + offset; break; default: unlock(file); return -EINVAL; } unlock(file); return file->pos; } /* Perform I/O control on a file */ int ioctl(int fd, unsigned long request, ...) { File *file = get_file_by_fd(fd); if (!file) return -EBADF; va_list args; va_start(args, request); uintptr_t argp = va_arg(args, uintptr_t); va_end(args); return file_ioctl(file, request, argp); } /* Get directory entries */ size_t getdents(int fd, void *buf, size_t count) { if (!verify_access(buf, count, PROT_WRITE)) return -EFAULT; if (count < sizeof(struct dirent)) return -EINVAL; int err; size_t i, size = 0; struct dirent *dent = buf; File *file = get_file_by_fd(fd); if (!file) return -EBADF; if (!S_ISDIR(file->inode->mode)) return -ENOTDIR; /* Read as many entries as possible */ for (i = 0; i < count / sizeof(struct dirent); i++) { err = file_readdir(file, dent, i); if (err < 0) goto out; size += sizeof(struct dirent) + dent->d_namelen; *((char *) buf + size - 1) = '\0'; dent = (void *) ((char *) buf + size); } out: if (!i) return err; return size; }