1/* 2 Unix SMB/CIFS implementation. 3 client directory list routines 4 Copyright (C) Andrew Tridgell 1994-2003 5 Copyright (C) James Myers 2003 <myersjj@samba.org> 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. 19*/ 20 21#include "includes.h" 22#include "libcli/libcli.h" 23#include "libcli/raw/libcliraw.h" 24#include "libcli/raw/raw_proto.h" 25 26struct search_private { 27 struct clilist_file_info *dirlist; 28 TALLOC_CTX *mem_ctx; 29 int dirlist_len; 30 int ff_searchcount; /* total received in 1 server trip */ 31 int total_received; /* total received all together */ 32 enum smb_search_data_level data_level; 33 const char *last_name; /* used to continue trans2 search */ 34 struct smb_search_id id; /* used for old-style search */ 35}; 36 37 38/**************************************************************************** 39 Interpret a long filename structure. 40****************************************************************************/ 41static bool interpret_long_filename(enum smb_search_data_level level, 42 const union smb_search_data *info, 43 struct clilist_file_info *finfo) 44{ 45 struct clilist_file_info finfo2; 46 47 if (!finfo) finfo = &finfo2; 48 ZERO_STRUCTP(finfo); 49 50 switch (level) { 51 case RAW_SEARCH_DATA_STANDARD: 52 finfo->size = info->standard.size; 53 finfo->mtime = info->standard.write_time; 54 finfo->attrib = info->standard.attrib; 55 finfo->name = info->standard.name.s; 56 finfo->short_name = info->standard.name.s; 57 break; 58 59 case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO: 60 finfo->size = info->both_directory_info.size; 61 finfo->mtime = nt_time_to_unix(info->both_directory_info.write_time); 62 finfo->attrib = info->both_directory_info.attrib; 63 finfo->short_name = info->both_directory_info.short_name.s; 64 finfo->name = info->both_directory_info.name.s; 65 break; 66 67 default: 68 DEBUG(0,("Unhandled level %d in interpret_long_filename\n", (int)level)); 69 return false; 70 } 71 72 return true; 73} 74 75/* callback function used for trans2 search */ 76static bool smbcli_list_new_callback(void *private_data, const union smb_search_data *file) 77{ 78 struct search_private *state = (struct search_private*) private_data; 79 struct clilist_file_info *tdl; 80 81 /* add file info to the dirlist pool */ 82 tdl = talloc_realloc(state, 83 state->dirlist, 84 struct clilist_file_info, 85 state->dirlist_len + 1); 86 if (!tdl) { 87 return false; 88 } 89 state->dirlist = tdl; 90 state->dirlist_len++; 91 92 interpret_long_filename(state->data_level, file, &state->dirlist[state->total_received]); 93 94 state->last_name = state->dirlist[state->total_received].name; 95 state->total_received++; 96 state->ff_searchcount++; 97 98 return true; 99} 100 101int smbcli_list_new(struct smbcli_tree *tree, const char *Mask, uint16_t attribute, 102 enum smb_search_data_level level, 103 void (*fn)(struct clilist_file_info *, const char *, void *), 104 void *caller_state) 105{ 106 union smb_search_first first_parms; 107 union smb_search_next next_parms; 108 struct search_private state; /* for callbacks */ 109 int received = 0; 110 bool first = true; 111 int num_received = 0; 112 int max_matches = 512; 113 char *mask; 114 int ff_eos = 0, i, ff_searchcount; 115 int ff_dir_handle=0; 116 117 /* initialize state for search */ 118 state.mem_ctx = talloc_init("smbcli_list_new"); 119 state.dirlist_len = 0; 120 state.total_received = 0; 121 122 state.dirlist = talloc_array(state.mem_ctx, 123 struct clilist_file_info, 0); 124 mask = talloc_strdup(state.mem_ctx, Mask); 125 126 if (level == RAW_SEARCH_DATA_GENERIC) { 127 if (tree->session->transport->negotiate.capabilities & CAP_NT_SMBS) { 128 level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO; 129 } else { 130 level = RAW_SEARCH_DATA_STANDARD; 131 } 132 } 133 state.data_level = level; 134 135 while (1) { 136 state.ff_searchcount = 0; 137 if (first) { 138 NTSTATUS status; 139 140 first_parms.t2ffirst.level = RAW_SEARCH_TRANS2; 141 first_parms.t2ffirst.data_level = state.data_level; 142 first_parms.t2ffirst.in.max_count = max_matches; 143 first_parms.t2ffirst.in.search_attrib = attribute; 144 first_parms.t2ffirst.in.pattern = mask; 145 first_parms.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END; 146 first_parms.t2ffirst.in.storage_type = 0; 147 148 status = smb_raw_search_first(tree, 149 state.mem_ctx, &first_parms, 150 (void*)&state, smbcli_list_new_callback); 151 if (!NT_STATUS_IS_OK(status)) { 152 talloc_free(state.mem_ctx); 153 return -1; 154 } 155 156 ff_dir_handle = first_parms.t2ffirst.out.handle; 157 ff_searchcount = first_parms.t2ffirst.out.count; 158 ff_eos = first_parms.t2ffirst.out.end_of_search; 159 160 received = first_parms.t2ffirst.out.count; 161 if (received <= 0) break; 162 if (ff_eos) break; 163 first = false; 164 } else { 165 NTSTATUS status; 166 167 next_parms.t2fnext.level = RAW_SEARCH_TRANS2; 168 next_parms.t2fnext.data_level = state.data_level; 169 next_parms.t2fnext.in.max_count = max_matches; 170 next_parms.t2fnext.in.last_name = state.last_name; 171 next_parms.t2fnext.in.handle = ff_dir_handle; 172 next_parms.t2fnext.in.resume_key = 0; 173 next_parms.t2fnext.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END; 174 175 status = smb_raw_search_next(tree, 176 state.mem_ctx, 177 &next_parms, 178 (void*)&state, 179 smbcli_list_new_callback); 180 181 if (!NT_STATUS_IS_OK(status)) { 182 return -1; 183 } 184 ff_searchcount = next_parms.t2fnext.out.count; 185 ff_eos = next_parms.t2fnext.out.end_of_search; 186 received = next_parms.t2fnext.out.count; 187 if (received <= 0) break; 188 if (ff_eos) break; 189 } 190 191 num_received += received; 192 } 193 194 for (i=0;i<state.total_received;i++) { 195 fn(&state.dirlist[i], Mask, caller_state); 196 } 197 198 talloc_free(state.mem_ctx); 199 200 return state.total_received; 201} 202 203/**************************************************************************** 204 Interpret a short filename structure. 205 The length of the structure is returned. 206****************************************************************************/ 207static bool interpret_short_filename(enum smb_search_data_level level, 208 const union smb_search_data *info, 209 struct clilist_file_info *finfo) 210{ 211 struct clilist_file_info finfo2; 212 213 if (!finfo) finfo = &finfo2; 214 ZERO_STRUCTP(finfo); 215 216 switch (level) { 217 case RAW_SEARCH_DATA_SEARCH: 218 finfo->mtime = info->search.write_time; 219 finfo->size = info->search.size; 220 finfo->attrib = info->search.attrib; 221 finfo->name = info->search.name; 222 finfo->short_name = info->search.name; 223 break; 224 225 default: 226 DEBUG(0,("Unhandled level %d in interpret_short_filename\n", (int)level)); 227 return false; 228 } 229 230 return true; 231} 232 233/* callback function used for smb_search */ 234static bool smbcli_list_old_callback(void *private_data, const union smb_search_data *file) 235{ 236 struct search_private *state = (struct search_private*) private_data; 237 struct clilist_file_info *tdl; 238 239 /* add file info to the dirlist pool */ 240 tdl = talloc_realloc(state, 241 state->dirlist, 242 struct clilist_file_info, 243 state->dirlist_len + 1); 244 245 if (!tdl) { 246 return false; 247 } 248 state->dirlist = tdl; 249 state->dirlist_len++; 250 251 interpret_short_filename(state->data_level, file, &state->dirlist[state->total_received]); 252 253 state->total_received++; 254 state->ff_searchcount++; 255 state->id = file->search.id; /* return resume info */ 256 257 return true; 258} 259 260int smbcli_list_old(struct smbcli_tree *tree, const char *Mask, uint16_t attribute, 261 void (*fn)(struct clilist_file_info *, const char *, void *), 262 void *caller_state) 263{ 264 union smb_search_first first_parms; 265 union smb_search_next next_parms; 266 struct search_private state; /* for callbacks */ 267 const int num_asked = 500; 268 int received = 0; 269 bool first = true; 270 int num_received = 0; 271 char *mask; 272 int i; 273 274 /* initialize state for search */ 275 state.mem_ctx = talloc_init("smbcli_list_old"); 276 state.dirlist_len = 0; 277 state.total_received = 0; 278 state.data_level = RAW_SEARCH_DATA_SEARCH; 279 280 state.dirlist = talloc_array(state.mem_ctx, struct clilist_file_info, 281 0); 282 mask = talloc_strdup(state.mem_ctx, Mask); 283 284 while (1) { 285 state.ff_searchcount = 0; 286 if (first) { 287 NTSTATUS status; 288 289 first_parms.search_first.level = RAW_SEARCH_SEARCH; 290 first_parms.search_first.data_level = RAW_SEARCH_DATA_SEARCH; 291 first_parms.search_first.in.max_count = num_asked; 292 first_parms.search_first.in.search_attrib = attribute; 293 first_parms.search_first.in.pattern = mask; 294 295 status = smb_raw_search_first(tree, state.mem_ctx, 296 &first_parms, 297 (void*)&state, 298 smbcli_list_old_callback); 299 300 if (!NT_STATUS_IS_OK(status)) { 301 talloc_free(state.mem_ctx); 302 return -1; 303 } 304 305 received = first_parms.search_first.out.count; 306 if (received <= 0) break; 307 first = false; 308 } else { 309 NTSTATUS status; 310 311 next_parms.search_next.level = RAW_SEARCH_SEARCH; 312 next_parms.search_next.data_level = RAW_SEARCH_DATA_SEARCH; 313 next_parms.search_next.in.max_count = num_asked; 314 next_parms.search_next.in.search_attrib = attribute; 315 next_parms.search_next.in.id = state.id; 316 317 status = smb_raw_search_next(tree, state.mem_ctx, 318 &next_parms, 319 (void*)&state, 320 smbcli_list_old_callback); 321 322 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) { 323 break; 324 } 325 if (!NT_STATUS_IS_OK(status)) { 326 talloc_free(state.mem_ctx); 327 return -1; 328 } 329 received = next_parms.search_next.out.count; 330 if (received <= 0) break; 331 } 332 333 num_received += received; 334 } 335 336 for (i=0;i<state.total_received;i++) { 337 fn(&state.dirlist[i], Mask, caller_state); 338 } 339 340 talloc_free(state.mem_ctx); 341 342 return state.total_received; 343} 344 345/**************************************************************************** 346 Do a directory listing, calling fn on each file found. 347 This auto-switches between old and new style. 348****************************************************************************/ 349 350int smbcli_list(struct smbcli_tree *tree, const char *Mask,uint16_t attribute, 351 void (*fn)(struct clilist_file_info *, const char *, void *), void *state) 352{ 353 if (tree->session->transport->negotiate.protocol <= PROTOCOL_LANMAN1) 354 return smbcli_list_old(tree, Mask, attribute, fn, state); 355 return smbcli_list_new(tree, Mask, attribute, RAW_SEARCH_DATA_GENERIC, fn, state); 356} 357