aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/cifs/cifsglob.h2
-rw-r--r--fs/cifs/inode.c23
-rw-r--r--fs/cifs/readdir.c40
-rw-r--r--fs/cifs/smb1ops.c21
-rw-r--r--fs/cifs/smb2inode.c16
-rw-r--r--fs/cifs/smb2proto.h2
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 */
543static void 543static void
544cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, 544cifs_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 */
141static bool
142cifs_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
153static void 137static void
154cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) 138cifs_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,
534static int 534static int
535cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, 535cifs_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)
123int 123int
124smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, 124smb2_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,
61extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, 61extern 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);
65extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, 65extern 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);