vserver 1.9.3
[linux-2.6.git] / fs / libfs.c
index dfe6ac5..5d3f82b 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/pagemap.h>
 #include <linux/mount.h>
 #include <linux/vfs.h>
+#include <asm/uaccess.h>
 
 int simple_getattr(struct vfsmount *mnt, struct dentry *dentry,
                   struct kstat *stat)
@@ -26,14 +27,27 @@ int simple_statfs(struct super_block *sb, struct kstatfs *buf)
 }
 
 /*
- * Lookup the data. This is trivial - if the dentry didn't already
- * exist, we know it is negative.
+ * Retaining negative dentries for an in-memory filesystem just wastes
+ * memory and lookup time: arrange for them to be deleted immediately.
  */
+static int simple_delete_dentry(struct dentry *dentry)
+{
+       return 1;
+}
 
+/*
+ * Lookup the data. This is trivial - if the dentry didn't already
+ * exist, we know it is negative.  Set d_op to delete negative dentries.
+ */
 struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
 {
+       static struct dentry_operations simple_dentry_operations = {
+               .d_delete = simple_delete_dentry,
+       };
+
        if (dentry->d_name.len > NAME_MAX)
                return ERR_PTR(-ENAMETOOLONG);
+       dentry->d_op = &simple_dentry_operations;
        d_add(dentry, NULL);
        return NULL;
 }
@@ -439,6 +453,74 @@ void simple_release_fs(struct vfsmount **mount, int *count)
        mntput(mnt);
 }
 
+ssize_t simple_read_from_buffer(void __user *to, size_t count, loff_t *ppos,
+                               const void *from, size_t available)
+{
+       loff_t pos = *ppos;
+       if (pos < 0)
+               return -EINVAL;
+       if (pos >= available)
+               return 0;
+       if (count > available - pos)
+               count = available - pos;
+       if (copy_to_user(to, from + pos, count))
+               return -EFAULT;
+       *ppos = pos + count;
+       return count;
+}
+
+/*
+ * Transaction based IO.
+ * The file expects a single write which triggers the transaction, and then
+ * possibly a read which collects the result - which is stored in a
+ * file-local buffer.
+ */
+char *simple_transaction_get(struct file *file, const char __user *buf, size_t size)
+{
+       struct simple_transaction_argresp *ar;
+       static spinlock_t simple_transaction_lock = SPIN_LOCK_UNLOCKED;
+
+       if (size > SIMPLE_TRANSACTION_LIMIT - 1)
+               return ERR_PTR(-EFBIG);
+
+       ar = (struct simple_transaction_argresp *)get_zeroed_page(GFP_KERNEL);
+       if (!ar)
+               return ERR_PTR(-ENOMEM);
+
+       spin_lock(&simple_transaction_lock);
+
+       /* only one write allowed per open */
+       if (file->private_data) {
+               spin_unlock(&simple_transaction_lock);
+               free_page((unsigned long)ar);
+               return ERR_PTR(-EBUSY);
+       }
+
+       file->private_data = ar;
+
+       spin_unlock(&simple_transaction_lock);
+
+       if (copy_from_user(ar->data, buf, size))
+               return ERR_PTR(-EFAULT);
+
+       return ar->data;
+}
+
+ssize_t simple_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
+{
+       struct simple_transaction_argresp *ar = file->private_data;
+
+       if (!ar)
+               return 0;
+       return simple_read_from_buffer(buf, size, pos, ar->data, ar->size);
+}
+
+int simple_transaction_release(struct inode *inode, struct file *file)
+{
+       free_page((unsigned long)file->private_data);
+       return 0;
+}
+
 EXPORT_SYMBOL(dcache_dir_close);
 EXPORT_SYMBOL(dcache_dir_lseek);
 EXPORT_SYMBOL(dcache_dir_open);
@@ -461,3 +543,7 @@ EXPORT_SYMBOL(simple_rmdir);
 EXPORT_SYMBOL(simple_statfs);
 EXPORT_SYMBOL(simple_sync_file);
 EXPORT_SYMBOL(simple_unlink);
+EXPORT_SYMBOL(simple_read_from_buffer);
+EXPORT_SYMBOL(simple_transaction_get);
+EXPORT_SYMBOL(simple_transaction_read);
+EXPORT_SYMBOL(simple_transaction_release);