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:
parent
343a1b777a
commit
d26c2ddd33
@ -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)
|
||||||
|
@ -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 *);
|
||||||
|
@ -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;
|
||||||
|
@ -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(¬ify, 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,
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user