Skip to content

Commit d26c2dd

Browse files
committed
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 <[email protected]> Reviewed-by: Aurelien Aptel <[email protected]> Acked-by: Paulo Alcantara (SUSE) <[email protected]>
1 parent 343a1b7 commit d26c2dd

File tree

5 files changed

+87
-0
lines changed

5 files changed

+87
-0
lines changed

fs/cifs/cifs_ioctl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,16 @@ struct smb3_key_debug_info {
6565
__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
6666
} __packed;
6767

68+
struct smb3_notify {
69+
__u32 completion_filter;
70+
bool watch_tree;
71+
} __packed;
72+
6873
#define CIFS_IOCTL_MAGIC 0xCF
6974
#define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int)
7075
#define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4)
7176
#define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info)
7277
#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array)
7378
#define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info)
7479
#define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info)
80+
#define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify)

fs/cifs/cifsglob.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,8 @@ struct smb_version_operations {
431431
struct cifsFileInfo *src_file);
432432
int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon,
433433
struct cifsFileInfo *src_file, void __user *);
434+
int (*notify)(const unsigned int xid, struct file *pfile,
435+
void __user *pbuf);
434436
int (*query_mf_symlink)(unsigned int, struct cifs_tcon *,
435437
struct cifs_sb_info *, const unsigned char *,
436438
char *, unsigned int *);

fs/cifs/ioctl.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
169169
unsigned int xid;
170170
struct cifsFileInfo *pSMBFile = filep->private_data;
171171
struct cifs_tcon *tcon;
172+
struct cifs_sb_info *cifs_sb;
172173
__u64 ExtAttrBits = 0;
173174
__u64 caps;
174175

@@ -299,6 +300,21 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
299300
else
300301
rc = 0;
301302
break;
303+
case CIFS_IOC_NOTIFY:
304+
if (!S_ISDIR(inode->i_mode)) {
305+
/* Notify can only be done on directories */
306+
rc = -EOPNOTSUPP;
307+
break;
308+
}
309+
cifs_sb = CIFS_SB(inode->i_sb);
310+
tcon = tlink_tcon(cifs_sb_tlink(cifs_sb));
311+
if (tcon && tcon->ses->server->ops->notify) {
312+
rc = tcon->ses->server->ops->notify(xid,
313+
filep, (void __user *)arg);
314+
cifs_dbg(FYI, "ioctl notify rc %d\n", rc);
315+
} else
316+
rc = -EOPNOTSUPP;
317+
break;
302318
default:
303319
cifs_dbg(FYI, "unsupported ioctl\n");
304320
break;

fs/cifs/smb2ops.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2045,6 +2045,66 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
20452045
return rc;
20462046
}
20472047

2048+
2049+
2050+
static int
2051+
smb3_notify(const unsigned int xid, struct file *pfile,
2052+
void __user *ioc_buf)
2053+
{
2054+
struct smb3_notify notify;
2055+
struct dentry *dentry = pfile->f_path.dentry;
2056+
struct inode *inode = file_inode(pfile);
2057+
struct cifs_sb_info *cifs_sb;
2058+
struct cifs_open_parms oparms;
2059+
struct cifs_fid fid;
2060+
struct cifs_tcon *tcon;
2061+
unsigned char *path = NULL;
2062+
__le16 *utf16_path = NULL;
2063+
u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
2064+
int rc = 0;
2065+
2066+
path = build_path_from_dentry(dentry);
2067+
if (path == NULL)
2068+
return -ENOMEM;
2069+
2070+
cifs_sb = CIFS_SB(inode->i_sb);
2071+
2072+
utf16_path = cifs_convert_path_to_utf16(path + 1, cifs_sb);
2073+
if (utf16_path == NULL) {
2074+
rc = -ENOMEM;
2075+
goto notify_exit;
2076+
}
2077+
2078+
if (copy_from_user(&notify, ioc_buf, sizeof(struct smb3_notify))) {
2079+
rc = -EFAULT;
2080+
goto notify_exit;
2081+
}
2082+
2083+
tcon = cifs_sb_master_tcon(cifs_sb);
2084+
oparms.tcon = tcon;
2085+
oparms.desired_access = FILE_READ_ATTRIBUTES;
2086+
oparms.disposition = FILE_OPEN;
2087+
oparms.create_options = cifs_create_options(cifs_sb, 0);
2088+
oparms.fid = &fid;
2089+
oparms.reconnect = false;
2090+
2091+
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
2092+
if (rc)
2093+
goto notify_exit;
2094+
2095+
rc = SMB2_change_notify(xid, tcon, fid.persistent_fid, fid.volatile_fid,
2096+
notify.watch_tree, notify.completion_filter);
2097+
2098+
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
2099+
2100+
cifs_dbg(FYI, "change notify for path %s rc %d\n", path, rc);
2101+
2102+
notify_exit:
2103+
kfree(path);
2104+
kfree(utf16_path);
2105+
return rc;
2106+
}
2107+
20482108
static int
20492109
smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
20502110
const char *path, struct cifs_sb_info *cifs_sb,
@@ -4841,6 +4901,7 @@ struct smb_version_operations smb30_operations = {
48414901
.dir_needs_close = smb2_dir_needs_close,
48424902
.fallocate = smb3_fallocate,
48434903
.enum_snapshots = smb3_enum_snapshots,
4904+
.notify = smb3_notify,
48444905
.init_transform_rq = smb3_init_transform_rq,
48454906
.is_transform_hdr = smb3_is_transform_hdr,
48464907
.receive_transform = smb3_receive_transform,
@@ -4951,6 +5012,7 @@ struct smb_version_operations smb311_operations = {
49515012
.dir_needs_close = smb2_dir_needs_close,
49525013
.fallocate = smb3_fallocate,
49535014
.enum_snapshots = smb3_enum_snapshots,
5015+
.notify = smb3_notify,
49545016
.init_transform_rq = smb3_init_transform_rq,
49555017
.is_transform_hdr = smb3_is_transform_hdr,
49565018
.receive_transform = smb3_receive_transform,

fs/cifs/smb2pdu.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3363,6 +3363,7 @@ SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst,
33633363

33643364
req->PersistentFileId = persistent_fid;
33653365
req->VolatileFileId = volatile_fid;
3366+
/* See note 354 of MS-SMB2, 64K max */
33663367
req->OutputBufferLength =
33673368
cpu_to_le32(SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE);
33683369
req->CompletionFilter = cpu_to_le32(completion_filter);

0 commit comments

Comments
 (0)