Commit b08cd744 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '6.1-rc-smb3-client-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6

Pull more cifs updates from Steve French:

 - fix a regression in guest mounts to old servers

 - improvements to directory leasing (caching directory entries safely
   beyond the root directory)

 - symlink improvement (reducing roundtrips needed to process symlinks)

 - an lseek fix (to problem where some dir entries could be skipped)

 - improved ioctl for returning more detailed information on directory
   change notifications

 - clarify multichannel interface query warning

 - cleanup fix (for better aligning buffers using ALIGN and round_up)

 - a compounding fix

 - fix some uninitialized variable bugs found by Coverity and the kernel
   test robot

* tag '6.1-rc-smb3-client-fixes-part2' of git://git.samba.org/sfrench/cifs-2.6:
  smb3: improve SMB3 change notification support
  cifs: lease key is uninitialized in two additional functions when smb1
  cifs: lease key is uninitialized in smb1 paths
  smb3: must initialize two ACL struct fields to zero
  cifs: fix double-fault crash during ntlmssp
  cifs: fix static checker warning
  cifs: use ALIGN() and round_up() macros
  cifs: find and use the dentry for cached non-root directories also
  cifs: enable caching of directories for which a lease is held
  cifs: prevent copying past input buffer boundaries
  cifs: fix uninitialised var in smb2_compound_op()
  cifs: improve symlink handling for smb2+
  smb3: clarify multichannel warning
  cifs: fix regression in very old smb1 mounts
  cifs: fix skipping to incorrect offset in emit_cached_dirents
