mqueue: simplify do_open() error handling
[safe/jmp/linux-2.6] / fs / cifs / dir.c
index 461750e..6ccf726 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/stat.h>
 #include <linux/slab.h>
 #include <linux/namei.h>
+#include <linux/mount.h>
 #include "cifsfs.h"
 #include "cifspdu.h"
 #include "cifsglob.h"
@@ -129,44 +130,45 @@ cifs_bp_rename_retry:
        return full_path;
 }
 
-static void
-cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle,
-                       struct cifsTconInfo *tcon, bool write_only)
+struct cifsFileInfo *
+cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle,
+                 struct file *file, struct vfsmount *mnt, unsigned int oflags)
 {
        int oplock = 0;
        struct cifsFileInfo *pCifsFile;
        struct cifsInodeInfo *pCifsInode;
+       struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb);
 
        pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
-
        if (pCifsFile == NULL)
-               return;
+               return pCifsFile;
 
        if (oplockEnabled)
                oplock = REQ_OPLOCK;
 
        pCifsFile->netfid = fileHandle;
        pCifsFile->pid = current->tgid;
-       pCifsFile->pInode = newinode;
+       pCifsFile->pInode = igrab(newinode);
+       pCifsFile->mnt = mnt;
+       pCifsFile->pfile = file;
        pCifsFile->invalidHandle = false;
        pCifsFile->closePend = false;
        mutex_init(&pCifsFile->fh_mutex);
        mutex_init(&pCifsFile->lock_mutex);
        INIT_LIST_HEAD(&pCifsFile->llist);
-       atomic_set(&pCifsFile->wrtPending, 0);
+       atomic_set(&pCifsFile->count, 1);
+       slow_work_init(&pCifsFile->oplock_break, &cifs_oplock_break_ops);
 
-       /* set the following in open now
-                       pCifsFile->pfile = file; */
        write_lock(&GlobalSMBSeslock);
-       list_add(&pCifsFile->tlist, &tcon->openFileList);
+       list_add(&pCifsFile->tlist, &cifs_sb->tcon->openFileList);
        pCifsInode = CIFS_I(newinode);
        if (pCifsInode) {
                /* if readable file instance put first in list*/
-               if (write_only)
+               if (oflags & FMODE_READ)
+                       list_add(&pCifsFile->flist, &pCifsInode->openFileList);
+               else
                        list_add_tail(&pCifsFile->flist,
                                      &pCifsInode->openFileList);
-               else
-                       list_add(&pCifsFile->flist, &pCifsInode->openFileList);
 
                if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
                        pCifsInode->clientCanCacheAll = true;
@@ -176,18 +178,19 @@ cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle,
                                pCifsInode->clientCanCacheRead = true;
        }
        write_unlock(&GlobalSMBSeslock);
+
+       return pCifsFile;
 }
 
 int cifs_posix_open(char *full_path, struct inode **pinode,
-                   struct super_block *sb, int mode, int oflags,
-                   int *poplock, __u16 *pnetfid, int xid)
+                   struct vfsmount *mnt, int mode, int oflags,
+                   __u32 *poplock, __u16 *pnetfid, int xid)
 {
        int rc;
-       __u32 oplock;
-       bool write_only = false;
        FILE_UNIX_BASIC_INFO *presp_data;
        __u32 posix_flags = 0;
-       struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+       struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb);
+       struct cifs_fattr fattr;
 
        cFYI(1, ("posix open %s", full_path));
 
@@ -211,9 +214,8 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
                posix_flags |= SMB_O_EXCL;
        if (oflags & O_TRUNC)
                posix_flags |= SMB_O_TRUNC;
-       if (oflags & O_APPEND)
-               posix_flags |= SMB_O_APPEND;
-       if (oflags & O_SYNC)
+       /* be safe and imply O_SYNC for O_DSYNC */
+       if (oflags & O_DSYNC)
                posix_flags |= SMB_O_SYNC;
        if (oflags & O_DIRECTORY)
                posix_flags |= SMB_O_DIRECTORY;
@@ -222,11 +224,9 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
        if (oflags & O_DIRECT)
                posix_flags |= SMB_O_DIRECT;
 
-       if (!(oflags & FMODE_READ))
-               write_only = true;
-
+       mode &= ~current_umask();
        rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode,
-                       pnetfid, presp_data, &oplock, full_path,
+                       pnetfid, presp_data, poplock, full_path,
                        cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
                                        CIFS_MOUNT_MAP_SPECIAL_CHR);
        if (rc)
@@ -235,24 +235,23 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
        if (presp_data->Type == cpu_to_le32(-1))
                goto posix_open_ret; /* open ok, caller does qpathinfo */
 
-       /* get new inode and set it up */
        if (!pinode)
                goto posix_open_ret; /* caller does not need info */
 
