cifs: fix regression with O_EXCL creates and optimize away lookup
[safe/jmp/linux-2.6] / fs / cifs / inode.c
index a8797cc..b223796 100644 (file)
@@ -77,126 +77,146 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)
        }
 }
 
-static void cifs_unix_info_to_inode(struct inode *inode,
-               FILE_UNIX_BASIC_INFO *info, int force_uid_gid)
+/* populate an inode with info from a cifs_fattr struct */
+void
+cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
 {
-       struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
-       struct cifsInodeInfo *cifsInfo = CIFS_I(inode);
-       __u64 num_of_bytes = le64_to_cpu(info->NumOfBytes);
-       __u64 end_of_file = le64_to_cpu(info->EndOfFile);
+       struct cifsInodeInfo *cifs_i = CIFS_I(inode);
+       unsigned long now = jiffies;
+
+       inode->i_atime = fattr->cf_atime;
+       inode->i_mtime = fattr->cf_mtime;
+       inode->i_ctime = fattr->cf_ctime;
+       inode->i_mode = fattr->cf_mode;
+       inode->i_rdev = fattr->cf_rdev;
+       inode->i_nlink = fattr->cf_nlink;
+       inode->i_uid = fattr->cf_uid;
+       inode->i_gid = fattr->cf_gid;
+
+       cifs_i->cifsAttrs = fattr->cf_cifsattrs;
+       cifs_i->uniqueid = fattr->cf_uniqueid;
 
-       inode->i_atime = cifs_NTtimeToUnix(le64_to_cpu(info->LastAccessTime));
-       inode->i_mtime =
-               cifs_NTtimeToUnix(le64_to_cpu(info->LastModificationTime));
-       inode->i_ctime = cifs_NTtimeToUnix(le64_to_cpu(info->LastStatusChange));
-       inode->i_mode = le64_to_cpu(info->Permissions);
+       cFYI(1, ("inode 0x%p old_time=%ld new_time=%ld", inode,
+                cifs_i->time, now));
+       cifs_i->time = now;
+
+       /*
+        * Can't safely change the file size here if the client is writing to
+        * it due to potential races.
+        */
+       spin_lock(&inode->i_lock);
+       if (is_size_safe_to_change(cifs_i, fattr->cf_eof)) {
+               i_size_write(inode, fattr->cf_eof);
+
+               /*
+                * i_blocks is not related to (i_size / i_blksize),
+                * but instead 512 byte (2**9) size is required for
+                * calculating num blocks.
+                */
+               inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9;
+       }
+       spin_unlock(&inode->i_lock);
+
+       cifs_set_ops(inode, fattr->cf_flags & CIFS_FATTR_DFS_REFERRAL);
+}
+
+/* Fill a cifs_fattr struct with info from FILE_UNIX_BASIC_INFO. */
+void
+cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info,
+                        struct cifs_sb_info *cifs_sb)
+{
+       memset(fattr, 0, sizeof(*fattr));
+       fattr->cf_uniqueid = le64_to_cpu(info->UniqueId);
+       fattr->cf_bytes = le64_to_cpu(info->NumOfBytes);
+       fattr->cf_eof = le64_to_cpu(info->EndOfFile);
+
+       fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
+       fattr->cf_mtime = cifs_NTtimeToUnix(info->LastModificationTime);
+       fattr->cf_ctime = cifs_NTtimeToUnix(info->LastStatusChange);
+       fattr->cf_mode = le64_to_cpu(info->Permissions);
 
        /*
         * Since we set the inode type below we need to mask off
         * to avoid strange results if bits set above.
         */
-       inode->i_mode &= ~S_IFMT;
+       fattr->cf_mode &= ~S_IFMT;
        switch (le32_to_cpu(info->Type)) {
        case UNIX_FILE:
-               inode->i_mode |= S_IFREG;
+               fattr->cf_mode |= S_IFREG;
+               fattr->cf_dtype = DT_REG;
                break;
        case UNIX_SYMLINK:
-               inode->i_mode |= S_IFLNK;
+               fattr->cf_mode |= S_IFLNK;
+               fattr->cf_dtype = DT_LNK;
                break;
        case UNIX_DIR:
-               inode->i_mode |= S_IFDIR;
+               fattr->cf_mode |= S_IFDIR;
+               fattr->cf_dtype = DT_DIR;
                break;
        case UNIX_CHARDEV:
-               inode->i_mode |= S_IFCHR;
-               inode->i_rdev = MKDEV(le64_to_cpu(info->DevMajor),
-                                     le64_to_cpu(info->DevMinor) & MINORMASK);
+               fattr->cf_mode |= S_IFCHR;
+               fattr->cf_dtype = DT_CHR;
+               fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor),
+                                      le64_to_cpu(info->DevMinor) & MINORMASK);
                break;
        case UNIX_BLOCKDEV:
