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

Pull smb client updates from Steve French:

 - fix for folios/netfs data corruption in cifs_extend_writeback

 - additional tracepoint added

 - updates for special files and symlinks: improvements to allow
   selecting use of either WSL or NFS reparse point format on creating
   special files

 - allocation size improvement for cached files

 - minor cleanup patches

 - fix to allow changing the password on remount when password for the
   session is expired.

 - lease key related fixes: caching hardlinked files, deletes of
   deferred close files, and an important fix to better reuse lease keys
   for compound operations, which also can avoid lease break timeouts
   when low on credits

 - fix potential data corruption with write/readdir races

 - compression cleanups and a fix for compression headers

* tag '6.9-rc-smb3-client-fixes-part1' of git://git.samba.org/sfrench/cifs-2.6: (24 commits)
  cifs: update internal module version number for cifs.ko
  smb: common: simplify compression headers
  smb: common: fix fields sizes in compression_pattern_payload_v1
  smb: client: negotiate compression algorithms
  smb3: add dynamic trace point for ioctls
  cifs: Fix writeback data corruption
  smb: client: return reparse type in /proc/mounts
  smb: client: set correct d_type for reparse DFS/DFSR and mount point
  smb: client: parse uid, gid, mode and dev from WSL reparse points
  smb: client: introduce SMB2_OP_QUERY_WSL_EA
  smb: client: Fix a NULL vs IS_ERR() check in wsl_set_xattrs()
  smb: client: add support for WSL reparse points
  smb: client: reduce number of parameters in smb2_compound_op()
  smb: client: fix potential broken compound request
  smb: client: move most of reparse point handling code to common file
  smb: client: introduce reparse mount option
  smb: client: retry compound request without reusing lease
  smb: client: do not defer close open handles to deleted files
  smb: client: reuse file lease key in compound operations
  smb3: update allocation size more accurately on write completion
  ...
This commit is contained in:
Linus Torvalds
2024-03-13 13:15:24 -07:00
27 changed files with 1579 additions and 721 deletions

View File

@@ -5255,6 +5255,7 @@ R: Paulo Alcantara <pc@manguebit.com> (DFS, global name space)
R: Ronnie Sahlberg <ronniesahlberg@gmail.com> (directory leases, sparse files)
R: Shyam Prasad N <sprasad@microsoft.com> (multichannel)
R: Tom Talpey <tom@talpey.com> (RDMA, smbdirect)
R: Bharath SM <bharathsm@microsoft.com> (deferred close, directory leases)
L: linux-cifs@vger.kernel.org
L: samba-technical@lists.samba.org (moderated for non-subscribers)
S: Supported

View File

@@ -12,7 +12,7 @@ cifs-y := trace.o cifsfs.o cifs_debug.o connect.o dir.o file.o \
smb2ops.o smb2maperror.o smb2transport.o \
smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \
dns_resolve.o cifs_spnego_negtokeninit.asn1.o asn1.o \
namespace.o
namespace.o reparse.o
$(obj)/asn1.o: $(obj)/cifs_spnego_negtokeninit.asn1.h

View File

