cifs and smb3 client fixes

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmgVJVUACgkQiiy9cAdy
 T1H7TQv9F41XJ6oY+hO5/+oJebC/7TlNhe1fZ0KpveEiU3SmY9R6EujO2XqZ5ybR
 U9iS8ytt+jub1/l3Nl2crLe2DdcEFgXQdlF6GSq4YsFUHEgVar2isoT7lv9HRQJH
 0KByqOweZBl4jHnUpe88vSUQV3goVhAi/hxOQc3aioqiqFrUk3HIkTh8cJ2ZRyI2
 cyNMRIr6S8zsmxwi6hHa+atQppcFchIUYqYcVnF5wcQ009FvWHkdKBX0cnHx7zF0
 d7PnTXE3J0Cu8rdLHOKJ9lorku7b92jfM1lxBmlp4Y+xtWpONMAkJZ4wlUDRpacx
 OOYlvF1Xkf4i717aqfJwCbwOFXkI4DmK9eUyAxgjVNVZMd+ODOooCGXrOBS4Glgt
 oZfTXaFDVkx2i3YYQT/Is1NXH0W6gVIGfBrBar77pFXlGtGQXs1DiD35GAgEZRWI
 WKueXYf/uYcaR8fn/tj6u0shSKjm89taEbyTYqOnMjsVP/7KjZyGt1SHErfGvJ4C
 SrFo1hR1
 =wCs2
 -----END PGP SIGNATURE-----

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

Pull smb client fixes from Steve French:

 - fix posix mkdir error to ksmbd (also avoids crash in
   cifs_destroy_request_bufs)

 - two smb1 fixes: fixing querypath info and setpathinfo to old servers

 - fix rsize/wsize when not multiple of page size to address DIO
   reads/writes

* tag '6.15-rc4-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smb: client: ensure aligned IO sizes
  cifs: Fix changing times and read-only attr over SMB1 smb_set_file_info() function
  cifs: Fix and improve cifs_query_path_info() and cifs_query_file_info()
  smb: client: fix zero length for mkdir POSIX create context
This commit is contained in:
Linus Torvalds 2025-05-02 14:37:16 -07:00
commit daad00c063
9 changed files with 324 additions and 75 deletions

View file

@ -1266,10 +1266,9 @@ typedef struct smb_com_query_information_rsp {
typedef struct smb_com_setattr_req {
struct smb_hdr hdr; /* wct = 8 */
__le16 attr;
__le16 time_low;
__le16 time_high;
__le32 last_write_time;
__le16 reserved[5]; /* must be zero */
__u16 ByteCount;
__le16 ByteCount;
__u8 BufferFormat; /* 4 = ASCII */
unsigned char fileName[];
} __attribute__((packed)) SETATTR_REQ;

View file

@ -395,6 +395,10 @@ extern int CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon);
extern int CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon,
struct kstatfs *FSData);
extern int SMBSetInformation(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, __le32 attributes, __le64 write_time,
const struct nls_table *nls_codepage,
struct cifs_sb_info *cifs_sb);
extern int CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, const FILE_BASIC_INFO *data,
const struct nls_table *nls_codepage,

View file

