summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/fs
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/fs')
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c99
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_pathname.c95
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c207
3 files changed, 260 insertions, 141 deletions
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c b/usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c
index 00198aa31f..dcfd771baa 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_qinfo_file.c
@@ -32,6 +32,7 @@ static uint32_t smb2_qif_internal(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_ea_size(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_access(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_name(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_normalized_name(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_position(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_full_ea(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_mode(smb_request_t *, smb_queryinfo_t *);
@@ -45,6 +46,7 @@ static uint32_t smb2_qif_pipe_rem(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_compr(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_opens(smb_request_t *, smb_queryinfo_t *);
static uint32_t smb2_qif_tags(smb_request_t *, smb_queryinfo_t *);
+static uint32_t smb2_qif_id_info(smb_request_t *, smb_queryinfo_t *);
uint32_t
@@ -80,6 +82,7 @@ smb2_qinfo_file(smb_request_t *sr, smb_queryinfo_t *qi)
break;
case FileNameInformation:
+ case FileNormalizedNameInformation:
getname = B_TRUE;
break;
@@ -99,6 +102,11 @@ smb2_qinfo_file(smb_request_t *sr, smb_queryinfo_t *qi)
case FileNetworkOpenInformation:
mask = SMB_AT_BASIC | SMB_AT_STANDARD;
+ break;
+
+ case FileIdInformation:
+ mask = SMB_AT_NODEID;
+ break;
default:
break;
@@ -141,6 +149,9 @@ smb2_qinfo_file(smb_request_t *sr, smb_queryinfo_t *qi)
case FileNameInformation:
status = smb2_qif_name(sr, qi);
break;
+ case FileNormalizedNameInformation:
+ status = smb2_qif_normalized_name(sr, qi);
+ break;
case FilePositionInformation:
status = smb2_qif_position(sr, qi);
break;
@@ -180,6 +191,9 @@ smb2_qinfo_file(smb_request_t *sr, smb_queryinfo_t *qi)
case FileAttributeTagInformation:
status = smb2_qif_tags(sr, qi);
break;
+ case FileIdInformation:
+ status = smb2_qif_id_info(sr, qi);
+ break;
default:
status = NT_STATUS_INVALID_INFO_CLASS;
break;
@@ -392,15 +406,51 @@ smb2_qif_access(smb_request_t *sr, smb_queryinfo_t *qi)
static uint32_t
smb2_qif_name(smb_request_t *sr, smb_queryinfo_t *qi)
{
+ char *name;
+ uint32_t nlen;
int rc;
- ASSERT(qi->qi_namelen > 0);
+ /* SMB2 leaves off the leading / */
+ nlen = qi->qi_namelen;
+ name = qi->qi_name;
+ if (qi->qi_name[0] == '\\') {
+ name++;
+ nlen -= 2;
+ }
rc = smb_mbc_encodef(
&sr->raw_data, "llU",
0, /* FileIndex (l) */
- qi->qi_namelen, /* l */
- qi->qi_name); /* U */
+ nlen, /* l */
+ name); /* U */
+ if (rc != 0)
+ return (NT_STATUS_BUFFER_OVERFLOW);
+
+ return (0);
+}
+
+/*
+ * FileNormalizedNameInformation
+ */
+static uint32_t
+smb2_qif_normalized_name(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ char *name;
+ uint32_t nlen;
+ int rc;
+
+ /* SMB2 leaves off the leading / */
+ nlen = qi->qi_namelen;
+ name = qi->qi_name;
+ if (qi->qi_name[0] == '\\') {
+ name++;
+ nlen -= 2;
+ }
+
+ rc = smb_mbc_encodef(
+ &sr->raw_data, "lU",
+ nlen, /* l */
+ name); /* U */
if (rc != 0)
return (NT_STATUS_BUFFER_OVERFLOW);
@@ -645,3 +695,46 @@ smb2_qif_tags(smb_request_t *sr, smb_queryinfo_t *qi)
return (0);
}
+
+/*
+ * FileIdInformation
+ *
+ * Returns a A FILE_ID_INFORMATION
+ * VolumeSerialNumber (8 bytes)
+ * FileId (16 bytes)
+ *
+ * Take the volume serial from the share root,
+ * and compose the FileId from the nodeid and fsid
+ * of the file (in case we crossed mounts)
+ */
+static uint32_t
+smb2_qif_id_info(smb_request_t *sr, smb_queryinfo_t *qi)
+{
+ smb_attr_t *sa = &qi->qi_attr;
+ smb_ofile_t *of = sr->fid_ofile;
+ smb_tree_t *tree = sr->tid_tree;
+ vfs_t *f_vfs; // file
+ vfs_t *s_vfs; // share
+ uint64_t nodeid;
+ int rc;
+
+ ASSERT((sa->sa_mask & SMB_AT_NODEID) != 0);
+ if (of->f_ftype != SMB_FTYPE_DISK)
+ return (NT_STATUS_INVALID_INFO_CLASS);
+
+ s_vfs = SMB_NODE_VFS(tree->t_snode);
+ f_vfs = SMB_NODE_VFS(of->f_node);
+ nodeid = (uint64_t)sa->sa_vattr.va_nodeid;
+
+ rc = smb_mbc_encodef(
+ &sr->raw_data, "llqll",
+ s_vfs->vfs_fsid.val[0], /* l */
+ s_vfs->vfs_fsid.val[1], /* l */
+ nodeid, /* q */
+ f_vfs->vfs_fsid.val[0], /* l */
+ f_vfs->vfs_fsid.val[1]); /* l */
+ if (rc != 0)
+ return (NT_STATUS_INFO_LENGTH_MISMATCH);
+
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_pathname.c b/usr/src/uts/common/fs/smbsrv/smb_pathname.c
index 3dd99c9a61..5edbecb733 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_pathname.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_pathname.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
- * Copyright 2021 RackTop Systems, Inc.
+ * Copyright 2022 RackTop Systems, Inc.
*/
#include <smbsrv/smb_kproto.h>
@@ -804,47 +804,99 @@ smb_pathname_init(smb_request_t *sr, smb_pathname_t *pn, char *path)
/* parse pn->pn_path into its constituent parts */
pname = pn->pn_path;
- fname = strrchr(pn->pn_path, '\\');
- if (fname) {
+ /*
+ * Split the string between the directory and filename.
+ * Either part may be empty.
+ *
+ * Fill in pn->pn_pname (the path name)
+ */
+ fname = strrchr(pname, '\\');
+ if (fname != NULL) {
if (fname == pname) {
+ /*
+ * Last '/' is at start of string.
+ * No directory part (dir is root)
+ */
pn->pn_pname = NULL;
} else {
+ /*
+ * Directory part ends at the last '/'
+ * Temporarily truncate and copy
+ */
*fname = '\0';
pn->pn_pname =
smb_pathname_strdup(sr, pname);
*fname = '\\';
}
++fname;
+ /* fname is just after the '/' */
} else {
+ /*
+ * No '/' at all in the string.
+ * It's all filename
+ */
fname = pname;
pn->pn_pname = NULL;
}
- if (fname[0] == '\0') {
- pn->pn_fname = NULL;
+ /*
+ * Find end of the filename part of the string,
+ * which may be the null terminator, or may be
+ * the start of the optional :sname suffix.
+ */
+ sname = strchr(fname, ':');
+ if (sname == NULL) {
+ /*
+ * No :sname suffix. We're done.
+ */
+ pn->pn_fname = smb_pathname_strdup(sr, fname);
return;
}
- if (!smb_is_stream_name(fname)) {
+ /*
+ * We have a stream name, and maybe a stream type.
+ * Can't use smb_is_stream_name(fname) here because
+ * we need to allow sname="::$DATA"
+ */
+ if (sname == fname) {
+ /*
+ * The ":sname" part is at the start of
+ * the file name, which means that the
+ * file name is "" and this pathname
+ * refers to a stream on the directory.
+ */
+ pn->pn_fname = NULL;
+ } else {
+ /*
+ * The filename part ends at the ':'
+ * Temporarily truncate and copy
+ */
+ *sname = '\0';
pn->pn_fname = smb_pathname_strdup(sr, fname);
- return;
+ *sname = ':';
}
/*
- * find sname and stype in fname.
- * sname can't be NULL smb_is_stream_name checks this
+ * Special case "::$DATA" which "points to"
+ * the "unnamed" stream (the file itself).
+ * Basically ignore the "::$DATA"
*/
- sname = strchr(fname, ':');
- if (sname == fname)
- fname = NULL;
- else {
+ if (strcasecmp(sname, "::$DATA") == 0) {
+ ASSERT(sname >= pname &&
+ sname < (pname + strlen(pname)));
*sname = '\0';
- pn->pn_fname =
- smb_pathname_strdup(sr, fname);
- *sname = ':';
+ return;
}
+ /*
+ * sname points to ":sname:stype" in pn_path
+ * If ":stype" is missing, add it, then set
+ * pn_stype to point after the 2nd ':'
+ *
+ * Caller knows pn_stype is NOT allocated.
+ * Allocations here are free'd via smb_srm_fini
+ */
pn->pn_sname = smb_pathname_strdup(sr, sname);
pn->pn_stype = strchr(pn->pn_sname + 1, ':');
if (pn->pn_stype) {
@@ -1065,6 +1117,9 @@ smb_validate_dirname(smb_request_t *sr, smb_pathname_t *pn)
}
}
+ if (pn->pn_sname)
+ return (smb_validate_stream_name(sr, pn));
+
return (B_TRUE);
}
@@ -1230,14 +1285,6 @@ smb_validate_stream_name(smb_request_t *sr, smb_pathname_t *pn)
ASSERT(pn);
ASSERT(pn->pn_sname);
- if ((!(pn->pn_sname)) ||
- ((pn->pn_pname) && !(pn->pn_fname))) {
- smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
- ERRDOS, ERROR_INVALID_NAME);
- return (B_FALSE);
- }
-
-
if (pn->pn_stype != NULL) {
for (i = 0; i < sizeof (strmtype) / sizeof (strmtype[0]); ++i) {
if (strcasecmp(pn->pn_stype, strmtype[i]) == 0)
diff --git a/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c b/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c
index 6baef805aa..6f903cb2e0 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_query_fileinfo.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2022 Tintri by DDN, Inc. All rights reserved.
+ * Copyright 2022 RackTop Systems, Inc.
*/
#include <smbsrv/smb_kproto.h>
@@ -88,8 +89,6 @@ static boolean_t smb_query_pipe_valid_infolev(smb_request_t *, uint16_t);
static int smb_query_encode_response(smb_request_t *, smb_xa_t *,
uint16_t, smb_queryinfo_t *);
-static boolean_t smb_stream_fits(smb_request_t *, mbuf_chain_t *,
- char *, uint32_t);
static int smb_query_pathname(smb_request_t *, smb_node_t *, boolean_t,
smb_queryinfo_t *);
@@ -599,10 +598,9 @@ smb_query_encode_response(smb_request_t *sr, smb_xa_t *xa,
* those streams but there should not be an entry for the unnamed
* stream.
*
- * Note that the stream name lengths exclude the null terminator but
- * the field lengths (i.e. next offset calculations) need to include
- * the null terminator and be padded to a multiple of 8 bytes. The
- * last entry does not seem to need any padding.
+ * Note that the stream names are NOT null terminated, and the lengths
+ * reflect that. Entries are aligned on 8-byte boundaries with padding
+ * and the "next offset" tells where the next entry begins.
*
* If an error is encountered when trying to read the stream entries
* (smb_odir_read_streaminfo) it is treated as if there are no [more]
@@ -619,19 +617,14 @@ smb_query_stream_info(smb_request_t *sr, mbuf_chain_t *mbc,
smb_queryinfo_t *qinfo)
{
char *stream_name;
- uint32_t next_offset;
uint32_t stream_nlen;
- uint32_t pad;
- u_offset_t datasz, allocsz;
- smb_streaminfo_t *sinfo, *sinfo_next;
+ smb_streaminfo_t *sinfo;
int rc = 0;
- boolean_t done = B_FALSE;
- boolean_t eos = B_FALSE;
+ int prev_ent_off;
+ int cur_ent_off;
smb_odir_t *od = NULL;
uint32_t status = 0;
-
smb_node_t *fnode = qinfo->qi_node;
- smb_attr_t *attr = &qinfo->qi_attr;
ASSERT(fnode);
if (SMB_IS_STREAM(fnode)) {
@@ -641,10 +634,50 @@ smb_query_stream_info(smb_request_t *sr, mbuf_chain_t *mbc,
ASSERT(fnode->n_magic == SMB_NODE_MAGIC);
ASSERT(fnode->n_state != SMB_NODE_STATE_DESTROYING);
- sinfo = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP);
- sinfo_next = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP);
- datasz = attr->sa_vattr.va_size;
- allocsz = attr->sa_allocsz;
+ sinfo = smb_srm_alloc(sr, sizeof (smb_streaminfo_t));
+
+ /*
+ * Keep track of where the last entry starts so we can
+ * come back and poke the NextEntryOffset field. Also,
+ * after enumeration finishes, the caller uses this to
+ * poke the last entry again with zero to mark it as
+ * the end of the enumeration.
+ */
+ ASSERT(mbc->chain_offset == 0);
+ cur_ent_off = prev_ent_off = 0;
+
+ /*
+ * If the unnamed stream is a file, encode an entry for
+ * the unnamed stream. Note we can't generally get the
+ * size or allocsize from qi_attr because those may be
+ * from one of the named streams. Get the sizes.
+ */
+ if (smb_node_is_file(fnode)) {
+ smb_attr_t attr;
+ uint64_t datasz, allocsz;
+
+ bzero(&attr, sizeof (attr));
+ attr.sa_mask = SMB_AT_SIZE | SMB_AT_ALLOCSZ;
+ rc = smb_node_getattr(sr, fnode, sr->user_cr, NULL, &attr);
+ if (rc != 0) {
+ status = smb_errno2status(rc);
+ goto out;
+ }
+
+ stream_name = "::$DATA";
+ stream_nlen = smb_ascii_or_unicode_strlen(sr, stream_name);
+ datasz = attr.sa_vattr.va_size;
+ allocsz = attr.sa_allocsz;
+ /* Leave NextEntryOffset=0, set later. */
+ rc = smb_mbc_encodef(mbc, "%llqq#u", sr,
+ 0, stream_nlen, datasz, allocsz,
+ stream_nlen, stream_name);
+ if (rc != 0) {
+ /* Ran out of room. */
+ status = NT_STATUS_BUFFER_OVERFLOW;
+ goto out;
+ }
+ }
status = smb_odir_openat(sr, fnode, &od, B_TRUE);
switch (status) {
@@ -655,88 +688,61 @@ smb_query_stream_info(smb_request_t *sr, mbuf_chain_t *mbc,
case NT_STATUS_NOT_SUPPORTED:
/* No streams. */
status = 0;
- done = B_TRUE;
- break;
+ goto out;
default:
- return (status);
+ goto out;
}
- if (!done) {
+ for (;;) {
+ boolean_t eos = B_FALSE;
rc = smb_odir_read_streaminfo(sr, od, sinfo, &eos);
- if ((rc != 0) || (eos))
- done = B_TRUE;
- }
-
- /* If not a directory, encode an entry for the unnamed stream. */
- if (qinfo->qi_isdir == 0) {
- stream_name = "::$DATA";
- stream_nlen = smb_ascii_or_unicode_strlen(sr, stream_name);
- next_offset = SMB_STREAM_ENCODE_FIXED_SZ + stream_nlen +
- smb_ascii_or_unicode_null_len(sr);
-
- /* Can unnamed stream fit in response buffer? */
- if (MBC_ROOM_FOR(mbc, next_offset) == 0) {
- done = B_TRUE;
- status = NT_STATUS_BUFFER_OVERFLOW;
- } else {
- /* Can first named stream fit in rsp buffer? */
- if (!done && !smb_stream_fits(sr, mbc, sinfo->si_name,
- next_offset)) {
- done = B_TRUE;
- status = NT_STATUS_BUFFER_OVERFLOW;
- }
-
- if (done)
- next_offset = 0;
-
- (void) smb_mbc_encodef(mbc, "%llqqu", sr,
- next_offset, stream_nlen, datasz, allocsz,
- stream_name);
- }
- }
-
- /*
- * If there is no next entry, or there is not enough space in
- * the response buffer for the next entry, the next_offset and
- * padding are 0.
- */
- while (!done) {
- stream_nlen = smb_ascii_or_unicode_strlen(sr, sinfo->si_name);
- sinfo_next->si_name[0] = 0;
-
- rc = smb_odir_read_streaminfo(sr, od, sinfo_next, &eos);
if ((rc != 0) || (eos)) {
- done = B_TRUE;
- } else {
- next_offset = SMB_STREAM_ENCODE_FIXED_SZ +
- stream_nlen +
- smb_ascii_or_unicode_null_len(sr);
- pad = smb_pad_align(next_offset, 8);
- next_offset += pad;
-
- /* Can next named stream fit in response buffer? */
- if (!smb_stream_fits(sr, mbc, sinfo_next->si_name,
- next_offset)) {
- done = B_TRUE;
- status = NT_STATUS_BUFFER_OVERFLOW;
- }
+ status = 0;
+ break; // normal termination
}
- if (done) {
- next_offset = 0;
- pad = 0;
+ /*
+ * We have a directory entry to process.
+ * Align before encoding.
+ */
+ rc = smb_mbc_put_align(mbc, 8);
+ if (rc != 0) {
+ status = NT_STATUS_BUFFER_OVERFLOW;
+ break;
}
+ cur_ent_off = mbc->chain_offset;
- (void) smb_mbc_encodef(mbc, "%llqqu#.",
- sr, next_offset, stream_nlen,
+ /*
+ * Encode it.
+ */
+ stream_name = sinfo->si_name;
+ stream_nlen = smb_ascii_or_unicode_strlen(sr, stream_name);
+ /* Leave NextEntryOffset=0, set later. */
+ rc = smb_mbc_encodef(mbc, "%llqq#u", sr,
+ 0, stream_nlen,
sinfo->si_size, sinfo->si_alloc_size,
- sinfo->si_name, pad);
+ stream_nlen, stream_name);
+ if (rc != 0) {
+ status = NT_STATUS_BUFFER_OVERFLOW;
+ break;
+ }
- (void) memcpy(sinfo, sinfo_next, sizeof (smb_streaminfo_t));
+ /*
+ * We succeeded encoding the current entry, so
+ * fill in NextEntryOffset in the previous entry.
+ * When listing streams on a file, we're always at
+ * the 2nd or later entry due to "::$DATA" above.
+ * However, when listing streams on a directory,
+ * there might not be previous entry.
+ */
+ if (cur_ent_off > 0) {
+ (void) smb_mbc_poke(mbc, prev_ent_off, "l",
+ cur_ent_off - prev_ent_off);
+ }
+ prev_ent_off = cur_ent_off;
}
- kmem_free(sinfo, sizeof (smb_streaminfo_t));
- kmem_free(sinfo_next, sizeof (smb_streaminfo_t));
+out:
if (od) {
smb_odir_close(od);
smb_odir_release(od);
@@ -746,33 +752,6 @@ smb_query_stream_info(smb_request_t *sr, mbuf_chain_t *mbc,
}
/*
- * smb_stream_fits
- *
- * Check if the named stream entry can fit in the response buffer.
- *
- * Required space =
- * offset (size of current entry)
- * + SMB_STREAM_ENCODE_FIXED_SIZE
- * + length of encoded stream name
- * + length of null terminator
- * + alignment padding
- */
-static boolean_t
-smb_stream_fits(smb_request_t *sr, mbuf_chain_t *mbc,
- char *name, uint32_t offset)
-{
- uint32_t len, pad;
-
- len = SMB_STREAM_ENCODE_FIXED_SZ +
- smb_ascii_or_unicode_strlen(sr, name) +
- smb_ascii_or_unicode_null_len(sr);
- pad = smb_pad_align(len, 8);
- len += pad;
-
- return (MBC_ROOM_FOR(mbc, offset + len) != 0);
-}
-
-/*
* smb_query_fileinfo
*
* Populate smb_queryinfo_t structure for SMB_FTYPE_DISK