18 smb3/cifs client fixes

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmfvTpMACgkQiiy9cAdy
 T1Honwv/SQEGf0ELO97Ek8aTio1dKi7Z+sDpwBwTQbVI9jdO8/Jj7MzbOHhZ9XSC
 JWGrUHZD8ssFGxktTQj+Mfk8LAMMm2yDeOOCwMZthLwkC00qPNIhaP5rvVdTbeSj
 J6jGzFWkBmPxIs1ljazKKts84YiiNu0QbZupmROgWe64KzNdYO7I3f32msgzjOKz
 wzdh6AQ+/WfrzCBX5AxpBohKrceY+NGjKYNc0YIvQ4MYC+P9pmK/71mgqucoUtrr
 gwQA7ctNsXd+tX93Q6yY0BmiuV1fzrsUrAjZGP58juwJ8uTapN0Mm5szcbiIVpF2
 CseFahAVAhtUg/AaAPgdsz9OqnvmKZLaabNtzGdrmgepz3l9Y+njZqx23kyKiwIT
 5cnXmytfghUr7TQToSlPP9PpC1oyJV/xwcuC90gx5LhCpa13+i6b4eFO/IlZe6LB
 UyE26MAm+ZDY8kcCfK0thSwdz36E1ieURwDmOEFqACOJL5wVMuy0GQTL4lsHqWfZ
 unukaR2e
 =KNE4
 -----END PGP SIGNATURE-----

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

Pull more smb client updates from Steve French:

 - reconnect fixes: three for updating rsize/wsize and an SMB1 reconnect
   fix

 - RFC1001 fixes: fixing connections to nonstandard ports, and negprot
   retries

 - fix mfsymlinks to old servers

 - make mapping of open flags for SMB1 more accurate

 - permission fixes: adding retry on open for write, and one for stat to
   workaround unexpected access denied

 - add two new xattrs, one for retrieving SACL and one for retrieving
   owner (without having to retrieve the whole ACL)

 - fix mount parm validation for echo_interval

 - minor cleanup (including removing now unneeded cifs_truncate_page)

* tag '6.15-rc-part2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: update internal version number
  cifs: Implement is_network_name_deleted for SMB1
  cifs: Remove cifs_truncate_page() as it should be superfluous
  cifs: Do not add FILE_READ_ATTRIBUTES when using GENERIC_READ/EXECUTE/ALL
  cifs: Improve SMB2+ stat() to work also without FILE_READ_ATTRIBUTES
  cifs: Add fallback for SMB2 CREATE without FILE_READ_ATTRIBUTES
  cifs: Fix querying and creating MF symlinks over SMB1
  cifs: Fix access_flags_to_smbopen_mode
  cifs: Fix negotiate retry functionality
  cifs: Improve handling of NetBIOS packets
  cifs: Allow to disable or force initialization of NetBIOS session
  cifs: Add a new xattr system.smb3_ntsd_owner for getting or setting owner
  cifs: Add a new xattr system.smb3_ntsd_sacl for getting or setting SACLs
  smb: client: Update IO sizes after reconnection
  smb: client: Store original IO parameters and prevent zero IO sizes
  smb:client: smb: client: Add reverse mapping from tcon to superblocks
  cifs: remove unreachable code in cifs_get_tcp_session()
  cifs: fix integer overflow in match_server()
This commit is contained in:
Linus Torvalds 2025-04-04 15:27:43 -07:00
commit 9f867ba24d
19 changed files with 437 additions and 85 deletions

View File

