source: trunk/server/source3/libsmb/clilist.c

Last change on this file was 879, checked in by Silvan Scherrer, 10 years ago

Samba Server 3.6: small change for the ndpsmb client

File size: 25.6 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 client directory list routines
4 Copyright (C) Andrew Tridgell 1994-1998
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include "includes.h"
21#include "libsmb/libsmb.h"
22#include "../lib/util/tevent_ntstatus.h"
23#include "async_smb.h"
24#include "trans2.h"
25
26/****************************************************************************
27 Calculate a safe next_entry_offset.
28****************************************************************************/
29
30#ifndef __OS2__
31static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
32#else
33size_t calc_next_entry_offset(const char *base, const char *pdata_end)
34#endif
35{
36 size_t next_entry_offset = (size_t)IVAL(base,0);
37
38 if (next_entry_offset == 0 ||
39 base + next_entry_offset < base ||
40 base + next_entry_offset > pdata_end) {
41 next_entry_offset = pdata_end - base;
42 }
43 return next_entry_offset;
44}
45
46/****************************************************************************
47 Interpret a long filename structure - this is mostly guesses at the moment.
48 The length of the structure is returned
49 The structure of a long filename depends on the info level.
50 SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
51 by NT and SMB_FIND_EA_SIZE is used by OS/2
52****************************************************************************/
53
54static size_t interpret_long_filename(TALLOC_CTX *ctx,
55 struct cli_state *cli,
56 int level,
57 const char *base_ptr,
58 uint16_t recv_flags2,
59 const char *p,
60 const char *pdata_end,
61 struct file_info *finfo,
62 uint32 *p_resume_key,
63 DATA_BLOB *p_last_name_raw)
64{
65 int len;
66 size_t ret;
67 const char *base = p;
68
69 data_blob_free(p_last_name_raw);
70
71 if (p_resume_key) {
72 *p_resume_key = 0;
73 }
74 ZERO_STRUCTP(finfo);
75
76 switch (level) {
77 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
78 /* these dates are converted to GMT by
79 make_unix_date */
80 if (pdata_end - base < 27) {
81 return pdata_end - base;
82 }
83 finfo->ctime_ts = convert_time_t_to_timespec(
84 make_unix_date2(p+4, cli->serverzone));
85 finfo->atime_ts = convert_time_t_to_timespec(
86 make_unix_date2(p+8, cli->serverzone));
87 finfo->mtime_ts = convert_time_t_to_timespec(
88 make_unix_date2(p+12, cli->serverzone));
89 finfo->size = IVAL(p,16);
90 finfo->mode = CVAL(p,24);
91 len = CVAL(p, 26);
92 p += 27;
93 p += align_string(base_ptr, p, 0);
94#ifdef __OS2__
95 finfo->easize = -1;
96#endif
97
98 /* We can safely use len here (which is required by OS/2)
99 * and the NAS-BASIC server instead of +2 or +1 as the
100 * STR_TERMINATE flag below is
101 * actually used as the length calculation.
102 * The len is merely an upper bound.
103 * Due to the explicit 2 byte null termination
104 * in cli_receive_trans/cli_receive_nt_trans
105 * we know this is safe. JRA + kukks
106 */
107
108 if (p + len > pdata_end) {
109 return pdata_end - base;
110 }
111
112 /* the len+2 below looks strange but it is
113 important to cope with the differences
114 between win2000 and win9x for this call
115 (tridge) */
116 ret = clistr_pull_talloc(ctx,
117 base_ptr,
118 recv_flags2,
119 &finfo->name,
120 p,
121 len+2,
122 STR_TERMINATE);
123 if (ret == (size_t)-1) {
124 return pdata_end - base;
125 }
126 p += ret;
127 return PTR_DIFF(p, base);
128
129 case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */
130 /* these dates are converted to GMT by
131 make_unix_date */
132 if (pdata_end - base < 31) {
133 return pdata_end - base;
134 }
135 finfo->ctime_ts = convert_time_t_to_timespec(
136 make_unix_date2(p+4, cli->serverzone));
137 finfo->atime_ts = convert_time_t_to_timespec(
138 make_unix_date2(p+8, cli->serverzone));
139 finfo->mtime_ts = convert_time_t_to_timespec(
140 make_unix_date2(p+12, cli->serverzone));
141 finfo->size = IVAL(p,16);
142 finfo->mode = CVAL(p,24);
143#ifdef __OS2__
144 finfo->easize = IVAL(p,26);
145#endif
146 len = CVAL(p, 30);
147 p += 31;
148 /* check for unisys! */
149 if (p + len + 1 > pdata_end) {
150 return pdata_end - base;
151 }
152 ret = clistr_pull_talloc(ctx,
153 base_ptr,
154 recv_flags2,
155 &finfo->name,
156 p,
157 len,
158 STR_NOALIGN);
159 if (ret == (size_t)-1) {
160 return pdata_end - base;
161 }
162 p += ret;
163 return PTR_DIFF(p, base) + 1;
164
165 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
166 {
167 size_t namelen, slen;
168
169 if (pdata_end - base < 94) {
170 return pdata_end - base;
171 }
172
173 p += 4; /* next entry offset */
174
175 if (p_resume_key) {
176 *p_resume_key = IVAL(p,0);
177 }
178 p += 4; /* fileindex */
179
180 /* Offset zero is "create time", not "change time". */
181 p += 8;
182 finfo->atime_ts = interpret_long_date(p);
183 p += 8;
184 finfo->mtime_ts = interpret_long_date(p);
185 p += 8;
186 finfo->ctime_ts = interpret_long_date(p);
187 p += 8;
188 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
189 p += 8;
190 p += 8; /* alloc size */
191 finfo->mode = CVAL(p,0);
192 p += 4;
193 namelen = IVAL(p,0);
194 p += 4;
195#ifdef __OS2__
196 finfo->easize = IVAL(p,0);
197#endif
198 p += 4; /* EA size */
199 slen = SVAL(p, 0);
200 if (slen > 24) {
201 /* Bad short name length. */
202 return pdata_end - base;
203 }
204 p += 2;
205#ifndef __OS2__
206 {
207 /* stupid NT bugs. grr */
208 int flags = 0;
209 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
210 clistr_pull(base_ptr, finfo->short_name, p,
211 sizeof(finfo->short_name),
212 slen, flags);
213 }
214#endif
215 p += 24; /* short name? */
216 if (p + namelen < p || p + namelen > pdata_end) {
217 return pdata_end - base;
218 }
219 ret = clistr_pull_talloc(ctx,
220 base_ptr,
221 recv_flags2,
222 &finfo->name,
223 p,
224 namelen,
225 0);
226 if (ret == (size_t)-1) {
227 return pdata_end - base;
228 }
229
230 /* To be robust in the face of unicode conversion failures
231 we need to copy the raw bytes of the last name seen here.
232 Namelen doesn't include the terminating unicode null, so
233 copy it here. */
234
235#ifndef __OS2__
236 if (p_last_name_raw) {
237 *p_last_name_raw = data_blob(NULL, namelen+2);
238 memcpy(p_last_name_raw->data, p, namelen);
239 SSVAL(p_last_name_raw->data, namelen, 0);
240 }
241#endif
242 return calc_next_entry_offset(base, pdata_end);
243 }
244 }
245
246 DEBUG(1,("Unknown long filename format %d\n",level));
247 return calc_next_entry_offset(base, pdata_end);
248}
249
250/****************************************************************************
251 Interpret a short filename structure.
252 The length of the structure is returned.
253****************************************************************************/
254
255static bool interpret_short_filename(TALLOC_CTX *ctx,
256 struct cli_state *cli,
257 char *p,
258 struct file_info *finfo)
259{
260 size_t ret;
261 ZERO_STRUCTP(finfo);
262
263 finfo->mode = CVAL(p,21);
264
265 /* this date is converted to GMT by make_unix_date */
266 finfo->ctime_ts.tv_sec = make_unix_date(p+22, cli->serverzone);
267 finfo->ctime_ts.tv_nsec = 0;
268 finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
269 finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
270 finfo->size = IVAL(p,26);
271 ret = clistr_pull_talloc(ctx,
272 cli->inbuf,
273 SVAL(cli->inbuf, smb_flg2),
274 &finfo->name,
275 p+30,
276 12,
277 STR_ASCII);
278 if (ret == (size_t)-1) {
279 return false;
280 }
281
282 if (finfo->name) {
283 strlcpy(finfo->short_name,
284 finfo->name,
285 sizeof(finfo->short_name));
286 }
287 return true;
288}
289
290struct cli_list_old_state {
291 struct tevent_context *ev;
292 struct cli_state *cli;
293 uint16_t vwv[2];
294 char *mask;
295 int num_asked;
296 uint16_t attribute;
297 uint8_t search_status[23];
298 bool first;
299 bool done;
300 uint8_t *dirlist;
301};
302
303static void cli_list_old_done(struct tevent_req *subreq);
304
305static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
306 struct tevent_context *ev,
307 struct cli_state *cli,
308 const char *mask,
309 uint16_t attribute)
310{
311 struct tevent_req *req, *subreq;
312 struct cli_list_old_state *state;
313 uint8_t *bytes;
314 static const uint16_t zero = 0;
315
316 req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
317 if (req == NULL) {
318 return NULL;
319 }
320 state->ev = ev;
321 state->cli = cli;
322 state->attribute = attribute;
323 state->first = true;
324 state->mask = talloc_strdup(state, mask);
325 if (tevent_req_nomem(state->mask, req)) {
326 return tevent_req_post(req, ev);
327 }
328 state->num_asked = (cli->max_xmit - 100) / DIR_STRUCT_SIZE;
329
330 SSVAL(state->vwv + 0, 0, state->num_asked);
331 SSVAL(state->vwv + 1, 0, state->attribute);
332
333 bytes = talloc_array(state, uint8_t, 1);
334 if (tevent_req_nomem(bytes, req)) {
335 return tevent_req_post(req, ev);
336 }
337 bytes[0] = 4;
338 bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), mask,
339 strlen(mask)+1, NULL);
340
341 bytes = smb_bytes_push_bytes(bytes, 5, (uint8_t *)&zero, 2);
342 if (tevent_req_nomem(bytes, req)) {
343 return tevent_req_post(req, ev);
344 }
345
346 subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch,
347 0, 2, state->vwv, talloc_get_size(bytes), bytes);
348 if (tevent_req_nomem(subreq, req)) {
349 return tevent_req_post(req, ev);
350 }
351 tevent_req_set_callback(subreq, cli_list_old_done, req);
352 return req;
353}
354
355static void cli_list_old_done(struct tevent_req *subreq)
356{
357 struct tevent_req *req = tevent_req_callback_data(
358 subreq, struct tevent_req);
359 struct cli_list_old_state *state = tevent_req_data(
360 req, struct cli_list_old_state);
361 NTSTATUS status;
362 uint8_t cmd;
363 uint8_t wct;
364 uint16_t *vwv;
365 uint32_t num_bytes;
366 uint8_t *bytes;
367 uint16_t received;
368 size_t dirlist_len;
369 uint8_t *tmp;
370
371 status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
372 &bytes);
373 if (!NT_STATUS_IS_OK(status)
374 && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
375 && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
376 TALLOC_FREE(subreq);
377 tevent_req_nterror(req, status);
378 return;
379 }
380 if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
381 || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
382 received = 0;
383 } else {
384 if (wct < 1) {
385 TALLOC_FREE(subreq);
386 tevent_req_nterror(
387 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
388 return;
389 }
390 received = SVAL(vwv + 0, 0);
391 }
392
393 if (received > 0) {
394 /*
395 * I don't think this can wrap. received is
396 * initialized from a 16-bit value.
397 */
398 if (num_bytes < (received * DIR_STRUCT_SIZE + 3)) {
399 TALLOC_FREE(subreq);
400 tevent_req_nterror(
401 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
402 return;
403 }
404
405 dirlist_len = talloc_get_size(state->dirlist);
406
407 tmp = TALLOC_REALLOC_ARRAY(
408 state, state->dirlist, uint8_t,
409 dirlist_len + received * DIR_STRUCT_SIZE);
410 if (tevent_req_nomem(tmp, req)) {
411 return;
412 }
413 state->dirlist = tmp;
414 memcpy(state->dirlist + dirlist_len, bytes + 3,
415 received * DIR_STRUCT_SIZE);
416
417 SSVAL(state->search_status, 0, 21);
418 memcpy(state->search_status + 2,
419 bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
420 cmd = SMBsearch;
421 } else {
422 if (state->first || state->done) {
423 tevent_req_done(req);
424 return;
425 }
426 state->done = true;
427 state->num_asked = 0;
428 cmd = SMBfclose;
429 }
430 TALLOC_FREE(subreq);
431
432 state->first = false;
433
434 SSVAL(state->vwv + 0, 0, state->num_asked);
435 SSVAL(state->vwv + 1, 0, state->attribute);
436
437 bytes = talloc_array(state, uint8_t, 1);
438 if (tevent_req_nomem(bytes, req)) {
439 return;
440 }
441 bytes[0] = 4;
442 bytes = smb_bytes_push_str(bytes, cli_ucs2(state->cli), "",
443 1, NULL);
444 bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
445 sizeof(state->search_status));
446 if (tevent_req_nomem(bytes, req)) {
447 return;
448 }
449 subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0,
450 2, state->vwv, talloc_get_size(bytes), bytes);
451 if (tevent_req_nomem(subreq, req)) {
452 return;
453 }
454 tevent_req_set_callback(subreq, cli_list_old_done, req);
455}
456
457static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
458 struct file_info **pfinfo)
459{
460 struct cli_list_old_state *state = tevent_req_data(
461 req, struct cli_list_old_state);
462 NTSTATUS status;
463 size_t i, num_received;
464 struct file_info *finfo;
465
466 if (tevent_req_is_nterror(req, &status)) {
467 return status;
468 }
469
470 num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
471
472 finfo = TALLOC_ARRAY(mem_ctx, struct file_info, num_received);
473 if (finfo == NULL) {
474 return NT_STATUS_NO_MEMORY;
475 }
476
477 for (i=0; i<num_received; i++) {
478 if (!interpret_short_filename(
479 finfo, state->cli,
480 (char *)state->dirlist + i * DIR_STRUCT_SIZE,
481 &finfo[i])) {
482 TALLOC_FREE(finfo);
483 return NT_STATUS_NO_MEMORY;
484 }
485 }
486 *pfinfo = finfo;
487 return NT_STATUS_OK;
488}
489
490NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
491 uint16 attribute,
492 NTSTATUS (*fn)(const char *, struct file_info *,
493 const char *, void *), void *state)
494{
495 TALLOC_CTX *frame = talloc_stackframe();
496 struct event_context *ev;
497 struct tevent_req *req;
498 NTSTATUS status = NT_STATUS_NO_MEMORY;
499 struct file_info *finfo;
500 size_t i, num_finfo;
501
502 if (cli_has_async_calls(cli)) {
503 /*
504 * Can't use sync call while an async call is in flight
505 */
506 status = NT_STATUS_INVALID_PARAMETER;
507 goto fail;
508 }
509 ev = event_context_init(frame);
510 if (ev == NULL) {
511 goto fail;
512 }
513 req = cli_list_old_send(frame, ev, cli, mask, attribute);
514 if (req == NULL) {
515 goto fail;
516 }
517 if (!tevent_req_poll(req, ev)) {
518 status = map_nt_error_from_unix(errno);
519 goto fail;
520 }
521 status = cli_list_old_recv(req, frame, &finfo);
522 if (!NT_STATUS_IS_OK(status)) {
523 goto fail;
524 }
525 num_finfo = talloc_array_length(finfo);
526 for (i=0; i<num_finfo; i++) {
527 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
528 if (!NT_STATUS_IS_OK(status)) {
529 goto fail;
530 }
531 }
532 fail:
533 TALLOC_FREE(frame);
534 return status;
535}
536
537struct cli_list_trans_state {
538 struct tevent_context *ev;
539 struct cli_state *cli;
540 char *mask;
541 uint16_t attribute;
542 uint16_t info_level;
543
544 int loop_count;
545 int total_received;
546 uint16_t max_matches;
547 bool first;
548
549 int ff_eos;
550 int ff_dir_handle;
551
552 uint16_t setup[1];
553 uint8_t *param;
554
555 struct file_info *finfo;
556};
557
558static void cli_list_trans_done(struct tevent_req *subreq);
559
560static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
561 struct tevent_context *ev,
562 struct cli_state *cli,
563 const char *mask,
564 uint16_t attribute,
565 uint16_t info_level)
566{
567 struct tevent_req *req, *subreq;
568 struct cli_list_trans_state *state;
569 size_t nlen, param_len;
570 char *p;
571
572 req = tevent_req_create(mem_ctx, &state,
573 struct cli_list_trans_state);
574 if (req == NULL) {
575 return NULL;
576 }
577 state->ev = ev;
578 state->cli = cli;
579 state->mask = talloc_strdup(state, mask);
580 if (tevent_req_nomem(state->mask, req)) {
581 return tevent_req_post(req, ev);
582 }
583 state->attribute = attribute;
584 state->info_level = info_level;
585 state->loop_count = 0;
586 state->first = true;
587
588 state->max_matches = 1366; /* Match W2k */
589
590 SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
591
592 nlen = 2*(strlen(mask)+1);
593 state->param = TALLOC_ARRAY(state, uint8_t, 12+nlen+2);
594 if (tevent_req_nomem(state->param, req)) {
595 return tevent_req_post(req, ev);
596 }
597
598 SSVAL(state->param, 0, state->attribute);
599 SSVAL(state->param, 2, state->max_matches);
600 SSVAL(state->param, 4,
601 FLAG_TRANS2_FIND_REQUIRE_RESUME
602 |FLAG_TRANS2_FIND_CLOSE_IF_END);
603 SSVAL(state->param, 6, state->info_level);
604 SIVAL(state->param, 8, 0);
605
606 p = ((char *)state->param)+12;
607 p += clistr_push(state->cli, p, state->mask, nlen,
608 STR_TERMINATE);
609 param_len = PTR_DIFF(p, state->param);
610
611 subreq = cli_trans_send(state, state->ev, state->cli,
612 SMBtrans2, NULL, -1, 0, 0,
613 state->setup, 1, 0,
614 state->param, param_len, 10,
615 NULL, 0, cli->max_xmit);
616 if (tevent_req_nomem(subreq, req)) {
617 return tevent_req_post(req, ev);
618 }
619 tevent_req_set_callback(subreq, cli_list_trans_done, req);
620 return req;
621}
622
623static void cli_list_trans_done(struct tevent_req *subreq)
624{
625 struct tevent_req *req = tevent_req_callback_data(
626 subreq, struct tevent_req);
627 struct cli_list_trans_state *state = tevent_req_data(
628 req, struct cli_list_trans_state);
629 NTSTATUS status;
630 uint8_t *param;
631 uint32_t num_param;
632 uint8_t *data;
633 char *data_end;
634 uint32_t num_data;
635 uint32_t min_param;
636 struct file_info *tmp;
637 size_t old_num_finfo;
638 uint16_t recv_flags2;
639 int ff_searchcount;
640 bool ff_eos;
641 char *p, *p2;
642 uint32_t resume_key = 0;
643 int i;
644 DATA_BLOB last_name_raw;
645 struct file_info *finfo = NULL;
646 size_t nlen, param_len;
647
648 min_param = (state->first ? 6 : 4);
649
650 status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
651 NULL, 0, NULL,
652 &param, min_param, &num_param,
653 &data, 0, &num_data);
654 TALLOC_FREE(subreq);
655 if (!NT_STATUS_IS_OK(status)) {
656 /*
657 * TODO: retry, OS/2 nofiles
658 */
659 tevent_req_nterror(req, status);
660 return;
661 }
662
663 if (state->first) {
664 state->ff_dir_handle = SVAL(param, 0);
665 ff_searchcount = SVAL(param, 2);
666 ff_eos = SVAL(param, 4) != 0;
667 } else {
668 ff_searchcount = SVAL(param, 0);
669 ff_eos = SVAL(param, 2) != 0;
670 }
671
672 old_num_finfo = talloc_array_length(state->finfo);
673
674 tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
675 old_num_finfo + ff_searchcount);
676 if (tevent_req_nomem(tmp, req)) {
677 return;
678 }
679 state->finfo = tmp;
680
681 p2 = p = (char *)data;
682 data_end = (char *)data + num_data;
683 last_name_raw = data_blob_null;
684
685 for (i=0; i<ff_searchcount; i++) {
686 if (p2 >= data_end) {
687 ff_eos = true;
688 break;
689 }
690 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
691 && (i == ff_searchcount-1)) {
692 /* Last entry - fixup the last offset length. */
693 SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
694 }
695
696 data_blob_free(&last_name_raw);
697
698 finfo = &state->finfo[old_num_finfo + i];
699
700 p2 += interpret_long_filename(
701 state->finfo, /* Stick fname to the array as such */
702 state->cli, state->info_level,
703 (char *)data, recv_flags2, p2,
704 data_end, finfo, &resume_key, &last_name_raw);
705
706 if (finfo->name == NULL) {
707 DEBUG(1, ("cli_list: Error: unable to parse name from "
708 "info level %d\n", state->info_level));
709 ff_eos = true;
710 break;
711 }
712 if (!state->first && (state->mask[0] != '\0') &&
713 strcsequal(finfo->name, state->mask)) {
714 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
715 "already been seen?\n", finfo->name));
716 ff_eos = true;
717 break;
718 }
719 }
720
721 if (ff_searchcount == 0) {
722 ff_eos = true;
723 }
724
725 TALLOC_FREE(param);
726 TALLOC_FREE(data);
727
728 /*
729 * Shrink state->finfo to the real length we received
730 */
731 tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
732 old_num_finfo + i);
733 if (tevent_req_nomem(tmp, req)) {
734 return;
735 }
736 state->finfo = tmp;
737
738 state->first = false;
739
740 if (ff_eos) {
741 data_blob_free(&last_name_raw);
742 tevent_req_done(req);
743 return;
744 }
745
746 TALLOC_FREE(state->mask);
747 state->mask = talloc_strdup(state, finfo->name);
748 if (tevent_req_nomem(state->mask, req)) {
749 return;
750 }
751
752 SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
753
754 nlen = 2*(strlen(state->mask) + 1);
755
756 param = TALLOC_REALLOC_ARRAY(state, state->param, uint8_t,
757 12 + nlen + last_name_raw.length + 2);
758 if (tevent_req_nomem(param, req)) {
759 return;
760 }
761 state->param = param;
762
763 SSVAL(param, 0, state->ff_dir_handle);
764 SSVAL(param, 2, state->max_matches); /* max count */
765 SSVAL(param, 4, state->info_level);
766 /*
767 * For W2K servers serving out FAT filesystems we *must* set
768 * the resume key. If it's not FAT then it's returned as zero.
769 */
770 SIVAL(param, 6, resume_key); /* ff_resume_key */
771 /*
772 * NB. *DON'T* use continue here. If you do it seems that W2K
773 * and bretheren can miss filenames. Use last filename
774 * continue instead. JRA
775 */
776 SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
777 |FLAG_TRANS2_FIND_CLOSE_IF_END));
778 p = ((char *)param)+12;
779 if (last_name_raw.length) {
780 memcpy(p, last_name_raw.data, last_name_raw.length);
781 p += last_name_raw.length;
782 data_blob_free(&last_name_raw);
783 } else {
784 p += clistr_push(state->cli, p, state->mask, nlen,
785 STR_TERMINATE);
786 }
787
788 param_len = PTR_DIFF(p, param);
789
790 subreq = cli_trans_send(state, state->ev, state->cli,
791 SMBtrans2, NULL, -1, 0, 0,
792 state->setup, 1, 0,
793 state->param, param_len, 10,
794 NULL, 0, state->cli->max_xmit);
795 if (tevent_req_nomem(subreq, req)) {
796 return;
797 }
798 tevent_req_set_callback(subreq, cli_list_trans_done, req);
799}
800
801static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
802 TALLOC_CTX *mem_ctx,
803 struct file_info **finfo)
804{
805 struct cli_list_trans_state *state = tevent_req_data(
806 req, struct cli_list_trans_state);
807 NTSTATUS status;
808
809 if (tevent_req_is_nterror(req, &status)) {
810 return status;
811 }
812 *finfo = talloc_move(mem_ctx, &state->finfo);
813 return NT_STATUS_OK;
814}
815
816NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
817 uint16_t attribute, int info_level,
818 NTSTATUS (*fn)(const char *mnt, struct file_info *finfo,
819 const char *mask, void *private_data),
820 void *private_data)
821{
822 TALLOC_CTX *frame = talloc_stackframe();
823 struct event_context *ev;
824 struct tevent_req *req;
825 int i, num_finfo;
826 struct file_info *finfo = NULL;
827 NTSTATUS status = NT_STATUS_NO_MEMORY;
828
829 if (cli_has_async_calls(cli)) {
830 /*
831 * Can't use sync call while an async call is in flight
832 */
833 status = NT_STATUS_INVALID_PARAMETER;
834 goto fail;
835 }
836 ev = event_context_init(frame);
837 if (ev == NULL) {
838 goto fail;
839 }
840 req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
841 if (req == NULL) {
842 goto fail;
843 }
844 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
845 goto fail;
846 }
847 status = cli_list_trans_recv(req, frame, &finfo);
848 if (!NT_STATUS_IS_OK(status)) {
849 goto fail;
850 }
851 num_finfo = talloc_array_length(finfo);
852 for (i=0; i<num_finfo; i++) {
853 status = fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
854 if (!NT_STATUS_IS_OK(status)) {
855 goto fail;
856 }
857 }
858 fail:
859 TALLOC_FREE(frame);
860 return status;
861}
862
863struct cli_list_state {
864 NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
865 struct file_info **finfo);
866 struct file_info *finfo;
867};
868
869static void cli_list_done(struct tevent_req *subreq);
870
871struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
872 struct tevent_context *ev,
873 struct cli_state *cli,
874 const char *mask,
875 uint16_t attribute,
876 uint16_t info_level)
877{
878 struct tevent_req *req, *subreq;
879 struct cli_list_state *state;
880
881 req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
882 if (req == NULL) {
883 return NULL;
884 }
885
886 if (cli->protocol <= PROTOCOL_LANMAN1) {
887 subreq = cli_list_old_send(state, ev, cli, mask, attribute);
888 state->recv_fn = cli_list_old_recv;
889 } else {
890 subreq = cli_list_trans_send(state, ev, cli, mask, attribute,
891 info_level);
892 state->recv_fn = cli_list_trans_recv;
893 }
894 if (tevent_req_nomem(subreq, req)) {
895 return tevent_req_post(req, ev);
896 }
897 tevent_req_set_callback(subreq, cli_list_done, req);
898 return req;
899}
900
901static void cli_list_done(struct tevent_req *subreq)
902{
903 struct tevent_req *req = tevent_req_callback_data(
904 subreq, struct tevent_req);
905 struct cli_list_state *state = tevent_req_data(
906 req, struct cli_list_state);
907 NTSTATUS status;
908
909 status = state->recv_fn(subreq, state, &state->finfo);
910 TALLOC_FREE(subreq);
911 if (!NT_STATUS_IS_OK(status)) {
912 tevent_req_nterror(req, status);
913 return;
914 }
915 tevent_req_done(req);
916}
917
918NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
919 struct file_info **finfo, size_t *num_finfo)
920{
921 struct cli_list_state *state = tevent_req_data(
922 req, struct cli_list_state);
923 NTSTATUS status;
924
925 if (tevent_req_is_nterror(req, &status)) {
926 return status;
927 }
928 *num_finfo = talloc_array_length(state->finfo);
929 *finfo = talloc_move(mem_ctx, &state->finfo);
930 return NT_STATUS_OK;
931}
932
933NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute,
934 NTSTATUS (*fn)(const char *, struct file_info *, const char *,
935 void *), void *state)
936{
937 TALLOC_CTX *frame = talloc_stackframe();
938 struct event_context *ev;
939 struct tevent_req *req;
940 NTSTATUS status = NT_STATUS_NO_MEMORY;
941 struct file_info *finfo;
942 size_t i, num_finfo;
943 uint16_t info_level;
944
945 if (cli_has_async_calls(cli)) {
946 /*
947 * Can't use sync call while an async call is in flight
948 */
949 status = NT_STATUS_INVALID_PARAMETER;
950 goto fail;
951 }
952 ev = event_context_init(frame);
953 if (ev == NULL) {
954 goto fail;
955 }
956
957 info_level = (cli->capabilities & CAP_NT_SMBS)
958 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
959
960 req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
961 if (req == NULL) {
962 goto fail;
963 }
964 if (!tevent_req_poll(req, ev)) {
965 status = map_nt_error_from_unix(errno);
966 goto fail;
967 }
968
969 status = cli_list_recv(req, frame, &finfo, &num_finfo);
970 if (!NT_STATUS_IS_OK(status)) {
971 goto fail;
972 }
973
974 for (i=0; i<num_finfo; i++) {
975 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
976 if (!NT_STATUS_IS_OK(status)) {
977 goto fail;
978 }
979 }
980 fail:
981 TALLOC_FREE(frame);
982 return status;
983}
Note: See TracBrowser for help on using the repository browser.