@@ -278,6 +278,24 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
return 0;
}
static __always_inline const char *compression_alg_str(__le16 alg)
{
switch (alg) {
case SMB3_COMPRESS_NONE:
return "NONE";
case SMB3_COMPRESS_LZNT1:
return "LZNT1";
case SMB3_COMPRESS_LZ77:
return "LZ77";
case SMB3_COMPRESS_LZ77_HUFF:
return "LZ77-Huffman";
case SMB3_COMPRESS_PATTERN:
return "Pattern_V1";
default:
return "invalid";
}
}
static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
{
struct mid_q_entry *mid_entry;
@@ -423,12 +441,6 @@ skip_rdma:
server->echo_credits,
server->oplock_credits,
server->dialect);
if (server->compress_algorithm == SMB3_COMPRESS_LZNT1)
seq_printf(m, " COMPRESS_LZNT1");
else if (server->compress_algorithm == SMB3_COMPRESS_LZ77)
seq_printf(m, " COMPRESS_LZ77");
else if (server->compress_algorithm == SMB3_COMPRESS_LZ77_HUFF)
seq_printf(m, " COMPRESS_LZ77_HUFF");
if (server->sign)
seq_printf(m, " signed");
if (server->posix_ext_supported)
@@ -460,6 +472,14 @@ skip_rdma:
server->leaf_fullpath);
}
seq_puts(m, "\nCompression: ");
if (!server->compression.requested)
seq_puts(m, "disabled on mount");
else if (server->compression.enabled)
seq_printf(m, "enabled (%s)", compression_alg_str(server->compression.alg));
else
seq_puts(m, "disabled (not supported by this server)");
seq_printf(m, "\n\n\tSessions: ");
i = 0;
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
@@ -488,6 +508,8 @@ skip_rdma:
ses->ses_count, ses->serverOS, ses->serverNOS,
ses->capabilities, ses->ses_status);
}
if (ses->expired_pwd)
seq_puts(m, "password no longer valid ");
spin_unlock(&ses->ses_lock);
seq_printf(m, "\n\tSecurity type: %s ",

View File

@@ -673,6 +673,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
seq_printf(s, ",backupgid=%u",
from_kgid_munged(&init_user_ns,
cifs_sb->ctx->backupgid));
seq_show_option(s, "reparse",
cifs_reparse_type_str(cifs_sb->ctx->reparse_type));
seq_printf(s, ",rsize=%u", cifs_sb->ctx->rsize);
seq_printf(s, ",wsize=%u", cifs_sb->ctx->wsize);

View File

@@ -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 47
#define CIFS_VERSION "2.47"
#define SMB3_PRODUCT_BUILD 48
#define CIFS_VERSION "2.48"
#endif /* _CIFSFS_H */

View File

@@ -153,6 +153,24 @@ enum securityEnum {
Kerberos, /* Kerberos via SPNEGO */
};
enum cifs_reparse_type {
CIFS_REPARSE_TYPE_NFS,
CIFS_REPARSE_TYPE_WSL,
CIFS_REPARSE_TYPE_DEFAULT = CIFS_REPARSE_TYPE_NFS,
};
static inline const char *cifs_reparse_type_str(enum cifs_reparse_type type)
{
switch (type) {
case CIFS_REPARSE_TYPE_NFS:
return "nfs";
case CIFS_REPARSE_TYPE_WSL:
return "wsl";
default:
return "unknown";
}
}
struct session_key {
unsigned int len;
char *response;
@@ -208,6 +226,10 @@ struct cifs_open_info_data {
struct reparse_posix_data *posix;
};
} reparse;
struct {
__u8 eas[SMB2_WSL_MAX_QUERY_EA_RESP_SIZE];
unsigned int eas_len;
} wsl;
char *symlink_target;
struct cifs_sid posix_owner;
struct cifs_sid posix_group;
@@ -217,19 +239,6 @@ struct cifs_open_info_data {
};
};
static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
{
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;
}
/*
*****************************************************************
* Except the CIFS PDUs themselves all the
@@ -371,7 +380,8 @@ struct smb_version_operations {
struct cifs_open_info_data *data);
/* set size by path */
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,
struct dentry *);
/* set size by file handle */
int (*set_file_size)(const unsigned int, struct cifs_tcon *,
struct cifsFileInfo *, __u64, bool);
@@ -401,7 +411,7 @@ struct smb_version_operations {
struct cifs_sb_info *);
/* unlink file */
int (*unlink)(const unsigned int, struct cifs_tcon *, const char *,
struct cifs_sb_info *);
struct cifs_sb_info *, struct dentry *);
/* open, rename and delete file */
int (*rename_pending_delete)(const char *, struct dentry *,
const unsigned int);
@@ -759,7 +769,11 @@ struct TCP_Server_Info {
unsigned int max_write;
unsigned int min_offload;
unsigned int retrans;
__le16 compress_algorithm;
struct {
bool requested; /* "compress" mount option set*/
bool enabled; /* actually negotiated with server */
__le16 alg; /* preferred alg negotiated with server */
} compression;
__u16 signing_algorithm;
__le16 cipher_type;
/* save initital negprot hash */
@@ -1066,6 +1080,7 @@ struct cifs_ses {
enum securityEnum sectype; /* what security flavor was specified? */
bool sign; /* is signing required? */
bool domainAuto:1;
bool expired_pwd; /* track if access denied or expired pwd so can know if need to update */
unsigned int flags;
__u16 session_flags;
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
@@ -1379,6 +1394,7 @@ struct cifs_open_parms {
umode_t mode;
bool reconnect:1;
bool replay:1; /* indicates that this open is for a replay */
struct kvec *ea_cctx;
};
struct cifs_fid {
@@ -1420,6 +1436,7 @@ struct cifsFileInfo {
bool invalidHandle:1; /* file closed via session abend */
bool swapfile:1;
bool oplock_break_cancelled:1;
bool status_file_deleted:1; /* file has been deleted */
unsigned int oplock_epoch; /* epoch from the lease break */
__u32 oplock_level; /* oplock/lease level from the lease break */
int count;
@@ -2277,6 +2294,17 @@ static inline void cifs_sg_set_buf(struct sg_table *sgtable,
}
}
#define CIFS_OPARMS(_cifs_sb, _tcon, _path, _da, _cd, _co, _mode) \
((struct cifs_open_parms) { \
.tcon = _tcon, \
.path = _path, \
.desired_access = (_da), \
.disposition = (_cd), \
.create_options = cifs_create_options(_cifs_sb, (_co)), \
.mode = (_mode), \
.cifs_sb = _cifs_sb, \
})
struct smb2_compound_vars {
struct cifs_open_parms oparms;
struct kvec rsp_iov[MAX_COMPOUND];
@@ -2288,6 +2316,7 @@ struct smb2_compound_vars {
struct kvec close_iov;
struct smb2_file_rename_info rename_info;
struct smb2_file_link_info link_info;
struct kvec ea_iov;
};
#endif /* _CIFS_GLOB_H */

View File

@@ -144,7 +144,8 @@ extern int cifs_reconnect(struct TCP_Server_Info *server,
extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr);
extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
extern bool backup_cred(struct cifs_sb_info *);
extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
extern bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 eof,
bool from_readdir);
extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
unsigned int bytes_written);
extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, int);
@@ -201,17 +202,14 @@ extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr,
struct cifs_sb_info *cifs_sb);
extern void cifs_dir_info_to_fattr(struct cifs_fattr *, FILE_DIRECTORY_INFO *,
struct cifs_sb_info *);
extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr,
bool from_readdir);
extern struct inode *cifs_iget(struct super_block *sb,
struct cifs_fattr *fattr);
int cifs_get_inode_info(struct inode **inode, const char *full_path,
struct cifs_open_info_data *data, struct super_block *sb, int xid,
const struct cifs_fid *fid);
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 **inode,
const char *full_path,
struct cifs_open_info_data *data,
@@ -296,6 +294,10 @@ extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon);
extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon,
const char *path);
extern void cifs_mark_open_handles_for_deleted_file(struct inode *inode,
const char *path);
extern struct TCP_Server_Info *
cifs_get_tcp_session(struct smb3_fs_context *ctx,
struct TCP_Server_Info *primary_server);
@@ -402,7 +404,8 @@ extern int CIFSSMBSetFileDisposition(const unsigned int xid,
__u32 pid_of_opener);
extern int CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
const char *file_name, __u64 size,
struct cifs_sb_info *cifs_sb, bool set_allocation);
struct cifs_sb_info *cifs_sb, bool set_allocation,
struct dentry *dentry);
extern int CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile, __u64 size,
bool set_allocation);
@@ -438,7 +441,8 @@ extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,
const struct nls_table *nls_codepage,
int remap_special_chars);
extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon,
const char *name, struct cifs_sb_info *cifs_sb);
const char *name, struct cifs_sb_info *cifs_sb,
struct dentry *dentry);
int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
struct dentry *source_dentry,
const char *from_name, const char *to_name,