@ -5171,6 +5171,63 @@ CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
return rc;
}
int
SMBSetInformation(const unsigned int xid, struct cifs_tcon *tcon,
const char *fileName, __le32 attributes, __le64 write_time,
const struct nls_table *nls_codepage,
struct cifs_sb_info *cifs_sb)
{
SETATTR_REQ *pSMB;
SETATTR_RSP *pSMBr;
struct timespec64 ts;
int bytes_returned;
int name_len;
int rc;
cifs_dbg(FYI, "In %s path %s\n", __func__, fileName);
retry:
rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
(void **) &pSMBr);
if (rc)
return rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
cifsConvertToUTF16((__le16 *) pSMB->fileName,
fileName, PATH_MAX, nls_codepage,
cifs_remap(cifs_sb));
name_len++; /* trailing null */
name_len *= 2;
} else {
name_len = copy_path_name(pSMB->fileName, fileName);
}
/* Only few attributes can be set by this command, others are not accepted by Win9x. */
pSMB->attr = cpu_to_le16(le32_to_cpu(attributes) &
(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_ARCHIVE));
/* Zero write time value (in both NT and SETATTR formats) means to not change it. */
if (le64_to_cpu(write_time) != 0) {
ts = cifs_NTtimeToUnix(write_time);
pSMB->last_write_time = cpu_to_le32(ts.tv_sec);
}
pSMB->BufferFormat = 0x04;
name_len++; /* account for buffer type byte */
inc_rfc1001_len(pSMB, (__u16)name_len);
pSMB->ByteCount = cpu_to_le16(name_len);
rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc)
cifs_dbg(FYI, "Send error in %s = %d\n", __func__, rc);
cifs_buf_release(pSMB);
if (rc == -EAGAIN)
goto retry;
return rc;
}
/* Some legacy servers such as NT4 require that the file times be set on
an open handle, rather than by pathname - this is awkward due to
potential access conflicts on the open, but it is unavoidable for these

View file

@ -3753,28 +3753,7 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx)
}
}
/*
* Clamp the rsize/wsize mount arguments if they are too big for the server
* and set the rsize/wsize to the negotiated values if not passed in by
* the user on mount
*/
if ((cifs_sb->ctx->wsize == 0) ||
(cifs_sb->ctx->wsize > server->ops->negotiate_wsize(tcon, ctx))) {
cifs_sb->ctx->wsize =
round_down(server->ops->negotiate_wsize(tcon, ctx), PAGE_SIZE);
/*
* in the very unlikely event that the server sent a max write size under PAGE_SIZE,
* (which would get rounded down to 0) then reset wsize to absolute minimum eg 4096
*/
if (cifs_sb->ctx->wsize == 0) {
cifs_sb->ctx->wsize = PAGE_SIZE;
cifs_dbg(VFS, "wsize too small, reset to minimum ie PAGE_SIZE, usually 4096\n");
}
}
if ((cifs_sb->ctx->rsize == 0) ||
(cifs_sb->ctx->rsize > server->ops->negotiate_rsize(tcon, ctx)))
cifs_sb->ctx->rsize = server->ops->negotiate_rsize(tcon, ctx);
cifs_negotiate_iosize(server, cifs_sb->ctx, tcon);
/*
* The cookie is initialized from volume info returned above.
* Inside cifs_fscache_get_super_cookie it checks

View file

@ -160,10 +160,8 @@ static int cifs_prepare_read(struct netfs_io_subrequest *subreq)
server = cifs_pick_channel(tlink_tcon(req->cfile->tlink)->ses);
rdata->server = server;
if (cifs_sb->ctx->rsize == 0)
cifs_sb->ctx->rsize =
server->ops->negotiate_rsize(tlink_tcon(req->cfile->tlink),
cifs_sb->ctx);
cifs_negotiate_rsize(server, cifs_sb->ctx,
tlink_tcon(req->cfile->tlink));
rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize,
&size, &rdata->credits);

View file

@ -1021,6 +1021,7 @@ static int smb3_reconfigure(struct fs_context *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;
unsigned int rsize = ctx->rsize, wsize = ctx->wsize;
char *new_password = NULL, *new_password2 = NULL;
bool need_recon = false;
int rc;
@ -1103,11 +1104,8 @@ static int smb3_reconfigure(struct fs_context *fc)
STEAL_STRING(cifs_sb, ctx, iocharset);
/* if rsize or wsize not passed in on remount, use previous values */
if (ctx->rsize == 0)
ctx->rsize = cifs_sb->ctx->rsize;
if (ctx->wsize == 0)
ctx->wsize = cifs_sb->ctx->wsize;
ctx->rsize = rsize ? CIFS_ALIGN_RSIZE(fc, rsize) : cifs_sb->ctx->rsize;
ctx->wsize = wsize ? CIFS_ALIGN_WSIZE(fc, wsize) : cifs_sb->ctx->wsize;
smb3_cleanup_fs_context_contents(cifs_sb->ctx);
rc = smb3_fs_context_dup(cifs_sb->ctx, ctx);
@ -1312,7 +1310,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
__func__);
goto cifs_parse_mount_err;
}
ctx->bsize = result.uint_32;
ctx->bsize = CIFS_ALIGN_BSIZE(fc, result.uint_32);
ctx->got_bsize = true;
break;
case Opt_rasize:
@ -1336,24 +1334,13 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
ctx->rasize = result.uint_32;
break;
case Opt_rsize:
ctx->rsize = result.uint_32;
ctx->rsize = CIFS_ALIGN_RSIZE(fc, result.uint_32);
ctx->got_rsize = true;
ctx->vol_rsize = ctx->rsize;
break;
case Opt_wsize:
ctx->wsize = result.uint_32;
ctx->wsize = CIFS_ALIGN_WSIZE(fc, result.uint_32);
ctx->got_wsize = true;
if (ctx->wsize % PAGE_SIZE != 0) {
ctx->wsize = round_down(ctx->wsize, PAGE_SIZE);
if (ctx->wsize == 0) {
ctx->wsize = PAGE_SIZE;
cifs_dbg(VFS, "wsize too small, reset to minimum %ld\n", PAGE_SIZE);
} else {
cifs_dbg(VFS,
"wsize rounded down to %d to multiple of PAGE_SIZE %ld\n",
ctx->wsize, PAGE_SIZE);
}
}
ctx->vol_wsize = ctx->wsize;
break;
case Opt_acregmax:

