mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-01 06:33:07 +00:00
Various smb client fixes, most related to better handling special file types
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmWfB8kACgkQiiy9cAdy T1HnJwv/ZhSkyu7iSsP9pWmYaCvzKkT6OljIvixTMKilzVzLe24g+sqSSkDb+SSw 6W9v26AWJqjfzVYyoy8PtR+Erbp5CKoJifAQg+1KK/jJ38ApWCqTpOtRBuo/vEX+ OFdAolOaNr74QWpylInyg5GCyjarH78cvyctukj6ZNO419fX3xKQfIc8z0yE0Osp 0M4R/xy8MuuoKuEzDVuimzY6J9qPqQCsv2jrUDXRZry1bBR0JYl7mIxg3wPT+nBM aFFVybF1kKlGvb0clmymSpI4isJg1i5v03Vy3lot01CQxI5kBftE/CkMdVNxMPBh WDs2ZIXk3c9zsyWmI7ijhEaOVeaaOG8s1MQ/374bBRMeUIzcqGY5rn/gn4JEQbnh rjy9+8oH17+h76qCxHzoVZpuoE2hwHuifQ4KKesoApV3edbPDOzMGol4gA+7KvQD pJAfIDPSZFyA9kNOQiyoritAOG2YulQ+cMcOXG4HeIHJWqnbLiiIq0mt3sJCdvqI 5PiZDR81 =K+9M -----END PGP SIGNATURE----- Merge tag 'v6.8-rc-part1-smb-client' of git://git.samba.org/sfrench/cifs-2.6 Pull smb client fixes from Steve French: "Various smb client fixes, most related to better handling special file types: - Improve handling of special file types: - performance improvement (better compounding and better caching of readdir entries that are reparse points) - extend support for creating special files (sockets, fifos, block/char devices) - fix renaming and hardlinking of reparse points - extend support for creating symlinks with IO_REPARSE_TAG_SYMLINK - Multichannel logging improvement - Exception handling fix - Minor cleanups" * tag 'v6.8-rc-part1-smb-client' of git://git.samba.org/sfrench/cifs-2.6: cifs: update internal module version number for cifs.ko cifs: remove unneeded return statement cifs: make cifs_chan_update_iface() a void function cifs: delete unnecessary NULL checks in cifs_chan_update_iface() cifs: get rid of dup length check in parse_reparse_point() smb: client: stop revalidating reparse points unnecessarily cifs: Pass unbyteswapped eof value into SMB2_set_eof() smb3: Improve exception handling in allocate_mr_list() cifs: fix in logging in cifs_chan_update_iface smb: client: handle special files and symlinks in SMB3 POSIX smb: client: cleanup smb2_query_reparse_point() smb: client: allow creating symlinks via reparse points smb: client: fix hardlinking of reparse points smb: client: fix renaming of reparse points smb: client: optimise reparse point querying smb: client: allow creating special files via reparse points smb: client: extend smb2_compound_op() to accept more commands smb: client: Fix minor whitespace errors and warnings
This commit is contained in:
commit
84e9a2d551
18 changed files with 1132 additions and 830 deletions
|
@ -152,6 +152,6 @@ extern const struct export_operations cifs_export_ops;
|
|||
#endif /* CONFIG_CIFS_NFSD_EXPORT */
|
||||
|
||||
/* when changing internal version - update following two lines at same time */
|
||||
#define SMB3_PRODUCT_BUILD 46
|
||||
#define CIFS_VERSION "2.46"
|
||||
#define SMB3_PRODUCT_BUILD 47
|
||||
#define CIFS_VERSION "2.47"
|
||||
#endif /* _CIFSFS_H */
|
||||
|
|
|
@ -192,6 +192,11 @@ struct cifs_open_info_data {
|
|||
bool symlink;
|
||||
};
|
||||
struct {
|
||||
/* ioctl response buffer */
|
||||
struct {
|
||||
int buftype;
|
||||
struct kvec iov;
|
||||
} io;
|
||||
__u32 tag;
|
||||
union {
|
||||
struct reparse_data_buffer *buf;
|
||||
|
@ -205,13 +210,17 @@ struct cifs_open_info_data {
|
|||
};
|
||||
};
|
||||
|
||||
#define cifs_open_data_reparse(d) \
|
||||
((d)->reparse_point || \
|
||||
(le32_to_cpu((d)->fi.Attributes) & ATTR_REPARSE))
|
||||
|
||||
static inline void cifs_free_open_info(struct cifs_open_info_data *data)
|
||||
static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
|
||||
{
|
||||
kfree(data->symlink_target);
|
||||
struct smb2_file_all_info *fi = &data->fi;
|
||||
u32 attrs = le32_to_cpu(fi->Attributes);
|
||||
bool ret;
|
||||
|
||||
ret = data->reparse_point || (attrs & ATTR_REPARSE);
|
||||
if (ret)
|
||||
attrs |= ATTR_REPARSE;
|
||||
fi->Attributes = cpu_to_le32(attrs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -390,12 +399,17 @@ struct smb_version_operations {
|
|||
int (*rename_pending_delete)(const char *, struct dentry *,
|
||||
const unsigned int);
|
||||
/* send rename request */
|
||||
int (*rename)(const unsigned int, struct cifs_tcon *, const char *,
|
||||
const char *, struct cifs_sb_info *);
|
||||
int (*rename)(const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
struct dentry *source_dentry,
|
||||
const char *from_name, const char *to_name,
|
||||
struct cifs_sb_info *cifs_sb);
|
||||
/* send create hardlink request */
|
||||
int (*create_hardlink)(const unsigned int, struct cifs_tcon *,
|
||||
const char *, const char *,
|
||||
struct cifs_sb_info *);
|
||||
int (*create_hardlink)(const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
struct dentry *source_dentry,
|
||||
const char *from_name, const char *to_name,
|
||||
struct cifs_sb_info *cifs_sb);
|
||||
/* query symlink target */
|
||||
int (*query_symlink)(const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
|
@ -560,6 +574,12 @@ struct smb_version_operations {
|
|||
int (*parse_reparse_point)(struct cifs_sb_info *cifs_sb,
|
||||
struct kvec *rsp_iov,
|
||||
struct cifs_open_info_data *data);
|
||||
int (*create_reparse_symlink)(const unsigned int xid,
|
||||
struct inode *inode,
|
||||
struct dentry *dentry,
|
||||
struct cifs_tcon *tcon,
|
||||
const char *full_path,
|
||||
const char *symname);
|
||||
};
|
||||
|
||||
struct smb_version_values {
|
||||
|
@ -1545,6 +1565,7 @@ struct cifsInodeInfo {
|
|||
spinlock_t deferred_lock; /* protection on deferred list */
|
||||
bool lease_granted; /* Flag to indicate whether lease or oplock is granted. */
|
||||
char *symlink_target;
|
||||
__u32 reparse_tag;
|
||||
};
|
||||
|
||||
static inline struct cifsInodeInfo *
|
||||
|
@ -2238,8 +2259,8 @@ static inline void cifs_sg_set_buf(struct sg_table *sgtable,
|
|||
|
||||
struct smb2_compound_vars {
|
||||
struct cifs_open_parms oparms;
|
||||
struct kvec rsp_iov[3];
|
||||
struct smb_rqst rqst[3];
|
||||
struct kvec rsp_iov[MAX_COMPOUND];
|
||||
struct smb_rqst rqst[MAX_COMPOUND];
|
||||
struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
|
||||
struct kvec qi_iov;
|
||||
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
|
||||
|
|
|
@ -211,8 +211,12 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
|
|||
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
|
||||
struct cifs_fattr *fattr,
|
||||
struct cifs_open_info_data *data);
|
||||
extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
|
||||
struct super_block *sb, unsigned int xid);
|
||||
|
||||
extern int smb311_posix_get_inode_info(struct inode **inode,
|
||||
const char *full_path,
|
||||
struct cifs_open_info_data *data,
|
||||
struct super_block *sb,
|
||||
const unsigned int xid);
|
||||
extern int cifs_get_inode_info_unix(struct inode **pinode,
|
||||
const unsigned char *search_path,
|
||||
struct super_block *sb, unsigned int xid);
|
||||
|
@ -435,16 +439,19 @@ extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
int remap_special_chars);
|
||||
extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *name, struct cifs_sb_info *cifs_sb);
|
||||
extern int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *from_name, const char *to_name,
|
||||
struct cifs_sb_info *cifs_sb);
|
||||
int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct dentry *source_dentry,
|
||||
const char *from_name, const char *to_name,
|
||||
struct cifs_sb_info *cifs_sb);
|
||||
extern int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
int netfid, const char *target_name,
|
||||
const struct nls_table *nls_codepage,
|
||||
int remap_special_chars);
|
||||
extern int CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *from_name, const char *to_name,
|
||||
struct cifs_sb_info *cifs_sb);
|
||||
int CIFSCreateHardLink(const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
struct dentry *source_dentry,
|
||||
const char *from_name, const char *to_name,
|
||||
struct cifs_sb_info *cifs_sb);
|
||||
extern int CIFSUnixCreateHardLink(const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
const char *fromName, const char *toName,
|
||||
|
@ -649,7 +656,7 @@ cifs_chan_is_iface_active(struct cifs_ses *ses,
|
|||
struct TCP_Server_Info *server);
|
||||
void
|
||||
cifs_disable_secondary_channels(struct cifs_ses *ses);
|
||||
int
|
||||
void
|
||||
cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server);
|
||||
int
|
||||
SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_mount);
|
||||
|
@ -760,4 +767,11 @@ static inline void release_mid(struct mid_q_entry *mid)
|
|||
kref_put(&mid->refcount, __release_mid);
|
||||
}
|
||||
|
||||
static inline void cifs_free_open_info(struct cifs_open_info_data *data)
|
||||
{
|
||||
kfree(data->symlink_target);
|
||||
free_rsp_buf(data->reparse.io.buftype, data->reparse.io.iov.iov_base);
|
||||
memset(data, 0, sizeof(*data));
|
||||
}
|
||||
|
||||
#endif /* _CIFSPROTO_H */
|
||||
|
|
|
@ -2149,10 +2149,10 @@ CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id)
|
|||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *from_name, const char *to_name,
|
||||
struct cifs_sb_info *cifs_sb)
|
||||
int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct dentry *source_dentry,
|
||||
const char *from_name, const char *to_name,
|
||||
struct cifs_sb_info *cifs_sb)
|
||||
{
|
||||
int rc = 0;
|
||||
RENAME_REQ *pSMB = NULL;
|
||||
|
@ -2530,10 +2530,11 @@ CIFSUnixCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *from_name, const char *to_name,
|
||||
struct cifs_sb_info *cifs_sb)
|
||||
int CIFSCreateHardLink(const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
struct dentry *source_dentry,
|
||||
const char *from_name, const char *to_name,
|
||||
struct cifs_sb_info *cifs_sb)
|
||||
{
|
||||
int rc = 0;
|
||||
NT_RENAME_REQ *pSMB = NULL;
|
||||
|
@ -2699,11 +2700,12 @@ int cifs_query_reparse_point(const unsigned int xid,
|
|||
u32 *tag, struct kvec *rsp,
|
||||
int *rsp_buftype)
|
||||
{
|
||||
struct reparse_data_buffer *buf;
|
||||
struct cifs_open_parms oparms;
|
||||
TRANSACT_IOCTL_REQ *io_req = NULL;
|
||||
TRANSACT_IOCTL_RSP *io_rsp = NULL;
|
||||
struct cifs_fid fid;
|
||||
__u32 data_offset, data_count;
|
||||
__u32 data_offset, data_count, len;
|
||||
__u8 *start, *end;
|
||||
int io_rsp_len;
|
||||
int oplock = 0;
|
||||
|
@ -2773,7 +2775,16 @@ int cifs_query_reparse_point(const unsigned int xid,
|
|||
goto error;
|
||||
}
|
||||
|
||||
*tag = le32_to_cpu(((struct reparse_data_buffer *)start)->ReparseTag);
|
||||
data_count = le16_to_cpu(io_rsp->ByteCount);
|
||||
buf = (struct reparse_data_buffer *)start;
|
||||
len = sizeof(*buf);
|
||||
if (data_count < len ||
|
||||
data_count < le16_to_cpu(buf->ReparseDataLength) + len) {
|
||||
rc = -EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
*tag = le32_to_cpu(buf->ReparseTag);
|
||||
rsp->iov_base = io_rsp;
|
||||
rsp->iov_len = io_rsp_len;
|
||||
*rsp_buftype = CIFS_LARGE_BUFFER;
|
||||
|
|
|
@ -483,6 +483,7 @@ static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_
|
|||
static int reconnect_dfs_server(struct TCP_Server_Info *server)
|
||||
{
|
||||
struct dfs_cache_tgt_iterator *target_hint = NULL;
|
||||
|
||||
DFS_CACHE_TGT_LIST(tl);
|
||||
int num_targets = 0;
|
||||
int rc = 0;
|
||||
|
@ -745,6 +746,7 @@ cifs_read_from_socket(struct TCP_Server_Info *server, char *buf,
|
|||
{
|
||||
struct msghdr smb_msg = {};
|
||||
struct kvec iov = {.iov_base = buf, .iov_len = to_read};
|
||||
|
||||
iov_iter_kvec(&smb_msg.msg_iter, ITER_DEST, &iov, 1, to_read);
|
||||
|
||||
return cifs_readv_from_socket(server, &smb_msg);
|
||||
|
@ -1400,11 +1402,13 @@ cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs)
|
|||
case AF_INET: {
|
||||
struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr;
|
||||
struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs;
|
||||
|
||||
return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr);
|
||||
}
|
||||
case AF_INET6: {
|
||||
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr;
|
||||
struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs;
|
||||
|
||||
return (ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr)
|
||||
&& saddr6->sin6_scope_id == vaddr6->sin6_scope_id);
|
||||
}
|
||||
|
@ -2599,8 +2603,8 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
|
|||
rc = -EOPNOTSUPP;
|
||||
goto out_fail;
|
||||
} else {
|
||||
cifs_dbg(VFS, "Check vers= mount option. SMB3.11 "
|
||||
"disabled but required for POSIX extensions\n");
|
||||
cifs_dbg(VFS,
|
||||
"Check vers= mount option. SMB3.11 disabled but required for POSIX extensions\n");
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out_fail;
|
||||
}
|
||||
|
@ -2743,7 +2747,6 @@ cifs_put_tlink(struct tcon_link *tlink)
|
|||
if (!IS_ERR(tlink_tcon(tlink)))
|
||||
cifs_put_tcon(tlink_tcon(tlink));
|
||||
kfree(tlink);
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -2884,6 +2887,7 @@ static inline void
|
|||
cifs_reclassify_socket4(struct socket *sock)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
|
||||
BUG_ON(!sock_allow_reclassification(sk));
|
||||
sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS",
|
||||
&cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]);
|
||||
|
@ -2893,6 +2897,7 @@ static inline void
|
|||
cifs_reclassify_socket6(struct socket *sock)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
|
||||
BUG_ON(!sock_allow_reclassification(sk));
|
||||
sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS",
|
||||
&cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]);
|
||||
|
@ -2927,15 +2932,18 @@ static int
|
|||
bind_socket(struct TCP_Server_Info *server)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (server->srcaddr.ss_family != AF_UNSPEC) {
|
||||
/* Bind to the specified local IP address */
|
||||
struct socket *socket = server->ssocket;
|
||||
|
||||
rc = kernel_bind(socket,
|
||||
(struct sockaddr *) &server->srcaddr,
|
||||
sizeof(server->srcaddr));
|
||||
if (rc < 0) {
|
||||
struct sockaddr_in *saddr4;
|
||||
struct sockaddr_in6 *saddr6;
|
||||
|
||||
saddr4 = (struct sockaddr_in *)&server->srcaddr;
|
||||
saddr6 = (struct sockaddr_in6 *)&server->srcaddr;
|
||||
if (saddr6->sin6_family == AF_INET6)
|
||||
|
@ -3165,6 +3173,7 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
|
|||
|
||||
if (!CIFSSMBQFSUnixInfo(xid, tcon)) {
|
||||
__u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
||||
|
||||
cifs_dbg(FYI, "unix caps which server supports %lld\n", cap);
|
||||
/*
|
||||
* check for reconnect case in which we do not
|
||||
|
@ -3668,7 +3677,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
|
|||
smb_buffer_response = smb_buffer;
|
||||
|
||||
header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
|
||||
NULL /*no tid */ , 4 /*wct */ );
|
||||
NULL /*no tid */, 4 /*wct */);
|
||||
|
||||
smb_buffer->Mid = get_next_mid(ses->server);
|
||||
smb_buffer->Uid = ses->Suid;
|
||||
|
@ -3687,12 +3696,12 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
|
|||
if (ses->server->sign)
|
||||
smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
|
||||
|
||||
if (ses->capabilities & CAP_STATUS32) {
|
||||
if (ses->capabilities & CAP_STATUS32)
|
||||
smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
|
||||
}
|
||||
if (ses->capabilities & CAP_DFS) {
|
||||
|
||||
if (ses->capabilities & CAP_DFS)
|
||||
smb_buffer->Flags2 |= SMBFLG2_DFS;
|
||||
}
|
||||
|
||||
if (ses->capabilities & CAP_UNICODE) {
|
||||
smb_buffer->Flags2 |= SMBFLG2_UNICODE;
|
||||
length =
|
||||
|
|
|
@ -680,9 +680,10 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
|
|||
full_path, d_inode(direntry));
|
||||
|
||||
again:
|
||||
if (pTcon->posix_extensions)
|
||||
rc = smb311_posix_get_inode_info(&newInode, full_path, parent_dir_inode->i_sb, xid);
|
||||
else if (pTcon->unix_ext) {
|
||||
if (pTcon->posix_extensions) {
|
||||
rc = smb311_posix_get_inode_info(&newInode, full_path, NULL,
|
||||
parent_dir_inode->i_sb, xid);
|
||||
} else if (pTcon->unix_ext) {
|
||||
rc = cifs_get_inode_info_unix(&newInode, full_path,
|
||||
parent_dir_inode->i_sb, xid);
|
||||
} else {
|
||||
|
|
|
@ -1020,14 +1020,16 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
|
|||
if (!is_interrupt_error(rc))
|
||||
mapping_set_error(inode->i_mapping, rc);
|
||||
|
||||
if (tcon->posix_extensions)
|
||||
rc = smb311_posix_get_inode_info(&inode, full_path, inode->i_sb, xid);
|
||||
else if (tcon->unix_ext)
|
||||
if (tcon->posix_extensions) {
|
||||
rc = smb311_posix_get_inode_info(&inode, full_path,
|
||||
NULL, inode->i_sb, xid);
|
||||
} else if (tcon->unix_ext) {
|
||||
rc = cifs_get_inode_info_unix(&inode, full_path,
|
||||
inode->i_sb, xid);
|
||||
else
|
||||
} else {
|
||||
rc = cifs_get_inode_info(&inode, full_path, NULL,
|
||||
inode->i_sb, xid, NULL);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Else we are writing out data to server already and could deadlock if
|
||||
|
|
|
@ -182,6 +182,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
|
|||
inode->i_mode = fattr->cf_mode;
|
||||
|
||||
cifs_i->cifsAttrs = fattr->cf_cifsattrs;
|
||||
cifs_i->reparse_tag = fattr->cf_cifstag;
|
||||
|
||||
if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL)
|
||||
cifs_i->time = 0;
|
||||
|
@ -209,7 +210,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
|
|||
inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9;
|
||||
}
|
||||
|
||||
if (S_ISLNK(fattr->cf_mode)) {
|
||||
if (S_ISLNK(fattr->cf_mode) && fattr->cf_symlink_target) {
|
||||
kfree(cifs_i->symlink_target);
|
||||
cifs_i->symlink_target = fattr->cf_symlink_target;
|
||||
fattr->cf_symlink_target = NULL;
|
||||
|
@ -691,29 +692,36 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
|
|||
fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj;
|
||||
}
|
||||
|
||||
/*
|
||||
* The srv fs device id is overridden on network mount so setting
|
||||
* @fattr->cf_rdev isn't needed here.
|
||||
*/
|
||||
fattr->cf_eof = le64_to_cpu(info->EndOfFile);
|
||||
fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
|
||||
fattr->cf_createtime = le64_to_cpu(info->CreationTime);
|
||||
|
||||
fattr->cf_nlink = le32_to_cpu(info->HardLinks);
|
||||
fattr->cf_mode = (umode_t) le32_to_cpu(info->Mode);
|
||||
/* The srv fs device id is overridden on network mount so setting rdev isn't needed here */
|
||||
/* fattr->cf_rdev = le32_to_cpu(info->DeviceId); */
|
||||
|
||||
if (data->symlink) {
|
||||
fattr->cf_mode |= S_IFLNK;
|
||||
fattr->cf_dtype = DT_LNK;
|
||||
fattr->cf_symlink_target = data->symlink_target;
|
||||
data->symlink_target = NULL;
|
||||
} else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
|
||||
if (cifs_open_data_reparse(data) &&
|
||||
cifs_reparse_point_to_fattr(cifs_sb, fattr, data))
|
||||
goto out_reparse;
|
||||
|
||||
fattr->cf_mode &= ~S_IFMT;
|
||||
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
|
||||
fattr->cf_mode |= S_IFDIR;
|
||||
fattr->cf_dtype = DT_DIR;
|
||||
} else { /* file */
|
||||
fattr->cf_mode |= S_IFREG;
|
||||
fattr->cf_dtype = DT_REG;
|
||||
}
|
||||
/* else if reparse point ... TODO: add support for FIFO and blk dev; special file types */
|
||||
|
||||
out_reparse:
|
||||
if (S_ISLNK(fattr->cf_mode)) {
|
||||
if (likely(data->symlink_target))
|
||||
fattr->cf_eof = strnlen(data->symlink_target, PATH_MAX);
|
||||
fattr->cf_symlink_target = data->symlink_target;
|
||||
data->symlink_target = NULL;
|
||||
}
|
||||
sid_to_id(cifs_sb, owner, fattr, SIDOWNER);
|
||||
sid_to_id(cifs_sb, group, fattr, SIDGROUP);
|
||||
|
||||
|
@ -738,25 +746,25 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
|
|||
if (tag == IO_REPARSE_TAG_NFS && buf) {
|
||||
switch (le64_to_cpu(buf->InodeType)) {
|
||||
case NFS_SPECFILE_CHR:
|
||||
fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_mode |= S_IFCHR;
|
||||
fattr->cf_dtype = DT_CHR;
|
||||
fattr->cf_rdev = nfs_mkdev(buf);
|
||||
break;
|
||||
case NFS_SPECFILE_BLK:
|
||||
fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_mode |= S_IFBLK;
|
||||
fattr->cf_dtype = DT_BLK;
|
||||
fattr->cf_rdev = nfs_mkdev(buf);
|
||||
break;
|
||||
case NFS_SPECFILE_FIFO:
|
||||
fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_mode |= S_IFIFO;
|
||||
fattr->cf_dtype = DT_FIFO;
|
||||
break;
|
||||
case NFS_SPECFILE_SOCK:
|
||||
fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_mode |= S_IFSOCK;
|
||||
fattr->cf_dtype = DT_SOCK;
|
||||
break;
|
||||
case NFS_SPECFILE_LNK:
|
||||
fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_mode |= S_IFLNK;
|
||||
fattr->cf_dtype = DT_LNK;
|
||||
break;
|
||||
default:
|
||||
|
@ -768,29 +776,29 @@ bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
|
|||
|
||||
switch (tag) {
|
||||
case IO_REPARSE_TAG_LX_SYMLINK:
|
||||
fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_mode |= S_IFLNK;
|
||||
fattr->cf_dtype = DT_LNK;
|
||||
break;
|
||||
case IO_REPARSE_TAG_LX_FIFO:
|
||||
fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_mode |= S_IFIFO;
|
||||
fattr->cf_dtype = DT_FIFO;
|
||||
break;
|
||||
case IO_REPARSE_TAG_AF_UNIX:
|
||||
fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_mode |= S_IFSOCK;
|
||||
fattr->cf_dtype = DT_SOCK;
|
||||
break;
|
||||
case IO_REPARSE_TAG_LX_CHR:
|
||||
fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_mode |= S_IFCHR;
|
||||
fattr->cf_dtype = DT_CHR;
|
||||
break;
|
||||
case IO_REPARSE_TAG_LX_BLK:
|
||||
fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_mode |= S_IFBLK;
|
||||
fattr->cf_dtype = DT_BLK;
|
||||
break;
|
||||
case 0: /* SMB1 symlink */
|
||||
case IO_REPARSE_TAG_SYMLINK:
|
||||
case IO_REPARSE_TAG_NFS:
|
||||
fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode;
|
||||
fattr->cf_mode |= S_IFLNK;
|
||||
fattr->cf_dtype = DT_LNK;
|
||||
break;
|
||||
default:
|
||||
|
@ -830,6 +838,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
|
|||
fattr->cf_createtime = le64_to_cpu(info->CreationTime);
|
||||
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
|
||||
|
||||
fattr->cf_mode = cifs_sb->ctx->file_mode;
|
||||
if (cifs_open_data_reparse(data) &&
|
||||
cifs_reparse_point_to_fattr(cifs_sb, fattr, data))
|
||||
goto out_reparse;
|
||||
|
@ -1061,7 +1070,9 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
|
|||
const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
const char *full_path,
|
||||
struct cifs_fattr *fattr)
|
||||
struct cifs_fattr *fattr,
|
||||
struct cifs_sid *owner,
|
||||
struct cifs_sid *group)
|
||||
{
|
||||
struct TCP_Server_Info *server = tcon->ses->server;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
|
@ -1076,6 +1087,9 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
|
|||
&rsp_iov, &rsp_buftype);
|
||||
if (!rc)
|
||||
iov = &rsp_iov;
|
||||
} else if (data->reparse.io.buftype != CIFS_NO_BUFFER &&
|
||||
data->reparse.io.iov.iov_base) {
|
||||
iov = &data->reparse.io.iov;
|
||||
}
|
||||
|
||||
rc = -EOPNOTSUPP;
|
||||
|
@ -1092,17 +1106,22 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
|
|||
rc = 0;
|
||||
goto out;
|
||||
default:
|
||||
if (data->symlink_target) {
|
||||
/* Check for cached reparse point data */
|
||||
if (data->symlink_target || data->reparse.buf) {
|
||||
rc = 0;
|
||||
} else if (server->ops->parse_reparse_point) {
|
||||
} else if (iov && server->ops->parse_reparse_point) {
|
||||
rc = server->ops->parse_reparse_point(cifs_sb,
|
||||
iov, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
cifs_open_info_to_fattr(fattr, data, sb);
|
||||
if (tcon->posix_extensions)
|
||||
smb311_posix_info_to_fattr(fattr, data, owner, group, sb);
|
||||
else
|
||||
cifs_open_info_to_fattr(fattr, data, sb);
|
||||
out:
|
||||
fattr->cf_cifstag = data->reparse.tag;
|
||||
free_rsp_buf(rsp_buftype, rsp_iov.iov_base);
|
||||
return rc;
|
||||
}
|
||||
|
@ -1152,7 +1171,8 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
|
|||
*/
|
||||
if (cifs_open_data_reparse(data)) {
|
||||
rc = reparse_info_to_fattr(data, sb, xid, tcon,
|
||||
full_path, fattr);
|
||||
full_path, fattr,
|
||||
NULL, NULL);
|
||||
} else {
|
||||
cifs_open_info_to_fattr(fattr, data, sb);
|
||||
}
|
||||
|
@ -1290,18 +1310,19 @@ int cifs_get_inode_info(struct inode **inode,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int smb311_posix_get_fattr(struct cifs_fattr *fattr,
|
||||
static int smb311_posix_get_fattr(struct cifs_open_info_data *data,
|
||||
struct cifs_fattr *fattr,
|
||||
const char *full_path,
|
||||
struct super_block *sb,
|
||||
const unsigned int xid)
|
||||
{
|
||||
struct cifs_open_info_data data = {};
|
||||
struct cifs_open_info_data tmp_data = {};
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
struct cifs_tcon *tcon;
|
||||
struct tcon_link *tlink;
|
||||
struct cifs_sid owner, group;
|
||||
int tmprc;
|
||||
int rc;
|
||||
int rc = 0;
|
||||
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
if (IS_ERR(tlink))
|
||||
|
@ -1309,12 +1330,14 @@ static int smb311_posix_get_fattr(struct cifs_fattr *fattr,
|
|||
tcon = tlink_tcon(tlink);
|
||||
|
||||
/*
|
||||
* 1. Fetch file metadata
|
||||
* 1. Fetch file metadata if not provided (data)
|
||||
*/
|
||||
|
||||
rc = smb311_posix_query_path_info(xid, tcon, cifs_sb,
|
||||
full_path, &data,
|
||||
&owner, &group);
|
||||
if (!data) {
|
||||
rc = smb311_posix_query_path_info(xid, tcon, cifs_sb,
|
||||
full_path, &tmp_data,
|
||||
&owner, &group);
|
||||
data = &tmp_data;
|
||||
}
|
||||
|
||||
/*
|
||||
* 2. Convert it to internal cifs metadata (fattr)
|
||||
|
@ -1322,7 +1345,14 @@ static int smb311_posix_get_fattr(struct cifs_fattr *fattr,
|
|||
|
||||
switch (rc) {
|
||||
case 0:
|
||||
smb311_posix_info_to_fattr(fattr, &data, &owner, &group, sb);
|
||||
if (cifs_open_data_reparse(data)) {
|
||||
rc = reparse_info_to_fattr(data, sb, xid, tcon,
|
||||
full_path, fattr,
|
||||
&owner, &group);
|
||||
} else {
|
||||
smb311_posix_info_to_fattr(fattr, data,
|
||||
&owner, &group, sb);
|
||||
}
|
||||
break;
|
||||
case -EREMOTE:
|
||||
/* DFS link, no metadata available on this server */
|
||||
|
@ -1353,12 +1383,15 @@ static int smb311_posix_get_fattr(struct cifs_fattr *fattr,
|
|||
|
||||
out:
|
||||
cifs_put_tlink(tlink);
|
||||
cifs_free_open_info(&data);
|
||||
cifs_free_open_info(data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int smb311_posix_get_inode_info(struct inode **inode, const char *full_path,
|
||||
struct super_block *sb, const unsigned int xid)
|
||||
int smb311_posix_get_inode_info(struct inode **inode,
|
||||
const char *full_path,
|
||||
struct cifs_open_info_data *data,
|
||||
struct super_block *sb,
|
||||
const unsigned int xid)
|
||||
{
|
||||
struct cifs_fattr fattr = {};
|
||||
int rc;
|
||||
|
@ -1368,7 +1401,7 @@ int smb311_posix_get_inode_info(struct inode **inode, const char *full_path,
|
|||
return 0;
|
||||
}
|
||||
|
||||
rc = smb311_posix_get_fattr(&fattr, full_path, sb, xid);
|
||||
rc = smb311_posix_get_fattr(data, &fattr, full_path, sb, xid);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
|
@ -1516,7 +1549,7 @@ struct inode *cifs_root_iget(struct super_block *sb)
|
|||
|
||||
convert_delimiter(path, CIFS_DIR_SEP(cifs_sb));
|
||||
if (tcon->posix_extensions)
|
||||
rc = smb311_posix_get_fattr(&fattr, path, sb, xid);
|
||||
rc = smb311_posix_get_fattr(NULL, &fattr, path, sb, xid);
|
||||
else
|
||||
rc = cifs_get_fattr(NULL, sb, xid, NULL, &fattr, &inode, path);
|
||||
|
||||
|
@ -1889,16 +1922,18 @@ cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
|
|||
int rc = 0;
|
||||
struct inode *inode = NULL;
|
||||
|
||||
if (tcon->posix_extensions)
|
||||
rc = smb311_posix_get_inode_info(&inode, full_path, parent->i_sb, xid);
|
||||
if (tcon->posix_extensions) {
|
||||
rc = smb311_posix_get_inode_info(&inode, full_path,
|
||||
NULL, parent->i_sb, xid);
|
||||
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
|
||||
else if (tcon->unix_ext)
|
||||
} else if (tcon->unix_ext) {
|
||||
rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb,
|
||||
xid);
|
||||
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
|
||||
else
|
||||
} else {
|
||||
rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb,
|
||||
xid, NULL);
|
||||
}
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
@ -2219,7 +2254,8 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry,
|
|||
return -ENOSYS;
|
||||
|
||||
/* try path-based rename first */
|
||||
rc = server->ops->rename(xid, tcon, from_path, to_path, cifs_sb);
|
||||
rc = server->ops->rename(xid, tcon, from_dentry,
|
||||
from_path, to_path, cifs_sb);
|
||||
|
||||
/*
|
||||
* Don't bother with rename by filehandle unless file is busy and
|
||||
|
@ -2579,13 +2615,15 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry)
|
|||
dentry, cifs_get_time(dentry), jiffies);
|
||||
|
||||
again:
|
||||
if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions)
|
||||
rc = smb311_posix_get_inode_info(&inode, full_path, sb, xid);
|
||||
else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
|
||||
if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions) {
|
||||
rc = smb311_posix_get_inode_info(&inode, full_path,
|
||||
NULL, sb, xid);
|
||||
} else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) {
|
||||
rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
|
||||
else
|
||||
} else {
|
||||
rc = cifs_get_inode_info(&inode, full_path, NULL, sb,
|
||||
xid, NULL);
|
||||
}
|
||||
if (rc == -EAGAIN && count++ < 10)
|
||||
goto again;
|
||||
out:
|
||||
|
|
|
@ -510,8 +510,8 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
|
|||
rc = -ENOSYS;
|
||||
goto cifs_hl_exit;
|
||||
}
|
||||
rc = server->ops->create_hardlink(xid, tcon, from_name, to_name,
|
||||
cifs_sb);
|
||||
rc = server->ops->create_hardlink(xid, tcon, old_file,
|
||||
from_name, to_name, cifs_sb);
|
||||
if ((rc == -EIO) || (rc == -EINVAL))
|
||||
rc = -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -569,6 +569,7 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
|
|||
int rc = -EOPNOTSUPP;
|
||||
unsigned int xid;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
struct TCP_Server_Info *server;
|
||||
struct tcon_link *tlink;
|
||||
struct cifs_tcon *pTcon;
|
||||
const char *full_path;
|
||||
|
@ -590,6 +591,7 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
|
|||
goto symlink_exit;
|
||||
}
|
||||
pTcon = tlink_tcon(tlink);
|
||||
server = cifs_pick_channel(pTcon->ses);
|
||||
|
||||
full_path = build_path_from_dentry(direntry, page);
|
||||
if (IS_ERR(full_path)) {
|
||||
|
@ -601,27 +603,32 @@ cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
|
|||
cifs_dbg(FYI, "symname is %s\n", symname);
|
||||
|
||||
/* BB what if DFS and this volume is on different share? BB */
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
|
||||
rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
|
||||
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
|
||||
else if (pTcon->unix_ext)
|
||||
} else if (pTcon->unix_ext) {
|
||||
rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
|
||||
cifs_sb->local_nls,
|
||||
cifs_remap(cifs_sb));
|
||||
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
|
||||
/* else
|
||||
rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
|
||||
cifs_sb_target->local_nls); */
|
||||
} else if (server->ops->create_reparse_symlink) {
|
||||
rc = server->ops->create_reparse_symlink(xid, inode, direntry,
|
||||
pTcon, full_path,
|
||||
symname);
|
||||
goto symlink_exit;
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
if (pTcon->posix_extensions)
|
||||
rc = smb311_posix_get_inode_info(&newinode, full_path, inode->i_sb, xid);
|
||||
else if (pTcon->unix_ext)
|
||||
if (pTcon->posix_extensions) {
|
||||
rc = smb311_posix_get_inode_info(&newinode, full_path,
|
||||
NULL, inode->i_sb, xid);
|
||||
} else if (pTcon->unix_ext) {
|
||||
rc = cifs_get_inode_info_unix(&newinode, full_path,
|
||||
inode->i_sb, xid);
|
||||
else
|
||||
} else {
|
||||
rc = cifs_get_inode_info(&newinode, full_path, NULL,
|
||||
inode->i_sb, xid, NULL);
|
||||
}
|
||||
|
||||
if (rc != 0) {
|
||||
cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n",
|
||||
|
|
|
@ -55,6 +55,23 @@ static inline void dump_cifs_file_struct(struct file *file, char *label)
|
|||
}
|
||||
#endif /* DEBUG2 */
|
||||
|
||||
/*
|
||||
* Match a reparse point inode if reparse tag and ctime haven't changed.
|
||||
*
|
||||
* Windows Server updates ctime of reparse points when their data have changed.
|
||||
* The server doesn't allow changing reparse tags from existing reparse points,
|
||||
* though it's worth checking.
|
||||
*/
|
||||
static inline bool reparse_inode_match(struct inode *inode,
|
||||
struct cifs_fattr *fattr)
|
||||
{
|
||||
struct timespec64 ctime = inode_get_ctime(inode);
|
||||
|
||||
return (CIFS_I(inode)->cifsAttrs & ATTR_REPARSE) &&
|
||||
CIFS_I(inode)->reparse_tag == fattr->cf_cifstag &&
|
||||
timespec64_equal(&ctime, &fattr->cf_ctime);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to preload the dcache with the results from the FIND_FIRST/NEXT
|
||||
*
|
||||
|
@ -71,6 +88,7 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
|
|||
struct super_block *sb = parent->d_sb;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
|
||||
int rc;
|
||||
|
||||
cifs_dbg(FYI, "%s: for %s\n", __func__, name->name);
|
||||
|
||||
|
@ -82,9 +100,11 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
|
|||
* We'll end up doing an on the wire call either way and
|
||||
* this spares us an invalidation.
|
||||
*/
|
||||
if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL)
|
||||
return;
|
||||
retry:
|
||||
if ((fattr->cf_cifsattrs & ATTR_REPARSE) ||
|
||||
(fattr->cf_flags & CIFS_FATTR_NEED_REVAL))
|
||||
return;
|
||||
|
||||
dentry = d_alloc_parallel(parent, name, &wq);
|
||||
}
|
||||
if (IS_ERR(dentry))
|
||||
|
@ -104,12 +124,34 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
|
|||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM))
|
||||
fattr->cf_uniqueid = CIFS_I(inode)->uniqueid;
|
||||
|
||||
/* update inode in place
|
||||
* if both i_ino and i_mode didn't change */
|
||||
if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid &&
|
||||
cifs_fattr_to_inode(inode, fattr) == 0) {
|
||||
dput(dentry);
|
||||
return;
|
||||
/*
|
||||
* Update inode in place if both i_ino and i_mode didn't
|
||||
* change.
|
||||
*/
|
||||
if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid) {
|
||||
/*
|
||||
* Query dir responses don't provide enough
|
||||
* information about reparse points other than
|
||||
* their reparse tags. Save an invalidation by
|
||||
* not clobbering the existing mode, size and
|
||||
* symlink target (if any) when reparse tag and
|
||||
* ctime haven't changed.
|
||||
*/
|
||||
rc = 0;
|
||||
if (fattr->cf_cifsattrs & ATTR_REPARSE) {
|
||||
if (likely(reparse_inode_match(inode, fattr))) {
|
||||
fattr->cf_mode = inode->i_mode;
|
||||
fattr->cf_eof = CIFS_I(inode)->server_eof;
|
||||
fattr->cf_symlink_target = NULL;
|
||||
} else {
|
||||
CIFS_I(inode)->time = 0;
|
||||
rc = -ESTALE;
|
||||
}
|
||||
}
|
||||
if (!rc && !cifs_fattr_to_inode(inode, fattr)) {
|
||||
dput(dentry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
d_invalidate(dentry);
|
||||
|
@ -127,29 +169,6 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
|
|||
dput(dentry);
|
||||
}
|
||||
|
||||
static bool reparse_file_needs_reval(const struct cifs_fattr *fattr)
|
||||
{
|
||||
if (!(fattr->cf_cifsattrs & ATTR_REPARSE))
|
||||
return false;
|
||||
/*
|
||||
* The DFS tags should be only intepreted by server side as per
|
||||
* MS-FSCC 2.1.2.1, but let's include them anyway.
|
||||
*
|
||||
* Besides, if cf_cifstag is unset (0), then we still need it to be
|
||||
* revalidated to know exactly what reparse point it is.
|
||||
*/
|
||||
switch (fattr->cf_cifstag) {
|
||||
case IO_REPARSE_TAG_DFS:
|
||||
case IO_REPARSE_TAG_DFSR:
|
||||
case IO_REPARSE_TAG_SYMLINK:
|
||||
case IO_REPARSE_TAG_NFS:
|
||||
case IO_REPARSE_TAG_MOUNT_POINT:
|
||||
case 0:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
|
||||
{
|
||||
|
@ -181,14 +200,6 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
|
|||
}
|
||||
|
||||
out_reparse:
|
||||
/*
|
||||
* We need to revalidate it further to make a decision about whether it
|
||||
* is a symbolic link, DFS referral or a reparse point with a direct
|
||||
* access like junctions, deduplicated files, NFS symlinks.
|
||||
*/
|
||||
if (reparse_file_needs_reval(fattr))
|
||||
fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
|
||||
|
||||
/* non-unix readdir doesn't provide nlink */
|
||||
fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
|
||||
|
||||
|
@ -269,9 +280,6 @@ cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info,
|
|||
fattr->cf_dtype = DT_REG;
|
||||
}
|
||||
|
||||
if (reparse_file_needs_reval(fattr))
|
||||
fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
|
||||
|
||||
sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER);
|
||||
sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP);
|
||||
}
|
||||
|
@ -331,38 +339,6 @@ cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info,
|
|||
cifs_fill_common_info(fattr, cifs_sb);
|
||||
}
|
||||
|
||||
/* BB eventually need to add the following helper function to
|
||||
resolve NT_STATUS_STOPPED_ON_SYMLINK return code when
|
||||
we try to do FindFirst on (NTFS) directory symlinks */
|
||||
/*
|
||||
int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb,
|
||||
unsigned int xid)
|
||||
{
|
||||
__u16 fid;
|
||||
int len;
|
||||
int oplock = 0;
|
||||
int rc;
|
||||
struct cifs_tcon *ptcon = cifs_sb_tcon(cifs_sb);
|
||||
char *tmpbuffer;
|
||||
|
||||
rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ,
|
||||
OPEN_REPARSE_POINT, &fid, &oplock, NULL,
|
||||
cifs_sb->local_nls,
|
||||
cifs_remap(cifs_sb);
|
||||
if (!rc) {
|
||||
tmpbuffer = kmalloc(maxpath);
|
||||
rc = CIFSSMBQueryReparseLinkInfo(xid, ptcon, full_path,
|
||||
tmpbuffer,
|
||||
maxpath -1,
|
||||
fid,
|
||||
cifs_sb->local_nls);
|
||||
if (CIFSSMBClose(xid, ptcon, fid)) {
|
||||
cifs_dbg(FYI, "Error closing temporary reparsepoint open\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
static int
|
||||
_initiate_cifs_search(const unsigned int xid, struct file *file,
|
||||
const char *full_path)
|
||||
|
@ -431,13 +407,10 @@ _initiate_cifs_search(const unsigned int xid, struct file *file,
|
|||
&cifsFile->fid, search_flags,
|
||||
&cifsFile->srch_inf);
|
||||
|
||||
if (rc == 0)
|
||||
if (rc == 0) {
|
||||
cifsFile->invalidHandle = false;
|
||||
/* BB add following call to handle readdir on new NTFS symlink errors
|
||||
else if STATUS_STOPPED_ON_SYMLINK
|
||||
call get_symlink_reparse_path and retry with new path */
|
||||
else if ((rc == -EOPNOTSUPP) &&
|
||||
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
|
||||
} else if ((rc == -EOPNOTSUPP) &&
|
||||
(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
|
||||
cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
|
||||
goto ffirst_retry;
|
||||
}
|
||||
|
|
|
@ -356,10 +356,9 @@ cifs_disable_secondary_channels(struct cifs_ses *ses)
|
|||
|
||||
/*
|
||||
* update the iface for the channel if necessary.
|
||||
* will return 0 when iface is updated, 1 if removed, 2 otherwise
|
||||
* Must be called with chan_lock held.
|
||||
*/
|
||||
int
|
||||
void
|
||||
cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
||||
{
|
||||
unsigned int chan_index;
|
||||
|
@ -368,20 +367,19 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
|||
struct cifs_server_iface *old_iface = NULL;
|
||||
struct cifs_server_iface *last_iface = NULL;
|
||||
struct sockaddr_storage ss;
|
||||
int rc = 0;
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ses->chans[chan_index].iface) {
|
||||
old_iface = ses->chans[chan_index].iface;
|
||||
if (old_iface->is_active) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
@ -394,7 +392,7 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
|||
if (!ses->iface_count) {
|
||||
spin_unlock(&ses->iface_lock);
|
||||
cifs_dbg(VFS, "server %s does not advertise interfaces\n", ses->server->hostname);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
last_iface = list_last_entry(&ses->iface_list, struct cifs_server_iface,
|
||||
|
@ -434,16 +432,21 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
|||
}
|
||||
|
||||
if (list_entry_is_head(iface, &ses->iface_list, iface_head)) {
|
||||
rc = 1;
|
||||
iface = NULL;
|
||||
cifs_dbg(FYI, "unable to find a suitable iface\n");
|
||||
}
|
||||
|
||||
if (!iface) {
|
||||
cifs_dbg(FYI, "unable to get the interface matching: %pIS\n",
|
||||
&ss);
|
||||
if (!chan_index)
|
||||
cifs_dbg(FYI, "unable to get the interface matching: %pIS\n",
|
||||
&ss);
|
||||
else {
|
||||
cifs_dbg(FYI, "unable to find another interface to replace: %pIS\n",
|
||||
&old_iface->sockaddr);
|
||||
}
|
||||
|
||||
spin_unlock(&ses->iface_lock);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* now drop the ref to the current iface */
|
||||
|
@ -459,34 +462,24 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
|
|||
iface->weight_fulfilled++;
|
||||
|
||||
kref_put(&old_iface->refcount, release_iface);
|
||||
} else if (old_iface) {
|
||||
/* if a new candidate is not found, keep things as is */
|
||||
cifs_dbg(FYI, "could not replace iface: %pIS\n",
|
||||
&old_iface->sockaddr);
|
||||
} else if (!chan_index) {
|
||||
/* special case: update interface for primary channel */
|
||||
if (iface) {
|
||||
cifs_dbg(FYI, "referencing primary channel iface: %pIS\n",
|
||||
&iface->sockaddr);
|
||||
iface->num_channels++;
|
||||
iface->weight_fulfilled++;
|
||||
}
|
||||
cifs_dbg(FYI, "referencing primary channel iface: %pIS\n",
|
||||
&iface->sockaddr);
|
||||
iface->num_channels++;
|
||||
iface->weight_fulfilled++;
|
||||
}
|
||||
spin_unlock(&ses->iface_lock);
|
||||
|
||||
if (iface) {
|
||||
spin_lock(&ses->chan_lock);
|
||||
chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ses->chans[chan_index].iface = iface;
|
||||
spin_lock(&ses->chan_lock);
|
||||
chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
if (chan_index == CIFS_INVAL_CHAN_INDEX) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
return rc;
|
||||
ses->chans[chan_index].iface = iface;
|
||||
spin_unlock(&ses->chan_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -23,17 +23,21 @@
|
|||
* Identifiers for functions that use the open, operation, close pattern
|
||||
* in smb2inode.c:smb2_compound_op()
|
||||
*/
|
||||
#define SMB2_OP_SET_DELETE 1
|
||||
#define SMB2_OP_SET_INFO 2
|
||||
#define SMB2_OP_QUERY_INFO 3
|
||||
#define SMB2_OP_QUERY_DIR 4
|
||||
#define SMB2_OP_MKDIR 5
|
||||
#define SMB2_OP_RENAME 6
|
||||
#define SMB2_OP_DELETE 7
|
||||
#define SMB2_OP_HARDLINK 8
|
||||
#define SMB2_OP_SET_EOF 9
|
||||
#define SMB2_OP_RMDIR 10
|
||||
#define SMB2_OP_POSIX_QUERY_INFO 11
|
||||
enum smb2_compound_ops {
|
||||
SMB2_OP_SET_DELETE = 1,
|
||||
SMB2_OP_SET_INFO,
|
||||
SMB2_OP_QUERY_INFO,
|
||||
SMB2_OP_QUERY_DIR,
|
||||
SMB2_OP_MKDIR,
|
||||
SMB2_OP_RENAME,
|
||||
SMB2_OP_DELETE,
|
||||
SMB2_OP_HARDLINK,
|
||||
SMB2_OP_SET_EOF,
|
||||
SMB2_OP_RMDIR,
|
||||
SMB2_OP_POSIX_QUERY_INFO,
|
||||
SMB2_OP_SET_REPARSE,
|
||||
SMB2_OP_GET_REPARSE
|
||||
};
|
||||
|
||||
/* Used when constructing chained read requests. */
|
||||
#define CHAINED_REQUEST 1
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1935,7 +1935,6 @@ static int
|
|||
smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifsFileInfo *cfile, __u64 size, bool set_alloc)
|
||||
{
|
||||
__le64 eof = cpu_to_le64(size);
|
||||
struct inode *inode;
|
||||
|
||||
/*
|
||||
|
@ -1952,7 +1951,7 @@ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
}
|
||||
|
||||
return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid, cfile->pid, &eof);
|
||||
cfile->fid.volatile_fid, cfile->pid, size);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -2948,18 +2947,6 @@ int parse_reparse_point(struct reparse_data_buffer *buf,
|
|||
u32 plen, struct cifs_sb_info *cifs_sb,
|
||||
bool unicode, struct cifs_open_info_data *data)
|
||||
{
|
||||
if (plen < sizeof(*buf)) {
|
||||
cifs_dbg(VFS, "%s: reparse buffer is too small. Must be at least 8 bytes but was %d\n",
|
||||
__func__, plen);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (plen < le16_to_cpu(buf->ReparseDataLength) + sizeof(*buf)) {
|
||||
cifs_dbg(VFS, "%s: invalid reparse buf length: %d\n",
|
||||
__func__, plen);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
data->reparse.buf = buf;
|
||||
|
||||
/* See MS-FSCC 2.1.2 */
|
||||
|
@ -2997,145 +2984,6 @@ static int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb,
|
|||
return parse_reparse_point(buf, plen, cifs_sb, true, data);
|
||||
}
|
||||
|
||||
static int smb2_query_reparse_point(const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb,
|
||||
const char *full_path,
|
||||
u32 *tag, struct kvec *rsp,
|
||||
int *rsp_buftype)
|
||||
{
|
||||
struct smb2_compound_vars *vars;
|
||||
int rc;
|
||||
__le16 *utf16_path = NULL;
|
||||
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
|
||||
struct cifs_open_parms oparms;
|
||||
struct cifs_fid fid;
|
||||
struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
|
||||
int flags = CIFS_CP_CREATE_CLOSE_OP;
|
||||
struct smb_rqst *rqst;
|
||||
int resp_buftype[3];
|
||||
struct kvec *rsp_iov;
|
||||
struct smb2_ioctl_rsp *ioctl_rsp;
|
||||
struct reparse_data_buffer *reparse_buf;
|
||||
u32 off, count, len;
|
||||
|
||||
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
|
||||
|
||||
if (smb3_encryption_required(tcon))
|
||||
flags |= CIFS_TRANSFORM_REQ;
|
||||
|
||||
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
|
||||
if (!utf16_path)
|
||||
return -ENOMEM;
|
||||
|
||||
resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
|
||||
vars = kzalloc(sizeof(*vars), GFP_KERNEL);
|
||||
if (!vars) {
|
||||
rc = -ENOMEM;
|
||||
goto out_free_path;
|
||||
}
|
||||
rqst = vars->rqst;
|
||||
rsp_iov = vars->rsp_iov;
|
||||
|
||||
/*
|
||||
* setup smb2open - TODO add optimization to call cifs_get_readable_path
|
||||
* to see if there is a handle already open that we can use
|
||||
*/
|
||||
rqst[0].rq_iov = vars->open_iov;
|
||||
rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
|
||||
|
||||
oparms = (struct cifs_open_parms) {
|
||||
.tcon = tcon,
|
||||
.path = full_path,
|
||||
.desired_access = FILE_READ_ATTRIBUTES,
|
||||
.disposition = FILE_OPEN,
|
||||
.create_options = cifs_create_options(cifs_sb, OPEN_REPARSE_POINT),
|
||||
.fid = &fid,
|
||||
};
|
||||
|
||||
rc = SMB2_open_init(tcon, server,
|
||||
&rqst[0], &oplock, &oparms, utf16_path);
|
||||
if (rc)
|
||||
goto query_rp_exit;
|
||||
smb2_set_next_command(tcon, &rqst[0]);
|
||||
|
||||
|
||||
/* IOCTL */
|
||||
rqst[1].rq_iov = vars->io_iov;
|
||||
rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
|
||||
|
||||
rc = SMB2_ioctl_init(tcon, server,
|
||||
&rqst[1], COMPOUND_FID,
|
||||
COMPOUND_FID, FSCTL_GET_REPARSE_POINT, NULL, 0,
|
||||
CIFSMaxBufSize -
|
||||
MAX_SMB2_CREATE_RESPONSE_SIZE -
|
||||
MAX_SMB2_CLOSE_RESPONSE_SIZE);
|
||||
if (rc)
|
||||
goto query_rp_exit;
|
||||
|
||||
smb2_set_next_command(tcon, &rqst[1]);
|
||||
smb2_set_related(&rqst[1]);
|
||||
|
||||
/* Close */
|
||||
rqst[2].rq_iov = &vars->close_iov;
|
||||
rqst[2].rq_nvec = 1;
|
||||
|
||||
rc = SMB2_close_init(tcon, server,
|
||||
&rqst[2], COMPOUND_FID, COMPOUND_FID, false);
|
||||
if (rc)
|
||||
goto query_rp_exit;
|
||||
|
||||
smb2_set_related(&rqst[2]);
|
||||
|
||||
rc = compound_send_recv(xid, tcon->ses, server,
|
||||
flags, 3, rqst,
|
||||
resp_buftype, rsp_iov);
|
||||
|
||||
ioctl_rsp = rsp_iov[1].iov_base;
|
||||
|
||||
/*
|
||||
* Open was successful and we got an ioctl response.
|
||||
*/
|
||||
if (rc == 0) {
|
||||
/* See MS-FSCC 2.3.23 */
|
||||
off = le32_to_cpu(ioctl_rsp->OutputOffset);
|
||||
count = le32_to_cpu(ioctl_rsp->OutputCount);
|
||||
if (check_add_overflow(off, count, &len) ||
|
||||
len > rsp_iov[1].iov_len) {
|
||||
cifs_tcon_dbg(VFS, "%s: invalid ioctl: off=%d count=%d\n",
|
||||
__func__, off, count);
|
||||
rc = -EIO;
|
||||
goto query_rp_exit;
|
||||
}
|
||||
|
||||
reparse_buf = (void *)((u8 *)ioctl_rsp + off);
|
||||
len = sizeof(*reparse_buf);
|
||||
if (count < len ||
|
||||
count < le16_to_cpu(reparse_buf->ReparseDataLength) + len) {
|
||||
cifs_tcon_dbg(VFS, "%s: invalid ioctl: off=%d count=%d\n",
|
||||
__func__, off, count);
|
||||
rc = -EIO;
|
||||
goto query_rp_exit;
|
||||
}
|
||||
*tag = le32_to_cpu(reparse_buf->ReparseTag);
|
||||
*rsp = rsp_iov[1];
|
||||
*rsp_buftype = resp_buftype[1];
|
||||
resp_buftype[1] = CIFS_NO_BUFFER;
|
||||
}
|
||||
|
||||
query_rp_exit:
|
||||
SMB2_open_free(&rqst[0]);
|
||||
SMB2_ioctl_free(&rqst[1]);
|
||||
SMB2_close_free(&rqst[2]);
|
||||
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
|
||||
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
|
||||
free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
|
||||
kfree(vars);
|
||||
out_free_path:
|
||||
kfree(utf16_path);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct cifs_ntsd *
|
||||
get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
|
||||
const struct cifs_fid *cifsfid, u32 *pacllen, u32 info)
|
||||
|
@ -3336,7 +3184,6 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
|||
unsigned long long new_size;
|
||||
long rc;
|
||||
unsigned int xid;
|
||||
__le64 eof;
|
||||
|
||||
xid = get_xid();
|
||||
|
||||
|
@ -3366,9 +3213,8 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
|
|||
*/
|
||||
new_size = offset + len;
|
||||
if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) {
|
||||
eof = cpu_to_le64(new_size);
|
||||
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid, cfile->pid, &eof);
|
||||
cfile->fid.volatile_fid, cfile->pid, new_size);
|
||||
if (rc >= 0) {
|
||||
truncate_setsize(inode, new_size);
|
||||
fscache_resize_cookie(cifs_inode_cookie(inode), new_size);
|
||||
|
@ -3561,7 +3407,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
|||
struct cifsFileInfo *cfile = file->private_data;
|
||||
long rc = -EOPNOTSUPP;
|
||||
unsigned int xid;
|
||||
__le64 eof;
|
||||
loff_t new_eof;
|
||||
|
||||
xid = get_xid();
|
||||
|
||||
|
@ -3590,14 +3436,14 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
|||
if (cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)
|
||||
smb2_set_sparse(xid, tcon, cfile, inode, false);
|
||||
|
||||
eof = cpu_to_le64(off + len);
|
||||
new_eof = off + len;
|
||||
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid, cfile->pid, &eof);
|
||||
cfile->fid.volatile_fid, cfile->pid, new_eof);
|
||||
if (rc == 0) {
|
||||
cifsi->server_eof = off + len;
|
||||
cifs_setsize(inode, off + len);
|
||||
cifsi->server_eof = new_eof;
|
||||
cifs_setsize(inode, new_eof);
|
||||
cifs_truncate_page(inode->i_mapping, inode->i_size);
|
||||
truncate_setsize(inode, off + len);
|
||||
truncate_setsize(inode, new_eof);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
@ -3688,8 +3534,7 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
|
|||
struct inode *inode = file_inode(file);
|
||||
struct cifsFileInfo *cfile = file->private_data;
|
||||
struct cifsInodeInfo *cifsi = CIFS_I(inode);
|
||||
__le64 eof;
|
||||
loff_t old_eof;
|
||||
loff_t old_eof, new_eof;
|
||||
|
||||
xid = get_xid();
|
||||
|
||||
|
@ -3714,9 +3559,9 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
|
|||
if (rc < 0)
|
||||
goto out_2;
|
||||
|
||||
eof = cpu_to_le64(old_eof - len);
|
||||
new_eof = old_eof - len;
|
||||
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid, cfile->pid, &eof);
|
||||
cfile->fid.volatile_fid, cfile->pid, new_eof);
|
||||
if (rc < 0)
|
||||
goto out_2;
|
||||
|
||||
|
@ -3740,8 +3585,7 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
|
|||
unsigned int xid;
|
||||
struct cifsFileInfo *cfile = file->private_data;
|
||||
struct inode *inode = file_inode(file);
|
||||
__le64 eof;
|
||||
__u64 count, old_eof;
|
||||
__u64 count, old_eof, new_eof;
|
||||
|
||||
xid = get_xid();
|
||||
|
||||
|
@ -3754,20 +3598,20 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
|
|||
}
|
||||
|
||||
count = old_eof - off;
|
||||
eof = cpu_to_le64(old_eof + len);
|
||||
new_eof = old_eof + len;
|
||||
|
||||
filemap_invalidate_lock(inode->i_mapping);
|
||||
rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof + len - 1);
|
||||
rc = filemap_write_and_wait_range(inode->i_mapping, off, new_eof - 1);
|
||||
if (rc < 0)
|
||||
goto out_2;
|
||||
truncate_pagecache_range(inode, off, old_eof);
|
||||
|
||||
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
|
||||
cfile->fid.volatile_fid, cfile->pid, &eof);
|
||||
cfile->fid.volatile_fid, cfile->pid, new_eof);
|
||||
if (rc < 0)
|
||||
goto out_2;
|
||||
|
||||
truncate_setsize(inode, old_eof + len);
|
||||
truncate_setsize(inode, new_eof);
|
||||
fscache_resize_cookie(cifs_inode_cookie(inode), i_size_read(inode));
|
||||
|
||||
rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len);
|
||||
|
@ -5171,11 +5015,154 @@ int cifs_sfu_make_node(unsigned int xid, struct inode *inode,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static inline u64 mode_nfs_type(mode_t mode)
|
||||
{
|
||||
switch (mode & S_IFMT) {
|
||||
case S_IFBLK: return NFS_SPECFILE_BLK;
|
||||
case S_IFCHR: return NFS_SPECFILE_CHR;
|
||||
case S_IFIFO: return NFS_SPECFILE_FIFO;
|
||||
case S_IFSOCK: return NFS_SPECFILE_SOCK;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfs_set_reparse_buf(struct reparse_posix_data *buf,
|
||||
mode_t mode, dev_t dev,
|
||||
struct kvec *iov)
|
||||
{
|
||||
u64 type;
|
||||
u16 len, dlen;
|
||||
|
||||
len = sizeof(*buf);
|
||||
|
||||
switch ((type = mode_nfs_type(mode))) {
|
||||
case NFS_SPECFILE_BLK:
|
||||
case NFS_SPECFILE_CHR:
|
||||
dlen = sizeof(__le64);
|
||||
break;
|
||||
case NFS_SPECFILE_FIFO:
|
||||
case NFS_SPECFILE_SOCK:
|
||||
dlen = 0;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS);
|
||||
buf->Reserved = 0;
|
||||
buf->InodeType = cpu_to_le64(type);
|
||||
buf->ReparseDataLength = cpu_to_le16(len + dlen -
|
||||
sizeof(struct reparse_data_buffer));
|
||||
*(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) |
|
||||
MINOR(dev));
|
||||
iov->iov_base = buf;
|
||||
iov->iov_len = len + dlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfs_make_node(unsigned int xid, struct inode *inode,
|
||||
struct dentry *dentry, struct cifs_tcon *tcon,
|
||||
const char *full_path, umode_t mode, dev_t dev)
|
||||
{
|
||||
struct cifs_open_info_data data;
|
||||
struct reparse_posix_data *p;
|
||||
struct inode *new;
|
||||
struct kvec iov;
|
||||
__u8 buf[sizeof(*p) + sizeof(__le64)];
|
||||
int rc;
|
||||
|
||||
p = (struct reparse_posix_data *)buf;
|
||||
rc = nfs_set_reparse_buf(p, mode, dev, &iov);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
data = (struct cifs_open_info_data) {
|
||||
.reparse_point = true,
|
||||
.reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, },
|
||||
};
|
||||
|
||||
new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
|
||||
tcon, full_path, &iov);
|
||||
if (!IS_ERR(new))
|
||||
d_instantiate(dentry, new);
|
||||
else
|
||||
rc = PTR_ERR(new);
|
||||
cifs_free_open_info(&data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int smb2_create_reparse_symlink(const unsigned int xid,
|
||||
struct inode *inode,
|
||||
struct dentry *dentry,
|
||||
struct cifs_tcon *tcon,
|
||||
const char *full_path,
|
||||
const char *symname)
|
||||
{
|
||||
struct reparse_symlink_data_buffer *buf = NULL;
|
||||
struct cifs_open_info_data data;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
struct inode *new;
|
||||
struct kvec iov;
|
||||
__le16 *path;
|
||||
char *sym;
|
||||
u16 len, plen;
|
||||
int rc = 0;
|
||||
|
||||
sym = kstrdup(symname, GFP_KERNEL);
|
||||
if (!sym)
|
||||
return -ENOMEM;
|
||||
|
||||
data = (struct cifs_open_info_data) {
|
||||
.reparse_point = true,
|
||||
.reparse = { .tag = IO_REPARSE_TAG_SYMLINK, },
|
||||
.symlink_target = sym,
|
||||
};
|
||||
|
||||
path = cifs_convert_path_to_utf16(symname, cifs_sb);
|
||||
if (!path) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX);
|
||||
len = sizeof(*buf) + plen * 2;
|
||||
buf = kzalloc(len, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK);
|
||||
buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer));
|
||||
buf->SubstituteNameOffset = cpu_to_le16(plen);
|
||||
buf->SubstituteNameLength = cpu_to_le16(plen);
|
||||
memcpy(&buf->PathBuffer[plen], path, plen);
|
||||
buf->PrintNameOffset = 0;
|
||||
buf->PrintNameLength = cpu_to_le16(plen);
|
||||
memcpy(buf->PathBuffer, path, plen);
|
||||
buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0);
|
||||
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = len;
|
||||
new = smb2_get_reparse_inode(&data, inode->i_sb, xid,
|
||||
tcon, full_path, &iov);
|
||||
if (!IS_ERR(new))
|
||||
d_instantiate(dentry, new);
|
||||
else
|
||||
rc = PTR_ERR(new);
|
||||
out:
|
||||
kfree(path);
|
||||
cifs_free_open_info(&data);
|
||||
kfree(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int smb2_make_node(unsigned int xid, struct inode *inode,
|
||||
struct dentry *dentry, struct cifs_tcon *tcon,
|
||||
const char *full_path, umode_t mode, dev_t dev)
|
||||
{
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Check if mounted with mount parm 'sfu' mount parm.
|
||||
|
@ -5183,15 +5170,14 @@ static int smb2_make_node(unsigned int xid, struct inode *inode,
|
|||
* supports block and char device (no socket & fifo),
|
||||
* and was used by default in earlier versions of Windows
|
||||
*/
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
|
||||
return -EPERM;
|
||||
/*
|
||||
* TODO: Add ability to create instead via reparse point. Windows (e.g.
|
||||
* their current NFS server) uses this approach to expose special files
|
||||
* over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions
|
||||
*/
|
||||
return cifs_sfu_make_node(xid, inode, dentry, tcon,
|
||||
full_path, mode, dev);
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
|
||||
rc = cifs_sfu_make_node(xid, inode, dentry, tcon,
|
||||
full_path, mode, dev);
|
||||
} else {
|
||||
rc = nfs_make_node(xid, inode, dentry, tcon,
|
||||
full_path, mode, dev);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
|
||||
|
@ -5247,6 +5233,7 @@ struct smb_version_operations smb20_operations = {
|
|||
.parse_reparse_point = smb2_parse_reparse_point,
|
||||
.query_mf_symlink = smb3_query_mf_symlink,
|
||||
.create_mf_symlink = smb3_create_mf_symlink,
|
||||
.create_reparse_symlink = smb2_create_reparse_symlink,
|
||||
.open = smb2_open_file,
|
||||
.set_fid = smb2_set_fid,
|
||||
.close = smb2_close_file,
|
||||
|
@ -5349,6 +5336,7 @@ struct smb_version_operations smb21_operations = {
|
|||
.parse_reparse_point = smb2_parse_reparse_point,
|
||||
.query_mf_symlink = smb3_query_mf_symlink,
|
||||
.create_mf_symlink = smb3_create_mf_symlink,
|
||||
.create_reparse_symlink = smb2_create_reparse_symlink,
|
||||
.open = smb2_open_file,
|
||||
.set_fid = smb2_set_fid,
|
||||
.close = smb2_close_file,
|
||||
|
@ -5454,6 +5442,7 @@ struct smb_version_operations smb30_operations = {
|
|||
.parse_reparse_point = smb2_parse_reparse_point,
|
||||
.query_mf_symlink = smb3_query_mf_symlink,
|
||||
.create_mf_symlink = smb3_create_mf_symlink,
|
||||
.create_reparse_symlink = smb2_create_reparse_symlink,
|
||||
.open = smb2_open_file,
|
||||
.set_fid = smb2_set_fid,
|
||||
.close = smb2_close_file,
|
||||
|
@ -5568,6 +5557,7 @@ struct smb_version_operations smb311_operations = {
|
|||
.parse_reparse_point = smb2_parse_reparse_point,
|
||||
.query_mf_symlink = smb3_query_mf_symlink,
|
||||
.create_mf_symlink = smb3_create_mf_symlink,
|
||||
.create_reparse_symlink = smb2_create_reparse_symlink,
|
||||
.open = smb2_open_file,
|
||||
.set_fid = smb2_set_fid,
|
||||
.close = smb2_close_file,
|
||||
|
|
|
@ -5347,18 +5347,18 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
|
||||
int
|
||||
SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
||||
u64 volatile_fid, u32 pid, __le64 *eof)
|
||||
u64 volatile_fid, u32 pid, loff_t new_eof)
|
||||
{
|
||||
struct smb2_file_eof_info info;
|
||||
void *data;
|
||||
unsigned int size;
|
||||
|
||||
info.EndOfFile = *eof;
|
||||
info.EndOfFile = cpu_to_le64(new_eof);
|
||||
|
||||
data = &info;
|
||||
size = sizeof(struct smb2_file_eof_info);
|
||||
|
||||
trace_smb3_set_eof(xid, persistent_fid, tcon->tid, tcon->ses->Suid, le64_to_cpu(*eof));
|
||||
trace_smb3_set_eof(xid, persistent_fid, tcon->tid, tcon->ses->Suid, new_eof);
|
||||
|
||||
return send_set_info(xid, tcon, persistent_fid, volatile_fid,
|
||||
pid, FILE_END_OF_FILE_INFORMATION, SMB2_O_INFO_FILE,
|
||||
|
|
|
@ -56,6 +56,18 @@ extern int smb3_handle_read_data(struct TCP_Server_Info *server,
|
|||
extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const char *path,
|
||||
__u32 *reparse_tag);
|
||||
struct inode *smb2_get_reparse_inode(struct cifs_open_info_data *data,
|
||||
struct super_block *sb,
|
||||
const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
const char *full_path,
|
||||
struct kvec *iov);
|
||||
int smb2_query_reparse_point(const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb,
|
||||
const char *full_path,
|
||||
u32 *tag, struct kvec *rsp,
|
||||
int *rsp_buftype);
|
||||
int smb2_query_path_info(const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb,
|
||||
|
@ -80,12 +92,16 @@ extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
const char *name, struct cifs_sb_info *cifs_sb);
|
||||
extern int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *name, struct cifs_sb_info *cifs_sb);
|
||||
extern int smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *from_name, const char *to_name,
|
||||
struct cifs_sb_info *cifs_sb);
|
||||
extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *from_name, const char *to_name,
|
||||
struct cifs_sb_info *cifs_sb);
|
||||
int smb2_rename_path(const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
struct dentry *source_dentry,
|
||||
const char *from_name, const char *to_name,
|
||||
struct cifs_sb_info *cifs_sb);
|
||||
int smb2_create_hardlink(const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
struct dentry *source_dentry,
|
||||
const char *from_name, const char *to_name,
|
||||
struct cifs_sb_info *cifs_sb);
|
||||
extern int smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
|
||||
struct cifs_sb_info *cifs_sb, const unsigned char *path,
|
||||
char *pbuf, unsigned int *pbytes_written);
|
||||
|
@ -205,7 +221,7 @@ extern int SMB2_query_directory_init(unsigned int xid, struct cifs_tcon *tcon,
|
|||
extern void SMB2_query_directory_free(struct smb_rqst *rqst);
|
||||
extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
u64 persistent_fid, u64 volatile_fid, u32 pid,
|
||||
__le64 *eof);
|
||||
loff_t new_eof);
|
||||
extern int SMB2_set_info_init(struct cifs_tcon *tcon,
|
||||
struct TCP_Server_Info *server,
|
||||
struct smb_rqst *rqst,
|
||||
|
@ -289,4 +305,5 @@ int smb311_posix_query_path_info(const unsigned int xid,
|
|||
int posix_info_parse(const void *beg, const void *end,
|
||||
struct smb2_posix_info_parsed *out);
|
||||
int posix_info_sid_size(const void *beg, const void *end);
|
||||
|
||||
#endif /* _SMB2PROTO_H */
|
||||
|
|
|
@ -2136,7 +2136,7 @@ static int allocate_mr_list(struct smbd_connection *info)
|
|||
for (i = 0; i < info->responder_resources * 2; i++) {
|
||||
smbdirect_mr = kzalloc(sizeof(*smbdirect_mr), GFP_KERNEL);
|
||||
if (!smbdirect_mr)
|
||||
goto out;
|
||||
goto cleanup_entries;
|
||||
smbdirect_mr->mr = ib_alloc_mr(info->pd, info->mr_type,
|
||||
info->max_frmr_depth);
|
||||
if (IS_ERR(smbdirect_mr->mr)) {
|
||||
|
@ -2162,7 +2162,7 @@ static int allocate_mr_list(struct smbd_connection *info)
|
|||
|
||||
out:
|
||||
kfree(smbdirect_mr);
|
||||
|
||||
cleanup_entries:
|
||||
list_for_each_entry_safe(smbdirect_mr, tmp, &info->mr_list, list) {
|
||||
list_del(&smbdirect_mr->list);
|
||||
ib_dereg_mr(smbdirect_mr->mr);
|
||||
|
|
|
@ -370,11 +370,12 @@ DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rename_enter);
|
|||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(rmdir_enter);
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_eof_enter);
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_info_compound_enter);
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(set_reparse_compound_enter);
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(get_reparse_compound_enter);
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(delete_enter);
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(mkdir_enter);
|
||||
DEFINE_SMB3_INF_COMPOUND_ENTER_EVENT(tdis_enter);
|
||||
|
||||
|
||||
DECLARE_EVENT_CLASS(smb3_inf_compound_done_class,
|
||||
TP_PROTO(unsigned int xid,
|
||||
__u32 tid,
|
||||
|
@ -408,6 +409,8 @@ DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rename_done);
|
|||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(rmdir_done);
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_eof_done);
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_info_compound_done);
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(set_reparse_compound_done);
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(get_reparse_compound_done);
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(delete_done);
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(mkdir_done);
|
||||
DEFINE_SMB3_INF_COMPOUND_DONE_EVENT(tdis_done);
|
||||
|
@ -451,6 +454,8 @@ DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rename_err);
|
|||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(rmdir_err);
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_eof_err);
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_info_compound_err);
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(set_reparse_compound_err);
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(get_reparse_compound_err);
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(mkdir_err);
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(delete_err);
|
||||
DEFINE_SMB3_INF_COMPOUND_ERR_EVENT(tdis_err);
|
||||
|
|
Loading…
Reference in a new issue