aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/cifs/inode.c133
1 files changed, 71 insertions, 62 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 24eb4d392155..4f0ee67eb954 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -30,7 +30,7 @@
30#include "cifs_fs_sb.h" 30#include "cifs_fs_sb.h"
31 31
32 32
33static void cifs_set_ops(struct inode *inode) 33static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)
34{ 34{
35 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 35 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
36 36
@@ -57,8 +57,12 @@ static void cifs_set_ops(struct inode *inode)
57 inode->i_data.a_ops = &cifs_addr_ops; 57 inode->i_data.a_ops = &cifs_addr_ops;
58 break; 58 break;
59 case S_IFDIR: 59 case S_IFDIR:
60 inode->i_op = &cifs_dir_inode_ops; 60 if (is_dfs_referral) {
61 inode->i_fop = &cifs_dir_ops; 61 inode->i_op = &cifs_dfs_referral_inode_operations;
62 } else {
63 inode->i_op = &cifs_dir_inode_ops;
64 inode->i_fop = &cifs_dir_ops;
65 }
62 break; 66 break;
63 case S_IFLNK: 67 case S_IFLNK:
64 inode->i_op = &cifs_symlink_inode_ops; 68 inode->i_op = &cifs_symlink_inode_ops;
@@ -153,6 +157,30 @@ static void cifs_unix_info_to_inode(struct inode *inode,
153 spin_unlock(&inode->i_lock); 157 spin_unlock(&inode->i_lock);
154} 158}
155 159
160static const unsigned char *cifs_get_search_path(struct cifsTconInfo *pTcon,
161 const char *search_path)
162{
163 int tree_len;
164 int path_len;
165 char *tmp_path;
166
167 if (!(pTcon->Flags & SMB_SHARE_IS_IN_DFS))
168 return search_path;
169
170 /* use full path name for working with DFS */
171 tree_len = strnlen(pTcon->treeName, MAX_TREE_SIZE + 1);
172 path_len = strnlen(search_path, MAX_PATHCONF);
173
174 tmp_path = kmalloc(tree_len+path_len+1, GFP_KERNEL);
175 if (tmp_path == NULL)
176 return search_path;
177
178 strncpy(tmp_path, pTcon->treeName, tree_len);
179 strncpy(tmp_path+tree_len, search_path, path_len);
180 tmp_path[tree_len+path_len] = 0;
181 return tmp_path;
182}
183
156int cifs_get_inode_info_unix(struct inode **pinode, 184int cifs_get_inode_info_unix(struct inode **pinode,
157 const unsigned char *search_path, struct super_block *sb, int xid) 185 const unsigned char *search_path, struct super_block *sb, int xid)
158{ 186{
@@ -161,41 +189,28 @@ int cifs_get_inode_info_unix(struct inode **pinode,
161 struct cifsTconInfo *pTcon; 189 struct cifsTconInfo *pTcon;
162 struct inode *inode; 190 struct inode *inode;
163 struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 191 struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
164 char *tmp_path; 192 const unsigned char *full_path;
193 bool is_dfs_referral = false;
165 194
166 pTcon = cifs_sb->tcon; 195 pTcon = cifs_sb->tcon;
167 cFYI(1, ("Getting info on %s", search_path)); 196 cFYI(1, ("Getting info on %s", search_path));
197
198 full_path = cifs_get_search_path(pTcon, search_path);
199
200try_again_CIFSSMBUnixQPathInfo:
168 /* could have done a find first instead but this returns more info */ 201 /* could have done a find first instead but this returns more info */
169 rc = CIFSSMBUnixQPathInfo(xid, pTcon, search_path, &findData, 202 rc = CIFSSMBUnixQPathInfo(xid, pTcon, full_path, &findData,
170 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & 203 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
171 CIFS_MOUNT_MAP_SPECIAL_CHR); 204 CIFS_MOUNT_MAP_SPECIAL_CHR);
172/* dump_mem("\nUnixQPathInfo return data", &findData, 205/* dump_mem("\nUnixQPathInfo return data", &findData,
173 sizeof(findData)); */ 206 sizeof(findData)); */
174 if (rc) { 207 if (rc) {
175 if (rc == -EREMOTE) { 208 if (rc == -EREMOTE && !is_dfs_referral) {
176 tmp_path = 209 is_dfs_referral = true;
177 kmalloc(strnlen(pTcon->treeName, 210 full_path = search_path;
178 MAX_TREE_SIZE + 1) + 211 goto try_again_CIFSSMBUnixQPathInfo;
179 strnlen(search_path, MAX_PATHCONF) + 1,
180 GFP_KERNEL);
181 if (tmp_path == NULL)
182 return -ENOMEM;
183
184 /* have to skip first of the double backslash of
185 UNC name */
186 strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE);
187 strncat(tmp_path, search_path, MAX_PATHCONF);
188 rc = connect_to_dfs_path(xid, pTcon->ses,
189 /* treename + */ tmp_path,
190 cifs_sb->local_nls,
191 cifs_sb->mnt_cifs_flags &
192 CIFS_MOUNT_MAP_SPECIAL_CHR);
193 kfree(tmp_path);
194
195 /* BB fix up inode etc. */
196 } else if (rc) {
197 return rc;
198 } 212 }
213 goto cgiiu_exit;
199 } else { 214 } else {
200 struct cifsInodeInfo *cifsInfo; 215 struct cifsInodeInfo *cifsInfo;
201 __u64 num_of_bytes = le64_to_cpu(findData.NumOfBytes); 216 __u64 num_of_bytes = le64_to_cpu(findData.NumOfBytes);
@@ -204,8 +219,10 @@ int cifs_get_inode_info_unix(struct inode **pinode,
204 /* get new inode */ 219 /* get new inode */
205 if (*pinode == NULL) { 220 if (*pinode == NULL) {
206 *pinode = new_inode(sb); 221 *pinode = new_inode(sb);
207 if (*pinode == NULL) 222 if (*pinode == NULL) {
208 return -ENOMEM; 223 rc = -ENOMEM;
224 goto cgiiu_exit;
225 }
209 /* Is an i_ino of zero legal? */ 226 /* Is an i_ino of zero legal? */
210 /* Are there sanity checks we can use to ensure that 227 /* Are there sanity checks we can use to ensure that
211 the server is really filling in that field? */ 228 the server is really filling in that field? */
@@ -237,8 +254,11 @@ int cifs_get_inode_info_unix(struct inode **pinode,
237 (unsigned long) inode->i_size, 254 (unsigned long) inode->i_size,
238 (unsigned long long)inode->i_blocks)); 255 (unsigned long long)inode->i_blocks));
239 256
240 cifs_set_ops(inode); 257 cifs_set_ops(inode, is_dfs_referral);
241 } 258 }
259cgiiu_exit:
260 if (full_path != search_path)
261 kfree(full_path);
242 return rc; 262 return rc;
243} 263}
244 264
@@ -353,9 +373,10 @@ int cifs_get_inode_info(struct inode **pinode,
353 struct cifsTconInfo *pTcon; 373 struct cifsTconInfo *pTcon;
354 struct inode *inode; 374 struct inode *inode;
355 struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 375 struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
356 char *tmp_path; 376 const unsigned char *full_path = NULL;
357 char *buf = NULL; 377 char *buf = NULL;
358 int adjustTZ = FALSE; 378 int adjustTZ = FALSE;
379 bool is_dfs_referral = false;
359 380
360 pTcon = cifs_sb->tcon; 381 pTcon = cifs_sb->tcon;
361 cFYI(1, ("Getting info on %s", search_path)); 382 cFYI(1, ("Getting info on %s", search_path));
@@ -373,8 +394,12 @@ int cifs_get_inode_info(struct inode **pinode,
373 if (buf == NULL) 394 if (buf == NULL)
374 return -ENOMEM; 395 return -ENOMEM;
375 pfindData = (FILE_ALL_INFO *)buf; 396 pfindData = (FILE_ALL_INFO *)buf;
397
398 full_path = cifs_get_search_path(pTcon, search_path);
399
400try_again_CIFSSMBQPathInfo:
376 /* could do find first instead but this returns more info */ 401 /* could do find first instead but this returns more info */
377 rc = CIFSSMBQPathInfo(xid, pTcon, search_path, pfindData, 402 rc = CIFSSMBQPathInfo(xid, pTcon, full_path, pfindData,
378 0 /* not legacy */, 403 0 /* not legacy */,
379 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & 404 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
380 CIFS_MOUNT_MAP_SPECIAL_CHR); 405 CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -382,7 +407,7 @@ int cifs_get_inode_info(struct inode **pinode,
382 when server claims no NT SMB support and the above call 407 when server claims no NT SMB support and the above call
383 failed at least once - set flag in tcon or mount */ 408 failed at least once - set flag in tcon or mount */
384 if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) { 409 if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) {
385 rc = SMBQueryInformation(xid, pTcon, search_path, 410 rc = SMBQueryInformation(xid, pTcon, full_path,
386 pfindData, cifs_sb->local_nls, 411 pfindData, cifs_sb->local_nls,
387 cifs_sb->mnt_cifs_flags & 412 cifs_sb->mnt_cifs_flags &
388 CIFS_MOUNT_MAP_SPECIAL_CHR); 413 CIFS_MOUNT_MAP_SPECIAL_CHR);
@@ -391,31 +416,12 @@ int cifs_get_inode_info(struct inode **pinode,
391 } 416 }
392 /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */ 417 /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */
393 if (rc) { 418 if (rc) {
394 if (rc == -EREMOTE) { 419 if (rc == -EREMOTE && !is_dfs_referral) {
395 tmp_path = 420 is_dfs_referral = true;
396 kmalloc(strnlen 421 full_path = search_path;
397 (pTcon->treeName, 422 goto try_again_CIFSSMBQPathInfo;
398 MAX_TREE_SIZE + 1) +
399 strnlen(search_path, MAX_PATHCONF) + 1,
400 GFP_KERNEL);
401 if (tmp_path == NULL) {
402 kfree(buf);
403 return -ENOMEM;
404 }
405
406 strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE);
407 strncat(tmp_path, search_path, MAX_PATHCONF);
408 rc = connect_to_dfs_path(xid, pTcon->ses,
409 /* treename + */ tmp_path,
410 cifs_sb->local_nls,
411 cifs_sb->mnt_cifs_flags &
412 CIFS_MOUNT_MAP_SPECIAL_CHR);
413 kfree(tmp_path);
414 /* BB fix up inode etc. */
415 } else if (rc) {
416 kfree(buf);
417 return rc;
418 } 423 }
424 goto cgii_exit;
419 } else { 425 } else {
420 struct cifsInodeInfo *cifsInfo; 426 struct cifsInodeInfo *cifsInfo;
421 __u32 attr = le32_to_cpu(pfindData->Attributes); 427 __u32 attr = le32_to_cpu(pfindData->Attributes);
@@ -424,8 +430,8 @@ int cifs_get_inode_info(struct inode **pinode,
424 if (*pinode == NULL) { 430 if (*pinode == NULL) {
425 *pinode = new_inode(sb); 431 *pinode = new_inode(sb);
426 if (*pinode == NULL) { 432 if (*pinode == NULL) {
427 kfree(buf); 433 rc = -ENOMEM;
428 return -ENOMEM; 434 goto cgii_exit;
429 } 435 }
430 /* Is an i_ino of zero legal? Can we use that to check 436 /* Is an i_ino of zero legal? Can we use that to check
431 if the server supports returning inode numbers? Are 437 if the server supports returning inode numbers? Are
@@ -573,8 +579,11 @@ int cifs_get_inode_info(struct inode **pinode,
573 atomic_set(&cifsInfo->inUse, 1); 579 atomic_set(&cifsInfo->inUse, 1);
574 } 580 }
575 581
576 cifs_set_ops(inode); 582 cifs_set_ops(inode, is_dfs_referral);
577 } 583 }
584cgii_exit:
585 if (full_path != search_path)
586 kfree(full_path);
578 kfree(buf); 587 kfree(buf);
579 return rc; 588 return rc;
580} 589}
@@ -804,7 +813,7 @@ static void posix_fill_in_inode(struct inode *tmp_inode,
804 local_size = tmp_inode->i_size; 813 local_size = tmp_inode->i_size;
805 814
806 cifs_unix_info_to_inode(tmp_inode, pData, 1); 815 cifs_unix_info_to_inode(tmp_inode, pData, 1);
807 cifs_set_ops(tmp_inode); 816 cifs_set_ops(tmp_inode, false);
808 817
809 if (!S_ISREG(tmp_inode->i_mode)) 818 if (!S_ISREG(tmp_inode->i_mode))
810 return; 819 return;