// SPDX-License-Identifier: LGPL-2.1 /* * * Copyright (C) International Business Machines Corp., 2002,2010 * Author(s): Steve French (sfrench@us.ibm.com) * * Contains the routines for constructing the SMB PDUs themselves *
*/
/* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */ /* These are mostly routines that operate on a pathname, or on a tree id */ /* (mounted volume), but there are eight handle based routines which must be */ /* treated slightly differently for reconnection purposes since we never */ /* want to reuse a stale file handle and only the caller knows the file info */
/* define the number of elements in the cifs dialect array */ #ifdef CONFIG_CIFS_POSIX #define CIFS_NUM_PROT 2 #else/* not posix */ #define CIFS_NUM_PROT 1 #endif/* CIFS_POSIX */
/* reconnect the socket, tcon, and smb session if needed */ staticint
cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
{ struct TCP_Server_Info *server; struct cifs_ses *ses; int rc;
/* * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for * tcp and smb session status done differently for those three - in the * calling routine
*/ if (!tcon) return 0;
ses = tcon->ses;
server = ses->server;
/* * only tree disconnect, open, and write, (and ulogoff which does not * have tcon) are allowed as we start umount
*/
spin_lock(&tcon->tc_lock); if (tcon->status == TID_EXITING) { if (smb_command != SMB_COM_TREE_DISCONNECT) {
spin_unlock(&tcon->tc_lock);
cifs_dbg(FYI, "can not send cmd %d while umounting\n",
smb_command); return -ENODEV;
}
}
spin_unlock(&tcon->tc_lock);
again:
rc = cifs_wait_for_server_reconnect(server, tcon->retry); if (rc) return rc;
mutex_lock(&ses->session_mutex); /* * Handle the case where a concurrent thread failed to negotiate or * killed a channel.
*/
spin_lock(&server->srv_lock); switch (server->tcpStatus) { case CifsExiting:
spin_unlock(&server->srv_lock);
mutex_unlock(&ses->session_mutex); return -EHOSTDOWN; case CifsNeedReconnect:
spin_unlock(&server->srv_lock);
mutex_unlock(&ses->session_mutex); if (!tcon->retry) return -EHOSTDOWN; goto again; default: break;
}
spin_unlock(&server->srv_lock);
/* * need to prevent multiple threads trying to simultaneously * reconnect the same SMB session
*/
spin_lock(&ses->ses_lock);
spin_lock(&ses->chan_lock); if (!cifs_chan_needs_reconnect(ses, server) &&
ses->ses_status == SES_GOOD) {
spin_unlock(&ses->chan_lock);
spin_unlock(&ses->ses_lock);
/* this means that we only need to tree connect */ if (tcon->need_reconnect) goto skip_sess_setup;
/* tell server Unix caps we support */ if (cap_unix(ses))
reset_cifs_unix_caps(0, tcon, NULL, NULL);
/* * Removed call to reopen open files here. It is safer (and faster) to * reopen files one at a time as needed in read and write. * * FIXME: what about file locks? don't we need to reclaim them ASAP?
*/
out: /* * Check if handle based operation so we know whether we can continue * or not without returning to caller to reset file handle
*/ switch (smb_command) { case SMB_COM_READ_ANDX: case SMB_COM_WRITE_ANDX: case SMB_COM_CLOSE: case SMB_COM_FIND_CLOSE2: case SMB_COM_LOCKING_ANDX:
rc = -EAGAIN;
}
return rc;
}
/* Allocate and return pointer to an SMB request buffer, and set basic SMB information in the SMB header. If the return code is zero, this
function must have filled in request_buf pointer */ staticint
small_smb_init(int smb_command, int wct, struct cifs_tcon *tcon, void **request_buf)
{ int rc;
rc = cifs_reconnect_tcon(tcon, smb_command); if (rc) return rc;
*request_buf = cifs_small_buf_get(); if (*request_buf == NULL) { /* BB should we add a retry in here if not a writepage? */ return -ENOMEM;
}
/* uid, tid can stay at zero as set in header assemble */
/* BB add support for turning on the signing when
this function is used after 1st of session setup requests */
return rc;
}
/* If the return code is zero, this function must fill in request_buf pointer */ staticint
__smb_init(int smb_command, int wct, struct cifs_tcon *tcon, void **request_buf, void **response_buf)
{
*request_buf = cifs_buf_get(); if (*request_buf == NULL) { /* BB should we add a retry in here if not a writepage? */ return -ENOMEM;
} /* Although the original thought was we needed the response buf for */ /* potential retries of smb operations it turns out we can determine */ /* from the mid flags when the request buffer can be resent without */ /* having to use a second distinct buffer for the response */ if (response_buf)
*response_buf = *request_buf;
if (tcon != NULL)
cifs_stats_inc(&tcon->num_smbs_sent);
return 0;
}
/* If the return code is zero, this function must fill in request_buf pointer */ staticint
smb_init(int smb_command, int wct, struct cifs_tcon *tcon, void **request_buf, void **response_buf)
{ int rc;
rc = cifs_reconnect_tcon(tcon, smb_command); if (rc) return rc;
/* check for plausible wct */ if (pSMB->hdr.WordCount < 10) goto vt2_err;
/* check for parm and data offset going beyond end of smb */ if (get_unaligned_le16(&pSMB->t2_rsp.ParameterOffset) > 1024 ||
get_unaligned_le16(&pSMB->t2_rsp.DataOffset) > 1024) goto vt2_err;
total_size = get_unaligned_le16(&pSMB->t2_rsp.ParameterCount); if (total_size >= 512) goto vt2_err;
/* check that bcc is at least as big as parms + data, and that it is * less than negotiated smb buffer
*/
total_size += get_unaligned_le16(&pSMB->t2_rsp.DataCount); if (total_size > get_bcc(&pSMB->hdr) ||
total_size >= CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) goto vt2_err;
count = 0; /* * We know that all the name entries in the protocols array * are short (< 16 bytes anyway) and are NUL terminated.
*/ for (i = 0; i < CIFS_NUM_PROT; i++) {
size_t len = strlen(protocols[i].name) + 1;
server->dialect = le16_to_cpu(pSMBr->DialectIndex);
cifs_dbg(FYI, "Dialect: %d\n", server->dialect); /* Check wct = 1 error case */ if ((pSMBr->hdr.WordCount <= 13) || (server->dialect == BAD_PROT)) { /* core returns wct = 1, but we do not ask for core - otherwise small wct just comes when dialect index is -1 indicating we
could not negotiate a common dialect */
rc = -EOPNOTSUPP; goto neg_err_exit;
} elseif (pSMBr->hdr.WordCount != 17) { /* unknown wct */
rc = -EOPNOTSUPP; goto neg_err_exit;
} /* else wct == 17, NTLM or better */
/* one byte, so no need to convert this or EncryptionKeyLen from
little endian */
server->maxReq = min_t(unsignedint, le16_to_cpu(pSMBr->MaxMpxCount),
cifs_max_pending);
set_credits(server, server->maxReq); /* probably no need to store and check maxvcs */
server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize); /* set up max_read for readahead check */
server->max_read = server->maxBuf;
server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
cifs_dbg(NOISY, "Max buf = %d\n", ses->server->maxBuf);
server->capabilities = le32_to_cpu(pSMBr->Capabilities);
server->session_key_id = pSMBr->SessionKey;
server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
server->timeAdj *= 60;
int
CIFSSMBTDis(constunsignedint xid, struct cifs_tcon *tcon)
{ struct smb_hdr *smb_buffer; int rc = 0;
cifs_dbg(FYI, "In tree disconnect\n");
/* BB: do we need to check this? These should never be NULL. */ if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) return -EIO;
/* * No need to return error on this operation if tid invalidated and * closed on server already e.g. due to tcp session crashing. Also, * the tcon is no longer on the list, so no need to take lock before * checking this.
*/
spin_lock(&tcon->ses->chan_lock); if ((tcon->need_reconnect) || CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses)) {
spin_unlock(&tcon->ses->chan_lock); return -EIO;
}
spin_unlock(&tcon->ses->chan_lock);
/* No need to return error on this operation if tid invalidated and
closed on server already e.g. due to tcp session crashing */ if (rc == -EAGAIN)
rc = 0;
return rc;
}
/* * This is a no-op for now. We're not really interested in the reply, but * rather in the fact that the server sent one and that server->lstrp * gets updated. * * FIXME: maybe we should consider checking that the reply matches request?
*/ staticvoid
cifs_echo_callback(struct mid_q_entry *mid)
{ struct TCP_Server_Info *server = mid->callback_data; struct cifs_credits credits = { .value = 1, .instance = 0 };
int
CIFSSMBLogoff(constunsignedint xid, struct cifs_ses *ses)
{
LOGOFF_ANDX_REQ *pSMB; int rc = 0;
cifs_dbg(FYI, "In SMBLogoff for session disconnect\n");
/* * BB: do we need to check validity of ses and server? They should * always be valid since we have an active reference. If not, that * should probably be a BUG()
*/ if (!ses || !ses->server) return -EIO;
mutex_lock(&ses->session_mutex);
spin_lock(&ses->chan_lock); if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) {
spin_unlock(&ses->chan_lock); goto session_already_dead; /* no need to send SMBlogoff if uid
already closed due to reconnect */
}
spin_unlock(&ses->chan_lock);
/* if session dead then we do not need to do ulogoff, since server closed smb session, no sense reporting
error */ if (rc == -EAGAIN)
rc = 0; return rc;
}
int
CIFSPOSIXDelFile(constunsignedint xid, struct cifs_tcon *tcon, constchar *fileName, __u16 type, conststruct nls_table *nls_codepage, int remap)
{
TRANSACTION2_SPI_REQ *pSMB = NULL;
TRANSACTION2_SPI_RSP *pSMBr = NULL; struct unlink_psx_rq *pRqD; int name_len; int rc = 0; int bytes_returned = 0;
__u16 params, param_offset, offset, byte_count;
cifs_dbg(FYI, "In POSIX delete\n");
PsxDelete:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr); if (rc) return rc;
if (rc || get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP)) {
rc = -EIO; /* bad smb */ goto psx_create_err;
}
/* copy return information to pRetData */
psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
+ le16_to_cpu(pSMBr->t2.DataOffset));
*pOplock = le16_to_cpu(psx_rsp->OplockFlags); if (netfid)
*netfid = psx_rsp->Fid; /* cifs fid stays in le */ /* Let caller know file was created so we can set the mode. */ /* Do we care about the CreateAction in any other cases? */ if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
*pOplock |= CIFS_CREATE_ACTION; /* check to make sure response data is there */ if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
pRetData->Type = cpu_to_le32(-1); /* unknown */
cifs_dbg(NOISY, "unknown type\n");
} else { if (get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP)
+ sizeof(FILE_UNIX_BASIC_INFO)) {
cifs_dbg(VFS, "Open response data too small\n");
pRetData->Type = cpu_to_le32(-1); goto psx_create_err;
}
memcpy((char *) pRetData,
(char *)psx_rsp + sizeof(OPEN_PSX_RSP), sizeof(FILE_UNIX_BASIC_INFO));
}
psx_create_err:
cifs_buf_release(pSMB);
if (posix_flags & SMB_O_DIRECTORY)
cifs_stats_inc(&tcon->stats.cifs_stats.num_posixmkdirs); else
cifs_stats_inc(&tcon->stats.cifs_stats.num_posixopens);
switch (disposition) { case FILE_SUPERSEDE:
ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; break; case FILE_OPEN:
ofun = SMBOPEN_OAPPEND; break; case FILE_CREATE:
ofun = SMBOPEN_OCREATE; break; case FILE_OPEN_IF:
ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND; break; case FILE_OVERWRITE:
ofun = SMBOPEN_OTRUNC; break; case FILE_OVERWRITE_IF:
ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC; break; default:
cifs_dbg(FYI, "unknown disposition %d\n", disposition);
ofun = SMBOPEN_OAPPEND; /* regular open */
} return ofun;
}
staticint
access_flags_to_smbopen_mode(constint access_flags)
{ /* * 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 (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
count = 1; /* account for one byte pad to word boundary */
name_len =
cifsConvertToUTF16((__le16 *) (pSMB->fileName + 1),
fileName, PATH_MAX, nls_codepage, remap);
name_len++; /* trailing null */
name_len *= 2;
} else {
count = 0; /* no pad */
name_len = copy_path_name(pSMB->fileName, fileName);
} if (*pOplock & REQ_OPLOCK)
pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK); elseif (*pOplock & REQ_BATCHOPLOCK)
pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags));
pSMB->Mode |= cpu_to_le16(0x40); /* deny none */ /* set file as system file if special file such as fifo, * socket, char or block and server expecting SFU style and
no Unix extensions */
pSMB->ByteCount = cpu_to_le16(count);
rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
(struct smb_hdr *)pSMBr, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_opens); if (rc) {
cifs_dbg(FYI, "Error in Open = %d\n", rc);
} else { /* BB verify if wct == 15 */
/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/
*netfid = pSMBr->Fid; /* cifs fid stays in le */ /* Let caller know file was created so we can set the mode. */ /* Do we care about the CreateAction in any other cases? */ /* BB FIXME BB */ /* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
*pOplock |= CIFS_CREATE_ACTION; */ /* BB FIXME END */
/* * Set file as system file if special file such as fifo, socket, char * or block and server expecting SFU style and no Unix extensions.
*/ if (create_options & CREATE_OPTION_SPECIAL)
req->FileAttributes = cpu_to_le32(ATTR_SYSTEM); else
req->FileAttributes = cpu_to_le32(ATTR_NORMAL);
/* * XP does not handle ATTR_POSIX_SEMANTICS but it helps speed up case * sensitive checks for other servers such as Samba.
*/ if (tcon->ses->capabilities & CAP_UNIX)
req->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
if (create_options & CREATE_OPTION_READONLY)
req->FileAttributes |= cpu_to_le32(ATTR_READONLY);
/* BB Experiment with various impersonation levels and verify */
req->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
req->SecurityFlags = SECURITY_CONTEXT_TRACKING|SECURITY_EFFECTIVE_ONLY;
count += name_len;
inc_rfc1001_len(req, count);
req->ByteCount = cpu_to_le16(count);
rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)req,
(struct smb_hdr *)rsp, &bytes_returned, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_opens); if (rc) {
cifs_dbg(FYI, "Error in Open = %d\n", rc);
cifs_buf_release(req); if (rc == -EAGAIN) goto openRetry; return rc;
}
/* 1 byte no need to le_to_cpu */
*oplock = rsp->OplockLevel; /* cifs fid stays in le */
oparms->fid->netfid = rsp->Fid;
oparms->fid->access = desired_access;
/* Let caller know file was created so we can set the mode. */ /* Do we care about the CreateAction in any other cases? */ if (cpu_to_le32(FILE_CREATE) == rsp->CreateAction)
*oplock |= CIFS_CREATE_ACTION;
if (buf) { /* copy commonly used attributes */
memcpy(&buf->common_attributes,
&rsp->common_attributes, sizeof(buf->common_attributes)); /* the file_info buf is endian converted by caller */
buf->AllocationSize = rsp->AllocationSize;
buf->EndOfFile = rsp->EndOfFile;
buf->NumberOfLinks = cpu_to_le32(1);
buf->DeletePending = 0;
}
switch (mid->mid_state) { case MID_RESPONSE_RECEIVED: /* result already set, check signature */ if (server->sign) { int rc = 0;
iov_iter_truncate(&rqst.rq_iter, rdata->got_bytes);
rc = cifs_verify_signature(&rqst, server,
mid->sequence_number); if (rc)
cifs_dbg(VFS, "SMB signature verification returned error = %d\n",
rc);
} /* FIXME: should this be counted toward the initiating task? */
task_io_account_read(rdata->got_bytes);
cifs_stats_bytes_read(tcon, rdata->got_bytes); break; case MID_REQUEST_SUBMITTED:
trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_req_submitted); goto do_retry; case MID_RETRY_NEEDED:
trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_retry_needed);
do_retry:
__set_bit(NETFS_SREQ_NEED_RETRY, &rdata->subreq.flags);
rdata->result = -EAGAIN; if (server->sign && rdata->got_bytes) /* reset bytes number since we can not check a sign */
rdata->got_bytes = 0; /* FIXME: should this be counted toward the initiating task? */
task_io_account_read(rdata->got_bytes);
cifs_stats_bytes_read(tcon, rdata->got_bytes); break; case MID_RESPONSE_MALFORMED:
trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_malformed);
rdata->result = -EIO; break; default:
trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_unknown);
rdata->result = -EIO; break;
}
if (rdata->result == -ENODATA) {
rdata->result = 0;
__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
} else {
size_t trans = rdata->subreq.transferred + rdata->got_bytes; if (trans < rdata->subreq.len &&
rdata->subreq.start + trans == ictx->remote_i_size) {
rdata->result = 0;
__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
} elseif (rdata->got_bytes > 0) {
__set_bit(NETFS_SREQ_MADE_PROGRESS, &rdata->subreq.flags);
} if (rdata->got_bytes)
__set_bit(NETFS_SREQ_MADE_PROGRESS, &rdata->subreq.flags);
}
/* cifs_async_readv - send an async write, and set up mid to handle result */ int
cifs_async_readv(struct cifs_io_subrequest *rdata)
{ int rc;
READ_REQ *smb = NULL; int wct; struct cifs_tcon *tcon = tlink_tcon(rdata->req->cfile->tlink); struct smb_rqst rqst = { .rq_iov = rdata->iov,
.rq_nvec = 2 };
if (tcon->ses->capabilities & CAP_LARGE_FILES)
wct = 12; else {
wct = 10; /* old style read */ if ((rdata->subreq.start >> 32) > 0) { /* can not handle this big offset for old */ return -EIO;
}
}
cifs_dbg(FYI, "Reading %d bytes on fid %d\n", count, netfid); if (tcon->ses->capabilities & CAP_LARGE_FILES)
wct = 12; else {
wct = 10; /* old style read */ if ((offset >> 32) > 0) { /* can not handle this big offset for old */ return -EIO;
}
}
/* cifs_dbg(FYI, "write at %lld %d bytes\n", offset, count);*/ if (tcon->ses == NULL) return -ECONNABORTED;
if (tcon->ses->capabilities & CAP_LARGE_FILES)
wct = 14; else {
wct = 12; if ((offset >> 32) > 0) { /* can not handle big offset for old srv */ return -EIO;
}
}
/* Can increase buffer size if buffer is big enough in some cases ie we can send more if LARGE_WRITE_X capability returned by the server and if our buffer is big enough or if we convert to iovecs on socket writes
and eliminate the copy to the CIFS buffer */ if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
bytes_sent = min_t(constunsignedint, CIFSMaxBufSize, count);
} else {
bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
& ~0xFF;
}
if (bytes_sent > count)
bytes_sent = count;
pSMB->DataOffset =
cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4); if (buf)
memcpy(pSMB->Data, buf, bytes_sent); elseif (count != 0) { /* No buffer */
cifs_buf_release(pSMB); return -EINVAL;
} /* else setting file size with write of zero bytes */ if (wct == 14)
byte_count = bytes_sent + 1; /* pad */ else/* wct == 12 */
byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
/* * Mask off high 16 bits when bytes written as returned by the * server is greater than bytes requested by the client. Some * OS/2 servers are known to set incorrect CountHigh values.
*/ if (*nbytes > count)
*nbytes &= 0xFFFF;
}
cifs_buf_release(pSMB);
/* Note: On -EAGAIN error only caller can retry on handle based calls
since file handle passed in no longer valid */
switch (mid->mid_state) { case MID_RESPONSE_RECEIVED:
result = cifs_check_receive(mid, tcon->ses->server, 0); if (result != 0) break;
written = le16_to_cpu(smb->CountHigh);
written <<= 16;
written += le16_to_cpu(smb->Count); /* * Mask off high 16 bits when bytes written as returned * by the server is greater than bytes requested by the * client. OS/2 servers are known to set incorrect * CountHigh values.
*/ if (written > wdata->subreq.len)
written &= 0xFFFF;
if (written < wdata->subreq.len) {
result = -ENOSPC;
} else {
result = written; if (written > 0)
__set_bit(NETFS_SREQ_MADE_PROGRESS, &wdata->subreq.flags);
} break; case MID_REQUEST_SUBMITTED:
trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_req_submitted);
__set_bit(NETFS_SREQ_NEED_RETRY, &wdata->subreq.flags);
result = -EAGAIN; break; case MID_RETRY_NEEDED:
trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_retry_needed);
__set_bit(NETFS_SREQ_NEED_RETRY, &wdata->subreq.flags);
result = -EAGAIN; break; case MID_RESPONSE_MALFORMED:
trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_malformed);
result = -EIO; break; default:
trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_unknown);
result = -EIO; break;
}
rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, 0,
&rsp_iov);
cifs_small_buf_release(pSMB);
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes); if (rc) {
cifs_dbg(FYI, "Send error Write2 = %d\n", rc);
} elseif (resp_buf_type == 0) { /* presumably this can not happen, but best to be safe */
rc = -EIO;
} else {
WRITE_RSP *pSMBr = (WRITE_RSP *)rsp_iov.iov_base;
*nbytes = le16_to_cpu(pSMBr->CountHigh);
*nbytes = (*nbytes) << 16;
*nbytes += le16_to_cpu(pSMBr->Count);
/* * Mask off high 16 bits when bytes written as returned by the * server is greater than bytes requested by the client. OS/2 * servers are known to set incorrect CountHigh values.
*/ if (*nbytes > count)
*nbytes &= 0xFFFF;
}
free_rsp_buf(resp_buf_type, rsp_iov.iov_base);
/* Note: On -EAGAIN error only caller can retry on handle based calls
since file handle passed in no longer valid */
if (rc) {
cifs_dbg(FYI, "Send error in Posix Lock = %d\n", rc);
} elseif (pLockData) { /* lock structure can be returned on get */
__u16 data_offset;
__u16 data_count;
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
/* Note: On -EAGAIN error only caller can retry on handle based calls
since file handle passed in no longer valid */
return rc;
}
int
CIFSSMBClose(constunsignedint xid, struct cifs_tcon *tcon, int smb_file_id)
{ int rc = 0;
CLOSE_REQ *pSMB = NULL;
cifs_dbg(FYI, "In CIFSSMBClose\n");
/* do not retry on dead session on close */
rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB); if (rc == -EAGAIN) return 0; if (rc) return rc;
pSMB->FileID = (__u16) smb_file_id;
pSMB->LastWriteTime = 0xFFFFFFFF;
pSMB->ByteCount = 0;
rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
cifs_small_buf_release(pSMB);
cifs_stats_inc(&tcon->stats.cifs_stats.num_closes); if (rc) { if (rc != -EINTR) { /* EINTR is expected when user ctl-c to kill app */
cifs_dbg(VFS, "Send error in Close = %d\n", rc);
}
}
/* Since session is dead, file will be closed on server already */ if (rc == -EAGAIN)
rc = 0;
return rc;
}
int
CIFSSMBFlush(constunsignedint xid, struct cifs_tcon *tcon, int smb_file_id)
{ int rc = 0;
FLUSH_REQ *pSMB = NULL;
cifs_dbg(FYI, "In CIFSSMBFlush\n");
/* SetupCount must be 1, otherwise offset to ByteCount is incorrect. */ if (io_rsp->SetupCount != 1) {
rc = -EIO; goto error;
}
/* * ReturnedDataLen is output length of executed IOCTL. * DataCount is output length transferred over network. * Check that we have full FSCTL_GET_REPARSE_POINT buffer.
*/ if (data_count != le16_to_cpu(io_rsp->ReturnedDataLen)) {
rc = -EIO; goto error;
}
/* * If server filesystem does not support reparse points then do not * attempt to create reparse point. This will prevent creating unusable * empty object on the server.
*/ if (!CIFS_REPARSE_SUPPORT(tcon)) return ERR_PTR(-EOPNOTSUPP);
#ifndef CONFIG_CIFS_XATTR if (xattr_iov) return ERR_PTR(-EOPNOTSUPP); #endif
if (!rc)
rc = cifs_get_inode_info(&new, full_path, data, sb, xid, NULL);
out_close:
CIFSSMBClose(xid, tcon, fid.netfid);
/* * If CREATE was successful but FSCTL_SET_REPARSE_POINT failed then * remove the intermediate object created by CREATE. Otherwise * empty object stay on the server when reparse call failed.
*/ if (rc)
CIFSSMBDelFile(xid, tcon, full_path, cifs_sb, NULL);
return rc ? ERR_PTR(rc) : new;
}
int
CIFSSMB_set_compression(constunsignedint xid, struct cifs_tcon *tcon,
__u16 fid)
{ int rc = 0; int bytes_returned; struct smb_com_transaction_compr_ioctl_req *pSMB; struct smb_com_transaction_ioctl_rsp *pSMBr;
/* * Note: On -EAGAIN error only caller can retry on handle based calls * since file handle passed in no longer valid.
*/ return rc;
}
#ifdef CONFIG_CIFS_POSIX
#ifdef CONFIG_FS_POSIX_ACL /** * cifs_init_posix_acl - convert ACL from cifs to POSIX ACL format * @ace: POSIX ACL entry to store converted ACL into * @cifs_ace: ACL in cifs format * * Convert an Access Control Entry from wire format to local POSIX xattr * format. * * Note that the @cifs_uid member is used to store both {g,u}id_t.
*/ staticvoid cifs_init_posix_acl(struct posix_acl_entry *ace, struct cifs_posix_ace *cifs_ace)
{ /* u8 cifs fields do not need le conversion */
ace->e_perm = cifs_ace->cifs_e_perm;
ace->e_tag = cifs_ace->cifs_e_tag;
switch (ace->e_tag) { case ACL_USER:
ace->e_uid = make_kuid(&init_user_ns,
le64_to_cpu(cifs_ace->cifs_uid)); break; case ACL_GROUP:
ace->e_gid = make_kgid(&init_user_ns,
le64_to_cpu(cifs_ace->cifs_uid)); break;
} return;
}
/** * cifs_to_posix_acl - copy cifs ACL format to POSIX ACL format * @acl: ACLs returned in POSIX ACL format * @src: ACLs in cifs format * @acl_type: type of POSIX ACL requested * @size_of_data_area: size of SMB we got * * This function converts ACLs from cifs format to POSIX ACL format. * If @acl is NULL then the size of the buffer required to store POSIX ACLs in * their uapi format is returned.
*/ staticint cifs_to_posix_acl(struct posix_acl **acl, char *src, constint acl_type, constint size_of_data_area)
{ int size = 0;
__u16 count; struct cifs_posix_ace *pACE; struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src; struct posix_acl *kacl = NULL; struct posix_acl_entry *pa, *pe;
if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION) return -EOPNOTSUPP;
if (acl_type == ACL_TYPE_ACCESS) {
count = le16_to_cpu(cifs_acl->access_entry_count);
pACE = &cifs_acl->ace_array[0];
size = sizeof(struct cifs_posix_acl);
size += sizeof(struct cifs_posix_ace) * count; /* check if we would go beyond end of SMB */ if (size_of_data_area < size) {
cifs_dbg(FYI, "bad CIFS POSIX ACL size %d vs. %d\n",
size_of_data_area, size); return -EINVAL;
}
} elseif (acl_type == ACL_TYPE_DEFAULT) {
count = le16_to_cpu(cifs_acl->access_entry_count);
size = sizeof(struct cifs_posix_acl);
size += sizeof(struct cifs_posix_ace) * count; /* skip past access ACEs to get to default ACEs */
pACE = &cifs_acl->ace_array[count];
count = le16_to_cpu(cifs_acl->default_entry_count);
size += sizeof(struct cifs_posix_ace) * count; /* check if we would go beyond end of SMB */ if (size_of_data_area < size) return -EINVAL;
} else { /* illegal type */ return -EINVAL;
}
/* Allocate number of POSIX ACLs to store in VFS format. */
kacl = posix_acl_alloc(count, GFP_NOFS); if (!kacl) return -ENOMEM;
/** * cifs_init_ace - convert ACL entry from POSIX ACL to cifs format * @cifs_ace: the cifs ACL entry to store into * @local_ace: the POSIX ACL entry to convert
*/ staticvoid cifs_init_ace(struct cifs_posix_ace *cifs_ace, conststruct posix_acl_entry *local_ace)
{
cifs_ace->cifs_e_perm = local_ace->e_perm;
cifs_ace->cifs_e_tag = local_ace->e_tag;
/** * posix_acl_to_cifs - convert ACLs from POSIX ACL to cifs format * @parm_data: ACLs in cifs format to convert to * @acl: ACLs in POSIX ACL format to convert from * @acl_type: the type of POSIX ACLs stored in @acl * * Return: the number cifs ACL entries after conversion
*/ static __u16 posix_acl_to_cifs(char *parm_data, conststruct posix_acl *acl, constint acl_type)
{
__u16 rc = 0; struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data; conststruct posix_acl_entry *pa, *pe; int count; int i = 0;
if ((acl == NULL) || (cifs_acl == NULL)) return 0;
count = acl->a_count;
cifs_dbg(FYI, "setting acl with %d entries\n", count);
/* * Note that the uapi POSIX ACL version is verified by the VFS and is * independent of the cifs ACL version. Changing the POSIX ACL version * is a uapi change and if it's changed we will pass down the POSIX ACL * version in struct posix_acl from the VFS. For now there's really * only one that all filesystems know how to deal with.
*/
cifs_acl->version = cpu_to_le16(1); if (acl_type == ACL_TYPE_ACCESS) {
cifs_acl->access_entry_count = cpu_to_le16(count);
cifs_acl->default_entry_count = cpu_to_le16(0xFFFF);
} elseif (acl_type == ACL_TYPE_DEFAULT) {
cifs_acl->default_entry_count = cpu_to_le16(count);
cifs_acl->access_entry_count = cpu_to_le16(0xFFFF);
} else {
cifs_dbg(FYI, "unknown ACL type %d\n", acl_type); return 0;
}
FOREACH_ACL_ENTRY(pa, acl, pe) {
cifs_init_ace(&cifs_acl->ace_array[i++], pa);
} if (rc == 0) {
rc = (__u16)(count * sizeof(struct cifs_posix_ace));
rc += sizeof(struct cifs_posix_acl); /* BB add check to make sure ACL does not overflow SMB */
} return rc;
}
int cifs_do_get_acl(constunsignedint xid, struct cifs_tcon *tcon, constunsignedchar *searchName, struct posix_acl **acl, constint acl_type, conststruct nls_table *nls_codepage, int remap)
{ /* SMB_QUERY_POSIX_ACL */
TRANSACTION2_QPI_REQ *pSMB = NULL;
TRANSACTION2_QPI_RSP *pSMBr = NULL; int rc = 0; int bytes_returned; int name_len;
__u16 params, byte_count;
cifs_dbg(FYI, "In GetPosixACL (Unix) for path %s\n", searchName);
/* * Initialize NT TRANSACT SMB into small smb request buffer. This assumes that * all NT TRANSACTS that we init here have total parm and data under about 400 * bytes (to fit in small cifs buffer size), which is the case so far, it * easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of * returned setup area) and MaxParameterCount (returned parms size) must be set * by caller
*/ staticint
smb_init_nttransact(const __u16 sub_command, constint setup_count, constint parm_len, struct cifs_tcon *tcon, void **ret_buf)
{ int rc;
__u32 temp_offset; struct smb_com_ntransact_req *pSMB;
/* should we also check that parm and data areas do not overlap? */ if (*ppparm > end_of_smb) {
cifs_dbg(FYI, "parms start after end of smb\n"); return -EINVAL;
} elseif (parm_count + *ppparm > end_of_smb) {
cifs_dbg(FYI, "parm end after end of smb\n"); return -EINVAL;
} elseif (*ppdata > end_of_smb) {
cifs_dbg(FYI, "data starts after end of smb\n"); return -EINVAL;
} elseif (data_count + *ppdata > end_of_smb) {
cifs_dbg(FYI, "data %p + count %d (%p) past smb end %p start %p\n",
*ppdata, data_count, (data_count + *ppdata),
end_of_smb, pSMBr); return -EINVAL;
} elseif (parm_count + data_count > bcc) {
cifs_dbg(FYI, "parm count and data count larger than SMB\n"); return -EINVAL;
}
*pdatalen = data_count;
*pparmlen = parm_count; return 0;
}
/* Get Security Descriptor (by handle) from remote server for a file or dir */ int
CIFSSMBGetCIFSACL(constunsignedint xid, struct cifs_tcon *tcon, __u16 fid, struct smb_ntsd **acl_inf, __u32 *pbuflen, __u32 info)
{ int rc = 0; int buf_type = 0;
QUERY_SEC_DESC_REQ *pSMB; struct kvec iov[1]; struct kvec rsp_iov;
cifs_dbg(FYI, "GetCifsACL\n");
*pbuflen = 0;
*acl_inf = NULL;
rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
8 /* parm len */, tcon, (void **) &pSMB); if (rc) return rc;
pSMB->MaxParameterCount = cpu_to_le32(4); /* BB TEST with big acls that might need to be e.g. larger than 16K */
pSMB->MaxSetupCount = 0;
pSMB->Fid = fid; /* file handle always le */
pSMB->AclFlags = cpu_to_le32(info);
pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
inc_rfc1001_len(pSMB, 11);
iov[0].iov_base = (char *)pSMB;
iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4;
cifs_dbg(FYI, "SetCIFSACL bytes_returned: %d, rc: %d\n",
bytes_returned, rc); if (rc)
cifs_dbg(FYI, "Set CIFS ACL returned %d\n", rc);
cifs_buf_release(pSMB);
if (rc == -EAGAIN) goto setCifsAclRetry;
return (rc);
}
/* Legacy Query Path Information call for lookup to old servers such
as Win9x/WinME */ int
SMBQueryInformation(constunsignedint xid, struct cifs_tcon *tcon, constchar *search_name, FILE_ALL_INFO *data, conststruct nls_table *nls_codepage, int remap)
{
QUERY_INFORMATION_REQ *pSMB;
QUERY_INFORMATION_RSP *pSMBr; int rc = 0; int bytes_returned; int name_len;
if (rc) /* BB add auto retry on EOPNOTSUPP? */
rc = -EIO; elseif (!legacy && get_bcc(&pSMBr->hdr) < 40)
rc = -EIO; /* bad smb */ elseif (legacy && get_bcc(&pSMBr->hdr) < 24)
rc = -EIO; /* 24 or 26 expected but we do not read
last field */ elseif (data) { int size;
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
/* * On legacy responses we do not read the last field, * EAsize, fortunately since it varies by subdialect and * also note it differs on Set vs Get, ie two bytes or 4 * bytes depending but we don't care here.
*/ if (legacy)
size = sizeof(FILE_INFO_STANDARD); else
size = sizeof(FILE_ALL_INFO);
memcpy((char *) data, (char *) &pSMBr->hdr.Protocol +
data_offset, size);
} else
rc = -ENOMEM;
}
cifs_buf_release(pSMB); if (rc == -EAGAIN) goto QPathInfoRetry;
return rc;
}
int
CIFSSMBUnixQFileInfo(constunsignedint xid, struct cifs_tcon *tcon,
u16 netfid, FILE_UNIX_BASIC_INFO *pFindData)
{ struct smb_t2_qfi_req *pSMB = NULL; struct smb_t2_qfi_rsp *pSMBr = NULL; int rc = 0; int bytes_returned;
__u16 params, byte_count;
/* BB what should we set StorageType to? Does it matter? BB */
pSMB->SearchStorageType = 0;
inc_rfc1001_len(pSMB, byte_count);
pSMB->ByteCount = cpu_to_le16(byte_count);
/* * BB: On error, should we leave previous search buf * (and count and last entry fields) intact or free the previous one? * * Note: On -EAGAIN error only caller can retry on handle based calls * since file handle passed in no longer valid.
*/ return 0;
}
int
CIFSFindClose(constunsignedint xid, struct cifs_tcon *tcon, const __u16 searchHandle)
{ int rc = 0;
FINDCLOSE_REQ *pSMB = NULL;
cifs_dbg(FYI, "In CIFSSMBFindClose\n");
rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
/* no sense returning error if session restarted
as file handle has been closed */ if (rc == -EAGAIN) return 0; if (rc) return rc;
rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
(struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) {
cifs_dbg(FYI, "error %d in QueryInternalInfo\n", rc);
} else { /* decode response */
rc = validate_t2((struct smb_t2_rsp *)pSMBr); /* BB also check enough total bytes returned */ if (rc || get_bcc(&pSMBr->hdr) < 2) /* If rc should we check for EOPNOSUPP and
disable the srvino flag? or in caller? */
rc = -EIO; /* bad smb */ else {
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
__u16 count = le16_to_cpu(pSMBr->t2.DataCount); struct file_internal_info *pfinfo; /* BB Do we need a cast or hash here ? */ if (count < 8) {
cifs_dbg(FYI, "Invalid size ret in QryIntrnlInf\n");
rc = -EIO; goto GetInodeNumOut;
}
pfinfo = (struct file_internal_info *)
(data_offset + (char *) &pSMBr->hdr.Protocol);
*inode_number = le64_to_cpu(pfinfo->UniqueId);
}
}
GetInodeNumOut:
cifs_buf_release(pSMB); if (rc == -EAGAIN) goto GetInodeNumberRetry; return rc;
}
int
CIFSGetDFSRefer(constunsignedint xid, struct cifs_ses *ses, constchar *search_name, struct dfs_info3_param **target_nodes, unsignedint *num_of_nodes, conststruct nls_table *nls_codepage, int remap)
{ /* TRANS2_GET_DFS_REFERRAL */
TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL; int rc = 0; int bytes_returned; int name_len;
__u16 params, byte_count;
*num_of_nodes = 0;
*target_nodes = NULL;
cifs_dbg(FYI, "In GetDFSRefer the path %s\n", search_name); if (ses == NULL || ses->tcon_ipc == NULL) return -ENODEV;
getDFSRetry: /* * Use smb_init_no_reconnect() instead of smb_init() as * CIFSGetDFSRefer() may be called from cifs_reconnect_tcon() and thus * causing an infinite recursion.
*/
rc = smb_init(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc,
(void **)&pSMB, (void **)&pSMBr); if (rc) return rc;
/* server pointer checked in called function,
but should never be null here anyway */
pSMB->hdr.Mid = get_next_mid(ses->server);
pSMB->hdr.Tid = ses->tcon_ipc->tid;
pSMB->hdr.Uid = ses->Suid; if (ses->capabilities & CAP_STATUS32)
pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS; if (ses->capabilities & CAP_DFS)
pSMB->hdr.Flags2 |= SMBFLG2_DFS;
/* parse returned result into more usable form */
rc = parse_dfs_referrals(&pSMBr->dfs_data,
le16_to_cpu(pSMBr->t2.DataCount),
num_of_nodes, target_nodes, nls_codepage,
remap, search_name,
(pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) != 0);
GetDFSRefExit:
cifs_buf_release(pSMB);
if (rc == -EAGAIN) goto getDFSRetry;
return rc;
}
/* Query File System Info such as free space to old servers such as Win 9x */ int
SMBOldQFSInfo(constunsignedint xid, struct cifs_tcon *tcon, struct kstatfs *FSData)
{ /* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
TRANSACTION2_QFSI_REQ *pSMB = NULL;
TRANSACTION2_QFSI_RSP *pSMBr = NULL;
FILE_SYSTEM_ALLOC_INFO *response_data; int rc = 0; int bytes_returned = 0;
__u16 params, byte_count;
response_data = (FILE_SYSTEM_ALLOC_INFO *)
(((char *) &pSMBr->hdr.Protocol) + data_offset);
FSData->f_bsize =
le16_to_cpu(response_data->BytesPerSector) *
le32_to_cpu(response_data->
SectorsPerAllocationUnit); /* * much prefer larger but if server doesn't report * a valid size than 4K is a reasonable minimum
*/ if (FSData->f_bsize < 512)
FSData->f_bsize = 4096;
if (rc || get_bcc(&pSMBr->hdr) < 24)
rc = -EIO; /* bad smb */ else {
__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
response_data =
(FILE_SYSTEM_INFO
*) (((char *) &pSMBr->hdr.Protocol) +
data_offset);
FSData->f_bsize =
le32_to_cpu(response_data->BytesPerSector) *
le32_to_cpu(response_data->
SectorsPerAllocationUnit); /* * much prefer larger but if server doesn't report * a valid size than 4K is a reasonable minimum
*/ if (FSData->f_bsize < 512)
FSData->f_bsize = 4096;
cifs_dbg(FYI, "In SETFSUnixInfo\n");
SETFSUnixRetry: /* BB switch to small buf init to save memory */
rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
(void **) &pSMB, (void **) &pSMBr); if (rc) return rc;
params = 4; /* 2 bytes zero followed by info level. */
pSMB->MaxSetupCount = 0;
pSMB->Reserved = 0;
pSMB->Flags = 0;
pSMB->Timeout = 0;
pSMB->Reserved2 = 0;
param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
- 4;
offset = param_offset + params;
/* * We can not use write of zero bytes trick to set file size due to need for * large file support. Also note that this SetPathInfo is preferred to * SetFileInfo based method in next routine which is only needed to work around * a sharing violation bugin Samba which this routine can run into.
*/ int
CIFSSMBSetEOF(constunsignedint xid, struct cifs_tcon *tcon, constchar *file_name, __u64 size, struct cifs_sb_info *cifs_sb, bool set_allocation, struct dentry *dentry)
{ struct smb_com_transaction2_spi_req *pSMB = NULL; struct smb_com_transaction2_spi_rsp *pSMBr = NULL; struct file_end_of_file_info *parm_data; int name_len; int rc = 0; int bytes_returned = 0; int remap = cifs_remap(cifs_sb);
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);
/* 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 old servers since the only other choice is to go from 100 nanosecond DCE time and resort to the original setpathinfo level which takes the ancient
DOS time format with 2 second granularity */ int
CIFSSMBSetFileInfo(constunsignedint xid, struct cifs_tcon *tcon, const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener)
{ struct smb_com_transaction2_sfi_req *pSMB = NULL; char *data_offset; int rc = 0;
__u16 params, param_offset, offset, byte_count, count;
if (uid_valid(args->uid))
uid = from_kuid(&init_user_ns, args->uid); if (gid_valid(args->gid))
gid = from_kgid(&init_user_ns, args->gid);
/* * Samba server ignores set of file size to zero due to bugs in some * older clients, but we should be precise - we use SetFileSize to * set file size and do not want to truncate file size to zero * accidentally as happened on one Samba server beta by putting * zero instead of -1 here
*/
data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64);
data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64);
data_offset->LastStatusChange = cpu_to_le64(args->ctime);
data_offset->LastAccessTime = cpu_to_le64(args->atime);
data_offset->LastModificationTime = cpu_to_le64(args->mtime);
data_offset->Uid = cpu_to_le64(uid);
data_offset->Gid = cpu_to_le64(gid); /* better to leave device as zero when it is */
data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));
data_offset->DevMinor = cpu_to_le64(MINOR(args->device));
data_offset->Permissions = cpu_to_le64(mode);
cifs_buf_release(pSMB); if (rc == -EAGAIN) goto setPermsRetry; return rc;
}
#ifdef CONFIG_CIFS_XATTR /* * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common * function used by listxattr and getxattr type calls. When ea_name is set, * it looks for that attribute name and stuffs that value into the EAData * buffer. When ea_name is NULL, it stuffs a list of attribute names into the * buffer. In both cases, the return value is either the length of the * resulting data or a negative error code. If EAData is a NULL pointer then * the data isn't copied to it, but the length is returned.
*/
ssize_t
CIFSSMBQAllEAs(constunsignedint xid, struct cifs_tcon *tcon, constunsignedchar *searchName, constunsignedchar *ea_name, char *EAData, size_t buf_size, struct cifs_sb_info *cifs_sb)
{ /* BB assumes one setup word */
TRANSACTION2_QPI_REQ *pSMB = NULL;
TRANSACTION2_QPI_RSP *pSMBr = NULL; int remap = cifs_remap(cifs_sb); struct nls_table *nls_codepage = cifs_sb->local_nls; int rc = 0; int bytes_returned; int list_len; struct fealist *ea_response_data; struct fea *temp_fea; char *temp_ptr; char *end_of_smb;
__u16 params, byte_count, data_offset; unsignedint ea_name_len = ea_name ? strlen(ea_name) : 0;
cifs_dbg(FYI, "In Query All EAs path %s\n", searchName);
QAllEAsRetry:
rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
(void **) &pSMBr); if (rc) return rc;
/* check that length of list is not more than bcc */ /* check that each entry does not go beyond length
of list */ /* check that each element of each entry does not
go beyond end of list */ /* validate_trans2_offsets() */ /* BB check if start of smb + data_offset > &bcc+ bcc */
list_len = le32_to_cpu(ea_response_data->list_len);
cifs_dbg(FYI, "ea length %d\n", list_len); if (list_len <= 8) {
cifs_dbg(FYI, "empty EA list returned from server\n"); /* didn't find the named attribute */ if (ea_name)
rc = -ENODATA; goto QAllEAsOut;
}
/* make sure list_len doesn't go past end of SMB */
end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr); if ((char *)ea_response_data + list_len > end_of_smb) {
cifs_dbg(FYI, "EA list appears to go beyond SMB\n");
rc = -EIO; goto QAllEAsOut;
}
/* account for ea list len */
list_len -= 4;
temp_fea = &ea_response_data->list;
temp_ptr = (char *)temp_fea; while (list_len > 0) { unsignedint name_len;
__u16 value_len;
list_len -= 4;
temp_ptr += 4; /* make sure we can read name_len and value_len */ if (list_len < 0) {
cifs_dbg(FYI, "EA entry goes beyond length of list\n");
rc = -EIO; goto QAllEAsOut;
}
/* done calculating parms using name_len of file name, now use name_len to calculate length of ea name
we are going to create in the inode xattrs */ if (ea_name == NULL)
name_len = 0; else
name_len = strnlen(ea_name, 255);
parm_data = (void *)pSMB + offsetof(struct smb_hdr, Protocol) + offset;
pSMB->ParameterOffset = cpu_to_le16(param_offset);
pSMB->DataOffset = cpu_to_le16(offset);
pSMB->SetupCount = 1;
pSMB->Reserved3 = 0;
pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
byte_count = 3 /* pad */ + params + count;
pSMB->DataCount = cpu_to_le16(count);
parm_data->list_len = cpu_to_le32(count);
parm_data->list.EA_flags = 0; /* we checked above that name len is less than 255 */
parm_data->list.name_len = (__u8)name_len; /* EA names are always ASCII and NUL-terminated */
strscpy(parm_data->list.name, ea_name ?: "", name_len + 1);
parm_data->list.value_len = cpu_to_le16(ea_value_len); /* caller ensures that ea_value_len is less than 64K but
we need to ensure that it fits within the smb */
/*BB add length check to see if it would fit in
negotiated SMB buffer size BB */ /* if (ea_value_len > buffer_size - 512 (enough for header)) */ if (ea_value_len)
memcpy(parm_data->list.name + name_len + 1,
ea_value, ea_value_len);
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.156Bemerkung:
(vorverarbeitet am 2026-04-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.