cifs: Add support for follow_link on dfs shares under posix extensions
[deliverable/linux.git] / fs / cifs / smb1ops.c
index 8233b174de3d62c6e5a3223919e83db8f7c57016..abd2cc9515c939213d7d3d4e793c00d974445737 100644 (file)
@@ -67,7 +67,7 @@ send_nt_cancel(struct TCP_Server_Info *server, void *buf,
        mutex_unlock(&server->srv_mutex);
 
        cifs_dbg(FYI, "issued NT_CANCEL for mid %u, rc = %d\n",
-                in_buf->Mid, rc);
+                get_mid(in_buf), rc);
 
        return rc;
 }
@@ -101,7 +101,7 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer)
 
        spin_lock(&GlobalMid_Lock);
        list_for_each_entry(mid, &server->pending_mid_q, qhead) {
-               if (mid->mid == buf->Mid &&
+               if (compare_mid(mid->mid, buf) &&
                    mid->mid_state == MID_REQUEST_SUBMITTED &&
                    le16_to_cpu(mid->command) == buf->Command) {
                        spin_unlock(&GlobalMid_Lock);
@@ -534,10 +534,12 @@ cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
 static int
 cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
                     struct cifs_sb_info *cifs_sb, const char *full_path,
-                    FILE_ALL_INFO *data, bool *adjustTZ)
+                    FILE_ALL_INFO *data, bool *adjustTZ, bool *symlink)
 {
        int rc;
 
+       *symlink = false;
+
        /* could do find first instead but this returns more info */
        rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */,
                              cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
@@ -554,6 +556,23 @@ cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
                                                CIFS_MOUNT_MAP_SPECIAL_CHR);
                *adjustTZ = true;
        }
+
+       if (!rc && (le32_to_cpu(data->Attributes) & ATTR_REPARSE)) {
+               int tmprc;
+               int oplock = 0;
+               __u16 netfid;
+
+               /* Need to check if this is a symbolic link or not */
+               tmprc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
+                                   FILE_READ_ATTRIBUTES, 0, &netfid, &oplock,
+                                   NULL, cifs_sb->local_nls,
+                       cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+               if (tmprc == -EOPNOTSUPP)
+                       *symlink = true;
+               else
+                       CIFSSMBClose(xid, tcon, netfid);
+       }
+
        return rc;
 }
 
@@ -806,6 +825,13 @@ out:
        return rc;
 }
 
+static int
+cifs_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
+                  struct cifsFileInfo *cfile)
+{
+       return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid);
+}
+
 static int
 cifs_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
                     const char *path, struct cifs_sb_info *cifs_sb,
@@ -881,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,
@@ -892,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;
 }
 
@@ -956,6 +1022,7 @@ struct smb_version_operations smb1_operations = {
        .set_path_size = CIFSSMBSetEOF,
        .set_file_size = CIFSSMBSetFileSize,
        .set_file_info = smb_set_file_info,
+       .set_compression = cifs_set_compression,
        .echo = CIFSSMBEcho,
        .mkdir = CIFSSMBMkDir,
        .mkdir_setinfo = cifs_mkdir_setinfo,
@@ -982,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.026391 seconds and 5 git commands to generate.