fs/cifs: make share unaccessible at root level mountable
[firefly-linux-kernel-4.4.55.git] / fs / cifs / connect.c
index 5481a6eb9a958090b7ca65ffb5eca09746ee66e8..61c3a5ab863725cd3ea3f1ceefc54bcb35a622cf 100644 (file)
@@ -3517,6 +3517,44 @@ cifs_get_volume_info(char *mount_data, const char *devname)
        return volume_info;
 }
 
+static int
+cifs_are_all_path_components_accessible(struct TCP_Server_Info *server,
+                                       unsigned int xid,
+                                       struct cifs_tcon *tcon,
+                                       struct cifs_sb_info *cifs_sb,
+                                       char *full_path)
+{
+       int rc;
+       char *s;
+       char sep, tmp;
+
+       sep = CIFS_DIR_SEP(cifs_sb);
+       s = full_path;
+
+       rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, "");
+       while (rc == 0) {
+               /* skip separators */
+               while (*s == sep)
+                       s++;
+               if (!*s)
+                       break;
+               /* next separator */
+               while (*s && *s != sep)
+                       s++;
+
+               /*
+                * temporarily null-terminate the path at the end of
+                * the current component
+                */
+               tmp = *s;
+               *s = 0;
+               rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
+                                                    full_path);
+               *s = tmp;
+       }
+       return rc;
+}
+
 int
 cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
 {
@@ -3654,6 +3692,16 @@ remote_path_check:
                        kfree(full_path);
                        goto mount_fail_check;
                }
+
+               rc = cifs_are_all_path_components_accessible(server,
+                                                            xid, tcon, cifs_sb,
+                                                            full_path);
+               if (rc != 0) {
+                       cifs_dbg(VFS, "cannot query dirs between root and final path, "
+                                "enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
+                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
+                       rc = 0;
+               }
                kfree(full_path);
        }
 
@@ -3923,6 +3971,7 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
 
        bdi_destroy(&cifs_sb->bdi);
        kfree(cifs_sb->mountdata);
+       kfree(cifs_sb->prepath);
        call_rcu(&cifs_sb->rcu, delayed_free);
 }