diff options
-rw-r--r-- | fs/cifs/cifsglob.h | 2 | ||||
-rw-r--r-- | fs/cifs/inode.c | 23 | ||||
-rw-r--r-- | fs/cifs/readdir.c | 40 | ||||
-rw-r--r-- | fs/cifs/smb1ops.c | 21 | ||||
-rw-r--r-- | fs/cifs/smb2inode.c | 16 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 2 |
6 files changed, 55 insertions, 49 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 26b1c1dc93f6..cddb807addde 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -261,7 +261,7 @@ struct smb_version_operations { | |||
261 | /* query path data from the server */ | 261 | /* query path data from the server */ |
262 | int (*query_path_info)(const unsigned int, struct cifs_tcon *, | 262 | int (*query_path_info)(const unsigned int, struct cifs_tcon *, |
263 | struct cifs_sb_info *, const char *, | 263 | struct cifs_sb_info *, const char *, |
264 | FILE_ALL_INFO *, bool *); | 264 | FILE_ALL_INFO *, bool *, bool *); |
265 | /* query file data from the server */ | 265 | /* query file data from the server */ |
266 | int (*query_file_info)(const unsigned int, struct cifs_tcon *, | 266 | int (*query_file_info)(const unsigned int, struct cifs_tcon *, |
267 | struct cifs_fid *, FILE_ALL_INFO *); | 267 | struct cifs_fid *, FILE_ALL_INFO *); |
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 867b7cdc794a..36f9ebb93ceb 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
@@ -542,7 +542,8 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, | |||
542 | /* Fill a cifs_fattr struct with info from FILE_ALL_INFO */ | 542 | /* Fill a cifs_fattr struct with info from FILE_ALL_INFO */ |
543 | static void | 543 | static void |
544 | cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, | 544 | cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, |
545 | struct cifs_sb_info *cifs_sb, bool adjust_tz) | 545 | struct cifs_sb_info *cifs_sb, bool adjust_tz, |
546 | bool symlink) | ||
546 | { | 547 | { |
547 | struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); | 548 | struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); |
548 | 549 | ||
@@ -569,7 +570,11 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, | |||
569 | fattr->cf_createtime = le64_to_cpu(info->CreationTime); | 570 | fattr->cf_createtime = le64_to_cpu(info->CreationTime); |
570 | 571 | ||
571 | fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); | 572 | fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); |
572 | if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { | 573 | |
574 | if (symlink) { | ||
575 | fattr->cf_mode = S_IFLNK; | ||
576 | fattr->cf_dtype = DT_LNK; | ||
577 | } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { | ||
573 | fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; | 578 | fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; |
574 | fattr->cf_dtype = DT_DIR; | 579 | fattr->cf_dtype = DT_DIR; |
575 | /* | 580 | /* |
@@ -578,10 +583,6 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, | |||
578 | */ | 583 | */ |
579 | if (!tcon->unix_ext) | 584 | if (!tcon->unix_ext) |
580 | fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; | 585 | fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; |
581 | } else if (fattr->cf_cifsattrs & ATTR_REPARSE) { | ||
582 | fattr->cf_mode = S_IFLNK; | ||
583 | fattr->cf_dtype = DT_LNK; | ||
584 | fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); | ||
585 | } else { | 586 | } else { |
586 | fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; | 587 | fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; |
587 | fattr->cf_dtype = DT_REG; | 588 | fattr->cf_dtype = DT_REG; |
@@ -626,7 +627,8 @@ cifs_get_file_info(struct file *filp) | |||
626 | rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data); | 627 | rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data); |
627 | switch (rc) { | 628 | switch (rc) { |
628 | case 0: | 629 | case 0: |
629 | cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false); | 630 | cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false, |
631 | false); | ||
630 | break; | 632 | break; |
631 | case -EREMOTE: | 633 | case -EREMOTE: |
632 | cifs_create_dfs_fattr(&fattr, inode->i_sb); | 634 | cifs_create_dfs_fattr(&fattr, inode->i_sb); |
@@ -673,6 +675,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, | |||
673 | bool adjust_tz = false; | 675 | bool adjust_tz = false; |
674 | struct cifs_fattr fattr; | 676 | struct cifs_fattr fattr; |
675 | struct cifs_search_info *srchinf = NULL; | 677 | struct cifs_search_info *srchinf = NULL; |
678 | bool symlink = false; | ||
676 | 679 | ||
677 | tlink = cifs_sb_tlink(cifs_sb); | 680 | tlink = cifs_sb_tlink(cifs_sb); |
678 | if (IS_ERR(tlink)) | 681 | if (IS_ERR(tlink)) |
@@ -702,12 +705,12 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, | |||
702 | } | 705 | } |
703 | data = (FILE_ALL_INFO *)buf; | 706 | data = (FILE_ALL_INFO *)buf; |
704 | rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, | 707 | rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, |
705 | data, &adjust_tz); | 708 | data, &adjust_tz, &symlink); |
706 | } | 709 | } |
707 | 710 | ||
708 | if (!rc) { | 711 | if (!rc) { |
709 | cifs_all_info_to_fattr(&fattr, (FILE_ALL_INFO *)data, cifs_sb, | 712 | cifs_all_info_to_fattr(&fattr, data, cifs_sb, adjust_tz, |
710 | adjust_tz); | 713 | symlink); |
711 | } else if (rc == -EREMOTE) { | 714 | } else if (rc == -EREMOTE) { |
712 | cifs_create_dfs_fattr(&fattr, sb); | 715 | cifs_create_dfs_fattr(&fattr, sb); |
713 | rc = 0; | 716 | rc = 0; |
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 53a75f3d0179..5940ecabbe6a 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c | |||
@@ -134,22 +134,6 @@ out: | |||
134 | dput(dentry); | 134 | dput(dentry); |
135 | } | 135 | } |
136 | 136 | ||
137 | /* | ||
138 | * Is it possible that this directory might turn out to be a DFS referral | ||
139 | * once we go to try and use it? | ||
140 | */ | ||
141 | static bool | ||
142 | cifs_dfs_is_possible(struct cifs_sb_info *cifs_sb) | ||
143 | { | ||
144 | #ifdef CONFIG_CIFS_DFS_UPCALL | ||
145 | struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); | ||
146 | |||
147 | if (tcon->Flags & SMB_SHARE_IS_IN_DFS) | ||
148 | return true; | ||
149 | #endif | ||
150 | return false; | ||
151 | } | ||
152 | |||
153 | static void | 137 | static void |
154 | cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) | 138 | cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) |
155 | { | 139 | { |
@@ -159,27 +143,19 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) | |||
159 | if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { | 143 | if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { |
160 | fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; | 144 | fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; |
161 | fattr->cf_dtype = DT_DIR; | 145 | fattr->cf_dtype = DT_DIR; |
162 | /* | ||
163 | * Windows CIFS servers generally make DFS referrals look | ||
164 | * like directories in FIND_* responses with the reparse | ||
165 | * attribute flag also set (since DFS junctions are | ||
166 | * reparse points). We must revalidate at least these | ||
167 | * directory inodes before trying to use them (if | ||
168 | * they are DFS we will get PATH_NOT_COVERED back | ||
169 | * when queried directly and can then try to connect | ||
170 | * to the DFS target) | ||
171 | */ | ||
172 | if (cifs_dfs_is_possible(cifs_sb) && | ||
173 | (fattr->cf_cifsattrs & ATTR_REPARSE)) | ||
174 | fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; | ||
175 | } else if (fattr->cf_cifsattrs & ATTR_REPARSE) { | ||
176 | fattr->cf_mode = S_IFLNK; | ||
177 | fattr->cf_dtype = DT_LNK; | ||
178 | } else { | 146 | } else { |
179 | fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; | 147 | fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; |
180 | fattr->cf_dtype = DT_REG; | 148 | fattr->cf_dtype = DT_REG; |
181 | } | 149 | } |
182 | 150 | ||
151 | /* | ||
152 | * We need to revalidate it further to make a decision about whether it | ||
153 | * is a symbolic link, DFS referral or a reparse point with a direct | ||
154 | * access like junctions, deduplicated files, NFS symlinks. | ||
155 | */ | ||
156 | if (fattr->cf_cifsattrs & ATTR_REPARSE) | ||
157 | fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; | ||
158 | |||
183 | /* non-unix readdir doesn't provide nlink */ | 159 | /* non-unix readdir doesn't provide nlink */ |
184 | fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; | 160 | fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; |
185 | 161 | ||
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 384cffe42850..5f5ba0dc2ee1 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c | |||
@@ -534,10 +534,12 @@ cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, | |||
534 | static int | 534 | static int |
535 | cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, | 535 | cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, |
536 | struct cifs_sb_info *cifs_sb, const char *full_path, | 536 | struct cifs_sb_info *cifs_sb, const char *full_path, |
537 | FILE_ALL_INFO *data, bool *adjustTZ) | 537 | FILE_ALL_INFO *data, bool *adjustTZ, bool *symlink) |
538 | { | 538 | { |
539 | int rc; | 539 | int rc; |
540 | 540 | ||
541 | *symlink = false; | ||
542 | |||
541 | /* could do find first instead but this returns more info */ | 543 | /* could do find first instead but this returns more info */ |
542 | rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */, | 544 | rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */, |
543 | cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & | 545 | 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, | |||
554 | CIFS_MOUNT_MAP_SPECIAL_CHR); | 556 | CIFS_MOUNT_MAP_SPECIAL_CHR); |
555 | *adjustTZ = true; | 557 | *adjustTZ = true; |
556 | } | 558 | } |
559 | |||
560 | if (!rc && (le32_to_cpu(data->Attributes) & ATTR_REPARSE)) { | ||
561 | int tmprc; | ||
562 | int oplock = 0; | ||
563 | __u16 netfid; | ||
564 | |||
565 | /* Need to check if this is a symbolic link or not */ | ||
566 | tmprc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, | ||
567 | FILE_READ_ATTRIBUTES, 0, &netfid, &oplock, | ||
568 | NULL, cifs_sb->local_nls, | ||
569 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
570 | if (tmprc == -EOPNOTSUPP) | ||
571 | *symlink = true; | ||
572 | else | ||
573 | CIFSSMBClose(xid, tcon, netfid); | ||
574 | } | ||
575 | |||
557 | return rc; | 576 | return rc; |
558 | } | 577 | } |
559 | 578 | ||
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 78ff88c467b9..84c012a6aba0 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c | |||
@@ -123,12 +123,13 @@ move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src) | |||
123 | int | 123 | int |
124 | smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, | 124 | smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, |
125 | struct cifs_sb_info *cifs_sb, const char *full_path, | 125 | struct cifs_sb_info *cifs_sb, const char *full_path, |
126 | FILE_ALL_INFO *data, bool *adjust_tz) | 126 | FILE_ALL_INFO *data, bool *adjust_tz, bool *symlink) |
127 | { | 127 | { |
128 | int rc; | 128 | int rc; |
129 | struct smb2_file_all_info *smb2_data; | 129 | struct smb2_file_all_info *smb2_data; |
130 | 130 | ||
131 | *adjust_tz = false; | 131 | *adjust_tz = false; |
132 | *symlink = false; | ||
132 | 133 | ||
133 | smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2, | 134 | smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2, |
134 | GFP_KERNEL); | 135 | GFP_KERNEL); |
@@ -136,9 +137,16 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, | |||
136 | return -ENOMEM; | 137 | return -ENOMEM; |
137 | 138 | ||
138 | rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, | 139 | rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, |
139 | FILE_READ_ATTRIBUTES, FILE_OPEN, | 140 | FILE_READ_ATTRIBUTES, FILE_OPEN, 0, |
140 | OPEN_REPARSE_POINT, smb2_data, | 141 | smb2_data, SMB2_OP_QUERY_INFO); |
141 | SMB2_OP_QUERY_INFO); | 142 | if (rc == -EOPNOTSUPP) { |
143 | *symlink = true; | ||
144 | /* Failed on a symbolic link - query a reparse point info */ | ||
145 | rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, | ||
146 | FILE_READ_ATTRIBUTES, FILE_OPEN, | ||
147 | OPEN_REPARSE_POINT, smb2_data, | ||
148 | SMB2_OP_QUERY_INFO); | ||
149 | } | ||
142 | if (rc) | 150 | if (rc) |
143 | goto out; | 151 | goto out; |
144 | 152 | ||
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 313813e4c19b..b4eea105b08c 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h | |||
@@ -61,7 +61,7 @@ extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst, | |||
61 | extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, | 61 | extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, |
62 | struct cifs_sb_info *cifs_sb, | 62 | struct cifs_sb_info *cifs_sb, |
63 | const char *full_path, FILE_ALL_INFO *data, | 63 | const char *full_path, FILE_ALL_INFO *data, |
64 | bool *adjust_tz); | 64 | bool *adjust_tz, bool *symlink); |
65 | extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, | 65 | extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, |
66 | const char *full_path, __u64 size, | 66 | const char *full_path, __u64 size, |
67 | struct cifs_sb_info *cifs_sb, bool set_alloc); | 67 | struct cifs_sb_info *cifs_sb, bool set_alloc); |