-               inode->i_mode |= S_IFBLK;
-               inode->i_rdev = MKDEV(le64_to_cpu(info->DevMajor),
-                                     le64_to_cpu(info->DevMinor) & MINORMASK);
+               fattr->cf_mode |= S_IFBLK;
+               fattr->cf_dtype = DT_BLK;
+               fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor),
+                                      le64_to_cpu(info->DevMinor) & MINORMASK);
                break;
        case UNIX_FIFO:
-               inode->i_mode |= S_IFIFO;
+               fattr->cf_mode |= S_IFIFO;
+               fattr->cf_dtype = DT_FIFO;
                break;
        case UNIX_SOCKET:
-               inode->i_mode |= S_IFSOCK;
+               fattr->cf_mode |= S_IFSOCK;
+               fattr->cf_dtype = DT_SOCK;
                break;
        default:
                /* safest to call it a file if we do not know */
-               inode->i_mode |= S_IFREG;
+               fattr->cf_mode |= S_IFREG;
+               fattr->cf_dtype = DT_REG;
                cFYI(1, ("unknown type %d", le32_to_cpu(info->Type)));
                break;
        }
 
-       if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) &&
-           !force_uid_gid)
-               inode->i_uid = cifs_sb->mnt_uid;
+       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
+               fattr->cf_uid = cifs_sb->mnt_uid;
        else
-               inode->i_uid = le64_to_cpu(info->Uid);
+               fattr->cf_uid = le64_to_cpu(info->Uid);
 
-       if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) &&
-           !force_uid_gid)
-               inode->i_gid = cifs_sb->mnt_gid;
+       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)
+               fattr->cf_gid = cifs_sb->mnt_gid;
        else
-               inode->i_gid = le64_to_cpu(info->Gid);
+               fattr->cf_gid = le64_to_cpu(info->Gid);
 
-       inode->i_nlink = le64_to_cpu(info->Nlinks);
-
-       spin_lock(&inode->i_lock);
-       if (is_size_safe_to_change(cifsInfo, end_of_file)) {
-               /*
-                * We can not safely change the file size here if the client
-                * is writing to it due to potential races.
-                */
-               i_size_write(inode, end_of_file);
-
-               /*
-                * i_blocks is not related to (i_size / i_blksize),
-                * but instead 512 byte (2**9) size is required for
-                * calculating num blocks.
-                */
-               inode->i_blocks = (512 - 1 + num_of_bytes) >> 9;
-       }
-       spin_unlock(&inode->i_lock);
+       fattr->cf_nlink = le64_to_cpu(info->Nlinks);
 }
 
-
 /*
- *     Needed to setup inode data for the directory which is the
- *     junction to the new submount (ie to setup the fake directory
- *      which represents a DFS referral)
+ * Fill a cifs_fattr struct with fake inode info.
+ *
+ * Needed to setup cifs_fattr data for the directory which is the
+ * junction to the new submount (ie to setup the fake directory
+ * which represents a DFS referral).
  */
