aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPavel Shilovsky <pshilovsky@samba.org>2013-08-14 11:25:21 -0400
committerSteve French <smfrench@gmail.com>2013-09-08 15:27:34 -0400
commitb42bf88828cde60772dc08201d0a4f1a0663d7bc (patch)
tree3b019e905c83aba6578b56ae5a49669f59db23cf
parent3ae35cde67c1ec50267bcc55d81f4953b5f637c2 (diff)
CIFS: Implement follow_link for SMB2
that allows to access files through symlink created on a server. Acked-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org> Signed-off-by: Steve French <smfrench@gmail.com>
-rw-r--r--fs/cifs/cifsglob.h3
-rw-r--r--fs/cifs/inode.c4
-rw-r--r--fs/cifs/link.c24
-rw-r--r--fs/cifs/readdir.c3
-rw-r--r--fs/cifs/smb2file.c2
-rw-r--r--fs/cifs/smb2inode.c9
-rw-r--r--fs/cifs/smb2misc.c4
-rw-r--r--fs/cifs/smb2ops.c60
-rw-r--r--fs/cifs/smb2pdu.c6
-rw-r--r--fs/cifs/smb2pdu.h14
-rw-r--r--fs/cifs/smb2proto.h3
11 files changed, 103 insertions, 29 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 52ca861ed35e..ec6c3fbb29eb 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -308,6 +308,9 @@ struct smb_version_operations {
308 int (*create_hardlink)(const unsigned int, struct cifs_tcon *, 308 int (*create_hardlink)(const unsigned int, struct cifs_tcon *,
309 const char *, const char *, 309 const char *, const char *,
310 struct cifs_sb_info *); 310 struct cifs_sb_info *);
311 /* query symlink target */
312 int (*query_symlink)(const unsigned int, struct cifs_tcon *,
313 const char *, char **, struct cifs_sb_info *);
311 /* open a file for non-posix mounts */ 314 /* open a file for non-posix mounts */
312 int (*open)(const unsigned int, struct cifs_open_parms *, 315 int (*open)(const unsigned int, struct cifs_open_parms *,
313 __u32 *, FILE_ALL_INFO *); 316 __u32 *, FILE_ALL_INFO *);
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 449b6cf09b09..ec0f3423cdac 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -549,6 +549,10 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
549 * when Unix extensions are disabled - fake it. 549 * when Unix extensions are disabled - fake it.
550 */ 550 */
551 fattr->cf_nlink = 2; 551 fattr->cf_nlink = 2;
552 } else if (fattr->cf_cifsattrs & ATTR_REPARSE) {
553 fattr->cf_mode = S_IFLNK;
554 fattr->cf_dtype = DT_LNK;
555 fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
552 } else { 556 } else {
553 fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; 557 fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
554 fattr->cf_dtype = DT_REG; 558 fattr->cf_dtype = DT_REG;
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index 562044f700e5..7e36ceba0c7a 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -509,6 +509,7 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
509 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 509 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
510 struct tcon_link *tlink = NULL; 510 struct tcon_link *tlink = NULL;
511 struct cifs_tcon *tcon; 511 struct cifs_tcon *tcon;
512 struct TCP_Server_Info *server;
512 513
513 xid = get_xid(); 514 xid = get_xid();
514 515
@@ -519,25 +520,7 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
519 goto out; 520 goto out;
520 } 521 }
521 tcon = tlink_tcon(tlink); 522 tcon = tlink_tcon(tlink);
522 523 server = tcon->ses->server;
523 /*
524 * For now, we just handle symlinks with unix extensions enabled.
525 * Eventually we should handle NTFS reparse points, and MacOS
526 * symlink support. For instance...
527 *
528 * rc = CIFSSMBQueryReparseLinkInfo(...)
529 *
530 * For now, just return -EACCES when the server doesn't support posix
531 * extensions. Note that we still allow querying symlinks when posix
532 * extensions are manually disabled. We could disable these as well
533 * but there doesn't seem to be any harm in allowing the client to
534 * read them.
535 */
536 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) &&
537 !cap_unix(tcon->ses)) {
538 rc = -EACCES;
539 goto out;
540 }
541 524
542 full_path = build_path_from_dentry(direntry); 525 full_path = build_path_from_dentry(direntry);
543 if (!full_path) 526 if (!full_path)
@@ -559,6 +542,9 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
559 if ((rc != 0) && cap_unix(tcon->ses)) 542 if ((rc != 0) && cap_unix(tcon->ses))
560 rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, 543 rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path,
561 cifs_sb->local_nls); 544 cifs_sb->local_nls);
545 else if (rc != 0 && server->ops->query_symlink)
546 rc = server->ops->query_symlink(xid, tcon, full_path,
547 &target_path, cifs_sb);
562 548
563 kfree(full_path); 549 kfree(full_path);
564out: 550out:
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 69d2c826a23b..42ef03be089f 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -172,6 +172,9 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
172 if (cifs_dfs_is_possible(cifs_sb) && 172 if (cifs_dfs_is_possible(cifs_sb) &&
173 (fattr->cf_cifsattrs & ATTR_REPARSE)) 173 (fattr->cf_cifsattrs & ATTR_REPARSE))
174 fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; 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;
175 } else { 178 } else {
176 fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; 179 fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
177 fattr->cf_dtype = DT_REG; 180 fattr->cf_dtype = DT_REG;
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
index 04a81a4142c3..020245d5c9a7 100644
--- a/fs/cifs/smb2file.c
+++ b/fs/cifs/smb2file.c
@@ -86,7 +86,7 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
86 if (oparms->tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) 86 if (oparms->tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
87 memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE); 87 memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE);
88 88
89 rc = SMB2_open(xid, oparms, smb2_path, smb2_oplock, smb2_data); 89 rc = SMB2_open(xid, oparms, smb2_path, smb2_oplock, smb2_data, NULL);
90 if (rc) 90 if (rc)
91 goto out; 91 goto out;
92 92
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index c6ec1633309a..78ff88c467b9 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -60,7 +60,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
60 oparms.fid = &fid; 60 oparms.fid = &fid;
61 oparms.reconnect = false; 61 oparms.reconnect = false;
62 62
63 rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL); 63 rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
64 if (rc) { 64 if (rc) {
65 kfree(utf16_path); 65 kfree(utf16_path);
66 return rc; 66 return rc;
@@ -136,7 +136,8 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
136 return -ENOMEM; 136 return -ENOMEM;
137 137
138 rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, 138 rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path,
139 FILE_READ_ATTRIBUTES, FILE_OPEN, 0, smb2_data, 139 FILE_READ_ATTRIBUTES, FILE_OPEN,
140 OPEN_REPARSE_POINT, smb2_data,
140 SMB2_OP_QUERY_INFO); 141 SMB2_OP_QUERY_INFO);
141 if (rc) 142 if (rc)
142 goto out; 143 goto out;
@@ -191,8 +192,8 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
191 struct cifs_sb_info *cifs_sb) 192 struct cifs_sb_info *cifs_sb)
192{ 193{
193 return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, 194 return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
194 CREATE_DELETE_ON_CLOSE, NULL, 195 CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
195 SMB2_OP_DELETE); 196 NULL, SMB2_OP_DELETE);
196} 197}
197 198
198static int 199static int
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index b0c43345cd98..6103359fb598 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -171,6 +171,10 @@ smb2_check_message(char *buf, unsigned int length)
171 if (4 + len != clc_len) { 171 if (4 + len != clc_len) {
172 cifs_dbg(FYI, "Calculated size %u length %u mismatch mid %llu\n", 172 cifs_dbg(FYI, "Calculated size %u length %u mismatch mid %llu\n",
173 clc_len, 4 + len, mid); 173 clc_len, 4 + len, mid);
174 /* create failed on symlink */
175 if (command == SMB2_CREATE_HE &&
176 hdr->Status == STATUS_STOPPED_ON_SYMLINK)
177 return 0;
174 /* Windows 7 server returns 24 bytes more */ 178 /* Windows 7 server returns 24 bytes more */
175 if (clc_len + 20 == len && command == SMB2_OPLOCK_BREAK_HE) 179 if (clc_len + 20 == len && command == SMB2_OPLOCK_BREAK_HE)
176 return 0; 180 return 0;
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index f259e6cc8357..91b9e5422e9a 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -24,6 +24,7 @@
24#include "smb2proto.h" 24#include "smb2proto.h"
25#include "cifsproto.h" 25#include "cifsproto.h"
26#include "cifs_debug.h" 26#include "cifs_debug.h"
27#include "cifs_unicode.h"
27#include "smb2status.h" 28#include "smb2status.h"
28#include "smb2glob.h" 29#include "smb2glob.h"
29 30
@@ -229,7 +230,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
229 oparms.fid = &fid; 230 oparms.fid = &fid;
230 oparms.reconnect = false; 231 oparms.reconnect = false;
231 232
232 rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL); 233 rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
233 if (rc) { 234 if (rc) {
234 kfree(utf16_path); 235 kfree(utf16_path);
235 return rc; 236 return rc;
@@ -463,7 +464,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
463 oparms.fid = fid; 464 oparms.fid = fid;
464 oparms.reconnect = false; 465 oparms.reconnect = false;
465 466
466 rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL); 467 rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL);
467 kfree(utf16_path); 468 kfree(utf16_path);
468 if (rc) { 469 if (rc) {
469 cifs_dbg(VFS, "open dir failed\n"); 470 cifs_dbg(VFS, "open dir failed\n");
@@ -550,7 +551,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
550 oparms.fid = &fid; 551 oparms.fid = &fid;
551 oparms.reconnect = false; 552 oparms.reconnect = false;
552 553
553 rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL); 554 rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL);
554 if (rc) 555 if (rc)
555 return rc; 556 return rc;
556 buf->f_type = SMB2_MAGIC_NUMBER; 557 buf->f_type = SMB2_MAGIC_NUMBER;
@@ -596,6 +597,57 @@ smb2_new_lease_key(struct cifs_fid *fid)
596 get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE); 597 get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE);
597} 598}
598 599
600static int
601smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
602 const char *full_path, char **target_path,
603 struct cifs_sb_info *cifs_sb)
604{
605 int rc;
606 __le16 *utf16_path;
607 __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
608 struct cifs_open_parms oparms;
609 struct cifs_fid fid;
610 struct smb2_err_rsp *err_buf = NULL;
611 struct smb2_symlink_err_rsp *symlink;
612 unsigned int sub_len, sub_offset;
613
614 cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
615
616 utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
617 if (!utf16_path)
618 return -ENOMEM;
619
620 oparms.tcon = tcon;
621 oparms.desired_access = FILE_READ_ATTRIBUTES;
622 oparms.disposition = FILE_OPEN;
623 oparms.create_options = 0;
624 oparms.fid = &fid;
625 oparms.reconnect = false;
626
627 rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, &err_buf);
628
629 if (!rc || !err_buf) {
630 kfree(utf16_path);
631 return -ENOENT;
632 }
633 /* open must fail on symlink - reset rc */
634 rc = 0;
635 symlink = (struct smb2_symlink_err_rsp *)err_buf->ErrorData;
636 sub_len = le16_to_cpu(symlink->SubstituteNameLength);
637 sub_offset = le16_to_cpu(symlink->SubstituteNameOffset);
638 *target_path = cifs_strndup_from_utf16(
639 (char *)symlink->PathBuffer + sub_offset,
640 sub_len, true, cifs_sb->local_nls);
641 if (!(*target_path)) {
642 kfree(utf16_path);
643 return -ENOMEM;
644 }
645 convert_delimiter(*target_path, '/');
646 cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
647 kfree(utf16_path);
648 return rc;
649}
650
599struct smb_version_operations smb21_operations = { 651struct smb_version_operations smb21_operations = {
600 .compare_fids = smb2_compare_fids, 652 .compare_fids = smb2_compare_fids,
601 .setup_request = smb2_setup_request, 653 .setup_request = smb2_setup_request,
@@ -638,6 +690,7 @@ struct smb_version_operations smb21_operations = {
638 .unlink = smb2_unlink, 690 .unlink = smb2_unlink,
639 .rename = smb2_rename_path, 691 .rename = smb2_rename_path,
640 .create_hardlink = smb2_create_hardlink, 692 .create_hardlink = smb2_create_hardlink,
693 .query_symlink = smb2_query_symlink,
641 .open = smb2_open_file, 694 .open = smb2_open_file,
642 .set_fid = smb2_set_fid, 695 .set_fid = smb2_set_fid,
643 .close = smb2_close_file, 696 .close = smb2_close_file,
@@ -706,6 +759,7 @@ struct smb_version_operations smb30_operations = {
706 .unlink = smb2_unlink, 759 .unlink = smb2_unlink,
707 .rename = smb2_rename_path, 760 .rename = smb2_rename_path,
708 .create_hardlink = smb2_create_hardlink, 761 .create_hardlink = smb2_create_hardlink,
762 .query_symlink = smb2_query_symlink,
709 .open = smb2_open_file, 763 .open = smb2_open_file,
710 .set_fid = smb2_set_fid, 764 .set_fid = smb2_set_fid,
711 .close = smb2_close_file, 765 .close = smb2_close_file,
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index abc9c2809b51..5a49861633a6 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -977,7 +977,8 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec,
977 977
978int 978int
979SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, 979SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
980 __u8 *oplock, struct smb2_file_all_info *buf) 980 __u8 *oplock, struct smb2_file_all_info *buf,
981 struct smb2_err_rsp **err_buf)
981{ 982{
982 struct smb2_create_req *req; 983 struct smb2_create_req *req;
983 struct smb2_create_rsp *rsp; 984 struct smb2_create_rsp *rsp;
@@ -1082,6 +1083,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
1082 1083
1083 if (rc != 0) { 1084 if (rc != 0) {
1084 cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); 1085 cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
1086 if (err_buf)
1087 *err_buf = kmemdup(rsp, get_rfc1002_length(rsp) + 4,
1088 GFP_KERNEL);
1085 goto creat_exit; 1089 goto creat_exit;
1086 } 1090 }
1087 1091
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 36b0d37ea69b..40baeae60b08 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -150,6 +150,20 @@ struct smb2_err_rsp {
150 __u8 ErrorData[1]; /* variable length */ 150 __u8 ErrorData[1]; /* variable length */
151} __packed; 151} __packed;
152 152
153struct smb2_symlink_err_rsp {
154 __le32 SymLinkLength;
155 __le32 SymLinkErrorTag;
156 __le32 ReparseTag;
157 __le16 ReparseDataLength;
158 __le16 UnparsedPathLength;
159 __le16 SubstituteNameOffset;
160 __le16 SubstituteNameLength;
161 __le16 PrintNameOffset;
162 __le16 PrintNameLength;
163 __le32 Flags;
164 __u8 PathBuffer[0];
165} __packed;
166
153#define SMB2_CLIENT_GUID_SIZE 16 167#define SMB2_CLIENT_GUID_SIZE 16
154 168
155extern __u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE]; 169extern __u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE];
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 1a5ecbed40ed..1db89fda1392 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -106,7 +106,8 @@ extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses,
106extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); 106extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);
107extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, 107extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
108 __le16 *path, __u8 *oplock, 108 __le16 *path, __u8 *oplock,
109 struct smb2_file_all_info *buf); 109 struct smb2_file_all_info *buf,
110 struct smb2_err_rsp **err_buf);
110extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, 111extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
111 u64 persistent_fid, u64 volatile_fid, u32 opcode, 112 u64 persistent_fid, u64 volatile_fid, u32 opcode,
112 bool is_fsctl, char *in_data, u32 indatalen, 113 bool is_fsctl, char *in_data, u32 indatalen,