diff options
Diffstat (limited to 'fs/cifs/inode.c')
-rw-r--r-- | fs/cifs/inode.c | 161 |
1 files changed, 91 insertions, 70 deletions
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 24eb4d392155..bc673c8c1e6b 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,16 @@ 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 | #ifdef CONFIG_CIFS_DFS_UPCALL |
61 | inode->i_fop = &cifs_dir_ops; | 61 | if (is_dfs_referral) { |
62 | inode->i_op = &cifs_dfs_referral_inode_operations; | ||
63 | } else { | ||
64 | #else /* NO DFS support, treat as a directory */ | ||
65 | { | ||
66 | #endif | ||
67 | inode->i_op = &cifs_dir_inode_ops; | ||
68 | inode->i_fop = &cifs_dir_ops; | ||
69 | } | ||
62 | break; | 70 | break; |
63 | case S_IFLNK: | 71 | case S_IFLNK: |
64 | inode->i_op = &cifs_symlink_inode_ops; | 72 | inode->i_op = &cifs_symlink_inode_ops; |
@@ -153,6 +161,30 @@ static void cifs_unix_info_to_inode(struct inode *inode, | |||
153 | spin_unlock(&inode->i_lock); | 161 | spin_unlock(&inode->i_lock); |
154 | } | 162 | } |
155 | 163 | ||
164 | static const unsigned char *cifs_get_search_path(struct cifsTconInfo *pTcon, | ||
165 | const char *search_path) | ||
166 | { | ||
167 | int tree_len; | ||
168 | int path_len; | ||
169 | char *tmp_path; | ||
170 | |||
171 | if (!(pTcon->Flags & SMB_SHARE_IS_IN_DFS)) | ||
172 | return search_path; | ||
173 | |||
174 | /* use full path name for working with DFS */ | ||
175 | tree_len = strnlen(pTcon->treeName, MAX_TREE_SIZE + 1); | ||
176 | path_len = strnlen(search_path, MAX_PATHCONF); | ||
177 | |||
178 | tmp_path = kmalloc(tree_len+path_len+1, GFP_KERNEL); | ||
179 | if (tmp_path == NULL) | ||
180 | return search_path; | ||
181 | |||
182 | strncpy(tmp_path, pTcon->treeName, tree_len); | ||
183 | strncpy(tmp_path+tree_len, search_path, path_len); | ||
184 | tmp_path[tree_len+path_len] = 0; | ||
185 | return tmp_path; | ||
186 | } | ||
187 | |||
156 | int cifs_get_inode_info_unix(struct inode **pinode, | 188 | int cifs_get_inode_info_unix(struct inode **pinode, |
157 | const unsigned char *search_path, struct super_block *sb, int xid) | 189 | const unsigned char *search_path, struct super_block *sb, int xid) |
158 | { | 190 | { |
@@ -161,41 +193,31 @@ int cifs_get_inode_info_unix(struct inode **pinode, | |||
161 | struct cifsTconInfo *pTcon; | 193 | struct cifsTconInfo *pTcon; |
162 | struct inode *inode; | 194 | struct inode *inode; |
163 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); | 195 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); |
164 | char *tmp_path; | 196 | const unsigned char *full_path; |
197 | bool is_dfs_referral = false; | ||
165 | 198 | ||
166 | pTcon = cifs_sb->tcon; | 199 | pTcon = cifs_sb->tcon; |
167 | cFYI(1, ("Getting info on %s", search_path)); | 200 | cFYI(1, ("Getting info on %s", search_path)); |
201 | |||
202 | full_path = cifs_get_search_path(pTcon, search_path); | ||
203 | |||
204 | try_again_CIFSSMBUnixQPathInfo: | ||
168 | /* could have done a find first instead but this returns more info */ | 205 | /* could have done a find first instead but this returns more info */ |
169 | rc = CIFSSMBUnixQPathInfo(xid, pTcon, search_path, &findData, | 206 | rc = CIFSSMBUnixQPathInfo(xid, pTcon, full_path, &findData, |
170 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & | 207 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & |
171 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 208 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
172 | /* dump_mem("\nUnixQPathInfo return data", &findData, | 209 | /* dump_mem("\nUnixQPathInfo return data", &findData, |
173 | sizeof(findData)); */ | 210 | sizeof(findData)); */ |
174 | if (rc) { | 211 | if (rc) { |
175 | if (rc == -EREMOTE) { | 212 | if (rc == -EREMOTE && !is_dfs_referral) { |
176 | tmp_path = | 213 | is_dfs_referral = true; |
177 | kmalloc(strnlen(pTcon->treeName, | 214 | if (full_path != search_path) { |
178 | MAX_TREE_SIZE + 1) + | 215 | kfree(full_path); |
179 | strnlen(search_path, MAX_PATHCONF) + 1, | 216 | full_path = search_path; |
180 | GFP_KERNEL); | 217 | } |
181 | if (tmp_path == NULL) | 218 | goto try_again_CIFSSMBUnixQPathInfo; |
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 | } | 219 | } |
220 | goto cgiiu_exit; | ||
199 | } else { | 221 | } else { |
200 | struct cifsInodeInfo *cifsInfo; | 222 | struct cifsInodeInfo *cifsInfo; |
201 | __u64 num_of_bytes = le64_to_cpu(findData.NumOfBytes); | 223 | __u64 num_of_bytes = le64_to_cpu(findData.NumOfBytes); |
@@ -204,8 +226,10 @@ int cifs_get_inode_info_unix(struct inode **pinode, | |||
204 | /* get new inode */ | 226 | /* get new inode */ |
205 | if (*pinode == NULL) { | 227 | if (*pinode == NULL) { |
206 | *pinode = new_inode(sb); | 228 | *pinode = new_inode(sb); |
207 | if (*pinode == NULL) | 229 | if (*pinode == NULL) { |
208 | return -ENOMEM; | 230 | rc = -ENOMEM; |
231 | goto cgiiu_exit; | ||
232 | } | ||
209 | /* Is an i_ino of zero legal? */ | 233 | /* Is an i_ino of zero legal? */ |
210 | /* Are there sanity checks we can use to ensure that | 234 | /* Are there sanity checks we can use to ensure that |
211 | the server is really filling in that field? */ | 235 | the server is really filling in that field? */ |
@@ -237,8 +261,11 @@ int cifs_get_inode_info_unix(struct inode **pinode, | |||
237 | (unsigned long) inode->i_size, | 261 | (unsigned long) inode->i_size, |
238 | (unsigned long long)inode->i_blocks)); | 262 | (unsigned long long)inode->i_blocks)); |
239 | 263 | ||
240 | cifs_set_ops(inode); | 264 | cifs_set_ops(inode, is_dfs_referral); |
241 | } | 265 | } |
266 | cgiiu_exit: | ||
267 | if (full_path != search_path) | ||
268 | kfree(full_path); | ||
242 | return rc; | 269 | return rc; |
243 | } | 270 | } |
244 | 271 | ||
@@ -347,15 +374,16 @@ static int get_sfu_mode(struct inode *inode, | |||
347 | 374 | ||
348 | int cifs_get_inode_info(struct inode **pinode, | 375 | int cifs_get_inode_info(struct inode **pinode, |
349 | const unsigned char *search_path, FILE_ALL_INFO *pfindData, | 376 | const unsigned char *search_path, FILE_ALL_INFO *pfindData, |
350 | struct super_block *sb, int xid) | 377 | struct super_block *sb, int xid, const __u16 *pfid) |
351 | { | 378 | { |
352 | int rc = 0; | 379 | int rc = 0; |
353 | struct cifsTconInfo *pTcon; | 380 | struct cifsTconInfo *pTcon; |
354 | struct inode *inode; | 381 | struct inode *inode; |
355 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); | 382 | struct cifs_sb_info *cifs_sb = CIFS_SB(sb); |
356 | char *tmp_path; | 383 | const unsigned char *full_path = NULL; |
357 | char *buf = NULL; | 384 | char *buf = NULL; |
358 | int adjustTZ = FALSE; | 385 | int adjustTZ = FALSE; |
386 | bool is_dfs_referral = false; | ||
359 | 387 | ||
360 | pTcon = cifs_sb->tcon; | 388 | pTcon = cifs_sb->tcon; |
361 | cFYI(1, ("Getting info on %s", search_path)); | 389 | cFYI(1, ("Getting info on %s", search_path)); |
@@ -373,8 +401,12 @@ int cifs_get_inode_info(struct inode **pinode, | |||
373 | if (buf == NULL) | 401 | if (buf == NULL) |
374 | return -ENOMEM; | 402 | return -ENOMEM; |
375 | pfindData = (FILE_ALL_INFO *)buf; | 403 | pfindData = (FILE_ALL_INFO *)buf; |
404 | |||
405 | full_path = cifs_get_search_path(pTcon, search_path); | ||
406 | |||
407 | try_again_CIFSSMBQPathInfo: | ||
376 | /* could do find first instead but this returns more info */ | 408 | /* could do find first instead but this returns more info */ |
377 | rc = CIFSSMBQPathInfo(xid, pTcon, search_path, pfindData, | 409 | rc = CIFSSMBQPathInfo(xid, pTcon, full_path, pfindData, |
378 | 0 /* not legacy */, | 410 | 0 /* not legacy */, |
379 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & | 411 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & |
380 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 412 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
@@ -382,7 +414,7 @@ int cifs_get_inode_info(struct inode **pinode, | |||
382 | when server claims no NT SMB support and the above call | 414 | when server claims no NT SMB support and the above call |
383 | failed at least once - set flag in tcon or mount */ | 415 | failed at least once - set flag in tcon or mount */ |
384 | if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) { | 416 | if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) { |
385 | rc = SMBQueryInformation(xid, pTcon, search_path, | 417 | rc = SMBQueryInformation(xid, pTcon, full_path, |
386 | pfindData, cifs_sb->local_nls, | 418 | pfindData, cifs_sb->local_nls, |
387 | cifs_sb->mnt_cifs_flags & | 419 | cifs_sb->mnt_cifs_flags & |
388 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 420 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
@@ -391,31 +423,15 @@ int cifs_get_inode_info(struct inode **pinode, | |||
391 | } | 423 | } |
392 | /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */ | 424 | /* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */ |
393 | if (rc) { | 425 | if (rc) { |
394 | if (rc == -EREMOTE) { | 426 | if (rc == -EREMOTE && !is_dfs_referral) { |
395 | tmp_path = | 427 | is_dfs_referral = true; |
396 | kmalloc(strnlen | 428 | if (full_path != search_path) { |
397 | (pTcon->treeName, | 429 | kfree(full_path); |
398 | MAX_TREE_SIZE + 1) + | 430 | full_path = search_path; |
399 | strnlen(search_path, MAX_PATHCONF) + 1, | ||
400 | GFP_KERNEL); | ||
401 | if (tmp_path == NULL) { | ||
402 | kfree(buf); | ||
403 | return -ENOMEM; | ||
404 | } | 431 | } |
405 | 432 | goto try_again_CIFSSMBQPathInfo; | |
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 | } | 433 | } |
434 | goto cgii_exit; | ||
419 | } else { | 435 | } else { |
420 | struct cifsInodeInfo *cifsInfo; | 436 | struct cifsInodeInfo *cifsInfo; |
421 | __u32 attr = le32_to_cpu(pfindData->Attributes); | 437 | __u32 attr = le32_to_cpu(pfindData->Attributes); |
@@ -424,8 +440,8 @@ int cifs_get_inode_info(struct inode **pinode, | |||
424 | if (*pinode == NULL) { | 440 | if (*pinode == NULL) { |
425 | *pinode = new_inode(sb); | 441 | *pinode = new_inode(sb); |
426 | if (*pinode == NULL) { | 442 | if (*pinode == NULL) { |
427 | kfree(buf); | 443 | rc = -ENOMEM; |
428 | return -ENOMEM; | 444 | goto cgii_exit; |
429 | } | 445 | } |
430 | /* Is an i_ino of zero legal? Can we use that to check | 446 | /* Is an i_ino of zero legal? Can we use that to check |
431 | if the server supports returning inode numbers? Are | 447 | if the server supports returning inode numbers? Are |
@@ -559,7 +575,7 @@ int cifs_get_inode_info(struct inode **pinode, | |||
559 | /* fill in 0777 bits from ACL */ | 575 | /* fill in 0777 bits from ACL */ |
560 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { | 576 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { |
561 | cFYI(1, ("Getting mode bits from ACL")); | 577 | cFYI(1, ("Getting mode bits from ACL")); |
562 | acl_to_uid_mode(inode, search_path); | 578 | acl_to_uid_mode(inode, search_path, pfid); |
563 | } | 579 | } |
564 | #endif | 580 | #endif |
565 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { | 581 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { |
@@ -573,8 +589,11 @@ int cifs_get_inode_info(struct inode **pinode, | |||
573 | atomic_set(&cifsInfo->inUse, 1); | 589 | atomic_set(&cifsInfo->inUse, 1); |
574 | } | 590 | } |
575 | 591 | ||
576 | cifs_set_ops(inode); | 592 | cifs_set_ops(inode, is_dfs_referral); |
577 | } | 593 | } |
594 | cgii_exit: | ||
595 | if (full_path != search_path) | ||
596 | kfree(full_path); | ||
578 | kfree(buf); | 597 | kfree(buf); |
579 | return rc; | 598 | return rc; |
580 | } | 599 | } |
@@ -603,7 +622,8 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino) | |||
603 | if (cifs_sb->tcon->unix_ext) | 622 | if (cifs_sb->tcon->unix_ext) |
604 | rc = cifs_get_inode_info_unix(&inode, "", inode->i_sb, xid); | 623 | rc = cifs_get_inode_info_unix(&inode, "", inode->i_sb, xid); |
605 | else | 624 | else |
606 | rc = cifs_get_inode_info(&inode, "", NULL, inode->i_sb, xid); | 625 | rc = cifs_get_inode_info(&inode, "", NULL, inode->i_sb, xid, |
626 | NULL); | ||
607 | if (rc && cifs_sb->tcon->ipc) { | 627 | if (rc && cifs_sb->tcon->ipc) { |
608 | cFYI(1, ("ipc connection - fake read inode")); | 628 | cFYI(1, ("ipc connection - fake read inode")); |
609 | inode->i_mode |= S_IFDIR; | 629 | inode->i_mode |= S_IFDIR; |
@@ -804,7 +824,7 @@ static void posix_fill_in_inode(struct inode *tmp_inode, | |||
804 | local_size = tmp_inode->i_size; | 824 | local_size = tmp_inode->i_size; |
805 | 825 | ||
806 | cifs_unix_info_to_inode(tmp_inode, pData, 1); | 826 | cifs_unix_info_to_inode(tmp_inode, pData, 1); |
807 | cifs_set_ops(tmp_inode); | 827 | cifs_set_ops(tmp_inode, false); |
808 | 828 | ||
809 | if (!S_ISREG(tmp_inode->i_mode)) | 829 | if (!S_ISREG(tmp_inode->i_mode)) |
810 | return; | 830 | return; |
@@ -936,7 +956,7 @@ mkdir_get_info: | |||
936 | inode->i_sb, xid); | 956 | inode->i_sb, xid); |
937 | else | 957 | else |
938 | rc = cifs_get_inode_info(&newinode, full_path, NULL, | 958 | rc = cifs_get_inode_info(&newinode, full_path, NULL, |
939 | inode->i_sb, xid); | 959 | inode->i_sb, xid, NULL); |
940 | 960 | ||
941 | if (pTcon->nocase) | 961 | if (pTcon->nocase) |
942 | direntry->d_op = &cifs_ci_dentry_ops; | 962 | direntry->d_op = &cifs_ci_dentry_ops; |
@@ -1218,7 +1238,7 @@ int cifs_revalidate(struct dentry *direntry) | |||
1218 | } | 1238 | } |
1219 | } else { | 1239 | } else { |
1220 | rc = cifs_get_inode_info(&direntry->d_inode, full_path, NULL, | 1240 | rc = cifs_get_inode_info(&direntry->d_inode, full_path, NULL, |
1221 | direntry->d_sb, xid); | 1241 | direntry->d_sb, xid, NULL); |
1222 | if (rc) { | 1242 | if (rc) { |
1223 | cFYI(1, ("error on getting revalidate info %d", rc)); | 1243 | cFYI(1, ("error on getting revalidate info %d", rc)); |
1224 | /* if (rc != -ENOENT) | 1244 | /* if (rc != -ENOENT) |
@@ -1407,11 +1427,10 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
1407 | } | 1427 | } |
1408 | cifsInode = CIFS_I(direntry->d_inode); | 1428 | cifsInode = CIFS_I(direntry->d_inode); |
1409 | 1429 | ||
1410 | /* BB check if we need to refresh inode from server now ? BB */ | 1430 | if ((attrs->ia_valid & ATTR_MTIME) || (attrs->ia_valid & ATTR_SIZE)) { |
1411 | |||
1412 | if (attrs->ia_valid & ATTR_SIZE) { | ||
1413 | /* | 1431 | /* |
1414 | Flush data before changing file size on server. If the | 1432 | Flush data before changing file size or changing the last |
1433 | write time of the file on the server. If the | ||
1415 | flush returns error, store it to report later and continue. | 1434 | flush returns error, store it to report later and continue. |
1416 | BB: This should be smarter. Why bother flushing pages that | 1435 | BB: This should be smarter. Why bother flushing pages that |
1417 | will be truncated anyway? Also, should we error out here if | 1436 | will be truncated anyway? Also, should we error out here if |
@@ -1422,7 +1441,9 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) | |||
1422 | CIFS_I(direntry->d_inode)->write_behind_rc = rc; | 1441 | CIFS_I(direntry->d_inode)->write_behind_rc = rc; |
1423 | rc = 0; | 1442 | rc = 0; |
1424 | } | 1443 | } |
1444 | } | ||
1425 | 1445 | ||
1446 | if (attrs->ia_valid & ATTR_SIZE) { | ||
1426 | /* To avoid spurious oplock breaks from server, in the case of | 1447 | /* To avoid spurious oplock breaks from server, in the case of |
1427 | inodes that we already have open, avoid doing path based | 1448 | inodes that we already have open, avoid doing path based |
1428 | setting of file size if we can do it by handle. | 1449 | setting of file size if we can do it by handle. |