-static void fill_fake_finddataunix(FILE_UNIX_BASIC_INFO *pfnd_dat,
-                              struct super_block *sb)
+void
+cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)
 {
-       struct inode *pinode = NULL;
-
-       memset(pfnd_dat, 0, sizeof(FILE_UNIX_BASIC_INFO));
+       struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
 
-/*     __le64 pfnd_dat->EndOfFile = cpu_to_le64(0);
-       __le64 pfnd_dat->NumOfBytes = cpu_to_le64(0);
-       __u64 UniqueId = 0;  */
-       pfnd_dat->LastStatusChange =
-               cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
-       pfnd_dat->LastAccessTime =
-               cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
-       pfnd_dat->LastModificationTime =
-               cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
-       pfnd_dat->Type = cpu_to_le32(UNIX_DIR);
-       pfnd_dat->Permissions = cpu_to_le64(S_IXUGO | S_IRWXU);
-       pfnd_dat->Nlinks = cpu_to_le64(2);
-       if (sb->s_root)
-               pinode = sb->s_root->d_inode;
-       if (pinode == NULL)
-               return;
-
-       /* fill in default values for the remaining based on root
-          inode since we can not query the server for this inode info */
-       pfnd_dat->DevMajor = cpu_to_le64(MAJOR(pinode->i_rdev));
-       pfnd_dat->DevMinor = cpu_to_le64(MINOR(pinode->i_rdev));
-       pfnd_dat->Uid = cpu_to_le64(pinode->i_uid);
-       pfnd_dat->Gid = cpu_to_le64(pinode->i_gid);
+       cFYI(1, ("creating fake fattr for DFS referral"));
+
+       memset(fattr, 0, sizeof(*fattr));
+       fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU;
+       fattr->cf_uid = cifs_sb->mnt_uid;
+       fattr->cf_gid = cifs_sb->mnt_gid;
+       fattr->cf_atime = CURRENT_TIME;
+       fattr->cf_ctime = CURRENT_TIME;
+       fattr->cf_mtime = CURRENT_TIME;
+       fattr->cf_nlink = 2;
+       fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL;
 }
 
 /**
@@ -243,65 +263,42 @@ cifs_new_inode(struct super_block *sb, __u64 *inum)
 }
 
 int cifs_get_inode_info_unix(struct inode **pinode,
-       const unsigned char *full_path, struct super_block *sb, int xid)
+                            const unsigned char *full_path,
+                            struct super_block *sb, int xid)
 {
-       int rc = 0;
+       int rc;
        FILE_UNIX_BASIC_INFO find_data;
-       struct cifsTconInfo *pTcon;
-       struct inode *inode;
+       struct cifs_fattr fattr;
+       struct cifsTconInfo *tcon;
        struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
-       bool is_dfs_referral = false;
-       struct cifsInodeInfo *cifsInfo;
-       __u64 num_of_bytes;
-       __u64 end_of_file;
 
-       pTcon = cifs_sb->tcon;
+       tcon = cifs_sb->tcon;
        cFYI(1, ("Getting info on %s", full_path));
 
        /* could have done a find first instead but this returns more info */
-       rc = CIFSSMBUnixQPathInfo(xid, pTcon, full_path, &find_data,
+       rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data,
                                  cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
                                        CIFS_MOUNT_MAP_SPECIAL_CHR);
-       if (rc == -EREMOTE && !is_dfs_referral) {
-               is_dfs_referral = true;
-               cFYI(DBG2, ("DFS ref"));
-               /* for DFS, server does not give us real inode data */
-               fill_fake_finddataunix(&find_data, sb);
-               rc = 0;
-       } else if (rc)
-               goto cgiiu_exit;
 
-       num_of_bytes = le64_to_cpu(find_data.NumOfBytes);
-       end_of_file = le64_to_cpu(find_data.EndOfFile);
+       if (!rc) {
+               cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);
+       } else if (rc == -EREMOTE) {
+               cifs_create_dfs_fattr(&fattr, sb);
+               rc = 0;
+       } else {
+               return rc;
+       }
 
-       /* get new inode */
        if (*pinode == NULL) {
-               *pinode = cifs_new_inode(sb, &find_data.UniqueId);
-               if (*pinode == NULL) {
+               /* get new inode */
+               *pinode = cifs_iget(sb, &fattr);
+               if (!*pinode)
                        rc = -ENOMEM;
-                       goto cgiiu_exit;
-               }
+       } else {
+               /* we already have inode, update it */
+               cifs_fattr_to_inode(*pinode, &fattr);
        }
 
-       inode = *pinode;
-       cifsInfo = CIFS_I(inode);
-
-       cFYI(1, ("Old time %ld", cifsInfo->time));
-       cifsInfo->time = jiffies;
-       cFYI(1, ("New time %ld", cifsInfo->time));
-       /* this is ok to set on every inode revalidate */
-       atomic_set(&cifsInfo->inUse, 1);
-
-       cifs_unix_info_to_inode(inode, &find_data, 0);
-
-       if (num_of_bytes < end_of_file)
-               cFYI(1, ("allocation size less than end of file"));
-       cFYI(1, ("Size %ld and blocks %llu",
-               (unsigned long) inode->i_size,
-               (unsigned long long)inode->i_blocks));
-
-       cifs_set_ops(inode, is_dfs_referral);
-cgiiu_exit:
        return rc;
 }
 