View File

@@ -738,7 +738,7 @@ PsxDelete:
int
CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
struct cifs_sb_info *cifs_sb)
struct cifs_sb_info *cifs_sb, struct dentry *dentry)
{
DELETE_FILE_REQ *pSMB = NULL;
DELETE_FILE_RSP *pSMBr = NULL;
@@ -4993,7 +4993,7 @@ QFSPosixRetry:
int
CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
const char *file_name, __u64 size, struct cifs_sb_info *cifs_sb,
bool set_allocation)
bool set_allocation, struct dentry *dentry)
{
struct smb_com_transaction2_spi_req *pSMB = NULL;
struct smb_com_transaction2_spi_rsp *pSMBr = NULL;

View File

@@ -1736,7 +1736,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
tcp_ses->channel_sequence_num = 0; /* only tracked for primary channel */
tcp_ses->reconnect_instance = 1;
tcp_ses->lstrp = jiffies;
tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression);
tcp_ses->compression.requested = ctx->compress;
spin_lock_init(&tcp_ses->req_lock);
spin_lock_init(&tcp_ses->srv_lock);
spin_lock_init(&tcp_ses->mid_lock);
@@ -2803,6 +2803,8 @@ compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data)
return 0;
if (old->ctx->closetimeo != new->ctx->closetimeo)
return 0;
if (old->ctx->reparse_type != new->ctx->reparse_type)
return 0;
return 1;
}

