diff mbox series

[CIFS] Add SMB3 Change Notify

Message ID CAH2r5mtQRVX3_-_sVjvigRSv2LpSoUBQo7YeY5v0nXm7BGaDig@mail.gmail.com
State New
Headers show
Series [CIFS] Add SMB3 Change Notify | expand

Commit Message

Steve French Feb. 6, 2020, 12:29 p.m. UTC
A commonly used SMB3 feature is change notification, allowing an
app to be notified about changes to a directory. The SMB3
Notify request blocks until the server detects a change to that
directory or its contents that matches the completion flags
that were passed in and the "watch_tree" flag (which indicates
whether subdirectories under this directory should be also
included).  See MS-SMB2 2.2.35 for additional detail.

To use this simply pass in the following structure to ioctl:

 struct __attribute__((__packed__)) smb3_notify {
        uint32_t completion_filter;
        bool    watch_tree;
 } __packed;

 using CIFS_IOC_NOTIFY  0x4005cf09
 or equivalently _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify)

SMB3 change notification is supported by all major servers.
The ioctl will block until the server detects a change to that
directory or its subdirectories (if watch_tree is set).

Comments

L Walsh Feb. 10, 2020, 11:31 a.m. UTC | #1
On 2020/02/06 04:29, Steve French wrote:
> A commonly used SMB3 feature is change notification, allowing an
> app to be notified about changes to a directory. The SMB3
> Notify request blocks until the server detects a change to that
> directory or its contents that matches the completion flags
> that were passed in and the "watch_tree" flag (which indicates
> whether subdirectories under this directory should be also
> included).  See MS-SMB2 2.2.35 for additional detail.
>   
----
    How does the SMB3 feature "change notification" differ from
the SMB2 feature described in MS-SMB2 2.2.35?

    Isn't it more typical to describe features by the spec version
that they were first publish under -- especially since the doc
describing the feature is under the SMB2 documents?

    By calling it a SMB3 feature, does that mean you are removing
it from SMB2?
Thanks!
Steve French Feb. 10, 2020, 2:30 p.m. UTC | #2
On Mon, Feb 10, 2020 at 5:31 AM L Walsh <cifs@tlinx.org> wrote:
>
> On 2020/02/06 04:29, Steve French wrote:
> > A commonly used SMB3 feature is change notification, allowing an
> > app to be notified about changes to a directory. The SMB3
> > Notify request blocks until the server detects a change to that
> > directory or its contents that matches the completion flags
> > that were passed in and the "watch_tree" flag (which indicates
> > whether subdirectories under this directory should be also
> > included).  See MS-SMB2 2.2.35 for additional detail.
> >
> ----
>     How does the SMB3 feature "change notification" differ from
> the SMB2 feature described in MS-SMB2 2.2.35?
>
>     Isn't it more typical to describe features by the spec version
> that they were first publish under -- especially since the doc
> describing the feature is under the SMB2 documents?
>
>     By calling it a SMB3 feature, does that mean you are removing
> it from SMB2?

That is a good question.  I should have made more clear that although
many servers support Change Notify prior to SMB3 dialect, we chose
to implement it in SMB3 (late 2012 and later dialect) to minimize testing
risks and since we want to encourage users to use SMB3 or later (or
at least SMB2.1 or later since security is significantly better for later
dialects than for SMB1 and even SMB2)

Change Notify is available in all dialects (SMB2, SMB2.1, SMB3, SMB3.1.1)
for many servers but for the client we just implemented it for SMB3 and later.
If you have a server that you want to support that requires
SMB2 or SMB2.1 mounts, I wouldn't mind a patch to add notify support
for those older dialects but I would like to encourage use of SMB3 or later (or
at least SMB2.1 or later) where possible.

Steve
L Walsh Feb. 12, 2020, 11:32 p.m. UTC | #3
On 2020/02/10 06:30, Steve French wrote:
>
>>     By calling it a SMB3 feature, does that mean you are removing
>> it from SMB2?
>>     
>
> That is a good question.  I should have made more clear that although
> many servers support Change Notify prior to SMB3 dialect, we chose
> to implement it in SMB3 (late 2012 and later dialect) to minimize testing
> risks and since we want to encourage users to use SMB3 or later (or
> at least SMB2.1 or later since security is significantly better for later
> dialects than for SMB1 and even SMB2)
>   
----
    SMB2.1 would be fine for my purposes, I find it a bit odd though that
my linux server running these changes won't be as capable of detecting
directory changes as an outdated Win7 machine. 

    There are many below-SMB3 speaking devices out in the world right now. 
Probably many below 2.1. 

    You say you want to "encourage users to use SMB3 or later (or at least
SMB2.1)", how does adding SMB3-only support allow users to use SMB2.1?
Say your encouragement of users is taken to heart, and they want to use 
SMB3.
How would those users upgrade the dialect of SMB used in their
machine or device?  I don't know of any easy way to upgrade existing 
devices -
even existing OS's, if a user ran Win7, how would they upgrade the CIFS
drivers to 3.0?

    If it is not possible to upgrade existing devices, then wouldn't that
encouragement boil down to junking the device and buying a new one?
> Change Notify is available in all dialects (SMB2, SMB2.1, SMB3, SMB3.1.1)
> for many servers but for the client we just implemented it for SMB3 and later.
>   
    Doesn't that mean that the linux client won't be able to access 
existing
NAS servers or Win-Client machine running anything other than Win10?  Does
the current version of samba provide full SMB3 support?  If not, doesn't 
that
imply that the client for CIFS won't be able to access or use these features
from another linux server?
> If you have a server that you want to support that requires
> SMB2 or SMB2.1 mounts, I wouldn't mind a patch to add notify support
> for those older dialects but I would like to encourage use of SMB3 or later (or
> at least SMB2.1 or later) where possible.
>   
    Again, how does implementing SMB3-only, only support SMB2.1 or later?

    If you feel it would be trivial to add such a patch, wouldn't you be in
the position of, probably, having the most knowledge about the subject 
and be
likely to do the best job without breaking anything else?  Certainly doesn't
mean someone else couldn't but seems riskier than offering a Linux 
client that
would be able to access the widest range of existing devices and 
computers from
the start.

Thanks!
Linda
Steve French Feb. 13, 2020, 4:43 a.m. UTC | #4
I don't object to adding the feature to 2.1, and if you have SMB2.1
devices to try even better (I can add your tested-by ...) but 99% of
my testing these days is with SMB3 or later target servers (Samba,
Azure, Windows 10, Windows 2016 or later, the cifsd kernel server
etc.).  We do some testing with the buildbot with SMB2.1 dialect but
it is a little different forcing the dialect to 2.1 on the mount (to a
server which would otherwise support later dialects) vs. actually
running to an older device (Samba server e.g. has supported SMB3 for a
very, very long time - at least seven years so we have to go back
pretty far).

If you have the ability to try the attached patch which enables it for
SMB 2.1 dialect let me know.  (I have also pushed it to cifs-2.6.git
for-next to allow it to be tested)


On Wed, Feb 12, 2020 at 5:33 PM L Walsh <cifs@tlinx.org> wrote:
>
> On 2020/02/10 06:30, Steve French wrote:
> >
> >>     By calling it a SMB3 feature, does that mean you are removing
> >> it from SMB2?
> >>
> >
> > That is a good question.  I should have made more clear that although
> > many servers support Change Notify prior to SMB3 dialect, we chose
> > to implement it in SMB3 (late 2012 and later dialect) to minimize testing
> > risks and since we want to encourage users to use SMB3 or later (or
> > at least SMB2.1 or later since security is significantly better for later
> > dialects than for SMB1 and even SMB2)
> >
> ----
>     SMB2.1 would be fine for my purposes, I find it a bit odd though that
> my linux server running these changes won't be as capable of detecting
> directory changes as an outdated Win7 machine.
>
>     There are many below-SMB3 speaking devices out in the world right now.
> Probably many below 2.1.
>
>     You say you want to "encourage users to use SMB3 or later (or at least
> SMB2.1)", how does adding SMB3-only support allow users to use SMB2.1?
> Say your encouragement of users is taken to heart, and they want to use
> SMB3.
> How would those users upgrade the dialect of SMB used in their
> machine or device?  I don't know of any easy way to upgrade existing
> devices -
> even existing OS's, if a user ran Win7, how would they upgrade the CIFS
> drivers to 3.0?
>
>     If it is not possible to upgrade existing devices, then wouldn't that
> encouragement boil down to junking the device and buying a new one?
> > Change Notify is available in all dialects (SMB2, SMB2.1, SMB3, SMB3.1.1)
> > for many servers but for the client we just implemented it for SMB3 and later.
> >
>     Doesn't that mean that the linux client won't be able to access
> existing
> NAS servers or Win-Client machine running anything other than Win10?  Does
> the current version of samba provide full SMB3 support?  If not, doesn't
> that
> imply that the client for CIFS won't be able to access or use these features
> from another linux server?
> > If you have a server that you want to support that requires
> > SMB2 or SMB2.1 mounts, I wouldn't mind a patch to add notify support
> > for those older dialects but I would like to encourage use of SMB3 or later (or
> > at least SMB2.1 or later) where possible.
> >
>     Again, how does implementing SMB3-only, only support SMB2.1 or later?
>
>     If you feel it would be trivial to add such a patch, wouldn't you be in
> the position of, probably, having the most knowledge about the subject
> and be
> likely to do the best job without breaking anything else?  Certainly doesn't
> mean someone else couldn't but seems riskier than offering a Linux
> client that
> would be able to access the widest range of existing devices and
> computers from
> the start.
>
> Thanks!
> Linda
>
>
>
>
>
>
>