@@ -552,14 +549,11 @@ int cifs_get_inode_info(struct inode **pinode,
 
        /* Linux can not store file creation time so ignore it */
        if (pfindData->LastAccessTime)
-               inode->i_atime = cifs_NTtimeToUnix
-                       (le64_to_cpu(pfindData->LastAccessTime));
+               inode->i_atime = cifs_NTtimeToUnix(pfindData->LastAccessTime);
        else /* do not need to use current_fs_time - time not stored */
                inode->i_atime = CURRENT_TIME;
-       inode->i_mtime =
-                   cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime));
-       inode->i_ctime =
-           cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime));
+       inode->i_mtime = cifs_NTtimeToUnix(pfindData->LastWriteTime);
+       inode->i_ctime = cifs_NTtimeToUnix(pfindData->ChangeTime);
        cFYI(DBG2, ("Attributes came in as 0x%x", attr));
        if (adjustTZ && (pTcon->ses) && (pTcon->ses->server)) {
                inode->i_ctime.tv_sec += pTcon->ses->server->timeAdj;
@@ -605,12 +599,12 @@ int cifs_get_inode_info(struct inode **pinode,
                        inode->i_mode |= S_IFREG;
        }
 
+       cifsInfo->server_eof = le64_to_cpu(pfindData->EndOfFile);
        spin_lock(&inode->i_lock);
-       if (is_size_safe_to_change(cifsInfo,
-                                  le64_to_cpu(pfindData->EndOfFile))) {
+       if (is_size_safe_to_change(cifsInfo, cifsInfo->server_eof)) {
                /* can not safely shrink the file size here if the
                   client is writing to it due to potential races */
-               i_size_write(inode, le64_to_cpu(pfindData->EndOfFile));
+               i_size_write(inode, cifsInfo->server_eof);
 
                /* 512 bytes (2**9) is the fake blocksize that must be
                   used for this calculation */
@@ -627,7 +621,7 @@ int cifs_get_inode_info(struct inode **pinode,
        /* fill in 0777 bits from ACL */
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
                cFYI(1, ("Getting mode bits from ACL"));
-               acl_to_uid_mode(inode, full_path, pfid);
+               acl_to_uid_mode(cifs_sb, inode, full_path, pfid);
        }
 #endif
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
@@ -696,33 +690,85 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb)
        return full_path;
 }
 
+static int
+cifs_find_inode(struct inode *inode, void *opaque)
+{
+       struct cifs_fattr *fattr = (struct cifs_fattr *) opaque;
+
+       if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid)
+               return 0;
+
+       return 1;
+}
+
+static int
+cifs_init_inode(struct inode *inode, void *opaque)
+{
+       struct cifs_fattr *fattr = (struct cifs_fattr *) opaque;
+
+       CIFS_I(inode)->uniqueid = fattr->cf_uniqueid;
+       return 0;
+}
+
+/* Given fattrs, get a corresponding inode */
+struct inode *
+cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
+{
+       unsigned long hash;
+       struct inode *inode;
+
+       cFYI(1, ("looking for uniqueid=%llu", fattr->cf_uniqueid));
+
+       /* hash down to 32-bits on 32-bit arch */
+       hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
+
+       inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr);
+
+       /* we have fattrs in hand, update the inode */
+       if (inode) {
+               cifs_fattr_to_inode(inode, fattr);
+               if (sb->s_flags & MS_NOATIME)
+                       inode->i_flags |= S_NOATIME | S_NOCMTIME;
+               if (inode->i_state & I_NEW) {
+                       inode->i_ino = hash;
+                       unlock_new_inode(inode);
+               }
+       }
+
+       return inode;
+}
+
 /* gets root inode */
-struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
+struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino)
 {
        int xid;
        struct cifs_sb_info *cifs_sb;
-       struct inode *inode;
+       struct inode *inode = NULL;
        long rc;
        char *full_path;
 
-       inode = iget_locked(sb, ino);
-       if (!inode)
-               return ERR_PTR(-ENOMEM);
-       if (!(inode->i_state & I_NEW))
-               return inode;
-
-       cifs_sb = CIFS_SB(inode->i_sb);
+       cifs_sb = CIFS_SB(sb);
        full_path = cifs_build_path_to_root(cifs_sb);
        if (full_path == NULL)
                return ERR_PTR(-ENOMEM);
 
        xid = GetXid();
-       if (cifs_sb->tcon->unix_ext)
-               rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb,
-                                               xid);
-       else
+       if (cifs_sb->tcon->unix_ext) {
+               rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
+               if (!inode)
+                       return ERR_PTR(-ENOMEM);
+       } else {
+               inode = iget_locked(sb, ino);
+               if (!inode)
+                       return ERR_PTR(-ENOMEM);
+               if (!(inode->i_state & I_NEW))
+                       return inode;
+
                rc = cifs_get_inode_info(&inode, full_path, NULL, inode->i_sb,
                                                xid, NULL);
+               unlock_new_inode(inode);
+       }
+
        if (rc && cifs_sb->tcon->ipc) {
                cFYI(1, ("ipc connection - fake read inode"));
                inode->i_mode |= S_IFDIR;
@@ -738,7 +784,6 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
                return ERR_PTR(rc);
        }
 
