generic_file_write() and file holes
[akaros.git] / kern / src / kfs.c
index 57e778a..63da657 100644 (file)
@@ -30,6 +30,7 @@
 
 /* VFS required Functions */
 /* These structs are declared again and initialized farther down */
+struct page_map_operations kfs_pm_op;
 struct super_operations kfs_s_op;
 struct inode_operations kfs_i_op;
 struct dentry_operations kfs_d_op;
@@ -110,6 +111,35 @@ void kfs_kill_sb(struct super_block *sb)
 struct fs_type kfs_fs_type = {"KFS", 0, kfs_get_sb, kfs_kill_sb, {0, 0},
                TAILQ_HEAD_INITIALIZER(kfs_fs_type.fs_supers)};
 
+/* Page Map Operations */
+
+/* Fills page with its contents from its backing store file.  Note that we do
+ * the zero padding here, instead of higher in the VFS.  Might change in the
+ * future. */
+int kfs_readpage(struct file *file, struct page *page)
+{
+       size_t pg_idx_byte = page->pg_index * PGSIZE;
+       struct kfs_i_info *k_i_info = (struct kfs_i_info*)file->f_inode->i_fs_info;
+       uintptr_t begin = (size_t)k_i_info->filestart + pg_idx_byte;
+       /* If we're beyond the initial start point, we just need a zero page.  This
+        * is for a hole or for extending a file (even though it won't be saved).
+        * Otherwise, we want the data from KFS, being careful to not copy from
+        * beyond the original EOF (and zero padding anything extra). */
+       if (pg_idx_byte >= k_i_info->init_size) {
+               memset(page2kva(page), 0, PGSIZE);
+       } else {
+               size_t copy_amt = MIN(PGSIZE, k_i_info->init_size - pg_idx_byte);
+               memcpy(page2kva(page), (void*)begin, copy_amt);
+               memset(page2kva(page) + copy_amt, 0, PGSIZE - copy_amt);
+       }
+       /* This is supposed to be done in the IO system when the operation is
+        * complete.  Since we aren't doing a real IO request, and it is already
+        * done, we can do it here. */
+       page->pg_flags |= PG_UPTODATE;
+       unlock_page(page);
+       return 0;
+}
+
 /* Super Operations */
 
 /* creates and initializes a new inode.  generic fields are filled in.  specific
@@ -135,6 +165,13 @@ struct inode *kfs_alloc_inode(struct super_block *sb)
        inode->i_fs_info = kmem_cache_alloc(kfs_i_kcache, 0);
        TAILQ_INIT(&((struct kfs_i_info*)inode->i_fs_info)->children);
        ((struct kfs_i_info*)inode->i_fs_info)->filestart = 0;
+       /* Set up the page_map structures.  Default is to use the embedded one. */
+       inode->i_mapping = &inode->i_pm;
+       inode->i_mapping->pm_host = inode;
+       radix_tree_init(&inode->i_mapping->pm_tree);
+       spinlock_init(&inode->i_mapping->pm_tree_lock);
+       inode->i_mapping->pm_op = &kfs_pm_op;
+       inode->i_mapping->pm_flags = 0;
        return inode;
        /* caller sets i_ino, i_list set when applicable */
 }
@@ -175,8 +212,7 @@ void kfs_read_inode(struct inode *inode)
        } else {
                panic("Not implemented");
        }
-       /* TODO: unused: inode->i_hash add to hash (saves on disc reading)
-        * i_mapping, i_data, when they mean something */
+       /* TODO: unused: inode->i_hash add to hash (saves on disc reading) */
 }
 
 /* called when an inode in memory is modified (journalling FS's care) */
@@ -506,83 +542,6 @@ off_t kfs_llseek(struct file *file, off_t offset, int whence)
        return temp_off;
 }
 
