diff options
| -rw-r--r-- | fs/cifs/inode.c | 133 |
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 | ||
| 33 | static void cifs_set_ops(struct inode *inode) | 33 | static 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 | ||
| 160 | static 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 | |||
| 156 | int cifs_get_inode_info_unix(struct inode **pinode, | 184 | int 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 | |||
| 200 | try_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 | } |
| 259 | cgiiu_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 | |||
| 400 | try_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 | } |
| 584 | cgii_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; |
