cifs: Add support for follow_link on dfs shares under posix extensions
[deliverable/linux.git] / fs / cifs / smb1ops.c
index 5f5ba0dc2ee1b9c7b3d26ede9590a185ac6925e0..abd2cc9515c939213d7d3d4e793c00d974445737 100644 (file)
@@ -907,6 +907,33 @@ cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
                           (__u8)type, wait, 0);
 }
 
+static int
+cifs_unix_dfs_readlink(const unsigned int xid, struct cifs_tcon *tcon,
+                      const unsigned char *searchName, char **symlinkinfo,
+                      const struct nls_table *nls_codepage)
+{
+#ifdef CONFIG_CIFS_DFS_UPCALL
+       int rc;
+       unsigned int num_referrals = 0;
+       struct dfs_info3_param *referrals = NULL;
+
+       rc = get_dfs_path(xid, tcon->ses, searchName, nls_codepage,
+                         &num_referrals, &referrals, 0);
+
+       if (!rc && num_referrals > 0) {
+               *symlinkinfo = kstrndup(referrals->node_name,
+                                       strlen(referrals->node_name),
+                                       GFP_KERNEL);
+               if (!*symlinkinfo)
+                       rc = -ENOMEM;
+               free_dfs_info_array(referrals, num_referrals);
+       }
+       return rc;
+#else /* No DFS support */
+       return -EREMOTE;
+#endif
+}
+
 static int
 cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
                   const char *full_path, char **target_path,
@@ -918,23 +945,36 @@ cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
 
        cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
 
+       /* Check for unix extensions */
+       if (cap_unix(tcon->ses)) {
+               rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path,
+                                            cifs_sb->local_nls);
+               if (rc == -EREMOTE)
+                       rc = cifs_unix_dfs_readlink(xid, tcon, full_path,
+                                                   target_path,
+                                                   cifs_sb->local_nls);
+
+               goto out;
+       }
+
        rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
                         FILE_READ_ATTRIBUTES, OPEN_REPARSE_POINT, &netfid,
                         &oplock, NULL, cifs_sb->local_nls,
                         cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
        if (rc)
-               return rc;
+               goto out;
 
        rc = CIFSSMBQuerySymLink(xid, tcon, netfid, target_path,
                                 cifs_sb->local_nls);
-       if (rc) {
-               CIFSSMBClose(xid, tcon, netfid);
-               return rc;
-       }
+       if (rc)
+               goto out_close;
 
        convert_delimiter(*target_path, '/');
+out_close:
        CIFSSMBClose(xid, tcon, netfid);
-       cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
+out:
+       if (!rc)
+               cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
        return rc;
 }
 
@@ -1009,7 +1049,8 @@ struct smb_version_operations smb1_operations = {
        .mand_lock = cifs_mand_lock,
        .mand_unlock_range = cifs_unlock_range,
        .push_mand_locks = cifs_push_mandatory_locks,
-       .query_mf_symlink = open_query_close_cifs_symlink,
+       .query_mf_symlink = cifs_query_mf_symlink,
+       .create_mf_symlink = cifs_create_mf_symlink,
        .is_read_op = cifs_is_read_op,
 };
 
This page took 0.028288 seconds and 5 git commands to generate.