--
Thanks,

Steve
diff mbox series

Patch

From ccd073542ccc436c05afc6feb3c2b42ca2318c68 Mon Sep 17 00:00:00 2001
From: Steve French <stfrench@microsoft.com>
Date: Thu, 6 Feb 2020 06:00:14 -0600
Subject: [PATCH] cifs: add SMB3 change notification support

A commonly used SMB3 feature is change notification, allowing an
app to be notified about changes to a directory. The SMB3
Notify request blocks until the server detects a change to that
directory or its contents that matches the completion flags
that were passed in and the "watch_tree" flag (which indicates
whether subdirectories under this directory should be also
included).  See MS-SMB2 2.2.35 for additional detail.

To use this simply pass in the following structure to ioctl:

 struct __attribute__((__packed__)) smb3_notify {
        uint32_t completion_filter;
        bool    watch_tree;
 } __packed;

 using CIFS_IOC_NOTIFY  0x4005cf09
 or equivalently _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify)

SMB3 change notification is supported by all major servers.
The ioctl will block until the server detects a change to that
directory or its subdirectories (if watch_tree is set).

Signed-off-by: Steve French <stfrench@microsoft.com>
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
---
 fs/cifs/cifs_ioctl.h |  6 +++++
 fs/cifs/cifsglob.h   |  2 ++
 fs/cifs/ioctl.c      | 16 ++++++++++++
 fs/cifs/smb2ops.c    | 62 ++++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/smb2pdu.c    |  1 +
 5 files changed, 87 insertions(+)

diff --git a/fs/cifs/cifs_ioctl.h b/fs/cifs/cifs_ioctl.h
index 0f0dc1c1fe41..153d5c842a9b 100644
--- a/fs/cifs/cifs_ioctl.h
+++ b/fs/cifs/cifs_ioctl.h
@@ -65,6 +65,11 @@  struct smb3_key_debug_info {
 	__u8	smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
 } __packed;
 
+struct smb3_notify {
+	__u32	completion_filter;
+	bool	watch_tree;
+} __packed;
+
 #define CIFS_IOCTL_MAGIC	0xCF
 #define CIFS_IOC_COPYCHUNK_FILE	_IOW(CIFS_IOCTL_MAGIC, 3, int)
 #define CIFS_IOC_SET_INTEGRITY  _IO(CIFS_IOCTL_MAGIC, 4)
@@ -72,3 +77,4 @@  struct smb3_key_debug_info {
 #define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array)
 #define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info)
 #define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info)
+#define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify)
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 1205041fd966..de82cfa44b1a 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -431,6 +431,8 @@  struct smb_version_operations {
 			     struct cifsFileInfo *src_file);
 	int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon,
 			     struct cifsFileInfo *src_file, void __user *);
+	int (*notify)(const unsigned int xid, struct file *pfile,
+			     void __user *pbuf);
 	int (*query_mf_symlink)(unsigned int, struct cifs_tcon *,
 				struct cifs_sb_info *, const unsigned char *,
 				char *, unsigned int *);
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index e4c935026d5e..4a73e63c4d43 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -169,6 +169,7 @@  long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
 	unsigned int xid;
 	struct cifsFileInfo *pSMBFile = filep->private_data;
 	struct cifs_tcon *tcon;
+	struct cifs_sb_info *cifs_sb;
 	__u64	ExtAttrBits = 0;
 	__u64   caps;
 
