This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / fs / nfs / file.c
index 91bd053..02deae0 100644 (file)
@@ -33,6 +33,8 @@
 
 #define NFSDBG_FACILITY                NFSDBG_FILE
 
+static long nfs_file_fcntl(int fd, unsigned int cmd,
+                       unsigned long arg, struct file *filp);
 static int nfs_file_open(struct inode *, struct file *);
 static int nfs_file_release(struct inode *, struct file *);
 static int  nfs_file_mmap(struct file *, struct vm_area_struct *);
@@ -41,7 +43,6 @@ static ssize_t nfs_file_read(struct kiocb *, char __user *, size_t, loff_t);
 static ssize_t nfs_file_write(struct kiocb *, const char __user *, size_t, loff_t);
 static int  nfs_file_flush(struct file *);
 static int  nfs_fsync(struct file *, struct dentry *dentry, int datasync);
-static int nfs_check_flags(int flags);
 
 struct file_operations nfs_file_operations = {
        .llseek         = remote_llseek,
@@ -56,7 +57,7 @@ struct file_operations nfs_file_operations = {
        .fsync          = nfs_fsync,
        .lock           = nfs_lock,
        .sendfile       = nfs_file_sendfile,
-       .check_flags    = nfs_check_flags,
+       .fcntl          = nfs_file_fcntl,
 };
 
 struct inode_operations nfs_file_inode_operations = {
@@ -70,12 +71,26 @@ struct inode_operations nfs_file_inode_operations = {
 # define IS_SWAPFILE(inode)    (0)
 #endif
 
-static int nfs_check_flags(int flags)
+#define nfs_invalid_flags      (O_APPEND | O_DIRECT)
+
+/*
+ * Check for special cases that NFS doesn't support, and
+ * pass the rest to the generic fcntl function.
+ */
+static long
+nfs_file_fcntl(int fd, unsigned int cmd,
+               unsigned long arg, struct file *filp)
 {
-       if ((flags & (O_APPEND | O_DIRECT)) == (O_APPEND | O_DIRECT))
-               return -EINVAL;
+       switch (cmd) {
+       case F_SETFL:
+               if ((filp->f_flags & nfs_invalid_flags) == nfs_invalid_flags)
+                       return -EINVAL;
+               break;
+       default:
+               break;
+       }
 
-       return 0;
+       return generic_file_fcntl(fd, cmd, arg, filp);
 }
 
 /*
@@ -86,11 +101,10 @@ nfs_file_open(struct inode *inode, struct file *filp)
 {
        struct nfs_server *server = NFS_SERVER(inode);
        int (*open)(struct inode *, struct file *);
-       int res;
+       int res = 0;
 
-       res = nfs_check_flags(filp->f_flags);
-       if (res)
-               return res;
+       if ((filp->f_flags & nfs_invalid_flags) == nfs_invalid_flags)
+               return -EINVAL;
 
        lock_kernel();
        /* Do NFSv4 open() call */
@@ -156,7 +170,163 @@ nfs_file_read(struct kiocb *iocb, char __user * buf, size_t count, loff_t pos)
 }
 
 static ssize_t
-nfs_file_sendfile(struct file *f
+nfs_file_sendfile(struct file *filp, loff_t *ppos, size_t count,
+               read_actor_t actor, void *target)
+{
+       struct dentry *dentry = filp->f_dentry;
+       struct inode *inode = dentry->d_inode;
+       ssize_t res;
+
+       dfprintk(VFS, "nfs: sendfile(%s/%s, %lu@%Lu)\n",
+               dentry->d_parent->d_name.name, dentry->d_name.name,
+               (unsigned long) count, (unsigned long long) *ppos);
+
+       res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+       if (!res)
+               res = generic_file_sendfile(filp, ppos, count, actor, target);
+       return res;
+}
+
+static int
+nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
+{
+       struct dentry *dentry = file->f_dentry;
+       struct inode *inode = dentry->d_inode;
+       int     status;
+
+       dfprintk(VFS, "nfs: mmap(%s/%s)\n",
+               dentry->d_parent->d_name.name, dentry->d_name.name);
+
+       status = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+       if (!status)
+               status = generic_file_mmap(file, vma);
+       return status;
+}
+
+/*
+ * Flush any dirty pages for this process, and check for write errors.
+ * The return status from this call provides a reliable indication of
+ * whether any write errors occurred for this process.
+ */
+static int
+nfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+       struct inode *inode = dentry->d_inode;
+       int status;
+
+       dfprintk(VFS, "nfs: fsync(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
+
+       lock_kernel();
+       status = nfs_wb_all(inode);
+       if (!status) {
+               status = file->f_error;
+               file->f_error = 0;
+       }
+       unlock_kernel();
+       return status;
+}
+
+/*
+ * This does the "real" work of the write. The generic routine has
+ * allocated the page, locked it, done all the page alignment stuff
+ * calculations etc. Now we should just copy the data from user
+ * space and write it back to the real medium..
+ *
+ * If the writer ends up delaying the write, the writer needs to
+ * increment the page use counts until he is done with the page.
+ */
+static int nfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+       return nfs_flush_incompatible(file, page);
+}
+
+static int nfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+       long status;
+
+       lock_kernel();
+       status = nfs_updatepage(file, page, offset, to-offset);
+       unlock_kernel();
+       return status;
+}
+
+struct address_space_operations nfs_file_aops = {
+       .readpage = nfs_readpage,
+       .readpages = nfs_readpages,
+       .set_page_dirty = __set_page_dirty_nobuffers,
+       .writepage = nfs_writepage,
+       .writepages = nfs_writepages,
+       .prepare_write = nfs_prepare_write,
+       .commit_write = nfs_commit_write,
+#ifdef CONFIG_NFS_DIRECTIO
+       .direct_IO = nfs_direct_IO,
+#endif
+};
+
+/* 
+ * Write to a file (through the page cache).
+ */
+static ssize_t
+nfs_file_write(struct kiocb *iocb, const char __user *buf, size_t count, loff_t pos)
+{
+       struct dentry * dentry = iocb->ki_filp->f_dentry;
+       struct inode * inode = dentry->d_inode;
+       ssize_t result;
+
+#ifdef CONFIG_NFS_DIRECTIO
+       if (iocb->ki_filp->f_flags & O_DIRECT)
+               return nfs_file_direct_write(iocb, buf, count, pos);
+#endif
+
+       dfprintk(VFS, "nfs: write(%s/%s(%ld), %lu@%lu)\n",
+               dentry->d_parent->d_name.name, dentry->d_name.name,
+               inode->i_ino, (unsigned long) count, (unsigned long) pos);
+
+       result = -EBUSY;
+       if (IS_SWAPFILE(inode))
+               goto out_swapfile;
+       result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+       if (result)
+               goto out;
+
+       result = count;
+       if (!count)
+               goto out;
+
+       result = generic_file_aio_write(iocb, buf, count, pos);
+out:
+       return result;
+
+out_swapfile:
+       printk(KERN_INFO "NFS: attempt to write to active swap file!\n");
+       goto out;
+}
+
+/*
+ * Lock a (portion of) a file
+ */
+int
+nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
+{
+       struct inode * inode = filp->f_mapping->host;
+       int     status = 0;
+       int     status2;
+
+       dprintk("NFS: nfs_lock(f=%s/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n",
+                       inode->i_sb->s_id, inode->i_ino,
+                       fl->fl_type, fl->fl_flags,
+                       (long long)fl->fl_start, (long long)fl->fl_end);
+
+       if (!inode)
+               return -EINVAL;
+
+       /* No mandatory locks over NFS */
+       if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+               return -ENOLCK;
+
+       if (NFS_PROTO(inode)->version != 4) {
+               /* Fake OK code if mounted without NLM support */
+               if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) {
                        if (IS_GETLK(cmd))
                                status = LOCK_USE_CLNT;
                        goto out_ok;