+       cifs_unix_basic_to_fattr(&fattr, presp_data, cifs_sb);
+
+       /* get new inode and set it up */
        if (*pinode == NULL) {
-               __u64 unique_id = le64_to_cpu(presp_data->UniqueId);
-               *pinode = cifs_new_inode(sb, &unique_id);
+               *pinode = cifs_iget(mnt->mnt_sb, &fattr);
+               if (!*pinode) {
+                       rc = -ENOMEM;
+                       goto posix_open_ret;
+               }
+       } else {
+               cifs_fattr_to_inode(*pinode, &fattr);
        }
-       /* else an inode was passed in. Update its info, don't create one */
 
-       /* We do not need to close the file if new_inode fails since
-          the caller will retry qpathinfo as long as inode is null */
-       if (*pinode == NULL)
-               goto posix_open_ret;
-
-       posix_fill_in_inode(*pinode, presp_data, 1);
-
-       cifs_fill_fileinfo(*pinode, *pnetfid, cifs_sb->tcon, write_only);
+       cifs_new_fileinfo(*pinode, *pnetfid, NULL, mnt, oflags);
 
 posix_open_ret:
        kfree(presp_data);
@@ -279,8 +278,9 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
        int rc = -ENOENT;
        int xid;
        int create_options = CREATE_NOT_DIR;
-       int oplock = 0;
+       __u32 oplock = 0;
        int oflags;
+       bool posix_create = false;
        /*
         * BB below access is probably too much for mknod to request
         *    but we have to do query and setpathinfo so requesting
@@ -296,7 +296,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
        FILE_ALL_INFO *buf = NULL;
        struct inode *newinode = NULL;
        int disposition = FILE_OVERWRITE_IF;
-       bool write_only = false;
 
        xid = GetXid();
 
@@ -305,11 +304,11 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
 
        full_path = build_path_from_dentry(direntry);
        if (full_path == NULL) {
+               rc = -ENOMEM;
                FreeXid(xid);
-               return -ENOMEM;
+               return rc;
        }
 
-       mode &= ~current_umask();
        if (oplockEnabled)
                oplock = REQ_OPLOCK;
 
@@ -321,19 +320,21 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
        if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
            (CIFS_UNIX_POSIX_PATH_OPS_CAP &
                        le64_to_cpu(tcon->fsUnixInfo.Capability))) {
-               rc = cifs_posix_open(full_path, &newinode, inode->i_sb,
+               rc = cifs_posix_open(full_path, &newinode, nd->path.mnt,
                                     mode, oflags, &oplock, &fileHandle, xid);
                /* EIO could indicate that (posix open) operation is not
                   supported, despite what server claimed in capability
                   negotation.  EREMOTE indicates DFS junction, which is not
                   handled in posix open */
 
-               if ((rc == 0) && (newinode == NULL))
-                       goto cifs_create_get_file_info; /* query inode info */
-               else if (rc == 0) /* success, no need to query */
-                       goto cifs_create_set_dentry;
-               else if ((rc != -EIO) && (rc != -EREMOTE) &&
-                        (rc != -EOPNOTSUPP)) /* path not found or net err */
+               if (rc == 0) {
+                       posix_create = true;
+                       if (newinode == NULL) /* query inode info */
+                               goto cifs_create_get_file_info;
+                       else /* success, no need to query */
+                               goto cifs_create_set_dentry;
+               } else if ((rc != -EIO) && (rc != -EREMOTE) &&
+                        (rc != -EOPNOTSUPP) && (rc != -EINVAL))
                        goto cifs_create_out;
                /* else fallthrough to retry, using older open call, this is
                   case where server does not support this SMB level, and
@@ -347,11 +348,8 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
                desiredAccess = 0;
                if (oflags & FMODE_READ)
                        desiredAccess |= GENERIC_READ; /* is this too little? */
-               if (oflags & FMODE_WRITE) {
+               if (oflags & FMODE_WRITE)
                        desiredAccess |= GENERIC_WRITE;
-                       if (!(oflags & FMODE_READ))
-                               write_only = true;
-               }
 
                if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
                        disposition = FILE_CREATE;
@@ -421,9 +419,10 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
                        args.uid = NO_CHANGE_64;
                        args.gid = NO_CHANGE_64;
                }