View file

@ -20,6 +20,21 @@
cifs_dbg(VFS, fmt, ## __VA_ARGS__); \
} while (0)
static inline size_t cifs_io_align(struct fs_context *fc,
const char *name, size_t size)
{
if (!size || !IS_ALIGNED(size, PAGE_SIZE)) {
cifs_errorf(fc, "unaligned %s, making it a multiple of %lu bytes\n",
name, PAGE_SIZE);
size = umax(round_down(size, PAGE_SIZE), PAGE_SIZE);
}
return size;
}
#define CIFS_ALIGN_WSIZE(_fc, _size) cifs_io_align(_fc, "wsize", _size)
#define CIFS_ALIGN_RSIZE(_fc, _size) cifs_io_align(_fc, "rsize", _size)
#define CIFS_ALIGN_BSIZE(_fc, _size) cifs_io_align(_fc, "bsize", _size)
enum smb_version {
Smb_1 = 1,
Smb_20,
@ -361,4 +376,36 @@ static inline void cifs_mount_unlock(void)
mutex_unlock(&cifs_mount_mutex);
}
static inline void cifs_negotiate_rsize(struct TCP_Server_Info *server,
struct smb3_fs_context *ctx,
struct cifs_tcon *tcon)
{
unsigned int size;
size = umax(server->ops->negotiate_rsize(tcon, ctx), PAGE_SIZE);
if (ctx->rsize)
size = umax(umin(ctx->rsize, size), PAGE_SIZE);
ctx->rsize = round_down(size, PAGE_SIZE);
}
static inline void cifs_negotiate_wsize(struct TCP_Server_Info *server,
struct smb3_fs_context *ctx,
struct cifs_tcon *tcon)
{
unsigned int size;
size = umax(server->ops->negotiate_wsize(tcon, ctx), PAGE_SIZE);
if (ctx->wsize)
size = umax(umin(ctx->wsize, size), PAGE_SIZE);
ctx->wsize = round_down(size, PAGE_SIZE);
}
static inline void cifs_negotiate_iosize(struct TCP_Server_Info *server,
struct smb3_fs_context *ctx,
struct cifs_tcon *tcon)
{
cifs_negotiate_rsize(server, ctx, tcon);
cifs_negotiate_wsize(server, ctx, tcon);
}
#endif

View file