File diff suppressed because it is too large Load Diff

View File

@@ -174,6 +174,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
fsparam_string("vers", Opt_vers),
fsparam_string("sec", Opt_sec),
fsparam_string("cache", Opt_cache),
fsparam_string("reparse", Opt_reparse),
/* Arguments that should be ignored */
fsparam_flag("guest", Opt_ignore),
@@ -296,6 +297,35 @@ cifs_parse_cache_flavor(struct fs_context *fc, char *value, struct smb3_fs_conte
return 0;
}
static const match_table_t reparse_flavor_tokens = {
{ Opt_reparse_default, "default" },
{ Opt_reparse_nfs, "nfs" },
{ Opt_reparse_wsl, "wsl" },
{ Opt_reparse_err, NULL },
};
static int parse_reparse_flavor(struct fs_context *fc, char *value,
struct smb3_fs_context *ctx)
{
substring_t args[MAX_OPT_ARGS];
switch (match_token(value, reparse_flavor_tokens, args)) {
case Opt_reparse_default:
ctx->reparse_type = CIFS_REPARSE_TYPE_DEFAULT;
break;
case Opt_reparse_nfs:
ctx->reparse_type = CIFS_REPARSE_TYPE_NFS;
break;
case Opt_reparse_wsl:
ctx->reparse_type = CIFS_REPARSE_TYPE_WSL;
break;
default:
cifs_errorf(fc, "bad reparse= option: %s\n", value);
return 1;
}
return 0;
}
#define DUP_CTX_STR(field) \
do { \
if (ctx->field) { \
@@ -772,7 +802,7 @@ static void smb3_fs_context_free(struct fs_context *fc)
*/
static int smb3_verify_reconfigure_ctx(struct fs_context *fc,
struct smb3_fs_context *new_ctx,
struct smb3_fs_context *old_ctx)
struct smb3_fs_context *old_ctx, bool need_recon)
{
if (new_ctx->posix_paths != old_ctx->posix_paths) {
cifs_errorf(fc, "can not change posixpaths during remount\n");
@@ -798,8 +828,15 @@ static int smb3_verify_reconfigure_ctx(struct fs_context *fc,
}
if (new_ctx->password &&
(!old_ctx->password || strcmp(new_ctx->password, old_ctx->password))) {
cifs_errorf(fc, "can not change password during remount\n");
return -EINVAL;
if (need_recon == false) {
cifs_errorf(fc,
"can not change password of active session during remount\n");
return -EINVAL;
} else if (old_ctx->sectype == Kerberos) {
cifs_errorf(fc,
"can not change password for Kerberos via remount\n");
return -EINVAL;
}
}
if (new_ctx->domainname &&
(!old_ctx->domainname || strcmp(new_ctx->domainname, old_ctx->domainname))) {
@@ -843,9 +880,14 @@ static int smb3_reconfigure(struct fs_context *fc)
struct smb3_fs_context *ctx = smb3_fc2context(fc);
struct dentry *root = fc->root;
struct cifs_sb_info *cifs_sb = CIFS_SB(root->d_sb);
struct cifs_ses *ses = cifs_sb_master_tcon(cifs_sb)->ses;
bool need_recon = false;
int rc;
rc = smb3_verify_reconfigure_ctx(fc, ctx, cifs_sb->ctx);
if (ses->expired_pwd)
need_recon = true;
rc = smb3_verify_reconfigure_ctx(fc, ctx, cifs_sb->ctx, need_recon);
if (rc)
return rc;
@@ -858,7 +900,12 @@ static int smb3_reconfigure(struct fs_context *fc)
STEAL_STRING(cifs_sb, ctx, UNC);
STEAL_STRING(cifs_sb, ctx, source);
STEAL_STRING(cifs_sb, ctx, username);
STEAL_STRING_SENSITIVE(cifs_sb, ctx, password);
if (need_recon == false)
STEAL_STRING_SENSITIVE(cifs_sb, ctx, password);
else {
kfree_sensitive(ses->password);
ses->password = kstrdup(ctx->password, GFP_KERNEL);
}
STEAL_STRING(cifs_sb, ctx, domainname);
STEAL_STRING(cifs_sb, ctx, nodename);
STEAL_STRING(cifs_sb, ctx, iocharset);
@@ -916,7 +963,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
switch (opt) {
case Opt_compress:
ctx->compression = UNKNOWN_TYPE;
ctx->compress = true;
cifs_dbg(VFS,
"SMB3 compression support is experimental\n");
break;
@@ -1549,6 +1596,10 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
case Opt_rdma:
ctx->rdma = true;
break;
case Opt_reparse:
if (parse_reparse_flavor(fc, param->string, ctx))
goto cifs_parse_mount_err;
break;
}
/* case Opt_ignore: - is ignored as expected ... */
@@ -1635,6 +1686,7 @@ int smb3_init_fs_context(struct fs_context *fc)
ctx->backupgid_specified = false; /* no backup intent for a group */
ctx->retrans = 1;
ctx->reparse_type = CIFS_REPARSE_TYPE_DEFAULT;
/*
* short int override_uid = -1;

View File

@@ -41,6 +41,13 @@ enum {
Opt_cache_err
};
enum cifs_reparse_parm {
Opt_reparse_default,
Opt_reparse_nfs,
Opt_reparse_wsl,
Opt_reparse_err
};
enum cifs_sec_param {
Opt_sec_krb5,
Opt_sec_krb5i,
@@ -148,6 +155,7 @@ enum cifs_param {
Opt_vers,
Opt_sec,
Opt_cache,
Opt_reparse,
/* Mount options to be ignored */
Opt_ignore,
@@ -265,12 +273,13 @@ struct smb3_fs_context {
unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
unsigned int max_channels;
unsigned int max_cached_dirs;
__u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
bool compress; /* enable SMB2 messages (READ/WRITE) de/compression */
bool rootfs:1; /* if it's a SMB root file system */
bool witness:1; /* use witness protocol */
char *leaf_fullpath;
struct cifs_ses *dfs_root_ses;
bool dfs_automount:1; /* set for dfs automount only */
enum cifs_reparse_type reparse_type;
};
extern const struct fs_parameter_spec smb3_fs_parameters[];

View File

@@ -26,6 +26,7 @@
#include "fs_context.h"
#include "cifs_ioctl.h"
#include "cached_dir.h"
#include "reparse.h"
static void cifs_set_ops(struct inode *inode)
{
@@ -147,7 +148,8 @@ cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
/* populate an inode with info from a cifs_fattr struct */
int
cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr,
bool from_readdir)
{
struct cifsInodeInfo *cifs_i = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
@@ -199,7 +201,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
* Can't safely change the file size here if the client is writing to
* it due to potential races.
*/
if (is_size_safe_to_change(cifs_i, fattr->cf_eof)) {
if (is_size_safe_to_change(cifs_i, fattr->cf_eof, from_readdir)) {
i_size_write(inode, fattr->cf_eof);
/*
@@ -368,7 +370,7 @@ static int update_inode_info(struct super_block *sb,
CIFS_I(*inode)->time = 0; /* force reval */
return -ESTALE;
}
return cifs_fattr_to_inode(*inode, fattr);
return cifs_fattr_to_inode(*inode, fattr, false);
}
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
@@ -403,7 +405,7 @@ cifs_get_file_info_unix(struct file *filp)
} else
goto cifs_gfiunix_out;
rc = cifs_fattr_to_inode(inode, &fattr);
rc = cifs_fattr_to_inode(inode, &fattr, false);
cifs_gfiunix_out:
free_xid(xid);
@@ -727,84 +729,6 @@ out_reparse:
fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
}
static inline dev_t nfs_mkdev(struct reparse_posix_data *buf)
{
u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer);
return MKDEV(v >> 32, v & 0xffffffff);
}
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
struct cifs_fattr *fattr,
struct cifs_open_info_data *data)
{
struct reparse_posix_data *buf = data->reparse.posix;
u32 tag = data->reparse.tag;
if (tag == IO_REPARSE_TAG_NFS && buf) {
switch (le64_to_cpu(buf->InodeType)) {
case NFS_SPECFILE_CHR:
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;
fattr->cf_dtype = DT_BLK;
fattr->cf_rdev = nfs_mkdev(buf);
break;
case NFS_SPECFILE_FIFO:
fattr->cf_mode |= S_IFIFO;
fattr->cf_dtype = DT_FIFO;
break;
case NFS_SPECFILE_SOCK:
fattr->cf_mode |= S_IFSOCK;
fattr->cf_dtype = DT_SOCK;
break;
case NFS_SPECFILE_LNK:
fattr->cf_mode |= S_IFLNK;
fattr->cf_dtype = DT_LNK;
break;
default:
WARN_ON_ONCE(1);
return false;
}
return true;
}
switch (tag) {
case IO_REPARSE_TAG_LX_SYMLINK:
fattr->cf_mode |= S_IFLNK;
fattr->cf_dtype = DT_LNK;
break;
case IO_REPARSE_TAG_LX_FIFO:
fattr->cf_mode |= S_IFIFO;
fattr->cf_dtype = DT_FIFO;
break;
case IO_REPARSE_TAG_AF_UNIX:
fattr->cf_mode |= S_IFSOCK;
fattr->cf_dtype = DT_SOCK;
break;
case IO_REPARSE_TAG_LX_CHR:
fattr->cf_mode |= S_IFCHR;
fattr->cf_dtype = DT_CHR;
break;
case IO_REPARSE_TAG_LX_BLK:
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;
fattr->cf_dtype = DT_LNK;
break;
default:
return false;
}
return true;
}
static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
struct cifs_open_info_data *data,
struct super_block *sb)
@@ -835,6 +759,8 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
fattr->cf_createtime = le64_to_cpu(info->CreationTime);
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
fattr->cf_uid = cifs_sb->ctx->linux_uid;
fattr->cf_gid = cifs_sb->ctx->linux_gid;
fattr->cf_mode = cifs_sb->ctx->file_mode;
if (cifs_open_data_reparse(data) &&
@@ -877,9 +803,6 @@ out_reparse:
fattr->cf_symlink_target = data->symlink_target;
data->symlink_target = NULL;
}
fattr->cf_uid = cifs_sb->ctx->linux_uid;
fattr->cf_gid = cifs_sb->ctx->linux_gid;
}
static int
@@ -893,6 +816,9 @@ cifs_get_file_info(struct file *filp)
struct cifsFileInfo *cfile = filp->private_data;
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server;
struct dentry *dentry = filp->f_path.dentry;
void *page = alloc_dentry_path();
const unsigned char *path;
if (!server->ops->query_file_info)
return -ENOSYS;
@@ -907,7 +833,14 @@ cifs_get_file_info(struct file *filp)
data.symlink = true;
data.reparse.tag = IO_REPARSE_TAG_SYMLINK;
}
path = build_path_from_dentry(dentry, page);
if (IS_ERR(path)) {
free_dentry_path(page);
return PTR_ERR(path);
}
cifs_open_info_to_fattr(&fattr, &data, inode->i_sb);
if (fattr.cf_flags & CIFS_FATTR_DELETE_PENDING)
cifs_mark_open_handles_for_deleted_file(inode, path);
break;
case -EREMOTE:
cifs_create_junction_fattr(&fattr, inode->i_sb);
@@ -934,9 +867,10 @@ cifs_get_file_info(struct file *filp)
fattr.cf_uniqueid = CIFS_I(inode)->uniqueid;
fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
/* if filetype is different, return error */
rc = cifs_fattr_to_inode(inode, &fattr);
rc = cifs_fattr_to_inode(inode, &fattr, false);
cgfi_exit:
cifs_free_open_info(&data);
free_dentry_path(page);
free_xid(xid);
return rc;
}
@@ -1075,6 +1009,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
struct kvec rsp_iov, *iov = NULL;
int rsp_buftype = CIFS_NO_BUFFER;
u32 tag = data->reparse.tag;
struct inode *inode = NULL;
int rc = 0;
if (!tag && server->ops->query_reparse_point) {
@@ -1114,8 +1049,12 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
if (tcon->posix_extensions)
smb311_posix_info_to_fattr(fattr, data, sb);
else
else {
cifs_open_info_to_fattr(fattr, data, sb);
inode = cifs_iget(sb, fattr);
if (inode && fattr->cf_flags & CIFS_FATTR_DELETE_PENDING)
cifs_mark_open_handles_for_deleted_file(inode, full_path);
}
out:
fattr->cf_cifstag = data->reparse.tag;
free_rsp_buf(rsp_buftype, rsp_iov.iov_base);
@@ -1170,6 +1109,8 @@ static int cifs_get_fattr(struct cifs_open_info_data *data,
full_path, fattr);
} else {
cifs_open_info_to_fattr(fattr, data, sb);
if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING)
cifs_mark_open_handles_for_deleted_file(*inode, full_path);
}
break;
case -EREMOTE:
@@ -1491,7 +1432,7 @@ retry_iget5_locked:
}
/* can't fail - see cifs_find_inode() */
cifs_fattr_to_inode(inode, fattr);
cifs_fattr_to_inode(inode, fattr, false);
if (sb->s_flags & SB_NOATIME)
inode->i_flags |= S_NOATIME | S_NOCMTIME;
if (inode->i_state & I_NEW) {
@@ -1846,20 +1787,24 @@ retry_std_delete:
goto psx_del_no_retry;
}
rc = server->ops->unlink(xid, tcon, full_path, cifs_sb);
rc = server->ops->unlink(xid, tcon, full_path, cifs_sb, dentry);
psx_del_no_retry:
if (!rc) {
if (inode)
if (inode) {
cifs_mark_open_handles_for_deleted_file(inode, full_path);
cifs_drop_nlink(inode);
}
} else if (rc == -ENOENT) {
d_drop(dentry);
} else if (rc == -EBUSY) {
if (server->ops->rename_pending_delete) {
rc = server->ops->rename_pending_delete(full_path,
dentry, xid);
if (rc == 0)
if (rc == 0) {
cifs_mark_open_handles_for_deleted_file(inode, full_path);
cifs_drop_nlink(inode);
}
}
} else if ((rc == -EACCES) && (dosattr == 0) && inode) {
attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
@@ -2797,7 +2742,7 @@ void cifs_setsize(struct inode *inode, loff_t offset)
static int
cifs_set_file_size(struct inode *inode, struct iattr *attrs,
unsigned int xid, const char *full_path)
unsigned int xid, const char *full_path, struct dentry *dentry)
{
int rc;
struct cifsFileInfo *open_file;
@@ -2848,7 +2793,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
*/
if (server->ops->set_path_size)
rc = server->ops->set_path_size(xid, tcon, full_path,
attrs->ia_size, cifs_sb, false);
attrs->ia_size, cifs_sb, false, dentry);
else
rc = -ENOSYS;
cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc);
@@ -2938,7 +2883,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
rc = 0;
if (attrs->ia_valid & ATTR_SIZE) {
rc = cifs_set_file_size(inode, attrs, xid, full_path);
rc = cifs_set_file_size(inode, attrs, xid, full_path, direntry);
if (rc != 0)
goto out;
}
@@ -3105,7 +3050,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
}
if (attrs->ia_valid & ATTR_SIZE) {
rc = cifs_set_file_size(inode, attrs, xid, full_path);
rc = cifs_set_file_size(inode, attrs, xid, full_path, direntry);
if (rc != 0)
goto cifs_setattr_exit;
}