@ -49,6 +49,7 @@
struct cifs_sb_info {
struct rb_root tlink_tree;
struct list_head tcon_sb_link;
spinlock_t tlink_tree_lock;
struct tcon_link *master_tlink;
struct nls_table *local_nls;

View File

@ -135,7 +135,6 @@ extern ssize_t cifs_file_copychunk_range(unsigned int xid,
extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern void cifs_setsize(struct inode *inode, loff_t offset);
extern int cifs_truncate_page(struct address_space *mapping, loff_t from);
struct smb3_fs_context;
extern struct dentry *cifs_smb3_do_mount(struct file_system_type *fs_type,
@ -146,6 +145,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 53
#define CIFS_VERSION "2.53"
#define SMB3_PRODUCT_BUILD 54
#define CIFS_VERSION "2.54"
#endif /* _CIFSFS_H */

View File

@ -714,6 +714,8 @@ struct TCP_Server_Info {
spinlock_t srv_lock; /* protect anything here that is not protected */
__u64 conn_id; /* connection identifier (useful for debugging) */
int srv_count; /* reference counter */
int rfc1001_sessinit; /* whether to estasblish netbios session */
bool with_rfc1001; /* if netbios session is used */
/* 15 character server name + 0x20 16th byte indicating type = srv */
char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
struct smb_version_operations *ops;
@ -1321,7 +1323,8 @@ struct cifs_tcon {
#endif
struct list_head pending_opens; /* list of incomplete opens */
struct cached_fids *cfids;
/* BB add field for back pointer to sb struct(s)? */
struct list_head cifs_sb_list;
spinlock_t sb_list_lock;
#ifdef CONFIG_CIFS_DFS_UPCALL
struct delayed_work dfs_cache_work;
struct list_head dfs_ses_list;
@ -1718,6 +1721,7 @@ struct mid_q_entry {
void *resp_buf; /* pointer to received SMB header */
unsigned int resp_buf_size;
int mid_state; /* wish this were enum but can not pass to wait_event */
int mid_rc; /* rc for MID_RC */
unsigned int mid_flags;
__le16 command; /* smb command code */
unsigned int optype; /* operation type */
@ -1880,6 +1884,7 @@ static inline bool is_replayable_error(int error)
#define MID_RESPONSE_MALFORMED 0x10
#define MID_SHUTDOWN 0x20
#define MID_RESPONSE_READY 0x40 /* ready for other process handle the rsp */
#define MID_RC 0x80 /* mid_rc contains custom rc */
/* Flags */
#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */

View File

@ -1041,15 +1041,31 @@ static __u16 convert_disposition(int disposition)
static int
access_flags_to_smbopen_mode(const int access_flags)
{
int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
/*
* SYSTEM_SECURITY grants both read and write access to SACL, treat is as read/write.
* MAXIMUM_ALLOWED grants as many access as possible, so treat it as read/write too.
* SYNCHRONIZE as is does not grant any specific access, so do not check its mask.
* If only SYNCHRONIZE bit is specified then fallback to read access.
*/
bool with_write_flags = access_flags & (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA |
FILE_DELETE_CHILD | FILE_WRITE_ATTRIBUTES | DELETE |
WRITE_DAC | WRITE_OWNER | SYSTEM_SECURITY |
MAXIMUM_ALLOWED | GENERIC_WRITE | GENERIC_ALL);
bool with_read_flags = access_flags & (FILE_READ_DATA | FILE_READ_EA | FILE_EXECUTE |
FILE_READ_ATTRIBUTES | READ_CONTROL |
SYSTEM_SECURITY | MAXIMUM_ALLOWED | GENERIC_ALL |
GENERIC_EXECUTE | GENERIC_READ);
bool with_execute_flags = access_flags & (FILE_EXECUTE | MAXIMUM_ALLOWED | GENERIC_ALL |
GENERIC_EXECUTE);
if (masked_flags == GENERIC_READ)
return SMBOPEN_READ;
else if (masked_flags == GENERIC_WRITE)
if (with_write_flags && with_read_flags)
return SMBOPEN_READWRITE;
else if (with_write_flags)
return SMBOPEN_WRITE;
/* just go for read/write */
return SMBOPEN_READWRITE;
else if (with_execute_flags)
return SMBOPEN_EXECUTE;
else
return SMBOPEN_READ;
}
int

View File

@ -371,7 +371,7 @@ static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num
*
*/
static int __cifs_reconnect(struct TCP_Server_Info *server,
bool mark_smb_session)
bool mark_smb_session, bool once)
{
int rc = 0;
@ -399,6 +399,9 @@ static int __cifs_reconnect(struct TCP_Server_Info *server,
if (rc) {
cifs_server_unlock(server);
cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
/* If was asked to reconnect only once, do not try it more times */
if (once)
break;
msleep(3000);
} else {
atomic_inc(&tcpSesReconnectCount);
@ -564,19 +567,33 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
return rc;
}
int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
static int
_cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session, bool once)
{
if (!server->leaf_fullpath)
return __cifs_reconnect(server, mark_smb_session);
return __cifs_reconnect(server, mark_smb_session, once);
return reconnect_dfs_server(server);
}
#else
int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
static int
_cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session, bool once)
{
return __cifs_reconnect(server, mark_smb_session);
return __cifs_reconnect(server, mark_smb_session, once);
}
#endif
int
cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
{
return _cifs_reconnect(server, mark_smb_session, false);
}
static int
cifs_reconnect_once(struct TCP_Server_Info *server)
{
return _cifs_reconnect(server, true, true);
}
static void
cifs_echo_request(struct work_struct *work)
{
@ -803,26 +820,110 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
/* Regular SMB response */
return true;
case RFC1002_SESSION_KEEP_ALIVE:
/*
* RFC 1002 session keep alive can sent by the server only when
* we established a RFC 1002 session. But Samba servers send
* RFC 1002 session keep alive also over port 445 on which
* RFC 1002 session is not established.
*/
cifs_dbg(FYI, "RFC 1002 session keep alive\n");
break;
case RFC1002_POSITIVE_SESSION_RESPONSE:
cifs_dbg(FYI, "RFC 1002 positive session response\n");
/*
* RFC 1002 positive session response cannot be returned
* for SMB request. RFC 1002 session response is handled
* exclusively in ip_rfc1001_connect() function.
*/
cifs_server_dbg(VFS, "RFC 1002 positive session response (unexpected)\n");
cifs_reconnect(server, true);
break;
case RFC1002_NEGATIVE_SESSION_RESPONSE:
/*
* We get this from Windows 98 instead of an error on
* SMB negprot response.
* SMB negprot response, when we have not established
* RFC 1002 session (which means ip_rfc1001_connect()
* was skipped). Note that same still happens with
* Windows Server 2022 when connecting via port 139.
* So for this case when mount option -o nonbsessinit
* was not specified, try to reconnect with establishing
* RFC 1002 session. If new socket establishment with
* RFC 1002 session was successful then return to the
* mid's caller -EAGAIN, so it can retry the request.
*/
cifs_dbg(FYI, "RFC 1002 negative session response\n");
/* give server a second to clean up */
msleep(1000);
/*
* Always try 445 first on reconnect since we get NACK
* on some if we ever connected to port 139 (the NACK
* is since we do not begin with RFC1001 session
* initialize frame).
*/
cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT);
if (!cifs_rdma_enabled(server) &&
server->tcpStatus == CifsInNegotiate &&
!server->with_rfc1001 &&
server->rfc1001_sessinit != 0) {
int rc, mid_rc;
struct mid_q_entry *mid, *nmid;
LIST_HEAD(dispose_list);
cifs_dbg(FYI, "RFC 1002 negative session response during SMB Negotiate, retrying with NetBIOS session\n");
/*
* Before reconnect, delete all pending mids for this
* server, so reconnect would not signal connection
* aborted error to mid's callbacks. Note that for this
* server there should be exactly one pending mid
* corresponding to SMB1/SMB2 Negotiate packet.
*/
spin_lock(&server->mid_lock);
list_for_each_entry_safe(mid, nmid, &server->pending_mid_q, qhead) {
kref_get(&mid->refcount);
list_move(&mid->qhead, &dispose_list);
mid->mid_flags |= MID_DELETED;
}
spin_unlock(&server->mid_lock);
/* Now try to reconnect once with NetBIOS session. */
server->with_rfc1001 = true;
rc = cifs_reconnect_once(server);
/*
* If reconnect was successful then indicate -EAGAIN
* to mid's caller. If reconnect failed with -EAGAIN
* then mask it as -EHOSTDOWN, so mid's caller would
* know that it failed.
*/
if (rc == 0)
mid_rc = -EAGAIN;
else if (rc == -EAGAIN)
mid_rc = -EHOSTDOWN;
else
mid_rc = rc;
/*
* After reconnect (either successful or unsuccessful)
* deliver reconnect status to mid's caller via mid's
* callback. Use MID_RC state which indicates that the
* return code should be read from mid_rc member.
*/
list_for_each_entry_safe(mid, nmid, &dispose_list, qhead) {
list_del_init(&mid->qhead);
mid->mid_rc = mid_rc;
mid->mid_state = MID_RC;
mid->callback(mid);
release_mid(mid);
}
/*
* If reconnect failed then wait two seconds. In most
* cases we were been called from the mount context and
* delivered failure to mid's callback will stop this
* receiver task thread and fails the mount process.
* So wait two seconds to prevent another reconnect
* in this task thread, which would be useless as the
* mount context will fail at all.
*/
if (rc != 0)
msleep(2000);
} else {
cifs_server_dbg(VFS, "RFC 1002 negative session response (unexpected)\n");
cifs_reconnect(server, true);
}
break;
case RFC1002_RETARGET_SESSION_RESPONSE:
cifs_server_dbg(VFS, "RFC 1002 retarget session response (unexpected)\n");
cifs_reconnect(server, true);
break;
default:
@ -1701,6 +1802,8 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
ctx->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
memcpy(tcp_ses->server_RFC1001_name,
ctx->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
tcp_ses->rfc1001_sessinit = ctx->rfc1001_sessinit;
tcp_ses->with_rfc1001 = false;
tcp_ses->session_estab = false;
tcp_ses->sequence_number = 0;
tcp_ses->channel_sequence_num = 0; /* only tracked for primary channel */
@ -1731,12 +1834,8 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
*/
tcp_ses->tcpStatus = CifsNew;
++tcp_ses->srv_count;
tcp_ses->echo_interval = ctx->echo_interval * HZ;
if (ctx->echo_interval >= SMB_ECHO_INTERVAL_MIN &&
ctx->echo_interval <= SMB_ECHO_INTERVAL_MAX)
tcp_ses->echo_interval = ctx->echo_interval * HZ;
else
tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ;
if (tcp_ses->rdma) {
#ifndef CONFIG_CIFS_SMB_DIRECT
cifs_dbg(VFS, "CONFIG_CIFS_SMB_DIRECT is not enabled\n");
@ -3221,6 +3320,7 @@ ip_rfc1001_connect(struct TCP_Server_Info *server)
return -EIO;
}
server->with_rfc1001 = true;
return 0;
}
@ -3332,7 +3432,16 @@ generic_ip_connect(struct TCP_Server_Info *server)
return rc;
}
trace_smb3_connect_done(server->hostname, server->conn_id, &server->dstaddr);
if (sport == htons(RFC1001_PORT))
/*
* Establish RFC1001 NetBIOS session when it was explicitly requested
* by mount option -o nbsessinit, or when connecting to default RFC1001
* server port (139) and it was not explicitly disabled by mount option
* -o nonbsessinit.
*/
if (server->with_rfc1001 ||
server->rfc1001_sessinit == 1 ||
(server->rfc1001_sessinit == -1 && sport == htons(RFC1001_PORT)))
rc = ip_rfc1001_connect(server);
return rc;
@ -3481,6 +3590,7 @@ int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb)
struct smb3_fs_context *ctx = cifs_sb->ctx;
INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks);
INIT_LIST_HEAD(&cifs_sb->tcon_sb_link);
spin_lock_init(&cifs_sb->tlink_tree_lock);
cifs_sb->tlink_tree = RB_ROOT;
@ -3713,6 +3823,10 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
spin_unlock(&cifs_sb->tlink_tree_lock);
spin_lock(&tcon->sb_list_lock);
list_add(&cifs_sb->tcon_sb_link, &tcon->cifs_sb_list);
spin_unlock(&tcon->sb_list_lock);
queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,
TLINK_IDLE_EXPIRE);
return 0;
@ -4054,9 +4168,19 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
struct rb_root *root = &cifs_sb->tlink_tree;
struct rb_node *node;
struct tcon_link *tlink;
struct cifs_tcon *tcon = NULL;
cancel_delayed_work_sync(&cifs_sb->prune_tlinks);
if (cifs_sb->master_tlink) {
tcon = cifs_sb->master_tlink->tl_tcon;
if (tcon) {
spin_lock(&tcon->sb_list_lock);
list_del_init(&cifs_sb->tcon_sb_link);
spin_unlock(&tcon->sb_list_lock);
}
}
spin_lock(&cifs_sb->tlink_tree_lock);
while ((node = rb_first(root))) {
tlink = rb_entry(node, struct tcon_link, tl_rbnode);
@ -4078,11 +4202,13 @@ int
cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server)
{
bool in_retry = false;
int rc = 0;
if (!server->ops->need_neg || !server->ops->negotiate)
return -ENOSYS;
retry:
/* only send once per connect */
spin_lock(&server->srv_lock);
if (server->tcpStatus != CifsGood &&
@ -4102,6 +4228,14 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
spin_unlock(&server->srv_lock);
rc = server->ops->negotiate(xid, ses, server);
if (rc == -EAGAIN) {
/* Allow one retry attempt */
if (!in_retry) {
in_retry = true;
goto retry;
}
rc = -EHOSTDOWN;
}
if (rc == 0) {
spin_lock(&server->srv_lock);
if (server->tcpStatus == CifsInNegotiate)

View File

@ -135,6 +135,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
fsparam_flag("witness", Opt_witness),
fsparam_flag_no("nativesocket", Opt_nativesocket),
fsparam_flag_no("unicode", Opt_unicode),
fsparam_flag_no("nbsessinit", Opt_nbsessinit),
/* Mount options which take uid or gid */
fsparam_uid("backupuid", Opt_backupuid),
@ -968,6 +969,10 @@ static int smb3_verify_reconfigure_ctx(struct fs_context *fc,
cifs_errorf(fc, "can not change unicode during remount\n");
return -EINVAL;
}
if (new_ctx->rfc1001_sessinit != old_ctx->rfc1001_sessinit) {
cifs_errorf(fc, "can not change nbsessinit during remount\n");
return -EINVAL;
}
return 0;
}
@ -1333,6 +1338,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
case Opt_rsize:
ctx->rsize = result.uint_32;
ctx->got_rsize = true;
ctx->vol_rsize = ctx->rsize;
break;
case Opt_wsize:
ctx->wsize = result.uint_32;
@ -1348,6 +1354,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
ctx->wsize, PAGE_SIZE);
}
}
ctx->vol_wsize = ctx->wsize;
break;
case Opt_acregmax:
if (result.uint_32 > CIFS_MAX_ACTIMEO / HZ) {
@ -1383,6 +1390,11 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
ctx->closetimeo = HZ * result.uint_32;
break;
case Opt_echo_interval:
if (result.uint_32 < SMB_ECHO_INTERVAL_MIN ||
result.uint_32 > SMB_ECHO_INTERVAL_MAX) {
cifs_errorf(fc, "echo interval is out of bounds\n");
goto cifs_parse_mount_err;
}
ctx->echo_interval = result.uint_32;
break;
case Opt_snapshot:
@ -1602,6 +1614,10 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
if (i == RFC1001_NAME_LEN && param->string[i] != 0)
pr_warn("server netbiosname longer than 15 truncated\n");
break;
case Opt_nbsessinit:
ctx->rfc1001_sessinit = !result.negated;
cifs_dbg(FYI, "rfc1001_sessinit set to %d\n", ctx->rfc1001_sessinit);
break;
case Opt_ver:
/* version of mount userspace tools, not dialect */
/* If interface changes in mount.cifs bump to new ver */
@ -1889,13 +1905,16 @@ int smb3_init_fs_context(struct fs_context *fc)
memset(ctx->source_rfc1001_name, 0x20, RFC1001_NAME_LEN);
for (i = 0; i < strnlen(nodename, RFC1001_NAME_LEN); i++)
ctx->source_rfc1001_name[i] = toupper(nodename[i]);
ctx->source_rfc1001_name[RFC1001_NAME_LEN] = 0;
/*
* null target name indicates to use *SMBSERVR default called name
* if we end up sending RFC1001 session initialize
*/
ctx->target_rfc1001_name[0] = 0;
ctx->rfc1001_sessinit = -1; /* autodetect based on port number */
ctx->cred_uid = current_uid();
ctx->linux_uid = current_uid();
ctx->linux_gid = current_gid();

View File

@ -174,6 +174,7 @@ enum cifs_param {
Opt_iocharset,
Opt_netbiosname,
Opt_servern,
Opt_nbsessinit,
Opt_ver,
Opt_vers,
Opt_sec,
@ -216,6 +217,7 @@ struct smb3_fs_context {
char *iocharset; /* local code page for mapping to and from Unicode */
char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */
char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */
int rfc1001_sessinit;
kuid_t cred_uid;
kuid_t linux_uid;
kgid_t linux_gid;
@ -280,6 +282,9 @@ struct smb3_fs_context {
bool use_client_guid:1;
/* reuse existing guid for multichannel */
u8 client_guid[SMB2_CLIENT_GUID_SIZE];
/* User-specified original r/wsize value */
unsigned int vol_rsize;
unsigned int vol_wsize;
unsigned int bsize;
unsigned int rasize;
unsigned int rsize;

View File

@ -2901,23 +2901,6 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
return -EOPNOTSUPP;
}
int cifs_truncate_page(struct address_space *mapping, loff_t from)
{
pgoff_t index = from >> PAGE_SHIFT;
unsigned offset = from & (PAGE_SIZE - 1);
struct page *page;
int rc = 0;
page = grab_cache_page(mapping, index);
if (!page)
return -ENOMEM;
zero_user_segment(page, offset, PAGE_SIZE);
unlock_page(page);
put_page(page);
return rc;
}
void cifs_setsize(struct inode *inode, loff_t offset)
{
struct cifsInodeInfo *cifs_i = CIFS_I(inode);
@ -3012,8 +2995,6 @@ set_size_out:
*/
attrs->ia_ctime = attrs->ia_mtime = current_time(inode);
attrs->ia_valid |= ATTR_CTIME | ATTR_MTIME;
cifs_truncate_page(inode->i_mapping, inode->i_size);
}
return rc;

View File

@ -258,7 +258,7 @@ cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_open_parms oparms;
struct cifs_io_parms io_parms = {0};
int buf_type = CIFS_NO_BUFFER;
FILE_ALL_INFO file_info;
struct cifs_open_info_data query_data;
oparms = (struct cifs_open_parms) {
.tcon = tcon,
@ -270,11 +270,11 @@ cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
.fid = &fid,
};
rc = CIFS_open(xid, &oparms, &oplock, &file_info);
rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &query_data);
if (rc)
return rc;
if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
if (query_data.fi.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
rc = -ENOENT;
/* it's not a symlink */
goto out;
@ -313,7 +313,7 @@ cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
.fid = &fid,
};
rc = CIFS_open(xid, &oparms, &oplock, NULL);
rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);
if (rc)
return rc;

View File

@ -137,8 +137,10 @@ tcon_info_alloc(bool dir_leases_enabled, enum smb3_tcon_ref_trace trace)
spin_lock_init(&ret_buf->tc_lock);
INIT_LIST_HEAD(&ret_buf->openFileList);
INIT_LIST_HEAD(&ret_buf->tcon_list);
INIT_LIST_HEAD(&ret_buf->cifs_sb_list);
spin_lock_init(&ret_buf->open_file_lock);
spin_lock_init(&ret_buf->stat_lock);
spin_lock_init(&ret_buf->sb_list_lock);
atomic_set(&ret_buf->num_local_opens, 0);
atomic_set(&ret_buf->num_remote_opens, 0);
ret_buf->stats_from_time = ktime_get_real_seconds();

View File

@ -14,6 +14,8 @@
#include "cifspdu.h"
#include "cifs_unicode.h"
#include "fs_context.h"
#include "nterr.h"
#include "smberr.h"
/*
* An NT cancel request header looks just like the original request except:
@ -426,13 +428,6 @@ cifs_negotiate(const unsigned int xid,
{
int rc;
rc = CIFSSMBNegotiate(xid, ses, server);
if (rc == -EAGAIN) {
/* retry only once on 1st time connection */
set_credits(server, 1);
rc = CIFSSMBNegotiate(xid, ses, server);
if (rc == -EAGAIN)
rc = -EHOSTDOWN;
}
return rc;
}
@ -444,8 +439,8 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
unsigned int wsize;
/* start with specified wsize, or default */
if (ctx->wsize)
wsize = ctx->wsize;
if (ctx->got_wsize)
wsize = ctx->vol_wsize;
else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
wsize = CIFS_DEFAULT_IOSIZE;
else
@ -497,7 +492,7 @@ cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
else
defsize = server->maxBuf - sizeof(READ_RSP);
rsize = ctx->rsize ? ctx->rsize : defsize;
rsize = ctx->got_rsize ? ctx->vol_rsize : defsize;
/*
* no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to
@ -1069,6 +1064,47 @@ cifs_make_node(unsigned int xid, struct inode *inode,
full_path, mode, dev);
}
static bool
cifs_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
{
struct smb_hdr *shdr = (struct smb_hdr *)buf;
struct TCP_Server_Info *pserver;
struct cifs_ses *ses;
struct cifs_tcon *tcon;
if (shdr->Flags2 & SMBFLG2_ERR_STATUS) {
if (shdr->Status.CifsError != cpu_to_le32(NT_STATUS_NETWORK_NAME_DELETED))
return false;
} else {
if (shdr->Status.DosError.ErrorClass != ERRSRV ||
shdr->Status.DosError.Error != cpu_to_le16(ERRinvtid))
return false;
}
/* If server is a channel, select the primary channel */
pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
if (cifs_ses_exiting(ses))
continue;
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
if (tcon->tid == shdr->Tid) {
spin_lock(&tcon->tc_lock);
tcon->need_reconnect = true;
spin_unlock(&tcon->tc_lock);
spin_unlock(&cifs_tcp_ses_lock);
pr_warn_once("Server share %s deleted.\n",
tcon->tree_name);
return true;
}
}
}
spin_unlock(&cifs_tcp_ses_lock);
return false;
}
struct smb_version_operations smb1_operations = {
.send_cancel = send_nt_cancel,
.compare_fids = cifs_compare_fids,
@ -1153,6 +1189,7 @@ struct smb_version_operations smb1_operations = {
.get_acl_by_fid = get_cifs_acl_by_fid,
.set_acl = set_cifs_acl,
.make_node = cifs_make_node,
.is_network_name_deleted = cifs_is_network_name_deleted,
};
struct smb_version_values smb1_values = {

View File

@ -152,16 +152,35 @@ int smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32
int err_buftype = CIFS_NO_BUFFER;
struct cifs_fid *fid = oparms->fid;
struct network_resiliency_req nr_ioctl_req;
bool retry_without_read_attributes = false;
smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb);
if (smb2_path == NULL)
return -ENOMEM;
oparms->desired_access |= FILE_READ_ATTRIBUTES;
/*
* GENERIC_READ, GENERIC_EXECUTE, GENERIC_ALL and MAXIMUM_ALLOWED
* contains also FILE_READ_ATTRIBUTES access right. So do not append
* FILE_READ_ATTRIBUTES when not needed and prevent calling code path
* for retry_without_read_attributes.
*/
if (!(oparms->desired_access & FILE_READ_ATTRIBUTES) &&
!(oparms->desired_access & GENERIC_READ) &&
!(oparms->desired_access & GENERIC_EXECUTE) &&
!(oparms->desired_access & GENERIC_ALL) &&
!(oparms->desired_access & MAXIMUM_ALLOWED)) {
oparms->desired_access |= FILE_READ_ATTRIBUTES;
retry_without_read_attributes = true;
}
smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH;
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
&err_buftype);
if (rc == -EACCES && retry_without_read_attributes) {
oparms->desired_access &= ~FILE_READ_ATTRIBUTES;
rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, &err_iov,
&err_buftype);
}
if (rc && data) {
struct smb2_hdr *hdr = err_iov.iov_base;

View File

@ -38,6 +38,7 @@ enum smb2_compound_ops {
SMB2_OP_SET_REPARSE,
SMB2_OP_GET_REPARSE,
SMB2_OP_QUERY_WSL_EA,
SMB2_OP_OPEN_QUERY,
};
/* Used when constructing chained read requests. */

View File

@ -176,6 +176,7 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
struct kvec *out_iov, int *out_buftype, struct dentry *dentry)
{
struct smb2_create_rsp *create_rsp = NULL;
struct smb2_query_info_rsp *qi_rsp = NULL;
struct smb2_compound_vars *vars = NULL;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
@ -265,7 +266,13 @@ replay_again:
num_rqst++;
rc = 0;
for (i = 0; i < num_cmds; i++) {
i = 0;
/* Skip the leading explicit OPEN operation */
if (num_cmds > 0 && cmds[0] == SMB2_OP_OPEN_QUERY)
i++;
for (; i < num_cmds; i++) {
/* Operation */
switch (cmds[i]) {
case SMB2_OP_QUERY_INFO:
@ -640,6 +647,27 @@ finished:
}
tmp_rc = rc;
if (rc == 0 && num_cmds > 0 && cmds[0] == SMB2_OP_OPEN_QUERY) {
create_rsp = rsp_iov[0].iov_base;
idata = in_iov[0].iov_base;
idata->fi.CreationTime = create_rsp->CreationTime;
idata->fi.LastAccessTime = create_rsp->LastAccessTime;
idata->fi.LastWriteTime = create_rsp->LastWriteTime;
idata->fi.ChangeTime = create_rsp->ChangeTime;
idata->fi.Attributes = create_rsp->FileAttributes;
idata->fi.AllocationSize = create_rsp->AllocationSize;
idata->fi.EndOfFile = create_rsp->EndofFile;
if (le32_to_cpu(idata->fi.NumberOfLinks) == 0)
idata->fi.NumberOfLinks = cpu_to_le32(1); /* dummy value */
idata->fi.DeletePending = 0;
idata->fi.Directory = !!(le32_to_cpu(create_rsp->FileAttributes) & ATTR_DIRECTORY);
/* smb2_parse_contexts() fills idata->fi.IndexNumber */
rc = smb2_parse_contexts(server, &rsp_iov[0], &oparms->fid->epoch,
oparms->fid->lease_key, &oplock, &idata->fi, NULL);
}
for (i = 0; i < num_cmds; i++) {
char *buf = rsp_iov[i + i].iov_base;
@ -978,6 +1006,43 @@ int smb2_query_path_info(const unsigned int xid,
case 0:
rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]);
break;
case -EACCES:
/*
* If SMB2_OP_QUERY_INFO (called when POSIX extensions are not used) failed with
* STATUS_ACCESS_DENIED then it means that caller does not have permission to
* open the path with FILE_READ_ATTRIBUTES access and therefore cannot issue
* SMB2_OP_QUERY_INFO command.
*
* There is an alternative way how to query limited information about path but still
* suitable for stat() syscall. SMB2 OPEN/CREATE operation returns in its successful
* response subset of query information.
*
* So try to open the path without FILE_READ_ATTRIBUTES but with MAXIMUM_ALLOWED
* access which will grant the maximum possible access to the file and the response
* will contain required query information for stat() syscall.
*/
if (tcon->posix_extensions)
break;
num_cmds = 1;
cmds[0] = SMB2_OP_OPEN_QUERY;
in_iov[0].iov_base = data;
in_iov[0].iov_len = sizeof(*data);
oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, MAXIMUM_ALLOWED,
FILE_OPEN, create_options, ACL_NO_MODE);
free_rsp_iov(out_iov, out_buftype, ARRAY_SIZE(out_iov));
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
&oparms, in_iov, cmds, num_cmds,
cfile, out_iov, out_buftype, NULL);
hdr = out_iov[0].iov_base;
if (!hdr || out_buftype[0] == CIFS_NO_BUFFER)
goto out;
if (!rc)
rc = parse_create_response(data, cifs_sb, full_path, &out_iov[0]);
break;
case -EOPNOTSUPP:
/*
* BB TODO: When support for special files added to Samba

View File

@ -464,12 +464,20 @@ smb2_negotiate(const unsigned int xid,
server->CurrentMid = 0;
spin_unlock(&server->mid_lock);
rc = SMB2_negotiate(xid, ses, server);
/* BB we probably don't need to retry with modern servers */
if (rc == -EAGAIN)
rc = -EHOSTDOWN;
return rc;
}
static inline unsigned int
prevent_zero_iosize(unsigned int size, const char *type)
{
if (size == 0) {
cifs_dbg(VFS, "SMB: Zero %ssize calculated, using minimum value %u\n",
type, CIFS_MIN_DEFAULT_IOSIZE);
return CIFS_MIN_DEFAULT_IOSIZE;
}
return size;
}
static unsigned int
smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
{
@ -477,12 +485,12 @@ smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
unsigned int wsize;
/* start with specified wsize, or default */
wsize = ctx->wsize ? ctx->wsize : CIFS_DEFAULT_IOSIZE;
wsize = ctx->got_wsize ? ctx->vol_wsize : CIFS_DEFAULT_IOSIZE;
wsize = min_t(unsigned int, wsize, server->max_write);
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE);
return wsize;
return prevent_zero_iosize(wsize, "w");
}
static unsigned int
@ -492,7 +500,7 @@ smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
unsigned int wsize;
/* start with specified wsize, or default */
wsize = ctx->wsize ? ctx->wsize : SMB3_DEFAULT_IOSIZE;
wsize = ctx->got_wsize ? ctx->vol_wsize : SMB3_DEFAULT_IOSIZE;
wsize = min_t(unsigned int, wsize, server->max_write);
#ifdef CONFIG_CIFS_SMB_DIRECT
if (server->rdma) {
@ -514,7 +522,7 @@ smb3_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE);
return wsize;
return prevent_zero_iosize(wsize, "w");
}
static unsigned int
@ -524,13 +532,13 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
unsigned int rsize;
/* start with specified rsize, or default */
rsize = ctx->rsize ? ctx->rsize : CIFS_DEFAULT_IOSIZE;
rsize = ctx->got_rsize ? ctx->vol_rsize : CIFS_DEFAULT_IOSIZE;
rsize = min_t(unsigned int, rsize, server->max_read);
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE);
return rsize;
return prevent_zero_iosize(rsize, "r");
}
static unsigned int
@ -540,7 +548,7 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
unsigned int rsize;
/* start with specified rsize, or default */
rsize = ctx->rsize ? ctx->rsize : SMB3_DEFAULT_IOSIZE;
rsize = ctx->got_rsize ? ctx->vol_rsize : SMB3_DEFAULT_IOSIZE;
rsize = min_t(unsigned int, rsize, server->max_read);
#ifdef CONFIG_CIFS_SMB_DIRECT
if (server->rdma) {
@ -563,7 +571,7 @@ smb3_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE);
return rsize;
return prevent_zero_iosize(rsize, "r");
}
/*
@ -3526,8 +3534,6 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
if (rc == 0) {
netfs_resize_file(&cifsi->netfs, new_eof, true);
cifs_setsize(inode, new_eof);
cifs_truncate_page(inode->i_mapping, inode->i_size);
truncate_setsize(inode, new_eof);
}
goto out;
}

View File

@ -43,6 +43,7 @@
#endif
#include "cached_dir.h"
#include "compress.h"
#include "fs_context.h"
/*
* The following table defines the expected "StructureSize" of SMB2 requests
@ -4089,6 +4090,24 @@ smb2_echo_callback(struct mid_q_entry *mid)
add_credits(server, &credits, CIFS_ECHO_OP);
}
static void cifs_renegotiate_iosize(struct TCP_Server_Info *server,
struct cifs_tcon *tcon)
{
struct cifs_sb_info *cifs_sb;
if (server == NULL || tcon == NULL)
return;
spin_lock(&tcon->sb_list_lock);
list_for_each_entry(cifs_sb, &tcon->cifs_sb_list, tcon_sb_link) {
cifs_sb->ctx->rsize =
server->ops->negotiate_rsize(tcon, cifs_sb->ctx);
cifs_sb->ctx->wsize =
server->ops->negotiate_wsize(tcon, cifs_sb->ctx);
}
spin_unlock(&tcon->sb_list_lock);
}
void smb2_reconnect_server(struct work_struct *work)
{
struct TCP_Server_Info *server = container_of(work,
@ -4174,9 +4193,10 @@ void smb2_reconnect_server(struct work_struct *work)
list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) {
rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server, true);
if (!rc)
if (!rc) {
cifs_renegotiate_iosize(server, tcon);
cifs_reopen_persistent_handles(tcon);
else
} else
resched = true;
list_del_init(&tcon->rlist);
if (tcon->ipc)

View File

@ -894,6 +894,9 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
case MID_SHUTDOWN:
rc = -EHOSTDOWN;
break;
case MID_RC:
rc = mid->mid_rc;
break;
default:
if (!(mid->mid_flags & MID_DELETED)) {
list_del_init(&mid->qhead);

View File

@ -31,6 +31,8 @@
* secure, replaced by SMB2 (then even more highly secure SMB3) many years ago
*/
#define SMB3_XATTR_CIFS_ACL "system.smb3_acl" /* DACL only */
#define SMB3_XATTR_CIFS_NTSD_SACL "system.smb3_ntsd_sacl" /* SACL only */
#define SMB3_XATTR_CIFS_NTSD_OWNER "system.smb3_ntsd_owner" /* owner only */
#define SMB3_XATTR_CIFS_NTSD "system.smb3_ntsd" /* owner plus DACL */
#define SMB3_XATTR_CIFS_NTSD_FULL "system.smb3_ntsd_full" /* owner/DACL/SACL */
#define SMB3_XATTR_ATTRIB "smb3.dosattrib" /* full name: user.smb3.dosattrib */
@ -38,6 +40,7 @@
/* BB need to add server (Samba e.g) support for security and trusted prefix */
enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT,
XATTR_CIFS_NTSD_SACL, XATTR_CIFS_NTSD_OWNER,
XATTR_CIFS_NTSD, XATTR_CIFS_NTSD_FULL };
static int cifs_attrib_set(unsigned int xid, struct cifs_tcon *pTcon,
@ -160,6 +163,8 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
break;
case XATTR_CIFS_ACL:
case XATTR_CIFS_NTSD_SACL:
case XATTR_CIFS_NTSD_OWNER:
case XATTR_CIFS_NTSD:
case XATTR_CIFS_NTSD_FULL: {
struct smb_ntsd *pacl;
@ -187,6 +192,13 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
CIFS_ACL_GROUP |
CIFS_ACL_DACL);
break;
case XATTR_CIFS_NTSD_OWNER:
aclflags = (CIFS_ACL_OWNER |
CIFS_ACL_GROUP);
break;
case XATTR_CIFS_NTSD_SACL:
aclflags = CIFS_ACL_SACL;
break;
case XATTR_CIFS_ACL:
default:
aclflags = CIFS_ACL_DACL;
@ -308,6 +320,8 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
break;
case XATTR_CIFS_ACL:
case XATTR_CIFS_NTSD_SACL:
case XATTR_CIFS_NTSD_OWNER:
case XATTR_CIFS_NTSD:
case XATTR_CIFS_NTSD_FULL: {
/*
@ -327,6 +341,12 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
case XATTR_CIFS_NTSD:
extra_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO;
break;
case XATTR_CIFS_NTSD_OWNER:
extra_info = OWNER_SECINFO | GROUP_SECINFO;
break;
case XATTR_CIFS_NTSD_SACL:
extra_info = SACL_SECINFO;
break;
case XATTR_CIFS_ACL:
default:
extra_info = DACL_SECINFO;
@ -448,6 +468,20 @@ static const struct xattr_handler smb3_acl_xattr_handler = {
.set = cifs_xattr_set,
};
static const struct xattr_handler smb3_ntsd_sacl_xattr_handler = {
.name = SMB3_XATTR_CIFS_NTSD_SACL,
.flags = XATTR_CIFS_NTSD_SACL,
.get = cifs_xattr_get,
.set = cifs_xattr_set,
};
static const struct xattr_handler smb3_ntsd_owner_xattr_handler = {
.name = SMB3_XATTR_CIFS_NTSD_OWNER,
.flags = XATTR_CIFS_NTSD_OWNER,
.get = cifs_xattr_get,
.set = cifs_xattr_set,
};
static const struct xattr_handler cifs_cifs_ntsd_xattr_handler = {
.name = CIFS_XATTR_CIFS_NTSD,
.flags = XATTR_CIFS_NTSD,
@ -493,6 +527,8 @@ const struct xattr_handler * const cifs_xattr_handlers[] = {
&cifs_os2_xattr_handler,
&cifs_cifs_acl_xattr_handler,
&smb3_acl_xattr_handler, /* alias for above since avoiding "cifs" */
&smb3_ntsd_sacl_xattr_handler,
&smb3_ntsd_owner_xattr_handler,
&cifs_cifs_ntsd_xattr_handler,
&smb3_ntsd_xattr_handler, /* alias for above since avoiding "cifs" */
&cifs_cifs_ntsd_full_xattr_handler,

View File

@ -95,6 +95,9 @@
*/
#define SMB3_DEFAULT_IOSIZE (4 * 1024 * 1024)
/* According to MS-SMB2 specification The minimum recommended value is 65536.*/
#define CIFS_MIN_DEFAULT_IOSIZE (65536)
/*
* SMB2 Header Definition
*