parents 80493877 e3e94634
This diff is collapsed.
...@@ -31,14 +31,17 @@ struct cached_dirents { ...@@ -31,14 +31,17 @@ struct cached_dirents {
}; };
struct cached_fid { struct cached_fid {
struct list_head entry;
struct cached_fids *cfids;
const char *path; const char *path;
bool is_valid:1; /* Do we have a useable root fid */
bool file_all_info_is_valid:1;
bool has_lease:1; bool has_lease:1;
bool is_open:1;
bool on_list:1;
bool file_all_info_is_valid:1;
unsigned long time; /* jiffies of when lease was taken */ unsigned long time; /* jiffies of when lease was taken */
struct kref refcount; struct kref refcount;
struct cifs_fid fid; struct cifs_fid fid;
struct mutex fid_mutex; spinlock_t fid_lock;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct dentry *dentry; struct dentry *dentry;
struct work_struct lease_break; struct work_struct lease_break;
...@@ -46,9 +49,14 @@ struct cached_fid { ...@@ -46,9 +49,14 @@ struct cached_fid {
struct cached_dirents dirents; struct cached_dirents dirents;
}; };
#define MAX_CACHED_FIDS 16
struct cached_fids { struct cached_fids {
struct mutex cfid_list_mutex; /* Must be held when:
struct cached_fid *cfid; * - accessing the cfids->entries list
*/
spinlock_t cfid_list_lock;
int num_entries;
struct list_head entries;
}; };
extern struct cached_fids *init_cached_dirs(void); extern struct cached_fids *init_cached_dirs(void);
...@@ -61,8 +69,6 @@ extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon, ...@@ -61,8 +69,6 @@ extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
struct dentry *dentry, struct dentry *dentry,
struct cached_fid **cfid); struct cached_fid **cfid);
extern void close_cached_dir(struct cached_fid *cfid); extern void close_cached_dir(struct cached_fid *cfid);
extern void close_cached_dir_lease(struct cached_fid *cfid);
extern void close_cached_dir_lease_locked(struct cached_fid *cfid);
extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb); extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon); extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon);
extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]); extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
......
...@@ -91,6 +91,13 @@ struct smb3_notify { ...@@ -91,6 +91,13 @@ struct smb3_notify {
bool watch_tree; bool watch_tree;
} __packed; } __packed;
struct smb3_notify_info {
__u32 completion_filter;
bool watch_tree;
__u32 data_len; /* size of notify data below */
__u8 notify_data[];
} __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)
...@@ -100,6 +107,7 @@ struct smb3_notify { ...@@ -100,6 +107,7 @@ struct smb3_notify {
#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) #define CIFS_IOC_NOTIFY _IOW(CIFS_IOCTL_MAGIC, 9, struct smb3_notify)
#define CIFS_DUMP_FULL_KEY _IOWR(CIFS_IOCTL_MAGIC, 10, struct smb3_full_key_debug_info) #define CIFS_DUMP_FULL_KEY _IOWR(CIFS_IOCTL_MAGIC, 10, struct smb3_full_key_debug_info)
#define CIFS_IOC_NOTIFY_INFO _IOWR(CIFS_IOCTL_MAGIC, 11, struct smb3_notify_info)
#define CIFS_IOC_SHUTDOWN _IOR ('X', 125, __u32) #define CIFS_IOC_SHUTDOWN _IOR ('X', 125, __u32)
/* /*
......
...@@ -396,6 +396,7 @@ cifs_alloc_inode(struct super_block *sb) ...@@ -396,6 +396,7 @@ cifs_alloc_inode(struct super_block *sb)
cifs_inode->epoch = 0; cifs_inode->epoch = 0;
spin_lock_init(&cifs_inode->open_file_lock); spin_lock_init(&cifs_inode->open_file_lock);
generate_random_uuid(cifs_inode->lease_key); generate_random_uuid(cifs_inode->lease_key);
cifs_inode->symlink_target = NULL;
/* /*
* Can not set i_flags here - they get immediately overwritten to zero * Can not set i_flags here - they get immediately overwritten to zero
...@@ -412,7 +413,11 @@ cifs_alloc_inode(struct super_block *sb) ...@@ -412,7 +413,11 @@ cifs_alloc_inode(struct super_block *sb)
static void static void
cifs_free_inode(struct inode *inode) cifs_free_inode(struct inode *inode)
{ {
kmem_cache_free(cifs_inode_cachep, CIFS_I(inode)); struct cifsInodeInfo *cinode = CIFS_I(inode);
if (S_ISLNK(inode->i_mode))
kfree(cinode->symlink_target);
kmem_cache_free(cifs_inode_cachep, cinode);
} }
static void static void
...@@ -1139,7 +1144,7 @@ const struct inode_operations cifs_file_inode_ops = { ...@@ -1139,7 +1144,7 @@ const struct inode_operations cifs_file_inode_ops = {
}; };
const struct inode_operations cifs_symlink_inode_ops = { const struct inode_operations cifs_symlink_inode_ops = {
.get_link = cifs_get_link, .get_link = simple_get_link,
.permission = cifs_permission, .permission = cifs_permission,
.listxattr = cifs_listxattr, .listxattr = cifs_listxattr,
}; };
......
...@@ -185,6 +185,19 @@ struct cifs_cred { ...@@ -185,6 +185,19 @@ struct cifs_cred {
struct cifs_ace *aces; struct cifs_ace *aces;
}; };
struct cifs_open_info_data {
char *symlink_target;
union {
struct smb2_file_all_info fi;
struct smb311_posix_qinfo posix_fi;
};
};
static inline void cifs_free_open_info(struct cifs_open_info_data *data)
{
kfree(data->symlink_target);
}
/* /*
***************************************************************** *****************************************************************
* Except the CIFS PDUs themselves all the * Except the CIFS PDUs themselves all the
...@@ -307,20 +320,20 @@ struct smb_version_operations { ...@@ -307,20 +320,20 @@ struct smb_version_operations {
int (*is_path_accessible)(const unsigned int, struct cifs_tcon *, int (*is_path_accessible)(const unsigned int, struct cifs_tcon *,
struct cifs_sb_info *, const char *); struct cifs_sb_info *, const char *);
/* query path data from the server */ /* query path data from the server */
int (*query_path_info)(const unsigned int, struct cifs_tcon *, int (*query_path_info)(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *, const char *, struct cifs_sb_info *cifs_sb, const char *full_path,
FILE_ALL_INFO *, bool *, bool *); struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse);
/* query file data from the server */ /* query file data from the server */
int (*query_file_info)(const unsigned int, struct cifs_tcon *, int (*query_file_info)(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_fid *, FILE_ALL_INFO *); struct cifsFileInfo *cfile, struct cifs_open_info_data *data);
/* query reparse tag from srv to determine which type of special file */ /* query reparse tag from srv to determine which type of special file */
int (*query_reparse_tag)(const unsigned int xid, struct cifs_tcon *tcon, int (*query_reparse_tag)(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *path, struct cifs_sb_info *cifs_sb, const char *path,
__u32 *reparse_tag); __u32 *reparse_tag);
/* get server index number */ /* get server index number */
int (*get_srv_inum)(const unsigned int, struct cifs_tcon *, int (*get_srv_inum)(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *, const char *, struct cifs_sb_info *cifs_sb, const char *full_path, u64 *uniqueid,
u64 *uniqueid, FILE_ALL_INFO *); struct cifs_open_info_data *data);
/* set size by path */ /* set size by path */
int (*set_path_size)(const unsigned int, struct cifs_tcon *, int (*set_path_size)(const unsigned int, struct cifs_tcon *,
const char *, __u64, struct cifs_sb_info *, bool); const char *, __u64, struct cifs_sb_info *, bool);
...@@ -369,8 +382,8 @@ struct smb_version_operations { ...@@ -369,8 +382,8 @@ struct smb_version_operations {
struct cifs_sb_info *, const char *, struct cifs_sb_info *, const char *,
char **, bool); char **, bool);
/* open a file for non-posix mounts */ /* open a file for non-posix mounts */
int (*open)(const unsigned int, struct cifs_open_parms *, int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
__u32 *, FILE_ALL_INFO *); void *buf);
/* set fid protocol-specific info */ /* set fid protocol-specific info */
void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32); void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32);
/* close a file */ /* close a file */
...@@ -441,7 +454,7 @@ struct smb_version_operations { ...@@ -441,7 +454,7 @@ struct smb_version_operations {
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, int (*notify)(const unsigned int xid, struct file *pfile,
void __user *pbuf); void __user *pbuf, bool return_changes);
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 *);
...@@ -1123,6 +1136,7 @@ struct cifs_fattr { ...@@ -1123,6 +1136,7 @@ struct cifs_fattr {
struct timespec64 cf_mtime; struct timespec64 cf_mtime;
struct timespec64 cf_ctime; struct timespec64 cf_ctime;
u32 cf_cifstag; u32 cf_cifstag;
char *cf_symlink_target;
}; };
/* /*
...@@ -1385,6 +1399,7 @@ struct cifsFileInfo { ...@@ -1385,6 +1399,7 @@ struct cifsFileInfo {
struct work_struct put; /* work for the final part of _put */ struct work_struct put; /* work for the final part of _put */
struct delayed_work deferred; struct delayed_work deferred;
bool deferred_close_scheduled; /* Flag to indicate close is scheduled */ bool deferred_close_scheduled; /* Flag to indicate close is scheduled */
char *symlink_target;
}; };
struct cifs_io_parms { struct cifs_io_parms {
...@@ -1543,6 +1558,7 @@ struct cifsInodeInfo { ...@@ -1543,6 +1558,7 @@ struct cifsInodeInfo {
struct list_head deferred_closes; /* list of deferred closes */ struct list_head deferred_closes; /* list of deferred closes */
spinlock_t deferred_lock; /* protection on deferred list */ spinlock_t deferred_lock; /* protection on deferred list */
bool lease_granted; /* Flag to indicate whether lease or oplock is granted. */ bool lease_granted; /* Flag to indicate whether lease or oplock is granted. */
char *symlink_target;
}; };
static inline struct cifsInodeInfo * static inline struct cifsInodeInfo *
...@@ -2111,4 +2127,14 @@ static inline size_t ntlmssp_workstation_name_size(const struct cifs_ses *ses) ...@@ -2111,4 +2127,14 @@ static inline size_t ntlmssp_workstation_name_size(const struct cifs_ses *ses)
return sizeof(ses->workstation_name); return sizeof(ses->workstation_name);
} }
static inline void move_cifs_info_to_smb2(struct smb2_file_all_info *dst, const FILE_ALL_INFO *src)
{
memcpy(dst, src, (size_t)((u8 *)&src->AccessFlags - (u8 *)src));
dst->AccessFlags = src->AccessFlags;
dst->CurrentByteOffset = src->CurrentByteOffset;
dst->Mode = src->Mode;
dst->AlignmentRequirement = src->AlignmentRequirement;
dst->FileNameLength = src->FileNameLength;
}
#endif /* _CIFS_GLOB_H */ #endif /* _CIFS_GLOB_H */
...@@ -182,10 +182,9 @@ extern int cifs_unlock_range(struct cifsFileInfo *cfile, ...@@ -182,10 +182,9 @@ extern int cifs_unlock_range(struct cifsFileInfo *cfile,
extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile); extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile);
extern void cifs_down_write(struct rw_semaphore *sem); extern void cifs_down_write(struct rw_semaphore *sem);
extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
struct file *file, struct tcon_link *tlink, __u32 oplock,
struct tcon_link *tlink, const char *symlink_target);
__u32 oplock);
extern int cifs_posix_open(const char *full_path, struct inode **inode, extern int cifs_posix_open(const char *full_path, struct inode **inode,
struct super_block *sb, int mode, struct super_block *sb, int mode,
unsigned int f_flags, __u32 *oplock, __u16 *netfid, unsigned int f_flags, __u32 *oplock, __u16 *netfid,
...@@ -200,9 +199,9 @@ extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr); ...@@ -200,9 +199,9 @@ extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
extern struct inode *cifs_iget(struct super_block *sb, extern struct inode *cifs_iget(struct super_block *sb,
struct cifs_fattr *fattr); struct cifs_fattr *fattr);
extern int cifs_get_inode_info(struct inode **inode, const char *full_path, int cifs_get_inode_info(struct inode **inode, const char *full_path,
FILE_ALL_INFO *data, struct super_block *sb, struct cifs_open_info_data *data, struct super_block *sb, int xid,
int xid, const struct cifs_fid *fid); const struct cifs_fid *fid);
extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path, extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
struct super_block *sb, unsigned int xid); struct super_block *sb, unsigned int xid);
extern int cifs_get_inode_info_unix(struct inode **pinode, extern int cifs_get_inode_info_unix(struct inode **pinode,
......
...@@ -2305,7 +2305,7 @@ int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon, ...@@ -2305,7 +2305,7 @@ int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon,
remap); remap);
} }
rename_info->target_name_len = cpu_to_le32(2 * len_of_str); rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str); count = sizeof(struct set_file_rename) + (2 * len_of_str);
byte_count += count; byte_count += count;
pSMB->DataCount = cpu_to_le16(count); pSMB->DataCount = cpu_to_le16(count);
pSMB->TotalDataCount = pSMB->DataCount; pSMB->TotalDataCount = pSMB->DataCount;
......
...@@ -2832,9 +2832,12 @@ ip_rfc1001_connect(struct TCP_Server_Info *server) ...@@ -2832,9 +2832,12 @@ ip_rfc1001_connect(struct TCP_Server_Info *server)
* sessinit is sent but no second negprot * sessinit is sent but no second negprot
*/ */
struct rfc1002_session_packet *ses_init_buf; struct rfc1002_session_packet *ses_init_buf;
unsigned int req_noscope_len;
struct smb_hdr *smb_buf; struct smb_hdr *smb_buf;
ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet), ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet),
GFP_KERNEL); GFP_KERNEL);
if (ses_init_buf) { if (ses_init_buf) {
ses_init_buf->trailer.session_req.called_len = 32; ses_init_buf->trailer.session_req.called_len = 32;
...@@ -2870,8 +2873,12 @@ ip_rfc1001_connect(struct TCP_Server_Info *server) ...@@ -2870,8 +2873,12 @@ ip_rfc1001_connect(struct TCP_Server_Info *server)
ses_init_buf->trailer.session_req.scope2 = 0; ses_init_buf->trailer.session_req.scope2 = 0;
smb_buf = (struct smb_hdr *)ses_init_buf; smb_buf = (struct smb_hdr *)ses_init_buf;
/* sizeof RFC1002_SESSION_REQUEST with no scope */ /* sizeof RFC1002_SESSION_REQUEST with no scopes */
smb_buf->smb_buf_length = cpu_to_be32(0x81000044); req_noscope_len = sizeof(struct rfc1002_session_packet) - 2;
/* == cpu_to_be32(0x81000044) */
smb_buf->smb_buf_length =
cpu_to_be32((RFC1002_SESSION_REQUEST << 24) | req_noscope_len);
rc = smb_send(server, smb_buf, 0x44); rc = smb_send(server, smb_buf, 0x44);
kfree(ses_init_buf); kfree(ses_init_buf);
/* /*
...@@ -3922,12 +3929,11 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses, ...@@ -3922,12 +3929,11 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
pSMB->AndXCommand = 0xFF; pSMB->AndXCommand = 0xFF;
pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO); pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO);
bcc_ptr = &pSMB->Password[0]; bcc_ptr = &pSMB->Password[0];
if (tcon->pipe || (ses->server->sec_mode & SECMODE_USER)) {
pSMB->PasswordLength = cpu_to_le16(1); /* minimum */ pSMB->PasswordLength = cpu_to_le16(1); /* minimum */
*bcc_ptr = 0; /* password is null byte */ *bcc_ptr = 0; /* password is null byte */
bcc_ptr++; /* skip password */ bcc_ptr++; /* skip password */
/* already aligned so no need to do it below */ /* already aligned so no need to do it below */
}
if (ses->server->sign) if (ses->server->sign)
smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
......
...@@ -165,10 +165,9 @@ check_name(struct dentry *direntry, struct cifs_tcon *tcon) ...@@ -165,10 +165,9 @@ check_name(struct dentry *direntry, struct cifs_tcon *tcon)
/* Inode operations in similar order to how they appear in Linux file fs.h */ /* Inode operations in similar order to how they appear in Linux file fs.h */
static int static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, struct tcon_link *tlink, unsigned int oflags, umode_t mode, __u32 *oplock,
struct tcon_link *tlink, unsigned oflags, umode_t mode, struct cifs_fid *fid, struct cifs_open_info_data *buf)
__u32 *oplock, struct cifs_fid *fid)
{ {
int rc = -ENOENT; int rc = -ENOENT;
int create_options = CREATE_NOT_DIR; int create_options = CREATE_NOT_DIR;
...@@ -177,7 +176,6 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, ...@@ -177,7 +176,6 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
struct cifs_tcon *tcon = tlink_tcon(tlink); struct cifs_tcon *tcon = tlink_tcon(tlink);
const char *full_path; const char *full_path;
void *page = alloc_dentry_path(); void *page = alloc_dentry_path();
FILE_ALL_INFO *buf = NULL;
struct inode *newinode = NULL; struct inode *newinode = NULL;
int disposition; int disposition;
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
...@@ -290,12 +288,6 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, ...@@ -290,12 +288,6 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
goto out; goto out;
} }
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
if (buf == NULL) {
rc = -ENOMEM;
goto out;
}
/* /*
* if we're not using unix extensions, see if we need to set * if we're not using unix extensions, see if we need to set
* ATTR_READONLY on the create call * ATTR_READONLY on the create call
...@@ -364,8 +356,7 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, ...@@ -364,8 +356,7 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
{ {
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
/* TODO: Add support for calling POSIX query info here, but passing in fid */ /* TODO: Add support for calling POSIX query info here, but passing in fid */
rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, xid, fid);
xid, fid);
if (newinode) { if (newinode) {
if (server->ops->set_lease_key) if (server->ops->set_lease_key)
server->ops->set_lease_key(newinode, fid); server->ops->set_lease_key(newinode, fid);
...@@ -402,7 +393,6 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, ...@@ -402,7 +393,6 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
d_add(direntry, newinode); d_add(direntry, newinode);
out: out:
kfree(buf);
free_dentry_path(page); free_dentry_path(page);
return rc; return rc;
...@@ -423,10 +413,11 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, ...@@ -423,10 +413,11 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
struct tcon_link *tlink; struct tcon_link *tlink;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct cifs_fid fid; struct cifs_fid fid = {};
struct cifs_pending_open open; struct cifs_pending_open open;
__u32 oplock; __u32 oplock;
struct cifsFileInfo *file_info; struct cifsFileInfo *file_info;
struct cifs_open_info_data buf = {};
if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb))))
return -EIO; return -EIO;
...@@ -484,8 +475,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, ...@@ -484,8 +475,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
cifs_add_pending_open(&fid, tlink, &open); cifs_add_pending_open(&fid, tlink, &open);
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
&oplock, &fid); &oplock, &fid, &buf);
if (rc) { if (rc) {
cifs_del_pending_open(&open); cifs_del_pending_open(&open);
goto out; goto out;
...@@ -510,7 +500,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, ...@@ -510,7 +500,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
file->f_op = &cifs_file_direct_ops; file->f_op = &cifs_file_direct_ops;
} }
file_info = cifs_new_fileinfo(&fid, file, tlink, oplock); file_info = cifs_new_fileinfo(&fid, file, tlink, oplock, buf.symlink_target);
if (file_info == NULL) { if (file_info == NULL) {
if (server->ops->close) if (server->ops->close)
server->ops->close(xid, tcon, &fid); server->ops->close(xid, tcon, &fid);
...@@ -526,6 +516,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, ...@@ -526,6 +516,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
out_free_xid: out_free_xid:
free_xid(xid); free_xid(xid);
cifs_free_open_info(&buf);
return rc; return rc;
} }
...@@ -547,6 +538,7 @@ int cifs_create(struct user_namespace *mnt_userns, struct inode *inode, ...@@ -547,6 +538,7 @@ int cifs_create(struct user_namespace *mnt_userns, struct inode *inode,
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct cifs_fid fid; struct cifs_fid fid;
__u32 oplock; __u32 oplock;
struct cifs_open_info_data buf = {};
cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n", cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
inode, direntry, direntry); inode, direntry, direntry);
...@@ -565,11 +557,11 @@ int cifs_create(struct user_namespace *mnt_userns, struct inode *inode, ...@@ -565,11 +557,11 @@ int cifs_create(struct user_namespace *mnt_userns, struct inode *inode,
if (server->ops->new_lease_key) if (server->ops->new_lease_key)
server->ops->new_lease_key(&fid); server->ops->new_lease_key(&fid);
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, &oplock, &fid, &buf);
&oplock, &fid);
if (!rc && server->ops->close) if (!rc && server->ops->close)
server->ops->close(xid, tcon, &fid); server->ops->close(xid, tcon, &fid);
cifs_free_open_info(&buf);
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
out_free_xid: out_free_xid:
free_xid(xid); free_xid(xid);
......
...@@ -209,16 +209,14 @@ int cifs_posix_open(const char *full_path, struct inode **pinode, ...@@ -209,16 +209,14 @@ int cifs_posix_open(const char *full_path, struct inode **pinode,
} }
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
static int static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock,
struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock, struct cifs_fid *fid, unsigned int xid, struct cifs_open_info_data *buf)
struct cifs_fid *fid, unsigned int xid)
{ {
int rc; int rc;
int desired_access; int desired_access;
int disposition; int disposition;
int create_options = CREATE_NOT_DIR; int create_options = CREATE_NOT_DIR;
FILE_ALL_INFO *buf;
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_open_parms oparms; struct cifs_open_parms oparms;
...@@ -255,10 +253,6 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci ...@@ -255,10 +253,6 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci
/* BB pass O_SYNC flag through on file attributes .. BB */ /* BB pass O_SYNC flag through on file attributes .. BB */
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* O_SYNC also has bit for O_DSYNC so following check picks up either */ /* O_SYNC also has bit for O_DSYNC so following check picks up either */
if (f_flags & O_SYNC) if (f_flags & O_SYNC)
create_options |= CREATE_WRITE_THROUGH; create_options |= CREATE_WRITE_THROUGH;
...@@ -276,9 +270,8 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci ...@@ -276,9 +270,8 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci
oparms.reconnect = false; oparms.reconnect = false;
rc = server->ops->open(xid, &oparms, oplock, buf); rc = server->ops->open(xid, &oparms, oplock, buf);
if (rc) if (rc)
goto out; return rc;
/* TODO: Add support for calling posix query info but with passing in fid */ /* TODO: Add support for calling posix query info but with passing in fid */
if (tcon->unix_ext) if (tcon->unix_ext)
...@@ -294,8 +287,6 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci ...@@ -294,8 +287,6 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci
rc = -EOPENSTALE; rc = -EOPENSTALE;
} }
out:
kfree(buf);
return rc; return rc;
} }
...@@ -325,9 +316,9 @@ cifs_down_write(struct rw_semaphore *sem) ...@@ -325,9 +316,9 @@ cifs_down_write(struct rw_semaphore *sem)
static void cifsFileInfo_put_work(struct work_struct *work); static void cifsFileInfo_put_work(struct work_struct *work);
struct cifsFileInfo * struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, struct tcon_link *tlink, __u32 oplock,
struct tcon_link *tlink, __u32 oplock) const char *symlink_target)
{ {
struct dentry *dentry = file_dentry(file); struct dentry *dentry = file_dentry(file);
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
...@@ -347,6 +338,15 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, ...@@ -347,6 +338,15 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
return NULL; return NULL;
} }
if (symlink_target) {
cfile->symlink_target = kstrdup(symlink_target, GFP_KERNEL);
if (!cfile->symlink_target) {
kfree(fdlocks);
kfree(cfile);
return NULL;
}
}
INIT_LIST_HEAD(&fdlocks->locks); INIT_LIST_HEAD(&fdlocks->locks);
fdlocks->cfile = cfile; fdlocks->cfile = cfile;
cfile->llist = fdlocks; cfile->llist = fdlocks;
...@@ -440,6 +440,7 @@ static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file) ...@@ -440,6 +440,7 @@ static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file)
cifs_put_tlink(cifs_file->tlink); cifs_put_tlink(cifs_file->tlink);
dput(cifs_file->dentry); dput(cifs_file->dentry);
cifs_sb_deactive(sb); cifs_sb_deactive(sb);
kfree(cifs_file->symlink_target);
kfree(cifs_file); kfree(cifs_file);
} }
...@@ -488,7 +489,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, ...@@ -488,7 +489,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
struct cifsInodeInfo *cifsi = CIFS_I(inode); struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifs_fid fid; struct cifs_fid fid = {};
struct cifs_pending_open open; struct cifs_pending_open open;
bool oplock_break_cancelled; bool oplock_break_cancelled;
...@@ -570,8 +571,9 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -570,8 +571,9 @@ int cifs_open(struct inode *inode, struct file *file)
void *page; void *page;
const char *full_path; const char *full_path;
bool posix_open_ok = false; bool posix_open_ok = false;
struct cifs_fid fid; struct cifs_fid fid = {};
struct cifs_pending_open open; struct cifs_pending_open open;
struct cifs_open_info_data data = {};
xid = get_xid(); xid = get_xid();
...@@ -662,15 +664,15 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -662,15 +664,15 @@ int cifs_open(struct inode *inode, struct file *file)
if (server->ops->get_lease_key) if (server->ops->get_lease_key)
server->ops->get_lease_key(inode, &fid); server->ops->get_lease_key(inode, &fid);
rc = cifs_nt_open(full_path, inode, cifs_sb, tcon, rc = cifs_nt_open(full_path, inode, cifs_sb, tcon, file->f_flags, &oplock, &fid,
file->f_flags, &oplock, &fid, xid); xid, &data);
if (rc) { if (rc) {
cifs_del_pending_open(&open); cifs_del_pending_open(&open);
goto out; goto out;
} }
} }
cfile = cifs_new_fileinfo(&fid, file, tlink, oplock); cfile = cifs_new_fileinfo(&fid, file, tlink, oplock, data.symlink_target);
if (cfile == NULL) { if (cfile == NULL) {
if (server->ops->close) if (server->ops->close)
server->ops->close(xid, tcon, &fid); server->ops->close(xid, tcon, &fid);
...@@ -712,6 +714,7 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -712,6 +714,7 @@ int cifs_open(struct inode *inode, struct file *file)
free_dentry_path(page); free_dentry_path(page);
free_xid(xid); free_xid(xid);
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
cifs_free_open_info(&data);
return rc; return rc;
} }
......
This diff is collapsed.
...@@ -484,12 +484,35 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) ...@@ -484,12 +484,35 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
tcon = tlink_tcon(tlink); tcon = tlink_tcon(tlink);
if (tcon && tcon->ses->server->ops->notify) { if (tcon && tcon->ses->server->ops->notify) {
rc = tcon->ses->server->ops->notify(xid, rc = tcon->ses->server->ops->notify(xid,
filep, (void __user *)arg); filep, (void __user *)arg,
false /* no ret data */);
cifs_dbg(FYI, "ioctl notify rc %d\n", rc); cifs_dbg(FYI, "ioctl notify rc %d\n", rc);
} else } else
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
break; break;
case CIFS_IOC_NOTIFY_INFO:
if (!S_ISDIR(inode->i_mode)) {
/* Notify can only be done on directories */
rc = -EOPNOTSUPP;
break;
}
cifs_sb = CIFS_SB(inode->i_sb);
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
rc = PTR_ERR(tlink);
break;
}
tcon = tlink_tcon(tlink);
if (tcon && tcon->ses->server->ops->notify) {
rc = tcon->ses->server->ops->notify(xid,
filep, (void __user *)arg,
true /* return details */);
cifs_dbg(FYI, "ioctl notify info rc %d\n", rc);
} else
rc = -EOPNOTSUPP;
cifs_put_tlink(tlink);
break;
case CIFS_IOC_SHUTDOWN: case CIFS_IOC_SHUTDOWN:
rc = cifs_shutdown(inode->i_sb, arg); rc = cifs_shutdown(inode->i_sb, arg);
break; break;
......
...@@ -201,40 +201,6 @@ create_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -201,40 +201,6 @@ create_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
static int
query_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const unsigned char *path,
char **symlinkinfo)
{
int rc;
u8 *buf = NULL;
unsigned int link_len = 0;
unsigned int bytes_read = 0;
buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (tcon->ses->server->ops->query_mf_symlink)
rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon,
cifs_sb, path, buf, &bytes_read);
else
rc = -ENOSYS;
if (rc)
goto out;
if (bytes_read == 0) { /* not a symlink */
rc = -EINVAL;
goto out;
}
rc = parse_mf_symlink(buf, bytes_read, &link_len, symlinkinfo);
out:
kfree(buf);
return rc;
}
int int
check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
...@@ -244,6 +210,7 @@ check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, ...@@ -244,6 +210,7 @@ check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
u8 *buf = NULL; u8 *buf = NULL;
unsigned int link_len = 0; unsigned int link_len = 0;
unsigned int bytes_read = 0; unsigned int bytes_read = 0;
char *symlink = NULL;
if (!couldbe_mf_symlink(fattr)) if (!couldbe_mf_symlink(fattr))
/* it's not a symlink */ /* it's not a symlink */
...@@ -265,7 +232,7 @@ check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, ...@@ -265,7 +232,7 @@ check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
if (bytes_read == 0) /* not a symlink */ if (bytes_read == 0) /* not a symlink */
goto out; goto out;
rc = parse_mf_symlink(buf, bytes_read, &link_len, NULL); rc = parse_mf_symlink(buf, bytes_read, &link_len, &symlink);
if (rc == -EINVAL) { if (rc == -EINVAL) {
/* it's not a symlink */ /* it's not a symlink */
rc = 0; rc = 0;
...@@ -280,6 +247,7 @@ check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, ...@@ -280,6 +247,7 @@ check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
fattr->cf_mode &= ~S_IFMT; fattr->cf_mode &= ~S_IFMT;
fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO; fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
fattr->cf_dtype = DT_LNK; fattr->cf_dtype = DT_LNK;
fattr->cf_symlink_target = symlink;
out: out:
kfree(buf); kfree(buf);
return rc; return rc;
...@@ -599,75 +567,6 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, ...@@ -599,75 +567,6 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
return rc; return rc;
} }
const char *
cifs_get_link(struct dentry *direntry, struct inode *inode,
struct delayed_call *done)
{
int rc = -ENOMEM;
unsigned int xid;
const char *full_path;
void *page;
char *target_path = NULL;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink = NULL;
struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
if (!direntry)
return ERR_PTR(-ECHILD);
xid = get_xid();
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
free_xid(xid);
return ERR_CAST(tlink);
}
tcon = tlink_tcon(tlink);
server = tcon->ses->server;
page = alloc_dentry_path();
full_path = build_path_from_dentry(direntry, page);
if (IS_ERR(full_path)) {
free_xid(xid);
cifs_put_tlink(tlink);
free_dentry_path(page);
return ERR_CAST(full_path);
}
cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", full_path, inode);
rc = -EACCES;
/*
* First try Minshall+French Symlinks, if configured
* and fallback to UNIX Extensions Symlinks.
*/
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
rc = query_mf_symlink(xid, tcon, cifs_sb, full_path,
&target_path);
if (rc != 0 && server->ops->query_symlink) {
struct cifsInodeInfo *cifsi = CIFS_I(inode);
bool reparse_point = false;
if (cifsi->cifsAttrs & ATTR_REPARSE)
reparse_point = true;
rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
&target_path, reparse_point);
}
free_dentry_path(page);
free_xid(xid);
cifs_put_tlink(tlink);
if (rc != 0) {
kfree(target_path);
return ERR_PTR(rc);
}
set_delayed_call(done, kfree_link, target_path);
return target_path;
}
int int
cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode, cifs_symlink(struct user_namespace *mnt_userns, struct inode *inode,
struct dentry *direntry, const char *symname) struct dentry *direntry, const char *symname)
......
...@@ -844,17 +844,34 @@ static bool emit_cached_dirents(struct cached_dirents *cde, ...@@ -844,17 +844,34 @@ static bool emit_cached_dirents(struct cached_dirents *cde,
struct dir_context *ctx) struct dir_context *ctx)
{ {
struct cached_dirent *dirent; struct cached_dirent *dirent;
int rc; bool rc;
list_for_each_entry(dirent, &cde->entries, entry) { list_for_each_entry(dirent, &cde->entries, entry) {
if (ctx->pos >= dirent->pos) /*
* Skip all early entries prior to the current lseek()
* position.
*/
if (ctx->pos > dirent->pos)
continue; continue;
/*
* We recorded the current ->pos value for the dirent
* when we stored it in the cache.
* However, this sequence of ->pos values may have holes
* in it, for example dot-dirs returned from the server
* are suppressed.
* Handle this bu forcing ctx->pos to be the same as the
* ->pos of the current dirent we emit from the cache.
* This means that when we emit these entries from the cache
* we now emit them with the same ->pos value as in the
* initial scan.
*/
ctx->pos = dirent->pos; ctx->pos = dirent->pos;
rc = dir_emit(ctx, dirent->name, dirent->namelen, rc = dir_emit(ctx, dirent->name, dirent->namelen,
dirent->fattr.cf_uniqueid, dirent->fattr.cf_uniqueid,
dirent->fattr.cf_dtype); dirent->fattr.cf_dtype);
if (!rc) if (!rc)
return rc; return rc;
ctx->pos++;
} }
return true; return true;
} }
...@@ -994,6 +1011,8 @@ static int cifs_filldir(char *find_entry, struct file *file, ...@@ -994,6 +1011,8 @@ static int cifs_filldir(char *find_entry, struct file *file,
cifs_unix_basic_to_fattr(&fattr, cifs_unix_basic_to_fattr(&fattr,
&((FILE_UNIX_INFO *)find_entry)->basic, &((FILE_UNIX_INFO *)find_entry)->basic,
cifs_sb); cifs_sb);
if (S_ISLNK(fattr.cf_mode))
fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
break; break;
case SMB_FIND_FILE_INFO_STANDARD: case SMB_FIND_FILE_INFO_STANDARD:
cifs_std_info_to_fattr(&fattr, cifs_std_info_to_fattr(&fattr,
...@@ -1202,10 +1221,10 @@ int cifs_readdir(struct file *file, struct dir_context *ctx) ...@@ -1202,10 +1221,10 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
ctx->pos, tmp_buf); ctx->pos, tmp_buf);
cifs_save_resume_key(current_entry, cifsFile); cifs_save_resume_key(current_entry, cifsFile);
break; break;
} else }
current_entry = current_entry =
nxt_dir_entry(current_entry, end_of_smb, nxt_dir_entry(current_entry, end_of_smb,
cifsFile->srch_inf.info_level); cifsFile->srch_inf.info_level);
} }
kfree(tmp_buf); kfree(tmp_buf);
......
...@@ -601,11 +601,6 @@ static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses, ...@@ -601,11 +601,6 @@ static void unicode_ssetup_strings(char **pbcc_area, struct cifs_ses *ses,
/* BB FIXME add check that strings total less /* BB FIXME add check that strings total less
than 335 or will need to send them as arrays */ than 335 or will need to send them as arrays */
/* unicode strings, must be word aligned before the call */
/* if ((long) bcc_ptr % 2) {
*bcc_ptr = 0;
bcc_ptr++;
} */
/* copy user */ /* copy user */
if (ses->user_name == NULL) { if (ses->user_name == NULL) {
/* null user mount */ /* null user mount */
...@@ -1213,16 +1208,18 @@ sess_alloc_buffer(struct sess_data *sess_data, int wct) ...@@ -1213,16 +1208,18 @@ sess_alloc_buffer(struct sess_data *sess_data, int wct)
static void static void
sess_free_buffer(struct sess_data *sess_data) sess_free_buffer(struct sess_data *sess_data)
{ {
int i; struct kvec *iov = sess_data->iov;
/* zero the session data before freeing, as it might contain sensitive info (keys, etc) */ /*
for (i = 0; i < 3; i++) * Zero the session data before freeing, as it might contain sensitive info (keys, etc).
if (sess_data->iov[i].iov_base) * Note that iov[1] is already freed by caller.
memzero_explicit(sess_data->iov[i].iov_base, sess_data->iov[i].iov_len); */
if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base)
memzero_explicit(iov[0].iov_base, iov[0].iov_len);
free_rsp_buf(sess_data->buf0_type, sess_data->iov[0].iov_base); free_rsp_buf(sess_data->buf0_type, iov[0].iov_base);
sess_data->buf0_type = CIFS_NO_BUFFER; sess_data->buf0_type = CIFS_NO_BUFFER;
kfree(sess_data->iov[2].iov_base); kfree_sensitive(iov[2].iov_base);
} }
static int static int
...@@ -1324,7 +1321,7 @@ sess_auth_ntlmv2(struct sess_data *sess_data) ...@@ -1324,7 +1321,7 @@ sess_auth_ntlmv2(struct sess_data *sess_data)
} }
if (ses->capabilities & CAP_UNICODE) { if (ses->capabilities & CAP_UNICODE) {
if (sess_data->iov[0].iov_len % 2) { if (!IS_ALIGNED(sess_data->iov[0].iov_len, 2)) {
*bcc_ptr = 0; *bcc_ptr = 0;
bcc_ptr++; bcc_ptr++;
} }
...@@ -1364,7 +1361,7 @@ sess_auth_ntlmv2(struct sess_data *sess_data) ...@@ -1364,7 +1361,7 @@ sess_auth_ntlmv2(struct sess_data *sess_data)
/* no string area to decode, do nothing */ /* no string area to decode, do nothing */
} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
/* unicode string area must be word-aligned */ /* unicode string area must be word-aligned */
if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) { if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
++bcc_ptr; ++bcc_ptr;
--bytes_remaining; --bytes_remaining;
} }
...@@ -1448,8 +1445,7 @@ sess_auth_kerberos(struct sess_data *sess_data) ...@@ -1448,8 +1445,7 @@ sess_auth_kerberos(struct sess_data *sess_data)
if (ses->capabilities & CAP_UNICODE) { if (ses->capabilities & CAP_UNICODE) {
/* unicode strings must be word aligned */ /* unicode strings must be word aligned */
if ((sess_data->iov[0].iov_len if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) {
+ sess_data->iov[1].iov_len) % 2) {
*bcc_ptr = 0; *bcc_ptr = 0;
bcc_ptr++; bcc_ptr++;
} }
...@@ -1500,7 +1496,7 @@ sess_auth_kerberos(struct sess_data *sess_data) ...@@ -1500,7 +1496,7 @@ sess_auth_kerberos(struct sess_data *sess_data)
/* no string area to decode, do nothing */ /* no string area to decode, do nothing */
} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
/* unicode string area must be word-aligned */ /* unicode string area must be word-aligned */
if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) { if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
++bcc_ptr; ++bcc_ptr;
--bytes_remaining; --bytes_remaining;
} }
...@@ -1552,7 +1548,7 @@ _sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data) ...@@ -1552,7 +1548,7 @@ _sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data)
bcc_ptr = sess_data->iov[2].iov_base; bcc_ptr = sess_data->iov[2].iov_base;
/* unicode strings must be word aligned */ /* unicode strings must be word aligned */
if ((sess_data->iov[0].iov_len + sess_data->iov[1].iov_len) % 2) { if (!IS_ALIGNED(sess_data->iov[0].iov_len + sess_data->iov[1].iov_len, 2)) {
*bcc_ptr = 0; *bcc_ptr = 0;
bcc_ptr++; bcc_ptr++;
} }
...@@ -1753,7 +1749,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data) ...@@ -1753,7 +1749,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
/* no string area to decode, do nothing */ /* no string area to decode, do nothing */
} else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) {
/* unicode string area must be word-aligned */ /* unicode string area must be word-aligned */
if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) { if (!IS_ALIGNED((unsigned long)bcc_ptr - (unsigned long)smb_buf, 2)) {
++bcc_ptr; ++bcc_ptr;
--bytes_remaining; --bytes_remaining;
} }
......
...@@ -542,31 +542,32 @@ cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -542,31 +542,32 @@ cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
static int static int cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const char *full_path,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_open_info_data *data, bool *adjustTZ, bool *symlink)
FILE_ALL_INFO *data, bool *adjustTZ, bool *symlink)
{ {
int rc; int rc;
FILE_ALL_INFO fi = {};
*symlink = false; *symlink = false;
/* could do find first instead but this returns more info */ /* could do find first instead but this returns more info */
rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */, rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */, cifs_sb->local_nls,
cifs_sb->local_nls, cifs_remap(cifs_sb)); cifs_remap(cifs_sb));
/* /*
* BB optimize code so we do not make the above call when server claims * BB optimize code so we do not make the above call when server claims
* no NT SMB support and the above call failed at least once - set flag * no NT SMB support and the above call failed at least once - set flag
* in tcon or mount. * in tcon or mount.
*/ */
if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) { if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) {
rc = SMBQueryInformation(xid, tcon, full_path, data, rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls,
cifs_sb->local_nls,
cifs_remap(cifs_sb)); cifs_remap(cifs_sb));
if (!rc)
move_cifs_info_to_smb2(&data->fi, &fi);
*adjustTZ = true; *adjustTZ = true;
} }
if (!rc && (le32_to_cpu(data->Attributes) & ATTR_REPARSE)) { if (!rc && (le32_to_cpu(fi.Attributes) & ATTR_REPARSE)) {
int tmprc; int tmprc;
int oplock = 0; int oplock = 0;
struct cifs_fid fid; struct cifs_fid fid;
...@@ -592,10 +593,9 @@ cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -592,10 +593,9 @@ cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
static int static int cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const char *full_path,
struct cifs_sb_info *cifs_sb, const char *full_path, u64 *uniqueid, struct cifs_open_info_data *unused)
u64 *uniqueid, FILE_ALL_INFO *data)
{ {
/* /*
* We can not use the IndexNumber field by default from Windows or * We can not use the IndexNumber field by default from Windows or
...@@ -613,11 +613,22 @@ cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -613,11 +613,22 @@ cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
cifs_remap(cifs_sb)); cifs_remap(cifs_sb));
} }
static int static int cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, struct cifsFileInfo *cfile, struct cifs_open_info_data *data)
struct cifs_fid *fid, FILE_ALL_INFO *data)
{ {
return CIFSSMBQFileInfo(xid, tcon, fid->netfid, data); int rc;
FILE_ALL_INFO fi = {};
if (cfile->symlink_target) {
data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (!data->symlink_target)
return -ENOMEM;
}
rc = CIFSSMBQFileInfo(xid, tcon, cfile->fid.netfid, &fi);
if (!rc)
move_cifs_info_to_smb2(&data->fi, &fi);
return rc;
} }
static void static void
...@@ -702,19 +713,20 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path, ...@@ -702,19 +713,20 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path,
cifsInode->cifsAttrs = dosattrs; cifsInode->cifsAttrs = dosattrs;
} }
static int static int cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms, void *buf)
__u32 *oplock, FILE_ALL_INFO *buf)
{ {
FILE_ALL_INFO *fi = buf;
if (!(oparms->tcon->ses->capabilities & CAP_NT_SMBS)) if (!(oparms->tcon->ses->capabilities & CAP_NT_SMBS))
return SMBLegacyOpen(xid, oparms->tcon, oparms->path, return SMBLegacyOpen(xid, oparms->tcon, oparms->path,
oparms->disposition, oparms->disposition,
oparms->desired_access, oparms->desired_access,
oparms->create_options, oparms->create_options,
&oparms->fid->netfid, oplock, buf, &oparms->fid->netfid, oplock, fi,
oparms->cifs_sb->local_nls, oparms->cifs_sb->local_nls,
cifs_remap(oparms->cifs_sb)); cifs_remap(oparms->cifs_sb));
return CIFS_open(xid, oparms, oplock, buf); return CIFS_open(xid, oparms, oplock, fi);
} }
static void static void
......
...@@ -20,40 +20,125 @@ ...@@ -20,40 +20,125 @@
#include "cifs_unicode.h" #include "cifs_unicode.h"
#include "fscache.h" #include "fscache.h"
#include "smb2proto.h" #include "smb2proto.h"
#include "smb2status.h"
int static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, {
__u32 *oplock, FILE_ALL_INFO *buf) struct smb2_err_rsp *err = iov->iov_base;
struct smb2_symlink_err_rsp *sym = ERR_PTR(-EINVAL);
u32 len;
if (err->ErrorContextCount) {
struct smb2_error_context_rsp *p, *end;
len = (u32)err->ErrorContextCount * (offsetof(struct smb2_error_context_rsp,
ErrorContextData) +
sizeof(struct smb2_symlink_err_rsp));
if (le32_to_cpu(err->ByteCount) < len || iov->iov_len < len + sizeof(*err))
return ERR_PTR(-EINVAL);
p = (struct smb2_error_context_rsp *)err->ErrorData;
end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len);
do {
if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) {
sym = (struct smb2_symlink_err_rsp *)&p->ErrorContextData;
break;
}
cifs_dbg(FYI, "%s: skipping unhandled error context: 0x%x\n",
__func__, le32_to_cpu(p->ErrorId));
len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
p = (struct smb2_error_context_rsp *)((u8 *)&p->ErrorContextData + len);
} while (p < end);
} else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) &&
iov->iov_len >= SMB2_SYMLINK_STRUCT_SIZE) {
sym = (struct smb2_symlink_err_rsp *)err->ErrorData;
}
if (!IS_ERR(sym) && (le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK))
sym = ERR_PTR(-EINVAL);
return sym;
}
int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path)
{
struct smb2_symlink_err_rsp *sym;
unsigned int sub_offs, sub_len;
unsigned int print_offs, print_len;
char *s;
if (!cifs_sb || !iov || !iov->iov_base || !iov->iov_len || !path)
return -EINVAL;
sym = symlink_data(iov);
if (IS_ERR(sym))
return PTR_ERR(sym);
sub_len = le16_to_cpu(sym->SubstituteNameLength);
sub_offs = le16_to_cpu(sym->SubstituteNameOffset);
print_len = le16_to_cpu(sym->PrintNameLength);
print_offs = le16_to_cpu(sym->PrintNameOffset);
if (iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offs + sub_len ||
iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len)
return -EINVAL;
s = cifs_strndup_from_utf16((char *)sym->PathBuffer + sub_offs, sub_len, true,
cifs_sb->local_nls);
if (!s)
return -ENOMEM;
convert_delimiter(s, '/');
cifs_dbg(FYI, "%s: symlink target: %s\n", __func__, s);
*path = s;
return 0;
}
int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, void *buf)
{ {
int rc; int rc;
__le16 *smb2_path; __le16 *smb2_path;
struct smb2_file_all_info *smb2_data = NULL;
__u8 smb2_oplock; __u8 smb2_oplock;
struct cifs_open_info_data *data = buf;
struct smb2_file_all_info file_info = {};
struct smb2_file_all_info *smb2_data = data ? &file_info : NULL;
struct kvec err_iov = {};
int err_buftype = CIFS_NO_BUFFER;
struct cifs_fid *fid = oparms->fid; struct cifs_fid *fid = oparms->fid;
struct network_resiliency_req nr_ioctl_req; struct network_resiliency_req nr_ioctl_req;
smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb); smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb);
if (smb2_path == NULL) { if (smb2_path == NULL)
rc = -ENOMEM; return -ENOMEM;
goto out;
}
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
GFP_KERNEL);
if (smb2_data == NULL) {
rc = -ENOMEM;
goto out;
}
oparms->desired_access |= FILE_READ_ATTRIBUTES; oparms->desired_access |= FILE_READ_ATTRIBUTES;
smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH; smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH;
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
NULL, NULL); &err_buftype);
if (rc && data) {
struct smb2_hdr *hdr = err_iov.iov_base;
if (unlikely(!err_iov.iov_base || err_buftype == CIFS_NO_BUFFER))
rc = -ENOMEM;
else if (hdr->Status == STATUS_STOPPED_ON_SYMLINK) {
rc = smb2_parse_symlink_response(oparms->cifs_sb, &err_iov,
&data->symlink_target);
if (!rc) {
memset(smb2_data, 0, sizeof(*smb2_data));
oparms->create_options |= OPEN_REPARSE_POINT;
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data,
NULL, NULL, NULL);
oparms->create_options &= ~OPEN_REPARSE_POINT;
}
}
}
if (rc) if (rc)
goto out; goto out;
if (oparms->tcon->use_resilient) { if (oparms->tcon->use_resilient) {
/* default timeout is 0, servers pick default (120 seconds) */ /* default timeout is 0, servers pick default (120 seconds) */
nr_ioctl_req.Timeout = nr_ioctl_req.Timeout =
...@@ -73,7 +158,7 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, ...@@ -73,7 +158,7 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
rc = 0; rc = 0;
} }
if (buf) { if (smb2_data) {
/* if open response does not have IndexNumber field - get it */ /* if open response does not have IndexNumber field - get it */
if (smb2_data->IndexNumber == 0) { if (smb2_data->IndexNumber == 0) {
rc = SMB2_get_srv_num(xid, oparms->tcon, rc = SMB2_get_srv_num(xid, oparms->tcon,
...@@ -89,12 +174,12 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, ...@@ -89,12 +174,12 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
rc = 0; rc = 0;
} }
} }
move_smb2_info_to_cifs(buf, smb2_data); memcpy(&data->fi, smb2_data, sizeof(data->fi));
} }
*oplock = smb2_oplock; *oplock = smb2_oplock;
out: out:
kfree(smb2_data); free_rsp_buf(err_buftype, err_iov.iov_base);
kfree(smb2_path); kfree(smb2_path);
return rc; return rc;
} }
......
This diff is collapsed.
...@@ -248,7 +248,7 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) ...@@ -248,7 +248,7 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
* Some windows servers (win2016) will pad also the final * Some windows servers (win2016) will pad also the final
* PDU in a compound to 8 bytes. * PDU in a compound to 8 bytes.
*/ */
if (((calc_len + 7) & ~7) == len) if (ALIGN(calc_len, 8) == len)
return 0; return 0;
/* /*
......
...@@ -550,7 +550,8 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, ...@@ -550,7 +550,8 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
/* avoid spamming logs every 10 minutes, so log only in mount */ /* avoid spamming logs every 10 minutes, so log only in mount */
if ((ses->chan_max > 1) && in_mount) if ((ses->chan_max > 1) && in_mount)
cifs_dbg(VFS, cifs_dbg(VFS,
"empty network interface list returned by server %s\n", "multichannel not available\n"
"Empty network interface list returned by server %s\n",
ses->server->hostname); ses->server->hostname);
rc = -EINVAL; rc = -EINVAL;
goto out; goto out;
...@@ -800,7 +801,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -800,7 +801,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid); rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid);
if (!rc) { if (!rc) {
if (cfid->is_valid) { if (cfid->has_lease) {
close_cached_dir(cfid); close_cached_dir(cfid);
return 0; return 0;
} }
...@@ -830,33 +831,25 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -830,33 +831,25 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
static int static int smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const char *full_path,
struct cifs_sb_info *cifs_sb, const char *full_path, u64 *uniqueid, struct cifs_open_info_data *data)
u64 *uniqueid, FILE_ALL_INFO *data)
{ {
*uniqueid = le64_to_cpu(data->IndexNumber); *uniqueid = le64_to_cpu(data->fi.IndexNumber);
return 0; return 0;
} }
static int static int smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon, struct cifsFileInfo *cfile, struct cifs_open_info_data *data)
struct cifs_fid *fid, FILE_ALL_INFO *data)
{ {
int rc; struct cifs_fid *fid = &cfile->fid;
struct smb2_file_all_info *smb2_data;
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
GFP_KERNEL);
if (smb2_data == NULL)
return -ENOMEM;
rc = SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, if (cfile->symlink_target) {
smb2_data); data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (!rc) if (!data->symlink_target)
move_smb2_info_to_cifs(data, smb2_data); return -ENOMEM;
kfree(smb2_data); }
return rc; return SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, &data->fi);
} }
#ifdef CONFIG_CIFS_XATTR #ifdef CONFIG_CIFS_XATTR
...@@ -2025,9 +2018,10 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2025,9 +2018,10 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
static int static int
smb3_notify(const unsigned int xid, struct file *pfile, smb3_notify(const unsigned int xid, struct file *pfile,
void __user *ioc_buf) void __user *ioc_buf, bool return_changes)
{ {
struct smb3_notify notify; struct smb3_notify_info notify;
struct smb3_notify_info __user *pnotify_buf;
struct dentry *dentry = pfile->f_path.dentry; struct dentry *dentry = pfile->f_path.dentry;
struct inode *inode = file_inode(pfile); struct inode *inode = file_inode(pfile);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
...@@ -2035,10 +2029,12 @@ smb3_notify(const unsigned int xid, struct file *pfile, ...@@ -2035,10 +2029,12 @@ smb3_notify(const unsigned int xid, struct file *pfile,
struct cifs_fid fid; struct cifs_fid fid;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
const unsigned char *path; const unsigned char *path;
char *returned_ioctl_info = NULL;
void *page = alloc_dentry_path(); void *page = alloc_dentry_path();
__le16 *utf16_path = NULL; __le16 *utf16_path = NULL;
u8 oplock = SMB2_OPLOCK_LEVEL_NONE; u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
int rc = 0; int rc = 0;
__u32 ret_len = 0;
path = build_path_from_dentry(dentry, page); path = build_path_from_dentry(dentry, page);
if (IS_ERR(path)) { if (IS_ERR(path)) {
...@@ -2052,9 +2048,17 @@ smb3_notify(const unsigned int xid, struct file *pfile, ...@@ -2052,9 +2048,17 @@ smb3_notify(const unsigned int xid, struct file *pfile,
goto notify_exit; goto notify_exit;
} }
if (copy_from_user(&notify, ioc_buf, sizeof(struct smb3_notify))) { if (return_changes) {
rc = -EFAULT; if (copy_from_user(&notify, ioc_buf, sizeof(struct smb3_notify_info))) {
goto notify_exit; rc = -EFAULT;
goto notify_exit;
}
} else {
if (copy_from_user(&notify, ioc_buf, sizeof(struct smb3_notify))) {
rc = -EFAULT;
goto notify_exit;
}
notify.data_len = 0;
} }
tcon = cifs_sb_master_tcon(cifs_sb); tcon = cifs_sb_master_tcon(cifs_sb);
...@@ -2071,12 +2075,22 @@ smb3_notify(const unsigned int xid, struct file *pfile, ...@@ -2071,12 +2075,22 @@ smb3_notify(const unsigned int xid, struct file *pfile,
goto notify_exit; goto notify_exit;
rc = SMB2_change_notify(xid, tcon, fid.persistent_fid, fid.volatile_fid, rc = SMB2_change_notify(xid, tcon, fid.persistent_fid, fid.volatile_fid,
notify.watch_tree, notify.completion_filter); notify.watch_tree, notify.completion_filter,
notify.data_len, &returned_ioctl_info, &ret_len);
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
cifs_dbg(FYI, "change notify for path %s rc %d\n", path, rc); cifs_dbg(FYI, "change notify for path %s rc %d\n", path, rc);
if (return_changes && (ret_len > 0) && (notify.data_len > 0)) {
if (ret_len > notify.data_len)
ret_len = notify.data_len;
pnotify_buf = (struct smb3_notify_info __user *)ioc_buf;
if (copy_to_user(pnotify_buf->notify_data, returned_ioctl_info, ret_len))
rc = -EFAULT;
else if (copy_to_user(&pnotify_buf->data_len, &ret_len, sizeof(ret_len)))
rc = -EFAULT;
}
kfree(returned_ioctl_info);
notify_exit: notify_exit:
free_dentry_path(page); free_dentry_path(page);
kfree(utf16_path); kfree(utf16_path);
...@@ -2827,9 +2841,6 @@ parse_reparse_point(struct reparse_data_buffer *buf, ...@@ -2827,9 +2841,6 @@ parse_reparse_point(struct reparse_data_buffer *buf,
} }
} }
#define SMB2_SYMLINK_STRUCT_SIZE \
(sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
static int static int
smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, struct cifs_sb_info *cifs_sb, const char *full_path,
...@@ -2841,13 +2852,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2841,13 +2852,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_open_parms oparms; struct cifs_open_parms oparms;
struct cifs_fid fid; struct cifs_fid fid;
struct kvec err_iov = {NULL, 0}; struct kvec err_iov = {NULL, 0};
struct smb2_err_rsp *err_buf = NULL;
struct smb2_symlink_err_rsp *symlink;
struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses); struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
unsigned int sub_len;
unsigned int sub_offset;
unsigned int print_len;
unsigned int print_offset;
int flags = CIFS_CP_CREATE_CLOSE_OP; int flags = CIFS_CP_CREATE_CLOSE_OP;
struct smb_rqst rqst[3]; struct smb_rqst rqst[3];
int resp_buftype[3]; int resp_buftype[3];
...@@ -2964,47 +2969,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2964,47 +2969,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
goto querty_exit; goto querty_exit;
} }
err_buf = err_iov.iov_base; rc = smb2_parse_symlink_response(cifs_sb, &err_iov, target_path);
if (le32_to_cpu(err_buf->ByteCount) < sizeof(struct smb2_symlink_err_rsp) ||
err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE) {
rc = -EINVAL;
goto querty_exit;
}
symlink = (struct smb2_symlink_err_rsp *)err_buf->ErrorData;
if (le32_to_cpu(symlink->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
le32_to_cpu(symlink->ReparseTag) != IO_REPARSE_TAG_SYMLINK) {
rc = -EINVAL;
goto querty_exit;
}
/* open must fail on symlink - reset rc */
rc = 0;
sub_len = le16_to_cpu(symlink->SubstituteNameLength);
sub_offset = le16_to_cpu(symlink->SubstituteNameOffset);
print_len = le16_to_cpu(symlink->PrintNameLength);
print_offset = le16_to_cpu(symlink->PrintNameOffset);
if (err_iov.iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len) {
rc = -EINVAL;
goto querty_exit;
}
if (err_iov.iov_len <
SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len) {
rc = -EINVAL;
goto querty_exit;
}
*target_path = cifs_strndup_from_utf16(
(char *)symlink->PathBuffer + sub_offset,
sub_len, true, cifs_sb->local_nls);
if (!(*target_path)) {
rc = -ENOMEM;
goto querty_exit;
}
convert_delimiter(*target_path, '/');
cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
querty_exit: querty_exit:
cifs_dbg(FYI, "query symlink rc %d\n", rc); cifs_dbg(FYI, "query symlink rc %d\n", rc);
...@@ -5114,7 +5079,7 @@ smb2_make_node(unsigned int xid, struct inode *inode, ...@@ -5114,7 +5079,7 @@ smb2_make_node(unsigned int xid, struct inode *inode,
{ {
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
int rc = -EPERM; int rc = -EPERM;
FILE_ALL_INFO *buf = NULL; struct cifs_open_info_data buf = {};
struct cifs_io_parms io_parms = {0}; struct cifs_io_parms io_parms = {0};
__u32 oplock = 0; __u32 oplock = 0;
struct cifs_fid fid; struct cifs_fid fid;
...@@ -5130,7 +5095,7 @@ smb2_make_node(unsigned int xid, struct inode *inode, ...@@ -5130,7 +5095,7 @@ smb2_make_node(unsigned int xid, struct inode *inode,
* and was used by default in earlier versions of Windows * and was used by default in earlier versions of Windows
*/ */
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
goto out; return rc;
/* /*
* TODO: Add ability to create instead via reparse point. Windows (e.g. * TODO: Add ability to create instead via reparse point. Windows (e.g.
...@@ -5139,16 +5104,10 @@ smb2_make_node(unsigned int xid, struct inode *inode, ...@@ -5139,16 +5104,10 @@ smb2_make_node(unsigned int xid, struct inode *inode,
*/ */
if (!S_ISCHR(mode) && !S_ISBLK(mode)) if (!S_ISCHR(mode) && !S_ISBLK(mode))
goto out; return rc;
cifs_dbg(FYI, "sfu compat create special file\n"); cifs_dbg(FYI, "sfu compat create special file\n");
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
if (buf == NULL) {
rc = -ENOMEM;
goto out;
}
oparms.tcon = tcon; oparms.tcon = tcon;
oparms.cifs_sb = cifs_sb; oparms.cifs_sb = cifs_sb;
oparms.desired_access = GENERIC_WRITE; oparms.desired_access = GENERIC_WRITE;
...@@ -5163,21 +5122,21 @@ smb2_make_node(unsigned int xid, struct inode *inode, ...@@ -5163,21 +5122,21 @@ smb2_make_node(unsigned int xid, struct inode *inode,
oplock = REQ_OPLOCK; oplock = REQ_OPLOCK;
else else
oplock = 0; oplock = 0;
rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf); rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf);
if (rc) if (rc)
goto out; return rc;
/* /*
* BB Do not bother to decode buf since no local inode yet to put * BB Do not bother to decode buf since no local inode yet to put
* timestamps in, but we can reuse it safely. * timestamps in, but we can reuse it safely.
*/ */
pdev = (struct win_dev *)buf; pdev = (struct win_dev *)&buf.fi;
io_parms.pid = current->tgid; io_parms.pid = current->tgid;
io_parms.tcon = tcon; io_parms.tcon = tcon;
io_parms.offset = 0; io_parms.offset = 0;
io_parms.length = sizeof(struct win_dev); io_parms.length = sizeof(struct win_dev);
iov[1].iov_base = buf; iov[1].iov_base = &buf.fi;
iov[1].iov_len = sizeof(struct win_dev); iov[1].iov_len = sizeof(struct win_dev);
if (S_ISCHR(mode)) { if (S_ISCHR(mode)) {
memcpy(pdev->type, "IntxCHR", 8); memcpy(pdev->type, "IntxCHR", 8);
...@@ -5196,8 +5155,8 @@ smb2_make_node(unsigned int xid, struct inode *inode, ...@@ -5196,8 +5155,8 @@ smb2_make_node(unsigned int xid, struct inode *inode,
d_drop(dentry); d_drop(dentry);
/* FIXME: add code here to set EAs */ /* FIXME: add code here to set EAs */
out:
kfree(buf); cifs_free_open_info(&buf);
return rc; return rc;
} }
......
...@@ -466,15 +466,14 @@ build_signing_ctxt(struct smb2_signing_capabilities *pneg_ctxt) ...@@ -466,15 +466,14 @@ build_signing_ctxt(struct smb2_signing_capabilities *pneg_ctxt)
/* /*
* Context Data length must be rounded to multiple of 8 for some servers * Context Data length must be rounded to multiple of 8 for some servers
*/ */
pneg_ctxt->DataLength = cpu_to_le16(DIV_ROUND_UP( pneg_ctxt->DataLength = cpu_to_le16(ALIGN(sizeof(struct smb2_signing_capabilities) -
sizeof(struct smb2_signing_capabilities) - sizeof(struct smb2_neg_context) +
sizeof(struct smb2_neg_context) + (num_algs * sizeof(u16)), 8));
(num_algs * 2 /* sizeof u16 */), 8) * 8);
pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(num_algs); pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(num_algs);
pneg_ctxt->SigningAlgorithms[0] = cpu_to_le16(SIGNING_ALG_AES_CMAC); pneg_ctxt->SigningAlgorithms[0] = cpu_to_le16(SIGNING_ALG_AES_CMAC);
ctxt_len += 2 /* sizeof le16 */ * num_algs; ctxt_len += sizeof(__le16) * num_algs;
ctxt_len = DIV_ROUND_UP(ctxt_len, 8) * 8; ctxt_len = ALIGN(ctxt_len, 8);
return ctxt_len; return ctxt_len;
/* TBD add SIGNING_ALG_AES_GMAC and/or SIGNING_ALG_HMAC_SHA256 */ /* TBD add SIGNING_ALG_AES_GMAC and/or SIGNING_ALG_HMAC_SHA256 */
} }
...@@ -511,8 +510,7 @@ build_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname) ...@@ -511,8 +510,7 @@ build_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname)
/* copy up to max of first 100 bytes of server name to NetName field */ /* copy up to max of first 100 bytes of server name to NetName field */
pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp)); pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp));
/* context size is DataLength + minimal smb2_neg_context */ /* context size is DataLength + minimal smb2_neg_context */
return DIV_ROUND_UP(le16_to_cpu(pneg_ctxt->DataLength) + return ALIGN(le16_to_cpu(pneg_ctxt->DataLength) + sizeof(struct smb2_neg_context), 8);
sizeof(struct smb2_neg_context), 8) * 8;
} }
static void static void
...@@ -557,18 +555,18 @@ assemble_neg_contexts(struct smb2_negotiate_req *req, ...@@ -557,18 +555,18 @@ assemble_neg_contexts(struct smb2_negotiate_req *req,
* round up total_len of fixed part of SMB3 negotiate request to 8 * round up total_len of fixed part of SMB3 negotiate request to 8
* byte boundary before adding negotiate contexts * byte boundary before adding negotiate contexts
*/ */
*total_len = roundup(*total_len, 8); *total_len = ALIGN(*total_len, 8);
pneg_ctxt = (*total_len) + (char *)req; pneg_ctxt = (*total_len) + (char *)req;
req->NegotiateContextOffset = cpu_to_le32(*total_len); req->NegotiateContextOffset = cpu_to_le32(*total_len);
build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt); build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt);
ctxt_len = DIV_ROUND_UP(sizeof(struct smb2_preauth_neg_context), 8) * 8; ctxt_len = ALIGN(sizeof(struct smb2_preauth_neg_context), 8);
*total_len += ctxt_len; *total_len += ctxt_len;
pneg_ctxt += ctxt_len; pneg_ctxt += ctxt_len;
build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt); build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt);
ctxt_len = DIV_ROUND_UP(sizeof(struct smb2_encryption_neg_context), 8) * 8; ctxt_len = ALIGN(sizeof(struct smb2_encryption_neg_context), 8);
*total_len += ctxt_len; *total_len += ctxt_len;
pneg_ctxt += ctxt_len; pneg_ctxt += ctxt_len;
...@@ -595,9 +593,7 @@ assemble_neg_contexts(struct smb2_negotiate_req *req, ...@@ -595,9 +593,7 @@ assemble_neg_contexts(struct smb2_negotiate_req *req,
if (server->compress_algorithm) { if (server->compress_algorithm) {
build_compression_ctxt((struct smb2_compression_capabilities_context *) build_compression_ctxt((struct smb2_compression_capabilities_context *)
pneg_ctxt); pneg_ctxt);
ctxt_len = DIV_ROUND_UP( ctxt_len = ALIGN(sizeof(struct smb2_compression_capabilities_context), 8);
sizeof(struct smb2_compression_capabilities_context),
8) * 8;
*total_len += ctxt_len; *total_len += ctxt_len;
pneg_ctxt += ctxt_len; pneg_ctxt += ctxt_len;
neg_context_count++; neg_context_count++;
...@@ -780,7 +776,7 @@ static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp, ...@@ -780,7 +776,7 @@ static int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp,
if (rc) if (rc)
break; break;
/* offsets must be 8 byte aligned */ /* offsets must be 8 byte aligned */
clen = (clen + 7) & ~0x7; clen = ALIGN(clen, 8);
offset += clen + sizeof(struct smb2_neg_context); offset += clen + sizeof(struct smb2_neg_context);
len_of_ctxts -= clen; len_of_ctxts -= clen;
} }
...@@ -2424,9 +2420,9 @@ create_sd_buf(umode_t mode, bool set_owner, unsigned int *len) ...@@ -2424,9 +2420,9 @@ create_sd_buf(umode_t mode, bool set_owner, unsigned int *len)
unsigned int acelen, acl_size, ace_count; unsigned int acelen, acl_size, ace_count;
unsigned int owner_offset = 0; unsigned int owner_offset = 0;
unsigned int group_offset = 0; unsigned int group_offset = 0;
struct smb3_acl acl; struct smb3_acl acl = {};
*len = roundup(sizeof(struct crt_sd_ctxt) + (sizeof(struct cifs_ace) * 4), 8); *len = round_up(sizeof(struct crt_sd_ctxt) + (sizeof(struct cifs_ace) * 4), 8);
if (set_owner) { if (set_owner) {
/* sizeof(struct owner_group_sids) is already multiple of 8 so no need to round */ /* sizeof(struct owner_group_sids) is already multiple of 8 so no need to round */
...@@ -2497,10 +2493,11 @@ create_sd_buf(umode_t mode, bool set_owner, unsigned int *len) ...@@ -2497,10 +2493,11 @@ create_sd_buf(umode_t mode, bool set_owner, unsigned int *len)
acl.AclRevision = ACL_REVISION; /* See 2.4.4.1 of MS-DTYP */ acl.AclRevision = ACL_REVISION; /* See 2.4.4.1 of MS-DTYP */
acl.AclSize = cpu_to_le16(acl_size); acl.AclSize = cpu_to_le16(acl_size);
acl.AceCount = cpu_to_le16(ace_count); acl.AceCount = cpu_to_le16(ace_count);
/* acl.Sbz1 and Sbz2 MBZ so are not set here, but initialized above */
memcpy(aclptr, &acl, sizeof(struct smb3_acl)); memcpy(aclptr, &acl, sizeof(struct smb3_acl));
buf->ccontext.DataLength = cpu_to_le32(ptr - (__u8 *)&buf->sd); buf->ccontext.DataLength = cpu_to_le32(ptr - (__u8 *)&buf->sd);
*len = roundup(ptr - (__u8 *)buf, 8); *len = round_up((unsigned int)(ptr - (__u8 *)buf), 8);
return buf; return buf;
} }
...@@ -2594,7 +2591,7 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, ...@@ -2594,7 +2591,7 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
* final path needs to be 8-byte aligned as specified in * final path needs to be 8-byte aligned as specified in
* MS-SMB2 2.2.13 SMB2 CREATE Request. * MS-SMB2 2.2.13 SMB2 CREATE Request.
*/ */
*out_size = roundup(*out_len * sizeof(__le16), 8); *out_size = round_up(*out_len * sizeof(__le16), 8);
*out_path = kzalloc(*out_size + sizeof(__le16) /* null */, GFP_KERNEL); *out_path = kzalloc(*out_size + sizeof(__le16) /* null */, GFP_KERNEL);
if (!*out_path) if (!*out_path)
return -ENOMEM; return -ENOMEM;
...@@ -2839,9 +2836,7 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, ...@@ -2839,9 +2836,7 @@ SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
/* MUST set path len (NameLength) to 0 opening root of share */ /* MUST set path len (NameLength) to 0 opening root of share */
req->NameLength = cpu_to_le16(uni_path_len - 2); req->NameLength = cpu_to_le16(uni_path_len - 2);
copy_size = uni_path_len; copy_size = round_up(uni_path_len, 8);
if (copy_size % 8 != 0)
copy_size = roundup(copy_size, 8);
copy_path = kzalloc(copy_size, GFP_KERNEL); copy_path = kzalloc(copy_size, GFP_KERNEL);
if (!copy_path) if (!copy_path)
return -ENOMEM; return -ENOMEM;
...@@ -3485,7 +3480,7 @@ smb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length, ...@@ -3485,7 +3480,7 @@ smb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length,
if (rc) if (rc)
return rc; return rc;
memcpy(data, begin_of_buf, buffer_length); memcpy(data, begin_of_buf, minbufsize);
return 0; return 0;
} }
...@@ -3609,7 +3604,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -3609,7 +3604,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset), rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength), le32_to_cpu(rsp->OutputBufferLength),
&rsp_iov, min_len, *data); &rsp_iov, dlen ? *dlen : min_len, *data);
if (rc && allocated) { if (rc && allocated) {
kfree(*data); kfree(*data);
*data = NULL; *data = NULL;
...@@ -3715,11 +3710,13 @@ SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst, ...@@ -3715,11 +3710,13 @@ SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst,
int int
SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, bool watch_tree, u64 persistent_fid, u64 volatile_fid, bool watch_tree,
u32 completion_filter) u32 completion_filter, u32 max_out_data_len, char **out_data,
u32 *plen /* returned data len */)
{ {
struct cifs_ses *ses = tcon->ses; struct cifs_ses *ses = tcon->ses;
struct TCP_Server_Info *server = cifs_pick_channel(ses); struct TCP_Server_Info *server = cifs_pick_channel(ses);
struct smb_rqst rqst; struct smb_rqst rqst;
struct smb2_change_notify_rsp *smb_rsp;
struct kvec iov[1]; struct kvec iov[1];
struct kvec rsp_iov = {NULL, 0}; struct kvec rsp_iov = {NULL, 0};
int resp_buftype = CIFS_NO_BUFFER; int resp_buftype = CIFS_NO_BUFFER;
...@@ -3735,6 +3732,9 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -3735,6 +3732,9 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
memset(&rqst, 0, sizeof(struct smb_rqst)); memset(&rqst, 0, sizeof(struct smb_rqst));
memset(&iov, 0, sizeof(iov)); memset(&iov, 0, sizeof(iov));
if (plen)
*plen = 0;
rqst.rq_iov = iov; rqst.rq_iov = iov;
rqst.rq_nvec = 1; rqst.rq_nvec = 1;
...@@ -3753,9 +3753,28 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -3753,9 +3753,28 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE); cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE);
trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid, trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid,
(u8)watch_tree, completion_filter, rc); (u8)watch_tree, completion_filter, rc);
} else } else {
trace_smb3_notify_done(xid, persistent_fid, tcon->tid, trace_smb3_notify_done(xid, persistent_fid, tcon->tid,
ses->Suid, (u8)watch_tree, completion_filter); ses->Suid, (u8)watch_tree, completion_filter);
/* validate that notify information is plausible */
if ((rsp_iov.iov_base == NULL) ||
(rsp_iov.iov_len < sizeof(struct smb2_change_notify_rsp)))
goto cnotify_exit;
smb_rsp = (struct smb2_change_notify_rsp *)rsp_iov.iov_base;
smb2_validate_iov(le16_to_cpu(smb_rsp->OutputBufferOffset),
le32_to_cpu(smb_rsp->OutputBufferLength), &rsp_iov,
sizeof(struct file_notify_information));
*out_data = kmemdup((char *)smb_rsp + le16_to_cpu(smb_rsp->OutputBufferOffset),
le32_to_cpu(smb_rsp->OutputBufferLength), GFP_KERNEL);
if (*out_data == NULL) {
rc = -ENOMEM;
goto cnotify_exit;
} else
*plen = le32_to_cpu(smb_rsp->OutputBufferLength);
}
cnotify_exit: cnotify_exit:
if (rqst.rq_iov) if (rqst.rq_iov)
...@@ -4103,7 +4122,7 @@ smb2_new_read_req(void **buf, unsigned int *total_len, ...@@ -4103,7 +4122,7 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
if (request_type & CHAINED_REQUEST) { if (request_type & CHAINED_REQUEST) {
if (!(request_type & END_OF_CHAIN)) { if (!(request_type & END_OF_CHAIN)) {
/* next 8-byte aligned request */ /* next 8-byte aligned request */
*total_len = DIV_ROUND_UP(*total_len, 8) * 8; *total_len = ALIGN(*total_len, 8);
shdr->NextCommand = cpu_to_le32(*total_len); shdr->NextCommand = cpu_to_le32(*total_len);
} else /* END_OF_CHAIN */ } else /* END_OF_CHAIN */
shdr->NextCommand = 0; shdr->NextCommand = 0;
......
...@@ -56,6 +56,9 @@ struct smb2_rdma_crypto_transform { ...@@ -56,6 +56,9 @@ struct smb2_rdma_crypto_transform {
#define COMPOUND_FID 0xFFFFFFFFFFFFFFFFULL #define COMPOUND_FID 0xFFFFFFFFFFFFFFFFULL
#define SMB2_SYMLINK_STRUCT_SIZE \
(sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
#define SYMLINK_ERROR_TAG 0x4c4d5953 #define SYMLINK_ERROR_TAG 0x4c4d5953
struct smb2_symlink_err_rsp { struct smb2_symlink_err_rsp {
......
...@@ -53,16 +53,12 @@ extern bool smb2_is_valid_oplock_break(char *buffer, ...@@ -53,16 +53,12 @@ extern bool smb2_is_valid_oplock_break(char *buffer,
struct TCP_Server_Info *srv); struct TCP_Server_Info *srv);
extern int smb3_handle_read_data(struct TCP_Server_Info *server, extern int smb3_handle_read_data(struct TCP_Server_Info *server,
struct mid_q_entry *mid); struct mid_q_entry *mid);
extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
struct smb2_file_all_info *src);
extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon, extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *path, struct cifs_sb_info *cifs_sb, const char *path,
__u32 *reparse_tag); __u32 *reparse_tag);
extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, struct cifs_sb_info *cifs_sb, const char *full_path,
const char *full_path, FILE_ALL_INFO *data, struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse);
bool *adjust_tz, bool *symlink);
extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
const char *full_path, __u64 size, const char *full_path, __u64 size,
struct cifs_sb_info *cifs_sb, bool set_alloc); struct cifs_sb_info *cifs_sb, bool set_alloc);
...@@ -95,9 +91,9 @@ extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, ...@@ -95,9 +91,9 @@ extern int smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, struct cifs_sb_info *cifs_sb,
const unsigned char *path, char *pbuf, const unsigned char *path, char *pbuf,
unsigned int *pbytes_read); unsigned int *pbytes_read);
extern int smb2_open_file(const unsigned int xid, int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec *iov, char **path);
struct cifs_open_parms *oparms, int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
__u32 *oplock, FILE_ALL_INFO *buf); void *buf);
extern int smb2_unlock_range(struct cifsFileInfo *cfile, extern int smb2_unlock_range(struct cifsFileInfo *cfile,
struct file_lock *flock, const unsigned int xid); struct file_lock *flock, const unsigned int xid);
extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile);
...@@ -148,7 +144,8 @@ extern int SMB2_ioctl_init(struct cifs_tcon *tcon, ...@@ -148,7 +144,8 @@ extern int SMB2_ioctl_init(struct cifs_tcon *tcon,
extern void SMB2_ioctl_free(struct smb_rqst *rqst); extern void SMB2_ioctl_free(struct smb_rqst *rqst);
extern int SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, bool watch_tree, u64 persistent_fid, u64 volatile_fid, bool watch_tree,
u32 completion_filter); u32 completion_filter, u32 max_out_data_len,
char **out_data, u32 *plen /* returned data len */);
extern int __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, extern int __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u64 persistent_fid, u64 volatile_fid,
...@@ -278,9 +275,9 @@ extern int smb2_query_info_compound(const unsigned int xid, ...@@ -278,9 +275,9 @@ extern int smb2_query_info_compound(const unsigned int xid,
struct kvec *rsp, int *buftype, struct kvec *rsp, int *buftype,
struct cifs_sb_info *cifs_sb); struct cifs_sb_info *cifs_sb);
/* query path info from the server using SMB311 POSIX extensions*/ /* query path info from the server using SMB311 POSIX extensions*/
extern int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, int smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *sb, const char *path, struct smb311_posix_qinfo *qinf, struct cifs_sb_info *cifs_sb, const char *full_path,
bool *adjust_tx, bool *symlink); struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse);
int posix_info_parse(const void *beg, const void *end, int posix_info_parse(const void *beg, const void *end,
struct smb2_posix_info_parsed *out); struct smb2_posix_info_parsed *out);
int posix_info_sid_size(const void *beg, const void *end); int posix_info_sid_size(const void *beg, const void *end);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment