Skip to content

Commit e3e9463

Browse files
author
Steve French
committed
smb3: improve SMB3 change notification support
Change notification is a commonly supported feature by most servers, but the current ioctl to request notification when a directory is changed does not return the information about what changed (even though it is returned by the server in the SMB3 change notify response), it simply returns when there is a change. This ioctl improves upon CIFS_IOC_NOTIFY by returning the notify information structure which includes the name of the file(s) that changed and why. See MS-SMB2 2.2.35 for details on the individual filter flags and the file_notify_information structure returned. To use this simply pass in the following (with enough space to fit at least one file_notify_information structure) struct __attribute__((__packed__)) smb3_notify { uint32_t completion_filter; bool watch_tree; uint32_t data_len; uint8_t data[]; } __packed; using CIFS_IOC_NOTIFY_INFO 0xc009cf0b or equivalently _IOWR(CIFS_IOCTL_MAGIC, 11, struct smb3_notify_info) The ioctl will block until the server detects a change to that directory or its subdirectories (if watch_tree is set). Acked-by: Paulo Alcantara (SUSE) <[email protected]> Acked-by: Ronnie Sahlberg <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 2bff065 commit e3e9463

File tree

6 files changed

+90
-13
lines changed

6 files changed

+90
-13
lines changed

fs/cifs/cifs_ioctl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,13 @@ struct smb3_notify {
9191
bool watch_tree;
9292
} __packed;
9393

94+
struct smb3_notify_info {
95+
__u32 completion_filter;
96+
bool watch_tree;
97+
__u32 data_len; /* size of notify data below */
98+
__u8 notify_data[];
99+
} __packed;
100+
94101
#define CIFS_IOCTL_MAGIC 0xCF
95102
#define CIFS_IOC_COPYCHUNK_FILE _IOW(CIFS_IOCTL_MAGIC, 3, int)
96103
#define CIFS_IOC_SET_INTEGRITY _IO(CIFS_IOCTL_MAGIC, 4)
@@ -100,6 +107,7 @@ struct smb3_notify {
100107
#define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info)
101108
#define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify)
102109
#define CIFS_DUMP_FULL_KEY _IOWR(CIFS_IOCTL_MAGIC, 10, struct smb3_full_key_debug_info)
110+
#define CIFS_IOC_NOTIFY_INFO _IOWR(CIFS_IOCTL_MAGIC, 11, struct smb3_notify_info)
103111
#define CIFS_IOC_SHUTDOWN _IOR ('X', 125, __u32)
104112

105113
/*

fs/cifs/cifsglob.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ struct smb_version_operations {
454454
int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon,
455455
struct cifsFileInfo *src_file, void __user *);
456456
int (*notify)(const unsigned int xid, struct file *pfile,
457-
void __user *pbuf);
457+
void __user *pbuf, bool return_changes);
458458
int (*query_mf_symlink)(unsigned int, struct cifs_tcon *,
459459
struct cifs_sb_info *, const unsigned char *,
460460
char *, unsigned int *);

fs/cifs/ioctl.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,12 +484,35 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
484484
tcon = tlink_tcon(tlink);
485485
if (tcon && tcon->ses->server->ops->notify) {
486486
rc = tcon->ses->server->ops->notify(xid,
487-
filep, (void __user *)arg);
487+
filep, (void __user *)arg,
488+
false /* no ret data */);
488489
cifs_dbg(FYI, "ioctl notify rc %d\n", rc);
489490
} else
490491
rc = -EOPNOTSUPP;
491492
cifs_put_tlink(tlink);
492493
break;
494+
case CIFS_IOC_NOTIFY_INFO:
495+
if (!S_ISDIR(inode->i_mode)) {
496+
/* Notify can only be done on directories */
497+
rc = -EOPNOTSUPP;
498+
break;
499+
}
500+
cifs_sb = CIFS_SB(inode->i_sb);
501+
tlink = cifs_sb_tlink(cifs_sb);
502+
if (IS_ERR(tlink)) {
503+
rc = PTR_ERR(tlink);
504+
break;
505+
}
506+
tcon = tlink_tcon(tlink);
507+
if (tcon && tcon->ses->server->ops->notify) {
508+
rc = tcon->ses->server->ops->notify(xid,
509+
filep, (void __user *)arg,
510+
true /* return details */);
511+
cifs_dbg(FYI, "ioctl notify info rc %d\n", rc);
512+
} else
513+
rc = -EOPNOTSUPP;
514+
cifs_put_tlink(tlink);
515+
break;
493516
case CIFS_IOC_SHUTDOWN:
494517
rc = cifs_shutdown(inode->i_sb, arg);
495518
break;

fs/cifs/smb2ops.c

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2018,20 +2018,23 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
20182018

20192019
static int
20202020
smb3_notify(const unsigned int xid, struct file *pfile,
2021-
void __user *ioc_buf)
2021+
void __user *ioc_buf, bool return_changes)
20222022
{
2023-
struct smb3_notify notify;
2023+
struct smb3_notify_info notify;
2024+
struct smb3_notify_info __user *pnotify_buf;
20242025
struct dentry *dentry = pfile->f_path.dentry;
20252026
struct inode *inode = file_inode(pfile);
20262027
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
20272028
struct cifs_open_parms oparms;
20282029
struct cifs_fid fid;
20292030
struct cifs_tcon *tcon;
20302031
const unsigned char *path;
2032+
char *returned_ioctl_info = NULL;
20312033
void *page = alloc_dentry_path();
20322034
__le16 *utf16_path = NULL;
20332035
u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
20342036
int rc = 0;
2037+
__u32 ret_len = 0;
20352038

20362039
path = build_path_from_dentry(dentry, page);
20372040
if (IS_ERR(path)) {
@@ -2045,9 +2048,17 @@ smb3_notify(const unsigned int xid, struct file *pfile,
20452048
goto notify_exit;
20462049
}
20472050

2048-
if (copy_from_user(&notify, ioc_buf, sizeof(struct smb3_notify))) {
2049-
rc = -EFAULT;
2050-
goto notify_exit;
2051+
if (return_changes) {
2052+
if (copy_from_user(&notify, ioc_buf, sizeof(struct smb3_notify_info))) {
2053+
rc = -EFAULT;
2054+
goto notify_exit;
2055+
}
2056+
} else {
2057+
if (copy_from_user(&notify, ioc_buf, sizeof(struct smb3_notify))) {
2058+
rc = -EFAULT;
2059+
goto notify_exit;
2060+
}
2061+
notify.data_len = 0;
20512062
}
20522063

20532064
tcon = cifs_sb_master_tcon(cifs_sb);
@@ -2064,12 +2075,22 @@ smb3_notify(const unsigned int xid, struct file *pfile,
20642075
goto notify_exit;
20652076

20662077
rc = SMB2_change_notify(xid, tcon, fid.persistent_fid, fid.volatile_fid,
2067-
notify.watch_tree, notify.completion_filter);
2078+
notify.watch_tree, notify.completion_filter,
2079+
notify.data_len, &returned_ioctl_info, &ret_len);
20682080

20692081
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
20702082

20712083
cifs_dbg(FYI, "change notify for path %s rc %d\n", path, rc);
2072-
2084+
if (return_changes && (ret_len > 0) && (notify.data_len > 0)) {
2085+
if (ret_len > notify.data_len)
2086+
ret_len = notify.data_len;
2087+
pnotify_buf = (struct smb3_notify_info __user *)ioc_buf;
2088+
if (copy_to_user(pnotify_buf->notify_data, returned_ioctl_info, ret_len))
2089+
rc = -EFAULT;
2090+
else if (copy_to_user(&pnotify_buf->data_len, &ret_len, sizeof(ret_len)))
2091+
rc = -EFAULT;
2092+
}
2093+
kfree(returned_ioctl_info);
20732094
notify_exit:
20742095
free_dentry_path(page);
20752096
kfree(utf16_path);

fs/cifs/smb2pdu.c

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3710,11 +3710,13 @@ SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst,
37103710
int
37113711
SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
37123712
u64 persistent_fid, u64 volatile_fid, bool watch_tree,
3713-
u32 completion_filter)
3713+
u32 completion_filter, u32 max_out_data_len, char **out_data,
3714+
u32 *plen /* returned data len */)
37143715
{
37153716
struct cifs_ses *ses = tcon->ses;
37163717
struct TCP_Server_Info *server = cifs_pick_channel(ses);
37173718
struct smb_rqst rqst;
3719+
struct smb2_change_notify_rsp *smb_rsp;
37183720
struct kvec iov[1];
37193721
struct kvec rsp_iov = {NULL, 0};
37203722
int resp_buftype = CIFS_NO_BUFFER;
@@ -3730,6 +3732,9 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
37303732

37313733
memset(&rqst, 0, sizeof(struct smb_rqst));
37323734
memset(&iov, 0, sizeof(iov));
3735+
if (plen)
3736+
*plen = 0;
3737+
37333738
rqst.rq_iov = iov;
37343739
rqst.rq_nvec = 1;
37353740

@@ -3748,9 +3753,28 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
37483753
cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE);
37493754
trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid,
37503755
(u8)watch_tree, completion_filter, rc);
3751-
} else
3756+
} else {
37523757
trace_smb3_notify_done(xid, persistent_fid, tcon->tid,
3753-
ses->Suid, (u8)watch_tree, completion_filter);
3758+
ses->Suid, (u8)watch_tree, completion_filter);
3759+
/* validate that notify information is plausible */
3760+
if ((rsp_iov.iov_base == NULL) ||
3761+
(rsp_iov.iov_len < sizeof(struct smb2_change_notify_rsp)))
3762+
goto cnotify_exit;
3763+
3764+
smb_rsp = (struct smb2_change_notify_rsp *)rsp_iov.iov_base;
3765+
3766+
smb2_validate_iov(le16_to_cpu(smb_rsp->OutputBufferOffset),
3767+
le32_to_cpu(smb_rsp->OutputBufferLength), &rsp_iov,
3768+
sizeof(struct file_notify_information));
3769+
3770+
*out_data = kmemdup((char *)smb_rsp + le16_to_cpu(smb_rsp->OutputBufferOffset),
3771+
le32_to_cpu(smb_rsp->OutputBufferLength), GFP_KERNEL);
3772+
if (*out_data == NULL) {
3773+
rc = -ENOMEM;
3774+
goto cnotify_exit;
3775+
} else
3776+
*plen = le32_to_cpu(smb_rsp->OutputBufferLength);
3777+
}
37543778

37553779
cnotify_exit:
37563780
if (rqst.rq_iov)

fs/cifs/smb2proto.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ extern int SMB2_ioctl_init(struct cifs_tcon *tcon,
144144
extern void SMB2_ioctl_free(struct smb_rqst *rqst);
145145
extern int SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
146146
u64 persistent_fid, u64 volatile_fid, bool watch_tree,
147-
u32 completion_filter);
147+
u32 completion_filter, u32 max_out_data_len,
148+
char **out_data, u32 *plen /* returned data len */);
148149

149150
extern int __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
150151
u64 persistent_fid, u64 volatile_fid,

0 commit comments

Comments
 (0)