-/* Read count bytes from the file into buf, starting at *offset, which is increased
- * accordingly. */
-ssize_t kfs_read(struct file *file, char *buf, size_t count, off_t *offset)
-{
-       struct kfs_i_info *k_i_info = (struct kfs_i_info*)file->f_inode->i_fs_info;
-       void *begin, *end;
-
-       /* TODO: handle this higher up in the VFS */
-       if (!count)
-               return 0;
-       if (*offset == file->f_inode->i_size)
-               return 0; /* EOF */
-
-       /* Make sure we don't go past the end of the file */
-       if (*offset + count > file->f_inode->i_size) {
-               count = file->f_inode->i_size - *offset;
-       }
-       begin = k_i_info->filestart + *offset;
-       end = begin + count;
-       if ((begin < k_i_info->filestart) ||
-           (end > k_i_info->filestart + file->f_inode->i_size) ||
-           (end < begin)) {
-               set_errno(current_tf, EINVAL);
-               return -1;
-       }
-
-       /* TODO: think about this.  if it's a user buffer, we're relying on current
-        * to detect whose it is (which should work for async calls).  Consider
-        * pushing this up the VFS stack. */
-       if (current) {
-               memcpy_to_user(current, buf, begin, count);
-       } else {
-               memcpy(buf, begin, count);
-       }
-       *offset += count;
-       return count;
-}
-
-/* Writes count bytes from buf to the file, starting at *offset, which is
- * increased accordingly */
-ssize_t kfs_write(struct file *file, const char *buf, size_t count,
-                  off_t *offset)
-{
-       struct kfs_i_info *k_i_info = (struct kfs_i_info*)file->f_inode->i_fs_info;
-       void *begin, *end;
-
-       /* TODO: handle this higher up in the VFS */
-       if (!count)
-               return 0;
-       if (*offset == file->f_inode->i_size)
-               return 0; /* EOF */
-
-       /* Make sure we don't go past the end of the file */
-       if (*offset + count > file->f_inode->i_size) {
-               count = file->f_inode->i_size - *offset;
-       }
-       begin = k_i_info->filestart + *offset;
-       end = begin + count;
-       if ((begin < k_i_info->filestart) ||
-           (end > k_i_info->filestart + file->f_inode->i_size) ||
-           (end < begin)) {
-               set_errno(current_tf, EINVAL);
-               return -1;
-       }
-
-       /* TODO: think about this.  if it's a user buffer, we're relying on current
-        * to detect whose it is (which should work for async calls).  Consider
-        * pushing this up the VFS stack. */
-       if (current) {
-               memcpy_from_user(current, begin, buf, count);
-       } else {
-               memcpy(begin, buf, count);
-       }
-       *offset += count;
-       return count;
-}
-
 /* Fills in the next directory entry (dirent), starting with d_off.  Like with
  * read and write, there will be issues with userspace and the *dirent buf.
  * TODO: we don't really do anything with userspace concerns here, in part
@@ -630,9 +589,18 @@ int kfs_readdir(struct file *dir, struct dirent *dirent)
        return 1;                                                       /* normal success for readdir */
 }
 
-/* Memory maps the file into the virtual memory area */
-int kfs_mmap(struct file *file, struct vm_area_struct *vma)
+/* This is called when a VMR is mapping a particular file.  The FS needs to do
+ * whatever it needs so that faults can be handled by read_page(), and handle all
+ * of the cases of MAP_SHARED, MAP_PRIVATE, whatever.  It also needs to ensure
+ * the file is not being mmaped in a way that conflicts with the manner in which
+ * the file was opened. */
+int kfs_mmap(struct file *file, struct vm_region *vmr)
 {
+       /* the file is not page-aligned yet, so we need to copy it to fresh pages.
+        * this should only be done once per SHARED file (inode), so only make fresh
+        * copies if people want new ones.  Also note that MAP_PRIVATE does not get
+        * carried through to the underlying file. */
+
        return -1;
 }
 
@@ -658,7 +626,7 @@ int kfs_open(struct inode *inode, struct file *file)
 //     struct event_poll_tailq         f_ep_links;
        spinlock_init(&file->f_ep_lock);
        file->f_fs_info = 0;
-//     struct address_space            *f_mapping;             /* page cache mapping */
+       file->f_mapping = inode->i_mapping;
        return 0;
 }
 
@@ -718,6 +686,10 @@ int kfs_check_flags(int flags)
 }
 
 /* Redeclaration and initialization of the FS ops structures */
+struct page_map_operations kfs_pm_op = {
+       kfs_readpage,
+};
+
 struct super_operations kfs_s_op = {
        kfs_alloc_inode,
        kfs_destroy_inode,
@@ -762,8 +734,8 @@ struct dentry_operations kfs_d_op = {
 
 struct file_operations kfs_f_op = {
        kfs_llseek,
-       kfs_read,
-       kfs_write,
+       generic_file_read,
+       generic_file_write,
        kfs_readdir,
        kfs_mmap,
        kfs_open,
@@ -920,6 +892,8 @@ static int __add_kfs_entry(struct dentry *parent, char *path,
                        inode = dentry->d_inode;
                        ((struct kfs_i_info*)inode->i_fs_info)->filestart =
                                                                c_bhdr->c_filestart;
+                       ((struct kfs_i_info*)inode->i_fs_info)->init_size =
+                                                               c_bhdr->c_filesize;
                }
                /* Set other info from the CPIO entry */
                inode->i_uid = c_bhdr->c_uid;
@@ -1026,7 +1000,7 @@ void print_dir_tree(struct dentry *dentry, int depth)
                print_dir_tree(d_i, depth + 1);
        }
        TAILQ_FOREACH(d_i, &k_i_info->children, d_subdirs_link) {
-               printk("%sDir %s has child file: %s", buf, dentry->d_name.name,
+               printk("%sDir %s has child file: %s ", buf, dentry->d_name.name,
                       d_i->d_name.name);
                printk("file starts at: %p\n",
                       ((struct kfs_i_info*)d_i->d_inode->i_fs_info)->filestart);