@@ -299,6 +300,21 @@  long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
 			else
 				rc = 0;
 			break;
+		case CIFS_IOC_NOTIFY:
+			if (!S_ISDIR(inode->i_mode)) {
+				/* Notify can only be done on directories */
+				rc = -EOPNOTSUPP;
+				break;
+			}
+			cifs_sb = CIFS_SB(inode->i_sb);
+			tcon = tlink_tcon(cifs_sb_tlink(cifs_sb));
+			if (tcon && tcon->ses->server->ops->notify) {
+				rc = tcon->ses->server->ops->notify(xid,
+						filep, (void __user *)arg);
+				cifs_dbg(FYI, "ioctl notify rc %d\n", rc);
+			} else
+				rc = -EOPNOTSUPP;
+			break;
 		default:
 			cifs_dbg(FYI, "unsupported ioctl\n");
 			break;
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index ac6628e28b12..5928f3467c13 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -2045,6 +2045,66 @@  smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+
+
+static int
+smb3_notify(const unsigned int xid, struct file *pfile,
+	    void __user *ioc_buf)
+{
+	struct smb3_notify notify;
+	struct dentry *dentry = pfile->f_path.dentry;
+	struct inode *inode = file_inode(pfile);
+	struct cifs_sb_info *cifs_sb;
+	struct cifs_open_parms oparms;
+	struct cifs_fid fid;
+	struct cifs_tcon *tcon;
+	unsigned char *path = NULL;
+	__le16 *utf16_path = NULL;
+	u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+	int rc = 0;
+
+	path = build_path_from_dentry(dentry);
+	if (path == NULL)
+		return -ENOMEM;
+
+	cifs_sb = CIFS_SB(inode->i_sb);
+
+	utf16_path = cifs_convert_path_to_utf16(path + 1, cifs_sb);
+	if (utf16_path == NULL) {
+		rc = -ENOMEM;
+		goto notify_exit;
+	}
+
+	if (copy_from_user(&notify, ioc_buf, sizeof(struct smb3_notify))) {
+		rc = -EFAULT;
+		goto notify_exit;
+	}
+
+	tcon = cifs_sb_master_tcon(cifs_sb);
+	oparms.tcon = tcon;
+	oparms.desired_access = FILE_READ_ATTRIBUTES;
+	oparms.disposition = FILE_OPEN;
+	oparms.create_options = cifs_create_options(cifs_sb, 0);
+	oparms.fid = &fid;
+	oparms.reconnect = false;
+
+	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
+	if (rc)
+		goto notify_exit;
+
+	rc = SMB2_change_notify(xid, tcon, fid.persistent_fid, fid.volatile_fid,
+				notify.watch_tree, notify.completion_filter);
+
+	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+
+	cifs_dbg(FYI, "change notify for path %s rc %d\n", path, rc);
+
+notify_exit:
+	kfree(path);
+	kfree(utf16_path);
+	return rc;
+}
+
 static int
 smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
 		     const char *path, struct cifs_sb_info *cifs_sb,
@@ -4841,6 +4901,7 @@  struct smb_version_operations smb30_operations = {
 	.dir_needs_close = smb2_dir_needs_close,
 	.fallocate = smb3_fallocate,
 	.enum_snapshots = smb3_enum_snapshots,
+	.notify = smb3_notify,
 	.init_transform_rq = smb3_init_transform_rq,
 	.is_transform_hdr = smb3_is_transform_hdr,
 	.receive_transform = smb3_receive_transform,
@@ -4951,6 +5012,7 @@  struct smb_version_operations smb311_operations = {
 	.dir_needs_close = smb2_dir_needs_close,
 	.fallocate = smb3_fallocate,
 	.enum_snapshots = smb3_enum_snapshots,
+	.notify = smb3_notify,
 	.init_transform_rq = smb3_init_transform_rq,
 	.is_transform_hdr = smb3_is_transform_hdr,
 	.receive_transform = smb3_receive_transform,
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 1a732ff71de4..47cce0bd1afe 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -3363,6 +3363,7 @@  SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst,
 
 	req->PersistentFileId = persistent_fid;
 	req->VolatileFileId = volatile_fid;
+	/* See note 354 of MS-SMB2, 64K max */
 	req->OutputBufferLength =
 		cpu_to_le32(SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE);
 	req->CompletionFilter = cpu_to_le32(completion_filter);
-- 
2.20.1