/* * This file implements the File object and associated functions. A file * represents an opened file, and holds a reference the the underlying inode. * Most of the functions here are just wrappers around the file operations. * They perform the necessary validation to ensure the operation can be called * on the particular file. If no operation is found, they may implement a * generic version. */ #include #include #include #include #include static void file_delete(Object *); static void file_copy(Object *, Object *); /* File object type */ ObjectType fileType = { .name = "FILE", .size = sizeof(File), .delete = file_delete, .copy = file_copy, }; /* Destroy a file */ static void file_delete(Object *obj) { File *file = (void *) obj; if (file->inode) put(file->inode); if (file->path) destroy_list(file->path); } /* Copy a file */ static void file_copy(Object *a, Object *b) { File *parent = (void *) a, *child = (void *) b; if (parent->inode) child->inode = copy(parent->inode); child->flags = parent->flags; child->pos = parent->pos; child->ops = parent->ops; if (parent->path) child->path = copy_list(parent->path); } /* Open a file */ int file_open(File *file) { if (!file->ops || !file->ops->open) return -EINVAL; return file->ops->open(file); } /* Read a file */ size_t file_read(File *file, char *buf, size_t size) { size_t count = 0; /* Irregular files cannot be cached */ if (!S_ISREG(file->inode->mode)) { if (file->ops && file->ops->read) count = file->ops->read(file, buf, size, file->pos); goto end; } if (file->pos > file->inode->size) goto end; if (file->pos + size > file->inode->size) size = file->inode->size - file->pos; /* Read with cache */ Page *page; char *data; uint16_t min, indent = file->pos % PAGE_SIZE; while (size) { min = (size > PAGE_SIZE) ? PAGE_SIZE : size; page = find_page(file->inode->pages, file->pos + (count % PAGE_SIZE)); if (page) { /* Cache hit */ data = map_page(page); memcpy(buf + count, data + indent, min); } else if (file->ops && file->ops->read) { /* Cache miss */ file->ops->read(file, buf + count, min, file->pos + count); } indent = 0; size -= min; count += min; } end: lock(file); file->pos += count; unlock(file); return count; } /* Write a file */ size_t file_write(File *file, char *buf, size_t size) { size_t count = 0; /* Irregular files cannot be cached */ if (!S_ISREG(file->inode->mode)) { if (file->ops && file->ops->write) count = file->ops->write(file, buf, size, file->pos); goto end; } if (file->pos + size > file->inode->size) file->inode->size = size + file->pos; /* Write with cache */ Page *page; char *data; uint16_t min, indent = file->pos % PAGE_SIZE; while (size) { min = (size > PAGE_SIZE) ? PAGE_SIZE : size; page = find_page(file->inode->pages, file->pos + (count % PAGE_SIZE)); if (page) { /* Cache hit */ data = map_page(page); memcpy(data + indent, buf + count, min); } else if (file->ops && file->ops->write) { /* Cache miss */ file->ops->read(file, buf + count, min, file->pos + count); } indent = 0; size -= min; count += min; } end: lock(file); file->pos += count; unlock(file); return count; } /* I/O Control */ int file_ioctl(File *file, unsigned long request, uintptr_t argp) { if (!file->ops) return -EINVAL; if (!file->ops->ioctl) return -ENOTTY; return file->ops->ioctl(file, request, argp); } /* Read a directory entry from a directory */ int file_readdir(File *file, struct dirent *dent, off_t index) { if (!file->ops || !file->ops->readdir) return -EINVAL; return file->ops->readdir(file, dent, index); } /* Map a file into memory */ void file_mmap(File *file, void *addr, size_t len, off_t offset) { if (!file->ops) return; if (file->ops->mmap) file->ops->mmap(file, addr, len, offset); else if (S_ISREG(file->inode->mode)) file->ops->read(file, addr, len, offset); }