-       unlock_new_inode(inode);
 
        kfree(full_path);
        /* can not call macro FreeXid here since in a void func
@@ -960,13 +1005,21 @@ undo_setattr:
        goto out_close;
 }
 
+
+/*
+ * If dentry->d_inode is null (usually meaning the cached dentry
+ * is a negative dentry) then we would attempt a standard SMB delete, but
+ * if that fails we can not attempt the fall back mechanisms on EACESS
+ * but will return the EACESS to the caller.  Note that the VFS does not call
+ * unlink on negative dentries currently.
+ */
 int cifs_unlink(struct inode *dir, struct dentry *dentry)
 {
        int rc = 0;
        int xid;
        char *full_path = NULL;
        struct inode *inode = dentry->d_inode;
-       struct cifsInodeInfo *cifsInode = CIFS_I(inode);
+       struct cifsInodeInfo *cifs_inode;
        struct super_block *sb = dir->i_sb;
        struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
        struct cifsTconInfo *tcon = cifs_sb->tcon;
@@ -981,8 +1034,9 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
         * sb->s_vfs_rename_mutex here */
        full_path = build_path_from_dentry(dentry);
        if (full_path == NULL) {
+               rc = -ENOMEM;
                FreeXid(xid);
-               return -ENOMEM;
+               return rc;
        }
 
        if ((tcon->ses->capabilities & CAP_UNIX) &&
@@ -1010,7 +1064,7 @@ psx_del_no_retry:
                rc = cifs_rename_pending_delete(full_path, dentry, xid);
                if (rc == 0)
                        drop_nlink(inode);
-       } else if (rc == -EACCES && dosattr == 0) {
+       } else if ((rc == -EACCES) && (dosattr == 0) && inode) {
                attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
                if (attrs == NULL) {
                        rc = -ENOMEM;
@@ -1018,7 +1072,8 @@ psx_del_no_retry:
                }
 
                /* try to reset dos attributes */
-               origattr = cifsInode->cifsAttrs;
+               cifs_inode = CIFS_I(inode);
+               origattr = cifs_inode->cifsAttrs;
                if (origattr == 0)
                        origattr |= ATTR_NORMAL;
                dosattr = origattr & ~ATTR_READONLY;
@@ -1039,13 +1094,13 @@ psx_del_no_retry:
 
 out_reval:
        if (inode) {
-               cifsInode = CIFS_I(inode);
-               cifsInode->time = 0;    /* will force revalidate to get info
+               cifs_inode = CIFS_I(inode);
+               cifs_inode->time = 0;   /* will force revalidate to get info
                                           when needed */
                inode->i_ctime = current_fs_time(sb);
        }
        dir->i_ctime = dir->i_mtime = current_fs_time(sb);
-       cifsInode = CIFS_I(dir);
+       cifs_inode = CIFS_I(dir);
        CIFS_I(dir)->time = 0;  /* force revalidate of dir as well */
 
        kfree(full_path);
@@ -1054,44 +1109,6 @@ out_reval:
        return rc;
 }
 
-void posix_fill_in_inode(struct inode *tmp_inode,
-       FILE_UNIX_BASIC_INFO *pData, int isNewInode)
-{
-       struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
-       loff_t local_size;
-       struct timespec local_mtime;
-
-       cifsInfo->time = jiffies;
-       atomic_inc(&cifsInfo->inUse);
-
-       /* save mtime and size */
-       local_mtime = tmp_inode->i_mtime;
-       local_size  = tmp_inode->i_size;
-
-       cifs_unix_info_to_inode(tmp_inode, pData, 1);
-       cifs_set_ops(tmp_inode, false);
-
-       if (!S_ISREG(tmp_inode->i_mode))
-               return;
-
-       /*
-        * No sense invalidating pages for new inode
-        * since we we have not started caching
-        * readahead file data yet.
-        */
-       if (isNewInode)
-               return;
-
-       if (timespec_equal(&tmp_inode->i_mtime, &local_mtime) &&
-               (local_size == tmp_inode->i_size)) {
-               cFYI(1, ("inode exists but unchanged"));
-       } else {
-               /* file may have changed on server */
-               cFYI(1, ("invalidate inode, readdir detected change"));
-               invalidate_remote_inode(tmp_inode);
-       }
-}
-
 int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
 {
        int rc = 0, tmprc;
@@ -1100,6 +1117,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
        struct cifsTconInfo *pTcon;
        char *full_path = NULL;
        struct inode *newinode = NULL;
+       struct cifs_fattr fattr;
 
        cFYI(1, ("In cifs_mkdir, mode = 0x%x inode = 0x%p", mode, inode));
 
@@ -1110,8 +1128,9 @@ int cifs_mkdir(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;
        }
 
        if ((pTcon->ses->capabilities & CAP_UNIX) &&
@@ -1125,7 +1144,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
                        goto mkdir_out;
                }
 
-               mode &= ~current->fs->umask;
+               mode &= ~current_umask();
                rc = CIFSPOSIXCreate(xid, pTcon, SMB_O_DIRECTORY | SMB_O_CREAT,
                                mode, NULL /* netfid */, pInfo, &oplock,
                                full_path, cifs_sb->local_nls,
@@ -1151,20 +1170,15 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
                        else
                                direntry->d_op = &cifs_dentry_ops;
 
-                       newinode = cifs_new_inode(inode->i_sb,
-                                                 &pInfo->UniqueId);
-                       if (newinode == NULL) {
+                       cifs_unix_basic_to_fattr(&fattr, pInfo, cifs_sb);
+                       newinode = cifs_iget(inode->i_sb, &fattr);
+                       if (!newinode) {
                                kfree(pInfo);
                                goto mkdir_get_info;
                        }
 
-                       newinode->i_nlink = 2;
                        d_instantiate(direntry, newinode);
 
-                       /* we already checked in POSIXCreate whether
-                          frame was long enough */
-                       posix_fill_in_inode(direntry->d_inode,
-                                       pInfo, 1 /* NewInode */);
 #ifdef CONFIG_CIFS_DEBUG2
                        cFYI(1, ("instantiated dentry %p %s to inode %p",
                                direntry, direntry->d_name.name, newinode));
@@ -1204,7 +1218,7 @@ mkdir_get_info:
                if ((direntry->d_inode) && (direntry->d_inode->i_nlink < 2))
                                direntry->d_inode->i_nlink = 2;
 
-               mode &= ~current->fs->umask;
+               mode &= ~current_umask();
                /* must turn on setgid bit if parent dir has it */
                if (inode->i_mode & S_ISGID)
                        mode |= S_ISGID;
@@ -1294,8 +1308,9 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
 
        full_path = build_path_from_dentry(direntry);
        if (full_path == NULL) {
+               rc = -ENOMEM;
                FreeXid(xid);
-               return -ENOMEM;
+               return rc;
        }
 
        rc = CIFSSMBRmDir(xid, pTcon, full_path, cifs_sb->local_nls,
@@ -1450,7 +1465,8 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
                     checking the UniqueId via FILE_INTERNAL_INFO */
 
 unlink_target:
-       if ((rc == -EACCES) || (rc == -EEXIST)) {
+       /* Try unlinking the target dentry if it's not negative */
+       if (target_dentry->d_inode && (rc == -EACCES || rc == -EEXIST)) {
                tmprc = cifs_unlink(target_dir, target_dentry);
                if (tmprc)
                        goto cifs_rename_exit;
@@ -1498,8 +1514,9 @@ int cifs_revalidate(struct dentry *direntry)
           since that would deadlock */
        full_path = build_path_from_dentry(direntry);
        if (full_path == NULL) {
+               rc = -ENOMEM;
                FreeXid(xid);
-               return -ENOMEM;
+               return rc;
        }
        cFYI(1, ("Revalidate: %s inode 0x%p count %d dentry: 0x%p d_time %ld "
                 "jiffies %ld", full_path, direntry->d_inode,
@@ -1608,6 +1625,7 @@ int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
        if (!err) {
                generic_fillattr(dentry->d_inode, stat);
                stat->blksize = CIFS_MAX_MSGSIZE;
+               stat->ino = CIFS_I(dentry->d_inode)->uniqueid;
        }
        return err;
 }
@@ -1753,6 +1771,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
        }
 
        if (rc == 0) {
+               cifsInode->server_eof = attrs->ia_size;
                rc = cifs_vmtruncate(inode, attrs->ia_size);
                cifs_truncate_page(inode->i_mapping, inode->i_size);
        }
@@ -1792,20 +1811,21 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
                goto out;
        }
 
-       if ((attrs->ia_valid & ATTR_MTIME) || (attrs->ia_valid & ATTR_SIZE)) {
-               /*
-                  Flush data before changing file size or changing the last
-                  write time of the file on the server. If the
-                  flush returns error, store it to report later and continue.
-                  BB: This should be smarter. Why bother flushing pages that
-                  will be truncated anyway? Also, should we error out here if
-                  the flush returns error?
-                */
-               rc = filemap_write_and_wait(inode->i_mapping);
-               if (rc != 0) {
-                       cifsInode->write_behind_rc = rc;
-                       rc = 0;
-               }
+       /*
+        * Attempt to flush data before changing attributes. We need to do
+        * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the
+        * ownership or mode then we may also need to do this. Here, we take
+        * the safe way out and just do the flush on all setattr requests. If
+        * the flush returns error, store it to report later and continue.
+        *
+        * BB: This should be smarter. Why bother flushing pages that
+        * will be truncated anyway? Also, should we error out here if
+        * the flush returns error?
+        */
+       rc = filemap_write_and_wait(inode->i_mapping);
+       if (rc != 0) {
+               cifsInode->write_behind_rc = rc;
+               rc = 0;
        }
 
        if (attrs->ia_valid & ATTR_SIZE) {
@@ -1899,24 +1919,26 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
 
        full_path = build_path_from_dentry(direntry);
        if (full_path == NULL) {
+               rc = -ENOMEM;
                FreeXid(xid);
-               return -ENOMEM;
+               return rc;
        }
 
-       if ((attrs->ia_valid & ATTR_MTIME) || (attrs->ia_valid & ATTR_SIZE)) {
-               /*
-                  Flush data before changing file size or changing the last
-                  write time of the file on the server. If the
-                  flush returns error, store it to report later and continue.
-                  BB: This should be smarter. Why bother flushing pages that
-                  will be truncated anyway? Also, should we error out here if
-                  the flush returns error?
-                */
-               rc = filemap_write_and_wait(inode->i_mapping);
-               if (rc != 0) {
-                       cifsInode->write_behind_rc = rc;
-                       rc = 0;
-               }
+       /*
+        * Attempt to flush data before changing attributes. We need to do
+        * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the
+        * ownership or mode then we may also need to do this. Here, we take
+        * the safe way out and just do the flush on all setattr requests. If
+        * the flush returns error, store it to report later and continue.
+        *
+        * BB: This should be smarter. Why bother flushing pages that
+        * will be truncated anyway? Also, should we error out here if
+        * the flush returns error?
+        */
+       rc = filemap_write_and_wait(inode->i_mapping);
+       if (rc != 0) {
+               cifsInode->write_behind_rc = rc;
+               rc = 0;
        }
 
        if (attrs->ia_valid & ATTR_SIZE) {