return status;
}
+struct cli_list_smb2_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ char *mask;
+ uint16_t attribute;
+ uint16_t info_level;
+
+ int loop_count;
+ int total_received;
+ uint16_t max_matches;
+ bool first;
+
+ int ff_eos;
+ int ff_dir_handle;
+
+ uint16_t setup[1];
+ uint8_t *param;
+
+ struct file_info *finfo;
+};
+
+static void cli_list_smb2_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_list_smb2_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *mask,
+ uint16_t attribute,
+ uint16_t info_level)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_list_smb2_state *state;
+ size_t param_len;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_list_smb2_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->mask = talloc_strdup(state, mask);
+ if (tevent_req_nomem(state->mask, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->attribute = attribute;
+ state->info_level = info_level;
+ state->loop_count = 0;
+ state->first = true;
+
+struct tevent_req *smb2cli_create_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *filename,
+ uint8_t oplock_level, /* SMB2_OPLOCK_LEVEL_* */
+ uint32_t impersonation_level, /* SMB2_IMPERSONATION_* */
+ uint32_t desired_access,
+ uint32_t file_attributes,
+ uint32_t share_access,
+ uint32_t create_disposition,
+ uint32_t create_options,
+ struct smb2_create_blobs *blobs)
+
+ status = smb2cli_create_send(state, state->ev,
+ state->cli, "",
+ SMB2_OPLOCK_LEVEL_NONE, 2,
+ //MAXIMUM_ALLOWED_ACCESS,
+ FILE_ATTRIBUTE_DIRECTORY, 0,
+ &state->fid_persistent,
+ &state->fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_create returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_query_directory_send(
+ cli, 1, 0, 0, fid_persistent, fid_volatile, "*", 0xffff,
+ talloc_tos(), &dir_data, &dir_data_length);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_query_directory returned %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = smb2cli_close(cli, 0, fid_persistent, fid_volatile);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("smb2cli_close returned %s\n", nt_errstr(status));
+ return false;
+ }
+ state->max_matches = 1366; /* Match W2k */
+
+ SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
+
+ state->param = talloc_array(state, uint8_t, 12);
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ SSVAL(state->param, 0, state->attribute);
+ SSVAL(state->param, 2, state->max_matches);
+ SSVAL(state->param, 4,
+ FLAG_TRANS2_FIND_REQUIRE_RESUME
+ |FLAG_TRANS2_FIND_CLOSE_IF_END);
+ SSVAL(state->param, 6, state->info_level);
+ SIVAL(state->param, 8, 0);
+
+ state->param = trans2_bytes_push_str(state->param, cli_ucs2(cli),
+ state->mask, strlen(state->mask)+1,
+ NULL);
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+ param_len = talloc_get_size(state->param);
+
+ subreq = cli_trans_send(state, state->ev, state->cli,
+ SMBtrans2, NULL, -1, 0, 0,
+ state->setup, 1, 0,
+ state->param, param_len, 10,
+ NULL, 0, CLI_BUFFER_SIZE);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_list_smb2_done, req);
+ return req;
+}
+
+static void cli_list_smb2_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_list_smb2_state *state = tevent_req_data(
+ req, struct cli_list_smb2_state);
+ NTSTATUS status;
+ uint8_t *param;
+ uint32_t num_param;
+ uint8_t *data;
+ char *data_end;
+ uint32_t num_data;
+ uint32_t min_param;
+ struct file_info *tmp;
+ size_t old_num_finfo;
+ uint16_t recv_flags2;
+ int ff_searchcount;
+ bool ff_eos;
+ char *p, *p2;
+ uint32_t resume_key = 0;
+ int i;
+ DATA_BLOB last_name_raw;
+ struct file_info *finfo = NULL;
+ size_t param_len;
+
+ min_param = (state->first ? 6 : 4);
+
+ status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
+ NULL, 0, NULL,
+ ¶m, min_param, &num_param,
+ &data, 0, &num_data);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * TODO: retry, OS/2 nofiles
+ */
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (state->first) {
+ state->ff_dir_handle = SVAL(param, 0);
+ ff_searchcount = SVAL(param, 2);
+ ff_eos = SVAL(param, 4) != 0;
+ } else {
+ ff_searchcount = SVAL(param, 0);
+ ff_eos = SVAL(param, 2) != 0;
+ }
+
+ old_num_finfo = talloc_array_length(state->finfo);
+
+ tmp = talloc_realloc(state, state->finfo, struct file_info,
+ old_num_finfo + ff_searchcount);
+ if (tevent_req_nomem(tmp, req)) {
+ return;
+ }
+ state->finfo = tmp;
+
+ p2 = p = (char *)data;
+ data_end = (char *)data + num_data;
+ last_name_raw = data_blob_null;
+
+ for (i=0; i<ff_searchcount; i++) {
+ if (p2 >= data_end) {
+ ff_eos = true;
+ break;
+ }
+ if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
+ && (i == ff_searchcount-1)) {
+ /* Last entry - fixup the last offset length. */
+ SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
+ }
+
+ data_blob_free(&last_name_raw);
+
+ finfo = &state->finfo[old_num_finfo + i];
+
+ p2 += interpret_long_filename(
+ state->finfo, /* Stick fname to the array as such */
+ state->cli, state->info_level,
+ (char *)data, recv_flags2, p2,
+ data_end, finfo, &resume_key, &last_name_raw);
+
+ if (finfo->name == NULL) {
+ DEBUG(1, ("cli_list: Error: unable to parse name from "
+ "info level %d\n", state->info_level));
+ ff_eos = true;
+ break;
+ }
+ if (!state->first && (state->mask[0] != '\0') &&
+ strcsequal(finfo->name, state->mask)) {
+ DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
+ "already been seen?\n", finfo->name));
+ ff_eos = true;
+ break;
+ }
+ }
+
+ if (ff_searchcount == 0) {
+ ff_eos = true;
+ }
+
+ TALLOC_FREE(param);
+ TALLOC_FREE(data);
+
+ /*
+ * Shrink state->finfo to the real length we received
+ */
+ tmp = talloc_realloc(state, state->finfo, struct file_info,
+ old_num_finfo + i);
+ if (tevent_req_nomem(tmp, req)) {
+ return;
+ }
+ state->finfo = tmp;
+
+ state->first = false;
+
+ if (ff_eos) {
+ data_blob_free(&last_name_raw);
+ tevent_req_done(req);
+ return;
+ }
+
+ TALLOC_FREE(state->mask);
+ state->mask = talloc_strdup(state, finfo->name);
+ if (tevent_req_nomem(state->mask, req)) {
+ return;
+ }
+
+ SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
+
+ param = talloc_realloc(state, state->param, uint8_t, 12);
+ if (tevent_req_nomem(param, req)) {
+ return;
+ }
+ state->param = param;
+
+ SSVAL(param, 0, state->ff_dir_handle);
+ SSVAL(param, 2, state->max_matches); /* max count */
+ SSVAL(param, 4, state->info_level);
+ /*
+ * For W2K servers serving out FAT filesystems we *must* set
+ * the resume key. If it's not FAT then it's returned as zero.
+ */
+ SIVAL(param, 6, resume_key); /* ff_resume_key */
+ /*
+ * NB. *DON'T* use continue here. If you do it seems that W2K
+ * and bretheren can miss filenames. Use last filename
+ * continue instead. JRA
+ */
+ SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
+ |FLAG_TRANS2_FIND_CLOSE_IF_END));
+ if (last_name_raw.length) {
+ state->param = trans2_bytes_push_bytes(state->param,
+ last_name_raw.data,
+ last_name_raw.length);
+ if (tevent_req_nomem(state->param, req)) {
+ return;
+ }
+ data_blob_free(&last_name_raw);
+ } else {
+ state->param = trans2_bytes_push_str(state->param,
+ cli_ucs2(state->cli),
+ state->mask,
+ strlen(state->mask)+1,
+ NULL);
+ if (tevent_req_nomem(state->param, req)) {
+ return;
+ }
+ }
+ param_len = talloc_get_size(state->param);
+
+ subreq = cli_trans_send(state, state->ev, state->cli,
+ SMBtrans2, NULL, -1, 0, 0,
+ state->setup, 1, 0,
+ state->param, param_len, 10,
+ NULL, 0, CLI_BUFFER_SIZE);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_list_smb2_done, req);
+}
+
+static NTSTATUS cli_list_smb2_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct file_info **finfo)
+{
+ struct cli_list_smb2_state *state = tevent_req_data(
+ req, struct cli_list_smb2_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *finfo = talloc_move(mem_ctx, &state->finfo);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_list_smb2(struct cli_state *cli, const char *mask,
+ uint16_t attribute, int info_level,
+ NTSTATUS (*fn)(const char *mnt, struct file_info *finfo,
+ const char *mask, void *private_data),
+ void *private_data)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct event_context *ev;
+ struct tevent_req *req;
+ int i, num_finfo;
+ struct file_info *finfo = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (cli_has_async_calls(cli)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = event_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_list_smb2_send(frame, ev, cli, mask, attribute, info_level);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_list_smb2_recv(req, frame, &finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ num_finfo = talloc_array_length(finfo);
+ for (i=0; i<num_finfo; i++) {
+ status = fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ }
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
struct cli_list_state {
NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
struct file_info **finfo);