#include "includes.h"
extern file_info def_finfo;
static size_t interpret_long_filename(struct cli_state *cli, int level,char *p,file_info *finfo,
uint32 *p_resume_key, DATA_BLOB *p_last_name_raw, uint32 *p_last_name_raw_len)
{
file_info finfo2;
int len;
char *base = p;
if (!finfo) {
finfo = &finfo2;
}
if (p_resume_key) {
*p_resume_key = 0;
}
memcpy(finfo,&def_finfo,sizeof(*finfo));
finfo->cli = cli;
switch (level) {
case 1:
finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
finfo->size = IVAL(p,16);
finfo->mode = CVAL(p,24);
len = CVAL(p, 26);
p += 27;
p += clistr_align_in(cli, p, 0);
p += clistr_pull(cli, finfo->name, p,
sizeof(finfo->name),
len+2,
STR_TERMINATE);
return PTR_DIFF(p, base);
case 2:
finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
finfo->size = IVAL(p,16);
finfo->mode = CVAL(p,24);
len = CVAL(p, 30);
p += 31;
p += clistr_pull(cli, finfo->name, p,
sizeof(finfo->name),
len,
STR_NOALIGN);
return PTR_DIFF(p, base) + 1;
case 260:
{
size_t namelen, slen;
p += 4;
if (p_resume_key) {
*p_resume_key = IVAL(p,0);
}
p += 4;
p += 8;
finfo->atime_ts = interpret_long_date(p);
p += 8;
finfo->mtime_ts = interpret_long_date(p);
p += 8;
finfo->ctime_ts = interpret_long_date(p);
p += 8;
finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
p += 8;
p += 8;
finfo->mode = CVAL(p,0);
p += 4;
namelen = IVAL(p,0);
p += 4;
p += 4;
slen = SVAL(p, 0);
p += 2;
{
int flags = 0;
if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
clistr_pull(cli, finfo->short_name, p,
sizeof(finfo->short_name),
slen, flags);
}
p += 24;
clistr_pull(cli, finfo->name, p,
sizeof(finfo->name),
namelen, 0);
if (p_last_name_raw && p_last_name_raw_len) {
if (namelen + 2 > p_last_name_raw->length) {
memset(p_last_name_raw->data, '\0', sizeof(p_last_name_raw->length));
*p_last_name_raw_len = 0;
} else {
memcpy(p_last_name_raw->data, p, namelen);
SSVAL(p_last_name_raw->data, namelen, 0);
*p_last_name_raw_len = namelen + 2;
}
}
return (size_t)IVAL(base, 0);
}
}
DEBUG(1,("Unknown long filename format %d\n",level));
return (size_t)IVAL(base,0);
}
int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
void (*fn)(const char *, file_info *, const char *, void *), void *state)
{
#if 1
int max_matches = 1366;
#else
int max_matches = 512;
#endif
int info_level;
char *p, *p2;
pstring mask;
file_info finfo;
int i;
char *dirlist = NULL;
int dirlist_len = 0;
int total_received = -1;
BOOL First = True;
int ff_searchcount=0;
int ff_eos=0;
int ff_dir_handle=0;
int loop_count = 0;
char *rparam=NULL, *rdata=NULL;
unsigned int param_len, data_len;
uint16 setup;
pstring param;
const char *mnt;
uint32 resume_key = 0;
uint32 last_name_raw_len = 0;
DATA_BLOB last_name_raw = data_blob(NULL, 2*sizeof(pstring));
info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
pstrcpy(mask,Mask);
while (ff_eos == 0) {
loop_count++;
if (loop_count > 200) {
DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
break;
}
if (First) {
setup = TRANSACT2_FINDFIRST;
SSVAL(param,0,attribute);
SSVAL(param,2,max_matches);
SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));
SSVAL(param,6,info_level);
SIVAL(param,8,0);
p = param+12;
p += clistr_push(cli, param+12, mask, sizeof(param)-12,
STR_TERMINATE);
} else {
setup = TRANSACT2_FINDNEXT;
SSVAL(param,0,ff_dir_handle);
SSVAL(param,2,max_matches);
SSVAL(param,4,info_level);
SIVAL(param,6,resume_key);
SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));
p = param+12;
if (last_name_raw_len && (last_name_raw_len < (sizeof(param)-12))) {
memcpy(p, last_name_raw.data, last_name_raw_len);
p += last_name_raw_len;
} else {
p += clistr_push(cli, param+12, mask, sizeof(param)-12, STR_TERMINATE);
}
}
param_len = PTR_DIFF(p, param);
if (!cli_send_trans(cli, SMBtrans2,
NULL,
-1, 0,
&setup, 1, 0,
param, param_len, 10,
NULL, 0,
#if 0
MIN(16384,cli->max_xmit)
#else
cli->max_xmit
#endif
)) {
break;
}
if (!cli_receive_trans(cli, SMBtrans2,
&rparam, ¶m_len,
&rdata, &data_len) &&
cli_is_dos_error(cli)) {
uint8 eclass;
uint32 ecode;
SAFE_FREE(rdata);
SAFE_FREE(rparam);
cli_dos_error(cli, &eclass, &ecode);
if (eclass != ERRSRV || ecode != ERRerror)
break;
smb_msleep(100);
continue;
}
if (cli_is_error(cli) || !rdata || !rparam) {
SAFE_FREE(rdata);
SAFE_FREE(rparam);
break;
}
if (total_received == -1)
total_received = 0;
p = rparam;
if (First) {
ff_dir_handle = SVAL(p,0);
ff_searchcount = SVAL(p,2);
ff_eos = SVAL(p,4);
} else {
ff_searchcount = SVAL(p,0);
ff_eos = SVAL(p,2);
}
if (ff_searchcount == 0) {
SAFE_FREE(rdata);
SAFE_FREE(rparam);
break;
}
p = rdata;
for (p2=p,i=0;i<ff_searchcount;i++) {
if ((info_level == 260) && (i == ff_searchcount-1)) {
SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
}
p2 += interpret_long_filename(cli,info_level,p2,&finfo,
&resume_key,&last_name_raw,&last_name_raw_len);
if (!First && *mask && strcsequal(finfo.name, mask)) {
DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
finfo.name));
ff_eos = 1;
break;
}
}
if (ff_searchcount > 0) {
pstrcpy(mask, finfo.name);
} else {
pstrcpy(mask,"");
}
dirlist = (char *)SMB_REALLOC(dirlist,dirlist_len + data_len);
if (!dirlist) {
DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
SAFE_FREE(rdata);
SAFE_FREE(rparam);
break;
}
memcpy(dirlist+dirlist_len,p,data_len);
dirlist_len += data_len;
total_received += ff_searchcount;
SAFE_FREE(rdata);
SAFE_FREE(rparam);
DEBUG(3,("received %d entries (eos=%d)\n",
ff_searchcount,ff_eos));
if (ff_searchcount > 0)
loop_count = 0;
First = False;
}
mnt = cli_cm_get_mntpoint( cli );
if (cli_is_error(cli)) {
total_received = -1;
} else {
for (p=dirlist,i=0;i<total_received;i++) {
p += interpret_long_filename(cli, info_level, p,
&finfo,NULL,NULL,NULL);
fn( mnt,&finfo, Mask, state );
}
}
SAFE_FREE(dirlist);
data_blob_free(&last_name_raw);
return(total_received);
}
static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
{
*finfo = def_finfo;
finfo->cli = cli;
finfo->mode = CVAL(p,21);
finfo->ctime_ts.tv_sec = cli_make_unix_date(cli, p+22);
finfo->ctime_ts.tv_nsec = 0;
finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
finfo->size = IVAL(p,26);
clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
}
return(DIR_STRUCT_SIZE);
}
int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
void (*fn)(const char *, file_info *, const char *, void *), void *state)
{
char *p;
int received = 0;
BOOL first = True;
char status[21];
int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
int num_received = 0;
int i;
char *dirlist = NULL;
pstring mask;
ZERO_ARRAY(status);
pstrcpy(mask,Mask);
while (1) {
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
set_message(cli->outbuf,2,0,True);
SCVAL(cli->outbuf,smb_com,SMBsearch);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
cli_setup_packet(cli);
SSVAL(cli->outbuf,smb_vwv0,num_asked);
SSVAL(cli->outbuf,smb_vwv1,attribute);
p = smb_buf(cli->outbuf);
*p++ = 4;
p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
*p++ = 5;
if (first) {
SSVAL(p,0,0);
p += 2;
} else {
SSVAL(p,0,21);
p += 2;
memcpy(p,status,21);
p += 21;
}
cli_setup_bcc(cli, p);
cli_send_smb(cli);
if (!cli_receive_smb(cli)) break;
received = SVAL(cli->inbuf,smb_vwv0);
if (received <= 0) break;
first = False;
dirlist = (char *)SMB_REALLOC(
dirlist,(num_received + received)*DIR_STRUCT_SIZE);
if (!dirlist) {
DEBUG(0,("cli_list_old: failed to expand dirlist"));
return 0;
}
p = smb_buf(cli->inbuf) + 3;
memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
p,received*DIR_STRUCT_SIZE);
memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
num_received += received;
if (cli_is_error(cli)) break;
}
if (!first) {
memset(cli->outbuf,'\0',smb_size);
memset(cli->inbuf,'\0',smb_size);
set_message(cli->outbuf,2,0,True);
SCVAL(cli->outbuf,smb_com,SMBfclose);
SSVAL(cli->outbuf,smb_tid,cli->cnum);
cli_setup_packet(cli);
SSVAL(cli->outbuf, smb_vwv0, 0);
SSVAL(cli->outbuf, smb_vwv1, attribute);
p = smb_buf(cli->outbuf);
*p++ = 4;
fstrcpy(p, "");
p += strlen(p) + 1;
*p++ = 5;
SSVAL(p, 0, 21);
p += 2;
memcpy(p,status,21);
p += 21;
cli_setup_bcc(cli, p);
cli_send_smb(cli);
if (!cli_receive_smb(cli)) {
DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
}
}
for (p=dirlist,i=0;i<num_received;i++) {
file_info finfo;
p += interpret_short_filename(cli, p,&finfo);
fn("\\", &finfo, Mask, state);
}
SAFE_FREE(dirlist);
return(num_received);
}
int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
void (*fn)(const char *, file_info *, const char *, void *), void *state)
{
if (cli->protocol <= PROTOCOL_LANMAN1)
return cli_list_old(cli, Mask, attribute, fn, state);
return cli_list_new(cli, Mask, attribute, fn, state);
}