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>
Acked-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
This commit is contained in:
Steve French 2020-02-06 06:00:14 -06:00
parent 343a1b777a
commit d26c2ddd33
5 changed files with 87 additions and 0 deletions

View File

@ -65,6 +65,11 @@ struct smb3_key_debug_info {
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE]; __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
} __packed; } __packed;
struct smb3_notify {
__u32 completion_filter;
bool watch_tree;
} __packed;
#define CIFS_IOCTL_MAGIC 0xCF #define CIFS_IOCTL_MAGIC 0xCF
#define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int) #define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int)
#define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4) #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_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_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_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info)
#define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify)

View File

@ -431,6 +431,8 @@ struct smb_version_operations {
struct cifsFileInfo *src_file); struct cifsFileInfo *src_file);
int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon, int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon,
struct cifsFileInfo *src_file, void __user *); 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 *, int (*query_mf_symlink)(unsigned int, struct cifs_tcon *,
struct cifs_sb_info *, const unsigned char *, struct cifs_sb_info *, const unsigned char *,
char *, unsigned int *); char *, unsigned int *);

View File

@ -169,6 +169,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
unsigned int xid; unsigned int xid;
struct cifsFileInfo *pSMBFile = filep->private_data; struct cifsFileInfo *pSMBFile = filep->private_data;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct cifs_sb_info *cifs_sb;
__u64 ExtAttrBits = 0; __u64 ExtAttrBits = 0;
__u64 caps; __u64 caps;
@ -299,6 +300,21 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
else else
rc = 0; rc = 0;
break; 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: default:
cifs_dbg(FYI, "unsupported ioctl\n"); cifs_dbg(FYI, "unsupported ioctl\n");
break; break;

View File

@ -2045,6 +2045,66 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
return rc; 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 static int
smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
const char *path, struct cifs_sb_info *cifs_sb, 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, .dir_needs_close = smb2_dir_needs_close,
.fallocate = smb3_fallocate, .fallocate = smb3_fallocate,
.enum_snapshots = smb3_enum_snapshots, .enum_snapshots = smb3_enum_snapshots,
.notify = smb3_notify,
.init_transform_rq = smb3_init_transform_rq, .init_transform_rq = smb3_init_transform_rq,
.is_transform_hdr = smb3_is_transform_hdr, .is_transform_hdr = smb3_is_transform_hdr,
.receive_transform = smb3_receive_transform, .receive_transform = smb3_receive_transform,
@ -4951,6 +5012,7 @@ struct smb_version_operations smb311_operations = {
.dir_needs_close = smb2_dir_needs_close, .dir_needs_close = smb2_dir_needs_close,
.fallocate = smb3_fallocate, .fallocate = smb3_fallocate,
.enum_snapshots = smb3_enum_snapshots, .enum_snapshots = smb3_enum_snapshots,
.notify = smb3_notify,
.init_transform_rq = smb3_init_transform_rq, .init_transform_rq = smb3_init_transform_rq,
.is_transform_hdr = smb3_is_transform_hdr, .is_transform_hdr = smb3_is_transform_hdr,
.receive_transform = smb3_receive_transform, .receive_transform = smb3_receive_transform,

View File

@ -3363,6 +3363,7 @@ SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst,
req->PersistentFileId = persistent_fid; req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_fid; req->VolatileFileId = volatile_fid;
/* See note 354 of MS-SMB2, 64K max */
req->OutputBufferLength = req->OutputBufferLength =
cpu_to_le32(SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE); cpu_to_le32(SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE);
req->CompletionFilter = cpu_to_le32(completion_filter); req->CompletionFilter = cpu_to_le32(completion_filter);