@ -432,7 +432,7 @@ cifs_negotiate(const unsigned int xid,
}
static unsigned int
cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
smb1_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
{
__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
struct TCP_Server_Info *server = tcon->ses->server;
@ -467,7 +467,7 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
}
static unsigned int
cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
smb1_negotiate_rsize(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
{
__u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
struct TCP_Server_Info *server = tcon->ses->server;
@ -543,24 +543,104 @@ static int cifs_query_path_info(const unsigned int xid,
const char *full_path,
struct cifs_open_info_data *data)
{
int rc;
int rc = -EOPNOTSUPP;
FILE_ALL_INFO fi = {};
struct cifs_search_info search_info = {};
bool non_unicode_wildcard = false;
data->reparse_point = false;
data->adjust_tz = false;
/* could do find first instead but this returns more info */
rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */, cifs_sb->local_nls,
cifs_remap(cifs_sb));
/*
* BB optimize code so we do not make the above call when server claims
* no NT SMB support and the above call failed at least once - set flag
* in tcon or mount.
* First try CIFSSMBQPathInfo() function which returns more info
* (NumberOfLinks) than CIFSFindFirst() fallback function.
* Some servers like Win9x do not support SMB_QUERY_FILE_ALL_INFO over
* TRANS2_QUERY_PATH_INFORMATION, but supports it with filehandle over
* TRANS2_QUERY_FILE_INFORMATION (function CIFSSMBQFileInfo(). But SMB
* Open command on non-NT servers works only for files, does not work
* for directories. And moreover Win9x SMB server returns bogus data in
* SMB_QUERY_FILE_ALL_INFO Attributes field. So for non-NT servers,
* do not even use CIFSSMBQPathInfo() or CIFSSMBQFileInfo() function.
*/
if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) {
if (tcon->ses->capabilities & CAP_NT_SMBS)
rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */,
cifs_sb->local_nls, cifs_remap(cifs_sb));
/*
* Non-UNICODE variant of fallback functions below expands wildcards,
* so they cannot be used for querying paths with wildcard characters.
*/
if (rc && !(tcon->ses->capabilities & CAP_UNICODE) && strpbrk(full_path, "*?\"><"))
non_unicode_wildcard = true;
/*
* Then fallback to CIFSFindFirst() which works also with non-NT servers
* but does not does not provide NumberOfLinks.
*/
if ((rc == -EOPNOTSUPP || rc == -EINVAL) &&
!non_unicode_wildcard) {
if (!(tcon->ses->capabilities & tcon->ses->server->vals->cap_nt_find))
search_info.info_level = SMB_FIND_FILE_INFO_STANDARD;
else
search_info.info_level = SMB_FIND_FILE_FULL_DIRECTORY_INFO;
rc = CIFSFindFirst(xid, tcon, full_path, cifs_sb, NULL,
CIFS_SEARCH_CLOSE_ALWAYS | CIFS_SEARCH_CLOSE_AT_END,
&search_info, false);
if (rc == 0) {
if (!(tcon->ses->capabilities & tcon->ses->server->vals->cap_nt_find)) {
FIND_FILE_STANDARD_INFO *di;
int offset = tcon->ses->server->timeAdj;
di = (FIND_FILE_STANDARD_INFO *)search_info.srch_entries_start;
fi.CreationTime = cpu_to_le64(cifs_UnixTimeToNT(cnvrtDosUnixTm(
di->CreationDate, di->CreationTime, offset)));
fi.LastAccessTime = cpu_to_le64(cifs_UnixTimeToNT(cnvrtDosUnixTm(
di->LastAccessDate, di->LastAccessTime, offset)));
fi.LastWriteTime = cpu_to_le64(cifs_UnixTimeToNT(cnvrtDosUnixTm(
di->LastWriteDate, di->LastWriteTime, offset)));
fi.ChangeTime = fi.LastWriteTime;
fi.Attributes = cpu_to_le32(le16_to_cpu(di->Attributes));
fi.AllocationSize = cpu_to_le64(le32_to_cpu(di->AllocationSize));
fi.EndOfFile = cpu_to_le64(le32_to_cpu(di->DataSize));
} else {
FILE_FULL_DIRECTORY_INFO *di;
di = (FILE_FULL_DIRECTORY_INFO *)search_info.srch_entries_start;
fi.CreationTime = di->CreationTime;
fi.LastAccessTime = di->LastAccessTime;
fi.LastWriteTime = di->LastWriteTime;
fi.ChangeTime = di->ChangeTime;
fi.Attributes = di->ExtFileAttributes;
fi.AllocationSize = di->AllocationSize;
fi.EndOfFile = di->EndOfFile;
fi.EASize = di->EaSize;
}
fi.NumberOfLinks = cpu_to_le32(1);
fi.DeletePending = 0;
fi.Directory = !!(le32_to_cpu(fi.Attributes) & ATTR_DIRECTORY);
cifs_buf_release(search_info.ntwrk_buf_start);
} else if (!full_path[0]) {
/*
* CIFSFindFirst() does not work on root path if the
* root path was exported on the server from the top
* level path (drive letter).
*/
rc = -EOPNOTSUPP;
}
}
/*
* If everything failed then fallback to the legacy SMB command
* SMB_COM_QUERY_INFORMATION which works with all servers, but
* provide just few information.
*/
if ((rc == -EOPNOTSUPP || rc == -EINVAL) && !non_unicode_wildcard) {
rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls,
cifs_remap(cifs_sb));
data->adjust_tz = true;
} else if ((rc == -EOPNOTSUPP || rc == -EINVAL) && non_unicode_wildcard) {
/* Path with non-UNICODE wildcard character cannot exist. */
rc = -ENOENT;
}
if (!rc) {
@ -639,6 +719,13 @@ static int cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
int rc;
FILE_ALL_INFO fi = {};
/*
* CIFSSMBQFileInfo() for non-NT servers returns bogus data in
* Attributes fields. So do not use this command for non-NT servers.
*/
if (!(tcon->ses->capabilities & CAP_NT_SMBS))
return -EOPNOTSUPP;
if (cfile->symlink_target) {
data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
if (!data->symlink_target)
@ -809,6 +896,9 @@ smb_set_file_info(struct inode *inode, const char *full_path,
struct cifs_fid fid;
struct cifs_open_parms oparms;
struct cifsFileInfo *open_file;
FILE_BASIC_INFO new_buf;
struct cifs_open_info_data query_data;
__le64 write_time = buf->LastWriteTime;
struct cifsInodeInfo *cinode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink = NULL;
@ -816,20 +906,58 @@ smb_set_file_info(struct inode *inode, const char *full_path,
/* if the file is already open for write, just use that fileid */
open_file = find_writable_file(cinode, FIND_WR_FSUID_ONLY);
if (open_file) {
fid.netfid = open_file->fid.netfid;
netpid = open_file->pid;
tcon = tlink_tcon(open_file->tlink);
goto set_via_filehandle;
} else {
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
rc = PTR_ERR(tlink);
tlink = NULL;
goto out;
}
tcon = tlink_tcon(tlink);
}
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
rc = PTR_ERR(tlink);
tlink = NULL;
goto out;
/*
* Non-NT servers interprets zero time value in SMB_SET_FILE_BASIC_INFO
* over TRANS2_SET_FILE_INFORMATION as a valid time value. NT servers
* interprets zero time value as do not change existing value on server.
* API of ->set_file_info() callback expects that zero time value has
* the NT meaning - do not change. Therefore if server is non-NT and
* some time values in "buf" are zero, then fetch missing time values.
*/
if (!(tcon->ses->capabilities & CAP_NT_SMBS) &&
(!buf->CreationTime || !buf->LastAccessTime ||
!buf->LastWriteTime || !buf->ChangeTime)) {
rc = cifs_query_path_info(xid, tcon, cifs_sb, full_path, &query_data);
if (rc) {
if (open_file) {
cifsFileInfo_put(open_file);
open_file = NULL;
}
goto out;
}
/*
* Original write_time from buf->LastWriteTime is preserved
* as SMBSetInformation() interprets zero as do not change.
*/
new_buf = *buf;
buf = &new_buf;
if (!buf->CreationTime)
buf->CreationTime = query_data.fi.CreationTime;
if (!buf->LastAccessTime)
buf->LastAccessTime = query_data.fi.LastAccessTime;
if (!buf->LastWriteTime)
buf->LastWriteTime = query_data.fi.LastWriteTime;
if (!buf->ChangeTime)
buf->ChangeTime = query_data.fi.ChangeTime;
}
tcon = tlink_tcon(tlink);
if (open_file)
goto set_via_filehandle;
rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls,
cifs_sb);
@ -850,8 +978,45 @@ smb_set_file_info(struct inode *inode, const char *full_path,
.fid = &fid,
};
cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for times not supported by this server\n");
rc = CIFS_open(xid, &oparms, &oplock, NULL);
if (S_ISDIR(inode->i_mode) && !(tcon->ses->capabilities & CAP_NT_SMBS)) {
/* Opening directory path is not possible on non-NT servers. */
rc = -EOPNOTSUPP;
} else {
/*
* Use cifs_open_file() instead of CIFS_open() as the
* cifs_open_file() selects the correct function which
* works also on non-NT servers.
*/
rc = cifs_open_file(xid, &oparms, &oplock, NULL);
/*
* Opening path for writing on non-NT servers is not
* possible when the read-only attribute is already set.
* Non-NT server in this case returns -EACCES. For those
* servers the only possible way how to clear the read-only
* bit is via SMB_COM_SETATTR command.
*/
if (rc == -EACCES &&
(cinode->cifsAttrs & ATTR_READONLY) &&
le32_to_cpu(buf->Attributes) != 0 && /* 0 = do not change attrs */
!(le32_to_cpu(buf->Attributes) & ATTR_READONLY) &&
!(tcon->ses->capabilities & CAP_NT_SMBS))
rc = -EOPNOTSUPP;
}
/* Fallback to SMB_COM_SETATTR command when absolutelty needed. */
if (rc == -EOPNOTSUPP) {
cifs_dbg(FYI, "calling SetInformation since SetPathInfo for attrs/times not supported by this server\n");
rc = SMBSetInformation(xid, tcon, full_path,
buf->Attributes != 0 ? buf->Attributes : cpu_to_le32(cinode->cifsAttrs),
write_time,
cifs_sb->local_nls, cifs_sb);
if (rc == 0)
cinode->cifsAttrs = le32_to_cpu(buf->Attributes);
else
rc = -EACCES;
goto out;
}
if (rc != 0) {
if (rc == -EIO)
rc = -EINVAL;
@ -859,6 +1024,7 @@ smb_set_file_info(struct inode *inode, const char *full_path,
}
netpid = current->tgid;
cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for attrs/times not supported by this server\n");
set_via_filehandle:
rc = CIFSSMBSetFileInfo(xid, tcon, buf, fid.netfid, netpid);
@ -869,6 +1035,21 @@ set_via_filehandle:
CIFSSMBClose(xid, tcon, fid.netfid);
else
cifsFileInfo_put(open_file);
/*
* Setting the read-only bit is not honered on non-NT servers when done
* via open-semantics. So for setting it, use SMB_COM_SETATTR command.
* This command works only after the file is closed, so use it only when
* operation was called without the filehandle.
*/
if (open_file == NULL &&
!(tcon->ses->capabilities & CAP_NT_SMBS) &&
le32_to_cpu(buf->Attributes) & ATTR_READONLY) {
SMBSetInformation(xid, tcon, full_path,
buf->Attributes,
0 /* do not change write time */,
cifs_sb->local_nls, cifs_sb);
}
out:
if (tlink != NULL)
cifs_put_tlink(tlink);
@ -1161,8 +1342,8 @@ struct smb_version_operations smb1_operations = {
.check_trans2 = cifs_check_trans2,
.need_neg = cifs_need_neg,
.negotiate = cifs_negotiate,
.negotiate_wsize = cifs_negotiate_wsize,
.negotiate_rsize = cifs_negotiate_rsize,
.negotiate_wsize = smb1_negotiate_wsize,
.negotiate_rsize = smb1_negotiate_rsize,
.sess_setup = CIFS_SessSetup,
.logoff = CIFSSMBLogoff,
.tree_connect = CIFSTCon,

View file

@ -2921,6 +2921,7 @@ replay_again:
req->CreateContextsOffset = cpu_to_le32(
sizeof(struct smb2_create_req) +
iov[1].iov_len);
le32_add_cpu(&req->CreateContextsLength, iov[n_iov-1].iov_len);
pc_buf = iov[n_iov-1].iov_base;
}
@ -4092,12 +4093,8 @@ static void cifs_renegotiate_iosize(struct TCP_Server_Info *server,
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);
}
list_for_each_entry(cifs_sb, &tcon->cifs_sb_list, tcon_sb_link)
cifs_negotiate_iosize(server, cifs_sb->ctx, tcon);
spin_unlock(&tcon->sb_list_lock);
}