#include <smbsrv/smb2_kproto.h>
#include <smbsrv/smb2_aapl.h>
#define FileIdMacOsDirectoryInformation (FileMaximumInformation + 10)
typedef struct smb2_find_args {
uint32_t fa_maxdata;
uint8_t fa_infoclass;
uint8_t fa_fflags;
uint16_t fa_maxcount;
uint16_t fa_eos;
uint16_t fa_fixedsize;
uint32_t fa_lastkey;
int fa_last_entry;
smb_fileinfo_t fa_fi;
smb_macinfo_t fa_mi;
} smb2_find_args_t;
static uint32_t smb2_find_entries(smb_request_t *,
smb_odir_t *, smb2_find_args_t *);
static uint32_t smb2_find_mbc_encode(smb_request_t *, smb2_find_args_t *);
uint16_t smb2_find_max = 1024;
smb_sdrc_t
smb2_query_dir(smb_request_t *sr)
{
smb2_find_args_t args;
smb_odir_resume_t odir_resume;
smb_ofile_t *of = NULL;
smb_odir_t *od = NULL;
char *pattern = NULL;
uint16_t StructSize;
uint32_t FileIndex;
uint16_t NameOffset;
uint16_t NameLength;
smb2fid_t smb2fid;
uint16_t sattr = SMB_SEARCH_ATTRIBUTES;
uint16_t DataOff;
uint32_t DataLen;
uint32_t status;
int skip, rc = 0;
bzero(&args, sizeof (args));
bzero(&odir_resume, sizeof (odir_resume));
rc = smb_mbc_decodef(
&sr->smb_data, "wbblqqwwl",
&StructSize,
&args.fa_infoclass,
&args.fa_fflags,
&FileIndex,
&smb2fid.persistent,
&smb2fid.temporal,
&NameOffset,
&NameLength,
&args.fa_maxdata);
if (rc || StructSize != 33)
return (SDRC_ERROR);
status = smb2sr_lookup_fid(sr, &smb2fid);
of = sr->fid_ofile;
DTRACE_SMB2_START(op__QueryDirectory, smb_request_t *, sr);
if (status)
goto errout;
if (NameLength >= (2 * MAXNAMELEN)) {
status = NT_STATUS_OBJECT_PATH_INVALID;
goto errout;
}
if (NameLength != 0) {
skip = (sr->smb2_cmd_hdr + NameOffset) -
sr->smb_data.chain_offset;
if (skip < 0) {
status = NT_STATUS_OBJECT_PATH_INVALID;
goto errout;
}
if (skip > 0)
(void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
rc = smb_mbc_decodef(&sr->smb_data, "%#U", sr,
NameLength, &pattern);
if (rc || pattern == NULL) {
status = NT_STATUS_OBJECT_PATH_INVALID;
goto errout;
}
} else
pattern = "*";
if (args.fa_maxdata > smb2_max_trans)
args.fa_maxdata = smb2_max_trans;
sr->raw_data.max_bytes = args.fa_maxdata;
switch (args.fa_infoclass) {
case FileDirectoryInformation:
args.fa_fixedsize = 64;
break;
case FileFullDirectoryInformation:
args.fa_fixedsize = 68;
break;
case FileBothDirectoryInformation:
args.fa_fixedsize = 94;
break;
case FileNamesInformation:
args.fa_fixedsize = 12;
break;
case FileIdBothDirectoryInformation:
args.fa_fixedsize = 96;
break;
case FileIdFullDirectoryInformation:
args.fa_fixedsize = 84;
break;
default:
status = NT_STATUS_INVALID_INFO_CLASS;
goto errout;
}
if ((sr->session->s_flags & SMB_SSN_AAPL_READDIR) != 0 &&
args.fa_infoclass == FileIdBothDirectoryInformation) {
args.fa_infoclass = FileIdMacOsDirectoryInformation;
args.fa_fixedsize = 96;
}
args.fa_maxcount = args.fa_maxdata / (args.fa_fixedsize + 4);
if (args.fa_maxcount == 0)
args.fa_maxcount = 1;
if ((smb2_find_max != 0) && (args.fa_maxcount > smb2_find_max))
args.fa_maxcount = smb2_find_max;
if (args.fa_fflags & SMB2_QDIR_FLAG_SINGLE)
args.fa_maxcount = 1;
mutex_enter(&of->f_mutex);
if ((od = of->f_odir) == NULL) {
status = smb_odir_openfh(sr, pattern, sattr, &od);
of->f_odir = od;
}
mutex_exit(&of->f_mutex);
if (od == NULL) {
if (status == 0)
status = NT_STATUS_INTERNAL_ERROR;
goto errout;
}
if (args.fa_fflags & SMB2_QDIR_FLAG_REOPEN) {
smb_odir_reopen(od, pattern, sattr);
}
if (args.fa_fflags & SMB2_QDIR_FLAG_RESTART) {
odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
odir_resume.or_cookie = 0;
} else if (args.fa_fflags & SMB2_QDIR_FLAG_INDEX) {
odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
odir_resume.or_cookie = FileIndex;
} else {
odir_resume.or_type = SMB_ODIR_RESUME_CONT;
}
smb_odir_resume_at(od, &odir_resume);
of->f_seek_pos = od->d_offset;
status = smb2_find_entries(sr, od, &args);
of->f_seek_pos = od->d_offset;
if ((args.fa_fflags & SMB2_QDIR_FLAG_SINGLE) &&
status == NT_STATUS_NO_MORE_FILES) {
status = NT_STATUS_NO_SUCH_FILE;
}
errout:
sr->smb2_status = status;
DTRACE_SMB2_DONE(op__QueryDirectory, smb_request_t *, sr);
if (status != 0) {
smb2sr_put_error(sr, status);
return (SDRC_SUCCESS);
}
StructSize = 9;
DataOff = SMB2_HDR_SIZE + 8;
DataLen = MBC_LENGTH(&sr->raw_data);
ASSERT(DataLen != 0);
rc = smb_mbc_encodef(
&sr->reply, "wwlC",
StructSize,
DataOff,
DataLen,
&sr->raw_data);
if (rc)
sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
return (SDRC_SUCCESS);
}
static uint32_t
smb2_find_entries(smb_request_t *sr, smb_odir_t *od, smb2_find_args_t *args)
{
smb_odir_resume_t odir_resume;
char *tbuf = NULL;
size_t tbuflen = 0;
uint16_t count;
uint16_t minsize;
uint32_t status = 0;
int rc = -1;
minsize = args->fa_fixedsize + 2;
if (args->fa_infoclass == FileIdMacOsDirectoryInformation) {
tbuflen = 2 * MAXNAMELEN;
tbuf = kmem_alloc(tbuflen, KM_SLEEP);
}
count = 0;
while (count < args->fa_maxcount) {
if (!MBC_ROOM_FOR(&sr->raw_data, minsize)) {
status = NT_STATUS_BUFFER_OVERFLOW;
break;
}
rc = smb_odir_read_fileinfo(sr, od,
&args->fa_fi, &args->fa_eos);
if (rc == ENOENT) {
status = NT_STATUS_NO_MORE_FILES;
break;
}
if (rc != 0) {
status = smb_errno2status(rc);
break;
}
if (args->fa_eos != 0) {
status = NT_STATUS_NO_MORE_FILES;
break;
}
if (args->fa_infoclass == FileIdMacOsDirectoryInformation)
(void) smb2_aapl_get_macinfo(sr, od,
&args->fa_fi, &args->fa_mi, tbuf, tbuflen);
if (smb2_aapl_use_file_ids == 0 &&
(sr->session->s_flags & SMB_SSN_AAPL_CCEXT) != 0)
args->fa_fi.fi_nodeid = 0;
status = smb2_find_mbc_encode(sr, args);
if (status) {
bzero(&odir_resume, sizeof (odir_resume));
odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
odir_resume.or_cookie = args->fa_lastkey;
smb_odir_resume_at(od, &odir_resume);
break;
}
args->fa_lastkey = args->fa_fi.fi_cookie;
++count;
}
if (count == 0) {
ASSERT(status != 0);
} else {
(void) smb_mbc_poke(&sr->raw_data,
args->fa_last_entry, "l", 0);
status = 0;
}
if (tbuf != NULL)
kmem_free(tbuf, tbuflen);
return (status);
}
static uint32_t
smb2_find_mbc_encode(smb_request_t *sr, smb2_find_args_t *args)
{
smb_fileinfo_t *fileinfo = &args->fa_fi;
smb_macinfo_t *macinfo = &args->fa_mi;
uint8_t buf83[26];
smb_msgbuf_t mb;
int namelen;
int shortlen = 0;
int rc, starting_offset;
uint32_t next_entry_offset;
uint32_t mb_flags = SMB_MSGBUF_UNICODE | SMB_MSGBUF_NOTERM;
uint32_t resume_key;
namelen = smb_wcequiv_strlen(fileinfo->fi_name);
if (namelen == -1)
return (NT_STATUS_INTERNAL_ERROR);
starting_offset = sr->raw_data.chain_offset;
resume_key = fileinfo->fi_cookie;
switch (args->fa_infoclass) {
case FileDirectoryInformation:
rc = smb_mbc_encodef(
&sr->raw_data, "llTTTTqqll",
0,
resume_key,
&fileinfo->fi_crtime,
&fileinfo->fi_atime,
&fileinfo->fi_mtime,
&fileinfo->fi_ctime,
fileinfo->fi_size,
fileinfo->fi_alloc_size,
fileinfo->fi_dosattr,
namelen);
break;
case FileFullDirectoryInformation:
rc = smb_mbc_encodef(
&sr->raw_data, "llTTTTqqlll",
0,
resume_key,
&fileinfo->fi_crtime,
&fileinfo->fi_atime,
&fileinfo->fi_mtime,
&fileinfo->fi_ctime,
fileinfo->fi_size,
fileinfo->fi_alloc_size,
fileinfo->fi_dosattr,
namelen,
0L);
break;
case FileIdFullDirectoryInformation:
rc = smb_mbc_encodef(
&sr->raw_data, "llTTTTqqllllq",
0,
resume_key,
&fileinfo->fi_crtime,
&fileinfo->fi_atime,
&fileinfo->fi_mtime,
&fileinfo->fi_ctime,
fileinfo->fi_size,
fileinfo->fi_alloc_size,
fileinfo->fi_dosattr,
namelen,
0L,
0L,
fileinfo->fi_nodeid);
break;
case FileBothDirectoryInformation:
bzero(buf83, sizeof (buf83));
smb_msgbuf_init(&mb, buf83, sizeof (buf83), mb_flags);
shortlen = smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname);
if (shortlen < 0) {
shortlen = 0;
bzero(buf83, sizeof (buf83));
}
rc = smb_mbc_encodef(
&sr->raw_data, "llTTTTqqlllb.24c",
0,
resume_key,
&fileinfo->fi_crtime,
&fileinfo->fi_atime,
&fileinfo->fi_mtime,
&fileinfo->fi_ctime,
fileinfo->fi_size,
fileinfo->fi_alloc_size,
fileinfo->fi_dosattr,
namelen,
0L,
shortlen,
buf83);
smb_msgbuf_term(&mb);
break;
case FileIdBothDirectoryInformation:
bzero(buf83, sizeof (buf83));
smb_msgbuf_init(&mb, buf83, sizeof (buf83), mb_flags);
shortlen = smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname);
if (shortlen < 0) {
shortlen = 0;
bzero(buf83, sizeof (buf83));
}
rc = smb_mbc_encodef(
&sr->raw_data, "llTTTTqqlllb.24c..q",
0,
resume_key,
&fileinfo->fi_crtime,
&fileinfo->fi_atime,
&fileinfo->fi_mtime,
&fileinfo->fi_ctime,
fileinfo->fi_size,
fileinfo->fi_alloc_size,
fileinfo->fi_dosattr,
namelen,
0L,
shortlen,
buf83,
fileinfo->fi_nodeid);
smb_msgbuf_term(&mb);
break;
case FileIdMacOsDirectoryInformation:
rc = smb_mbc_encodef(
&sr->raw_data, "llTTTTqqll",
0,
resume_key,
&fileinfo->fi_crtime,
&fileinfo->fi_atime,
&fileinfo->fi_mtime,
&fileinfo->fi_ctime,
fileinfo->fi_size,
fileinfo->fi_alloc_size,
fileinfo->fi_dosattr,
namelen);
if (rc != 0)
break;
rc = smb_mbc_encodef(
&sr->raw_data, "l..q16cwq",
macinfo->mi_maxaccess,
macinfo->mi_rforksize,
macinfo->mi_finderinfo,
macinfo->mi_unixmode,
fileinfo->fi_nodeid);
break;
case FileNamesInformation:
rc = smb_mbc_encodef(
&sr->raw_data, "lll",
0,
resume_key,
namelen);
break;
default:
return (NT_STATUS_INVALID_INFO_CLASS);
}
if (rc)
return (NT_STATUS_BUFFER_OVERFLOW);
rc = smb_mbc_encodef(
&sr->raw_data, "U",
fileinfo->fi_name);
if (rc)
return (NT_STATUS_BUFFER_OVERFLOW);
(void) smb_mbc_put_align(&sr->raw_data, 8);
next_entry_offset = sr->raw_data.chain_offset - starting_offset;
(void) smb_mbc_poke(&sr->raw_data, starting_offset, "l",
next_entry_offset);
args->fa_last_entry = starting_offset;
return (0);
}