/* * An NT cancel request header looks just like the original request except: * * The Command is SMB_COM_NT_CANCEL * The WordCount is zeroed out * The ByteCount is zeroed out * * This function mangles an existing request buffer into a * SMB_COM_NT_CANCEL request and then sends it.
*/ staticint
send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst, struct mid_q_entry *mid)
{ int rc = 0; struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
/* -4 for RFC1001 length and +2 for BCC field */
in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2);
in_buf->Command = SMB_COM_NT_CANCEL;
in_buf->WordCount = 0;
put_bcc(0, in_buf);
/* * The response to this call was already factored into the sequence * number when the call went out, so we must adjust it back downward * after signing here.
*/
--server->sequence_number;
rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); if (rc < 0)
server->sequence_number--;
/* * Find a free multiplex id (SMB mid). Otherwise there could be * mid collisions which might cause problems, demultiplexing the * wrong response to this request. Multiplex ids could collide if * one of a series requests takes much longer than the others, or * if a very large number of long lived requests (byte range * locks or FindNotify requests) are pending. No more than * 64K-1 requests can be outstanding at one time. If no * mids are available, return zero. A future optimization * could make the combination of mids and uid the key we use * to demultiplex on (rather than mid alone). * In addition to the above check, the cifs demultiplex * code already used the command code as a secondary * check of the frame and if signing is negotiated the * response would be discarded if the mid were the same * but the signature was wrong. Since the mid is not put in the * pending queue until later (when it is about to be dispatched) * we do have to limit the number of outstanding requests * to somewhat less than 64K-1 although it is hard to imagine * so many threads being in the vfs at one time.
*/ static __u64
cifs_get_next_mid(struct TCP_Server_Info *server)
{
__u64 mid = 0;
__u16 last_mid, cur_mid; bool collision, reconnect = false;
spin_lock(&server->mid_counter_lock); /* mid is 16 bit only for CIFS/SMB */
cur_mid = (__u16)((server->current_mid) & 0xffff); /* we do not want to loop forever */
last_mid = cur_mid;
cur_mid++; /* avoid 0xFFFF MID */ if (cur_mid == 0xffff)
cur_mid++;
/* * This nested loop looks more expensive than it is. * In practice the list of pending requests is short, * fewer than 50, and the mids are likely to be unique * on the first pass through the loop unless some request * takes longer than the 64 thousand requests before it * (and it would also have to have been a request that * did not time out).
*/ while (cur_mid != last_mid) { struct mid_q_entry *mid_entry; unsignedint num_mids;
collision = false; if (cur_mid == 0)
cur_mid++;
num_mids = 0;
spin_lock(&server->mid_queue_lock);
list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) {
++num_mids; if (mid_entry->mid == cur_mid &&
mid_entry->mid_state == MID_REQUEST_SUBMITTED) { /* This mid is in use, try a different one */
collision = true; break;
}
}
spin_unlock(&server->mid_queue_lock);
/* * if we have more than 32k mids in the list, then something * is very wrong. Possibly a local user is trying to DoS the * box by issuing long-running calls and SIGKILL'ing them. If * we get to 2^16 mids then we're in big trouble as this * function could loop forever. * * Go ahead and assign out the mid in this situation, but force * an eventual reconnect to clean out the pending_mid_q.
*/ if (num_mids > 32768)
reconnect = true;
if (reconnect) {
cifs_signal_cifsd_for_reconnect(server, false);
}
return mid;
}
/* return codes: 0 not a transact2, or all data present >0 transact2 with that much data missing -EINVAL invalid transact2
*/ staticint
check2ndT2(char *buf)
{ struct smb_hdr *pSMB = (struct smb_hdr *)buf; struct smb_t2_rsp *pSMBt; int remaining;
__u16 total_data_size, data_in_this_rsp;
if (pSMB->Command != SMB_COM_TRANSACTION2) return 0;
/* check for plausible wct, bcc and t2 data and parm sizes */ /* check for parm and data offset going beyond end of smb */ if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */
cifs_dbg(FYI, "Invalid transact2 word count\n"); return -EINVAL;
}
if (tgt_total_cnt != src_total_cnt)
cifs_dbg(FYI, "total data count of primary and secondary t2 differ source=%hu target=%hu\n",
src_total_cnt, tgt_total_cnt);
if (remaining < 0) {
cifs_dbg(FYI, "Server sent too much data. tgt_total_cnt=%hu total_in_tgt=%u\n",
tgt_total_cnt, total_in_tgt); return -EPROTO;
}
if (remaining == 0) { /* nothing to do, ignore */
cifs_dbg(FYI, "no more data remains\n"); return 0;
}
total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); if (remaining < total_in_src)
cifs_dbg(FYI, "transact2 2nd response contains too much data\n");
/* find end of first SMB data area */
data_area_of_tgt = (char *)&pSMBt->hdr.Protocol +
get_unaligned_le16(&pSMBt->t2_rsp.DataOffset);
total_in_tgt += total_in_src; /* is the result too big for the field? */ if (total_in_tgt > USHRT_MAX) {
cifs_dbg(FYI, "coalesced DataCount too large (%u)\n",
total_in_tgt); return -EPROTO;
}
put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount);
/* fix up the BCC */
byte_count = get_bcc(target_hdr);
byte_count += total_in_src; /* is the result too big for the field? */ if (byte_count > USHRT_MAX) {
cifs_dbg(FYI, "coalesced BCC too large (%u)\n", byte_count); return -EPROTO;
}
put_bcc(byte_count, target_hdr);
/* start with specified wsize, or default */ if (ctx->got_wsize)
wsize = ctx->vol_wsize; elseif (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
wsize = CIFS_DEFAULT_IOSIZE; else
wsize = CIFS_DEFAULT_NON_POSIX_WSIZE;
/* can server support 24-bit write sizes? (via UNIX extensions) */ if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
wsize = min_t(unsignedint, wsize, CIFS_MAX_RFC1002_WSIZE);
/* * no CAP_LARGE_WRITE_X or is signing enabled without CAP_UNIX set? * Limit it to max buffer offered by the server, minus the size of the * WRITEX header, not including the 4 byte RFC1001 length.
*/ if (!(server->capabilities & CAP_LARGE_WRITE_X) ||
(!(server->capabilities & CAP_UNIX) && server->sign))
wsize = min_t(unsignedint, wsize,
server->maxBuf - sizeof(WRITE_REQ) + 4);
/* hard limit of CIFS_MAX_WSIZE */
wsize = min_t(unsignedint, wsize, CIFS_MAX_WSIZE);
/* * Set default value... * * HACK alert! Ancient servers have very small buffers. Even though * MS-CIFS indicates that servers are only limited by the client's * bufsize for reads, testing against win98se shows that it throws * INVALID_PARAMETER errors if you try to request too large a read. * OS/2 just sends back short reads. * * If the server doesn't advertise CAP_LARGE_READ_X, then assume that * it can't handle a read request larger than its MaxBufferSize either.
*/ if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_READ_CAP))
defsize = CIFS_DEFAULT_IOSIZE; elseif (server->capabilities & CAP_LARGE_READ_X)
defsize = CIFS_DEFAULT_NON_POSIX_RSIZE; else
defsize = server->maxBuf - sizeof(READ_RSP);
/* * no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to * the client's MaxBufferSize.
*/ if (!(server->capabilities & CAP_LARGE_READ_X))
rsize = min_t(unsignedint, CIFSMaxBufSize, rsize);
/* hard limit of CIFS_MAX_RSIZE */
rsize = min_t(unsignedint, rsize, CIFS_MAX_RSIZE);
/* * 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 (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 = (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);
} elseif (!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;
} elseif ((rc == -EOPNOTSUPP || rc == -EINVAL) && non_unicode_wildcard) { /* Path with non-UNICODE wildcard character cannot exist. */
rc = -ENOENT;
}
#ifdef CONFIG_CIFS_XATTR /* * For non-symlink WSL reparse points it is required to fetch * EA $LXMOD which contains in its S_DT part the mandatory file type.
*/ if (!rc && data->reparse_point) { struct smb2_file_full_ea_info *ea;
u32 next = 0;
ea = (struct smb2_file_full_ea_info *)data->wsl.eas; do {
ea = (void *)((u8 *)ea + next);
next = le32_to_cpu(ea->next_entry_offset);
} while (next); if (le16_to_cpu(ea->ea_value_length)) {
ea->next_entry_offset = cpu_to_le32(ALIGN(sizeof(*ea) +
ea->ea_name_length + 1 +
le16_to_cpu(ea->ea_value_length), 4));
ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset));
}
rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_MODE,
&ea->ea_data[SMB2_WSL_XATTR_NAME_LEN + 1],
SMB2_WSL_XATTR_MODE_SIZE, cifs_sb); if (rc == SMB2_WSL_XATTR_MODE_SIZE) {
ea->next_entry_offset = cpu_to_le32(0);
ea->flags = 0;
ea->ea_name_length = SMB2_WSL_XATTR_NAME_LEN;
ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_MODE_SIZE);
memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_MODE, SMB2_WSL_XATTR_NAME_LEN + 1);
data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
SMB2_WSL_XATTR_MODE_SIZE, 4);
rc = 0;
} elseif (rc >= 0) { /* It is an error if EA $LXMOD has wrong size. */
rc = -EINVAL;
} else { /* * In all other cases ignore error if fetching * of EA $LXMOD failed. It is needed only for * non-symlink WSL reparse points and wsl_to_fattr() * handle the case when EA is missing.
*/
rc = 0;
}
}
/* * For WSL CHR and BLK reparse points it is required to fetch * EA $LXDEV which contains major and minor device numbers.
*/ if (!rc && data->reparse_point) { struct smb2_file_full_ea_info *ea;
u32 next = 0;
ea = (struct smb2_file_full_ea_info *)data->wsl.eas; do {
ea = (void *)((u8 *)ea + next);
next = le32_to_cpu(ea->next_entry_offset);
} while (next); if (le16_to_cpu(ea->ea_value_length)) {
ea->next_entry_offset = cpu_to_le32(ALIGN(sizeof(*ea) +
ea->ea_name_length + 1 +
le16_to_cpu(ea->ea_value_length), 4));
ea = (void *)((u8 *)ea + le32_to_cpu(ea->next_entry_offset));
}
rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_DEV,
&ea->ea_data[SMB2_WSL_XATTR_NAME_LEN + 1],
SMB2_WSL_XATTR_DEV_SIZE, cifs_sb); if (rc == SMB2_WSL_XATTR_DEV_SIZE) {
ea->next_entry_offset = cpu_to_le32(0);
ea->flags = 0;
ea->ea_name_length = SMB2_WSL_XATTR_NAME_LEN;
ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_DEV_SIZE);
memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_DEV, SMB2_WSL_XATTR_NAME_LEN + 1);
data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
SMB2_WSL_XATTR_MODE_SIZE, 4);
rc = 0;
} elseif (rc >= 0) { /* It is an error if EA $LXDEV has wrong size. */
rc = -EINVAL;
} else { /* * In all other cases ignore error if fetching * of EA $LXDEV failed. It is needed only for * WSL CHR and BLK reparse points and wsl_to_fattr() * handle the case when EA is missing.
*/
rc = 0;
}
} #endif
return rc;
}
staticint cifs_get_srv_inum(constunsignedint xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, constchar *full_path,
u64 *uniqueid, struct cifs_open_info_data *unused)
{ /* * We can not use the IndexNumber field by default from Windows or * Samba (in ALL_INFO buf) but we can request it explicitly. The SNIA * CIFS spec claims that this value is unique within the scope of a * share, and the windows docs hint that it's actually unique * per-machine. * * There may be higher info levels that work but are there Windows * server or network appliances for which IndexNumber field is not * guaranteed unique? * * CIFSGetSrvInodeNumber() uses SMB_QUERY_FILE_INTERNAL_INFO * which is SMB PASSTHROUGH level therefore check for capability. * Note that this function can be called with tcon == NULL.
*/ if (tcon && !(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)) return -EOPNOTSUPP; return CIFSGetSrvInodeNumber(xid, tcon, full_path, uniqueid,
cifs_sb->local_nls,
cifs_remap(cifs_sb));
}
staticint cifs_query_file_info(constunsignedint xid, struct cifs_tcon *tcon, struct cifsFileInfo *cfile, struct cifs_open_info_data *data)
{ 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) return -ENOMEM;
}
/* * 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;
}
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 absolutely 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; goto out;
}
netpid = current->tgid;
cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for attrs/times not supported by this server\n");
if (open_file == NULL)
CIFSSMBClose(xid, tcon, fid.netfid); else
cifsFileInfo_put(open_file);
/* * Setting the read-only bit is not honored 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); return rc;
}
/* * We could add a second check for a QFS Unix capability bit
*/ if ((tcon->ses->capabilities & CAP_UNIX) &&
(CIFS_POSIX_EXTENSIONS & le64_to_cpu(tcon->fsUnixInfo.Capability)))
rc = CIFSSMBQFSPosixInfo(xid, tcon, buf);
/* * Only need to call the old QFSInfo if failed on newer one, * e.g. by OS/2.
**/ if (rc && (tcon->ses->capabilities & CAP_NT_SMBS))
rc = CIFSSMBQFSInfo(xid, tcon, buf);
/* * Some old Windows servers also do not support level 103, retry with * older level one if old server failed the previous call or we * bypassed it because we detected that this was an older LANMAN sess
*/ if (rc)
rc = SMBOldQFSInfo(xid, tcon, buf); return rc;
}
if (rc == 0)
d_instantiate(dentry, newinode); return rc;
} elseif (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { /* * Check if mounted with mount parm 'sfu' mount parm. * SFU emulation should work with all servers * and was used by default in earlier versions of Windows.
*/ return cifs_sfu_make_node(xid, inode, dentry, tcon,
full_path, mode, dev);
} elseif (CIFS_REPARSE_SUPPORT(tcon)) { /* * mknod via reparse points requires server support for * storing reparse points, which is available since * Windows 2000, but was not widely used until release * of Windows Server 2012 by the Windows NFS server.
*/ return mknod_reparse(xid, inode, dentry, tcon,
full_path, mode, dev);
} else { return -EOPNOTSUPP;
}
}
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.