View File

@@ -345,6 +345,11 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
xid = get_xid();
cifs_dbg(FYI, "cifs ioctl 0x%x\n", command);
if (pSMBFile == NULL)
trace_smb3_ioctl(xid, 0, command);
else
trace_smb3_ioctl(xid, pSMBFile->fid.persistent_fid, command);
switch (command) {
case FS_IOC_GETFLAGS:
if (pSMBFile == NULL)

View File

@@ -853,6 +853,40 @@ cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path)
free_dentry_path(page);
}
/*
* If a dentry has been deleted, all corresponding open handles should know that
* so that we do not defer close them.
*/
void cifs_mark_open_handles_for_deleted_file(struct inode *inode,
const char *path)
{
struct cifsFileInfo *cfile;
void *page;
const char *full_path;
struct cifsInodeInfo *cinode = CIFS_I(inode);
page = alloc_dentry_path();
spin_lock(&cinode->open_file_lock);
/*
* note: we need to construct path from dentry and compare only if the
* inode has any hardlinks. When number of hardlinks is 1, we can just
* mark all open handles since they are going to be from the same file.
*/
if (inode->i_nlink > 1) {
list_for_each_entry(cfile, &cinode->openFileList, flist) {
full_path = build_path_from_dentry(cfile->dentry, page);
if (!IS_ERR(full_path) && strcmp(full_path, path) == 0)
cfile->status_file_deleted = true;
}
} else {
list_for_each_entry(cfile, &cinode->openFileList, flist)
cfile->status_file_deleted = true;
}
spin_unlock(&cinode->open_file_lock);
free_dentry_path(page);
}
/* parses DFS referral V3 structure
* caller is responsible for freeing target_nodes
* returns:

View File

@@ -22,6 +22,7 @@
#include "smb2proto.h"
#include "fs_context.h"
#include "cached_dir.h"
#include "reparse.h"
/*
* To be safe - for UCS to UTF-8 with strings loaded with the rare long
@@ -55,23 +56,6 @@ 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
*
@@ -141,6 +125,8 @@ retry:
if (likely(reparse_inode_match(inode, fattr))) {
fattr->cf_mode = inode->i_mode;
fattr->cf_rdev = inode->i_rdev;
fattr->cf_uid = inode->i_uid;
fattr->cf_gid = inode->i_gid;
fattr->cf_eof = CIFS_I(inode)->netfs.remote_i_size;
fattr->cf_symlink_target = NULL;
} else {
@@ -148,7 +134,7 @@ retry:
rc = -ESTALE;
}
}
if (!rc && !cifs_fattr_to_inode(inode, fattr)) {
if (!rc && !cifs_fattr_to_inode(inode, fattr, true)) {
dput(dentry);
return;
}

532
fs/smb/client/reparse.c Normal file

File diff suppressed because it is too large Load Diff

113
fs/smb/client/reparse.h Normal file
View File

@@ -0,0 +1,113 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2024 Paulo Alcantara <pc@manguebit.com>
*/
#ifndef _CIFS_REPARSE_H
#define _CIFS_REPARSE_H
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/uidgid.h>
#include "fs_context.h"
#include "cifsglob.h"
static inline dev_t reparse_nfs_mkdev(struct reparse_posix_data *buf)
{
u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer);
return MKDEV(v >> 32, v & 0xffffffff);
}
static inline dev_t wsl_mkdev(void *ptr)
{
u64 v = le64_to_cpu(*(__le64 *)ptr);
return MKDEV(v & 0xffffffff, v >> 32);
}
static inline kuid_t wsl_make_kuid(struct cifs_sb_info *cifs_sb,
void *ptr)
{
u32 uid = le32_to_cpu(*(__le32 *)ptr);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
return cifs_sb->ctx->linux_uid;
return make_kuid(current_user_ns(), uid);
}
static inline kgid_t wsl_make_kgid(struct cifs_sb_info *cifs_sb,
void *ptr)
{
u32 gid = le32_to_cpu(*(__le32 *)ptr);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)
return cifs_sb->ctx->linux_gid;
return make_kgid(current_user_ns(), gid);
}
static inline u64 reparse_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 inline u32 reparse_mode_wsl_tag(mode_t mode)
{
switch (mode & S_IFMT) {
case S_IFBLK: return IO_REPARSE_TAG_LX_BLK;
case S_IFCHR: return IO_REPARSE_TAG_LX_CHR;
case S_IFIFO: return IO_REPARSE_TAG_LX_FIFO;
case S_IFSOCK: return IO_REPARSE_TAG_AF_UNIX;
}
return 0;
}
/*
* 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);
}
static inline bool cifs_open_data_reparse(struct cifs_open_info_data *data)
{
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;
}
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
struct cifs_fattr *fattr,
struct cifs_open_info_data *data);
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);
int smb2_mknod_reparse(unsigned int xid, struct inode *inode,
struct dentry *dentry, struct cifs_tcon *tcon,
const char *full_path, umode_t mode, dev_t dev);
int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, struct kvec *rsp_iov,
struct cifs_open_info_data *data);
#endif /* _CIFS_REPARSE_H */

View File

@@ -36,7 +36,8 @@ enum smb2_compound_ops {
SMB2_OP_RMDIR,
SMB2_OP_POSIX_QUERY_INFO,
SMB2_OP_SET_REPARSE,
SMB2_OP_GET_REPARSE
SMB2_OP_GET_REPARSE,
SMB2_OP_QUERY_WSL_EA,
};
/* Used when constructing chained read requests. */

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More