-               CIFSSMBUnixSetInfo(xid, tcon, full_path, &args,
-                       cifs_sb->local_nls,
-                       cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+               CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
+                                       cifs_sb->local_nls,
+                                       cifs_sb->mnt_cifs_flags &
+                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
        } else {
                /* BB implement mode setting via Windows security
                   descriptors e.g. */
@@ -464,9 +463,9 @@ cifs_create_set_dentry:
        if ((nd == NULL) || (!(nd->flags & LOOKUP_OPEN))) {
                /* mknod case - do not leave file open */
                CIFSSMBClose(xid, tcon, fileHandle);
-       } else if (newinode) {
-                       cifs_fill_fileinfo(newinode, fileHandle,
-                                       cifs_sb->tcon, write_only);
+       } else if (!(posix_create) && (newinode)) {
+                       cifs_new_fileinfo(newinode, fileHandle, NULL,
+                                               nd->path.mnt, oflags);
        }
 cifs_create_out:
        kfree(buf);
@@ -511,10 +510,10 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
                        args.uid = NO_CHANGE_64;
                        args.gid = NO_CHANGE_64;
                }
-               rc = CIFSSMBUnixSetInfo(xid, pTcon, full_path,
-                       &args, cifs_sb->local_nls,
-                       cifs_sb->mnt_cifs_flags &
-                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+               rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, &args,
+                                           cifs_sb->local_nls,
+                                           cifs_sb->mnt_cifs_flags &
+                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
 
                if (!rc) {
                        rc = cifs_get_inode_info_unix(&newinode, full_path,
@@ -537,8 +536,9 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
                        buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
                        if (buf == NULL) {
                                kfree(full_path);
+                               rc = -ENOMEM;
                                FreeXid(xid);
-                               return -ENOMEM;
+                               return rc;
                        }
 
                        rc = CIFSSMBOpen(xid, pTcon, full_path,
@@ -605,8 +605,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
 {
        int xid;
        int rc = 0; /* to get around spurious gcc warning, set to zero here */
-       int oplock = 0;
-       int mode;
+       __u32 oplock = 0;
        __u16 fileHandle = 0;
        bool posix_open = false;
        struct cifs_sb_info *cifs_sb;
@@ -639,6 +638,15 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
                        }
        }
 
+       /*
+        * O_EXCL: optimize away the lookup, but don't hash the dentry. Let
+        * the VFS handle the create.
+        */
+       if (nd && (nd->flags & LOOKUP_EXCL)) {
+               d_instantiate(direntry, NULL);
+               return NULL;
+       }
+
        /* can not grab the rename sem here since it would
        deadlock in the cases (beginning of sys_rename itself)
        in which we already have the sb rename sem */
@@ -655,30 +663,35 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
        }
        cFYI(1, ("Full path: %s inode = 0x%p", full_path, direntry->d_inode));
 
+       /* Posix open is only called (at lookup time) for file create now.
+        * For opens (rather than creates), because we do not know if it
+        * is a file or directory yet, and current Samba no longer allows
+        * us to do posix open on dirs, we could end up wasting an open call
+        * on what turns out to be a dir. For file opens, we wait to call posix
+        * open till cifs_open.  It could be added here (lookup) in the future
+        * but the performance tradeoff of the extra network request when EISDIR
+        * or EACCES is returned would have to be weighed against the 50%
+        * reduction in network traffic in the other paths.
+        */
        if (pTcon->unix_ext) {
-               if (!(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) &&
-                               (nd->flags & LOOKUP_OPEN)) {
-                       if (!((nd->intent.open.flags & O_CREAT) &&
-                                       (nd->intent.open.flags & O_EXCL))) {
-                               mode = nd->intent.open.create_mode &
-                                               ~current_umask();
-                               rc = cifs_posix_open(full_path, &newInode,
-                                       parent_dir_inode->i_sb, mode,
+               if (nd && !(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) &&
+                    (nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open &&
+                    (nd->intent.open.flags & O_CREAT)) {
+                       rc = cifs_posix_open(full_path, &newInode, nd->path.mnt,
+                                       nd->intent.open.create_mode,
                                        nd->intent.open.flags, &oplock,
                                        &fileHandle, xid);
-                               /*
-                                * This code works around a bug in
-                                * samba posix open in samba versions 3.3.1
-                                * and earlier where create works
-                                * but open fails with invalid parameter.
-                                * If either of these error codes are
-                                * returned, follow the normal lookup.
-                                * Otherwise, the error during posix open
-                                * is handled.
-                                */
-                               if ((rc != -EINVAL) && (rc != -EOPNOTSUPP))
-                                       posix_open = true;
-                       }
+                       /*
+                        * The check below works around a bug in POSIX
+                        * open in samba versions 3.3.1 and earlier where
+                        * open could incorrectly fail with invalid parameter.
+                        * If either that or op not supported returned, follow
+                        * the normal lookup.
+                        */
+                       if ((rc == 0) || (rc == -ENOENT))
+                               posix_open = true;
+                       else if ((rc == -EINVAL) || (rc != -EOPNOTSUPP))
+                               pTcon->broken_posix_open = true;
                }
                if (!posix_open)
                        rc = cifs_get_inode_info_unix(&newInode, full_path,