x86: Fix boot on Twinhead H12Y
[firefly-linux-kernel-4.4.55.git] / fs / cifs / dir.c
index 0521492f5b3b284dc24aa918ce6fb182bdeae333..ed5c07b0cdb174260e994000096de97dabe227df 100644 (file)
@@ -50,12 +50,12 @@ build_path_from_dentry(struct dentry *direntry)
 {
        struct dentry *temp;
        int namelen;
-       int pplen;
        int dfsplen;
        char *full_path;
        char dirsep;
        struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
-       struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
+       struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+       unsigned seq;
 
        if (direntry == NULL)
                return NULL;  /* not much we can do if dentry is freed and
@@ -63,29 +63,35 @@ build_path_from_dentry(struct dentry *direntry)
                when the server crashed */
 
        dirsep = CIFS_DIR_SEP(cifs_sb);
-       pplen = cifs_sb->prepathlen;
        if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
                dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
        else
                dfsplen = 0;
 cifs_bp_rename_retry:
-       namelen = pplen + dfsplen;
+       namelen = dfsplen;
+       seq = read_seqbegin(&rename_lock);
+       rcu_read_lock();
        for (temp = direntry; !IS_ROOT(temp);) {
                namelen += (1 + temp->d_name.len);
                temp = temp->d_parent;
                if (temp == NULL) {
                        cERROR(1, "corrupt dentry");
+                       rcu_read_unlock();
                        return NULL;
                }
        }
+       rcu_read_unlock();
 
        full_path = kmalloc(namelen+1, GFP_KERNEL);
        if (full_path == NULL)
                return full_path;
        full_path[namelen] = 0; /* trailing null */
+       rcu_read_lock();
        for (temp = direntry; !IS_ROOT(temp);) {
+               spin_lock(&temp->d_lock);
                namelen -= 1 + temp->d_name.len;
                if (namelen < 0) {
+                       spin_unlock(&temp->d_lock);
                        break;
                } else {
                        full_path[namelen] = dirsep;
@@ -93,16 +99,19 @@ cifs_bp_rename_retry:
                                temp->d_name.len);
                        cFYI(0, "name: %s", full_path + namelen);
                }
+               spin_unlock(&temp->d_lock);
                temp = temp->d_parent;
                if (temp == NULL) {
                        cERROR(1, "corrupt dentry");
+                       rcu_read_unlock();
                        kfree(full_path);
                        return NULL;
                }
        }
-       if (namelen != pplen + dfsplen) {
-               cERROR(1, "did not end path lookup where expected namelen is %d",
-                       namelen);
+       rcu_read_unlock();
+       if (namelen != dfsplen || read_seqretry(&rename_lock, seq)) {
+               cFYI(1, "did not end path lookup where expected. namelen=%d "
+                       "dfsplen=%d", namelen, dfsplen);
                /* presumably this is only possible if racing with a rename
                of one of the parent directories  (we can not lock the dentries
                above us to prevent this, but retrying should be harmless) */
@@ -126,7 +135,6 @@ cifs_bp_rename_retry:
                        }
                }
        }
-       strncpy(full_path + dfsplen, CIFS_SB(direntry->d_sb)->prepath, pplen);
        return full_path;
 }
 
@@ -152,7 +160,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
        __u16 fileHandle;
        struct cifs_sb_info *cifs_sb;
        struct tcon_link *tlink;
-       struct cifsTconInfo *tcon;
+       struct cifs_tcon *tcon;
        char *full_path = NULL;
        FILE_ALL_INFO *buf = NULL;
        struct inode *newinode = NULL;
@@ -356,7 +364,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
        int xid;
        struct cifs_sb_info *cifs_sb;
        struct tcon_link *tlink;
-       struct cifsTconInfo *pTcon;
+       struct cifs_tcon *pTcon;
        struct cifs_io_parms io_parms;
        char *full_path = NULL;
        struct inode *newinode = NULL;
@@ -488,7 +496,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
        bool posix_open = false;
        struct cifs_sb_info *cifs_sb;
        struct tcon_link *tlink;
-       struct cifsTconInfo *pTcon;
+       struct cifs_tcon *pTcon;
        struct cifsFileInfo *cfile;
        struct inode *newInode = NULL;
        char *full_path = NULL;
@@ -575,10 +583,26 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
                         * If either that or op not supported returned, follow
                         * the normal lookup.
                         */
-                       if ((rc == 0) || (rc == -ENOENT))
+                       switch (rc) {
+                       case 0:
+                               /*
+                                * The server may allow us to open things like
+                                * FIFOs, but the client isn't set up to deal
+                                * with that. If it's not a regular file, just
+                                * close it and proceed as if it were a normal
+                                * lookup.
+                                */
+                               if (newInode && !S_ISREG(newInode->i_mode)) {
+                                       CIFSSMBClose(xid, pTcon, fileHandle);
+                                       break;
+                               }
+                       case -ENOENT:
                                posix_open = true;
-                       else if ((rc == -EINVAL) || (rc != -EOPNOTSUPP))
+                       case -EOPNOTSUPP:
+                               break;
+                       default:
                                pTcon->broken_posix_open = true;
+                       }
                }
                if (!posix_open)
                        rc = cifs_get_inode_info_unix(&newInode, full_path,
@@ -633,7 +657,7 @@ lookup_out:
 static int
 cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
 {
-       if (nd->flags & LOOKUP_RCU)
+       if (nd && (nd->flags & LOOKUP_RCU))
                return -ECHILD;
 
        if (direntry->d_inode) {