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 2 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, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21 
22 #include "includes.h"
23 #include "libcli/libcli.h"
24 #include "libcli/raw/libcliraw.h"
25 
26 struct 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 ****************************************************************************/
interpret_long_filename(enum smb_search_data_level level,union smb_search_data * info,struct clilist_file_info * finfo)41 static BOOL interpret_long_filename(enum smb_search_data_level level,
42 				    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 */
smbcli_list_new_callback(void * private,union smb_search_data * file)76 static BOOL smbcli_list_new_callback(void *private, union smb_search_data *file)
77 {
78 	struct search_private *state = (struct search_private*) private;
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 
smbcli_list_new(struct smbcli_tree * tree,const char * Mask,uint16_t attribute,enum smb_search_data_level level,void (* fn)(struct clilist_file_info *,const char *,void *),void * caller_state)101 int 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_new(state.mem_ctx);
123 	mask = talloc_strdup(state.mem_ctx, Mask);
124 
125 	if (level == RAW_SEARCH_DATA_GENERIC) {
126 		if (tree->session->transport->negotiate.capabilities & CAP_NT_SMBS) {
127 			level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
128 		} else {
129 			level = RAW_SEARCH_DATA_STANDARD;
130 		}
131 	}
132 	state.data_level = level;
133 
134 	while (1) {
135 		state.ff_searchcount = 0;
136 		if (first) {
137 			NTSTATUS status;
138 
139 			first_parms.t2ffirst.level = RAW_SEARCH_TRANS2;
140 			first_parms.t2ffirst.data_level = state.data_level;
141 			first_parms.t2ffirst.in.max_count = max_matches;
142 			first_parms.t2ffirst.in.search_attrib = attribute;
143 			first_parms.t2ffirst.in.pattern = mask;
144 			first_parms.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
145 			first_parms.t2ffirst.in.storage_type = 0;
146 
147 			status = smb_raw_search_first(tree,
148 						      state.mem_ctx, &first_parms,
149 						      (void*)&state, smbcli_list_new_callback);
150 			if (!NT_STATUS_IS_OK(status)) {
151 				talloc_free(state.mem_ctx);
152 				return -1;
153 			}
154 
155 			ff_dir_handle = first_parms.t2ffirst.out.handle;
156 			ff_searchcount = first_parms.t2ffirst.out.count;
157 			ff_eos = first_parms.t2ffirst.out.end_of_search;
158 
159 			received = first_parms.t2ffirst.out.count;
160 			if (received <= 0) break;
161 			if (ff_eos) break;
162 			first = False;
163 		} else {
164 			NTSTATUS status;
165 
166 			next_parms.t2fnext.level = RAW_SEARCH_TRANS2;
167 			next_parms.t2fnext.data_level = state.data_level;
168 			next_parms.t2fnext.in.max_count = max_matches;
169 			next_parms.t2fnext.in.last_name = state.last_name;
170 			next_parms.t2fnext.in.handle = ff_dir_handle;
171 			next_parms.t2fnext.in.resume_key = 0;
172 			next_parms.t2fnext.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
173 
174 			status = smb_raw_search_next(tree,
175 						     state.mem_ctx,
176 						     &next_parms,
177 						     (void*)&state,
178 						     smbcli_list_new_callback);
179 
180 			if (!NT_STATUS_IS_OK(status)) {
181 				return -1;
182 			}
183 			ff_searchcount = next_parms.t2fnext.out.count;
184 			ff_eos = next_parms.t2fnext.out.end_of_search;
185 			received = next_parms.t2fnext.out.count;
186 			if (received <= 0) break;
187 			if (ff_eos) break;
188 		}
189 
190 		num_received += received;
191 	}
192 
193 	for (i=0;i<state.total_received;i++) {
194 		fn(&state.dirlist[i], Mask, caller_state);
195 	}
196 
197 	talloc_free(state.mem_ctx);
198 
199 	return state.total_received;
200 }
201 
202 /****************************************************************************
203  Interpret a short filename structure.
204  The length of the structure is returned.
205 ****************************************************************************/
interpret_short_filename(enum smb_search_data_level level,union smb_search_data * info,struct clilist_file_info * finfo)206 static BOOL interpret_short_filename(enum smb_search_data_level level,
207 				     union smb_search_data *info,
208 				     struct clilist_file_info *finfo)
209 {
210 	struct clilist_file_info finfo2;
211 
212 	if (!finfo) finfo = &finfo2;
213 	ZERO_STRUCTP(finfo);
214 
215 	switch (level) {
216 	case RAW_SEARCH_DATA_SEARCH:
217 		finfo->mtime = info->search.write_time;
218 		finfo->size = info->search.size;
219 		finfo->attrib = info->search.attrib;
220 		finfo->name = info->search.name;
221 		finfo->short_name = info->search.name;
222 		break;
223 
224 	default:
225 		DEBUG(0,("Unhandled level %d in interpret_short_filename\n", (int)level));
226 		return False;
227 	}
228 
229 	return True;
230 }
231 
232 /* callback function used for smb_search */
smbcli_list_old_callback(void * private,union smb_search_data * file)233 static BOOL smbcli_list_old_callback(void *private, union smb_search_data *file)
234 {
235 	struct search_private *state = (struct search_private*) private;
236 	struct clilist_file_info *tdl;
237 
238 	/* add file info to the dirlist pool */
239 	tdl = talloc_realloc(state,
240 			     state->dirlist,
241 			     struct clilist_file_info,
242 			     state->dirlist_len + 1);
243 
244 	if (!tdl) {
245 		return False;
246 	}
247 	state->dirlist = tdl;
248 	state->dirlist_len++;
249 
250 	interpret_short_filename(state->data_level, file, &state->dirlist[state->total_received]);
251 
252 	state->total_received++;
253 	state->ff_searchcount++;
254 	state->id = file->search.id; /* return resume info */
255 
256 	return True;
257 }
258 
smbcli_list_old(struct smbcli_tree * tree,const char * Mask,uint16_t attribute,void (* fn)(struct clilist_file_info *,const char *,void *),void * caller_state)259 int smbcli_list_old(struct smbcli_tree *tree, const char *Mask, uint16_t attribute,
260 		 void (*fn)(struct clilist_file_info *, const char *, void *),
261 		 void *caller_state)
262 {
263 	union smb_search_first first_parms;
264 	union smb_search_next next_parms;
265 	struct search_private state;  /* for callbacks */
266 	const int num_asked = 500;
267 	int received = 0;
268 	BOOL first = True;
269 	int num_received = 0;
270 	char *mask;
271 	int i;
272 
273 	/* initialize state for search */
274 	state.mem_ctx = talloc_init("smbcli_list_old");
275 	state.dirlist_len = 0;
276 	state.total_received = 0;
277 	state.data_level = RAW_SEARCH_DATA_SEARCH;
278 
279 	state.dirlist = talloc_new(state.mem_ctx);
280 	mask = talloc_strdup(state.mem_ctx, Mask);
281 
282 	while (1) {
283 		state.ff_searchcount = 0;
284 		if (first) {
285 			NTSTATUS status;
286 
287 			first_parms.search_first.level = RAW_SEARCH_SEARCH;
288 			first_parms.search_first.data_level = RAW_SEARCH_DATA_SEARCH;
289 			first_parms.search_first.in.max_count = num_asked;
290 			first_parms.search_first.in.search_attrib = attribute;
291 			first_parms.search_first.in.pattern = mask;
292 
293 			status = smb_raw_search_first(tree, state.mem_ctx,
294 						      &first_parms,
295 						      (void*)&state,
296 						      smbcli_list_old_callback);
297 
298 			if (!NT_STATUS_IS_OK(status)) {
299 				talloc_free(state.mem_ctx);
300 				return -1;
301 			}
302 
303 			received = first_parms.search_first.out.count;
304 			if (received <= 0) break;
305 			first = False;
306 		} else {
307 			NTSTATUS status;
308 
309 			next_parms.search_next.level = RAW_SEARCH_SEARCH;
310 			next_parms.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
311 			next_parms.search_next.in.max_count = num_asked;
312 			next_parms.search_next.in.search_attrib = attribute;
313 			next_parms.search_next.in.id = state.id;
314 
315 			status = smb_raw_search_next(tree, state.mem_ctx,
316 						     &next_parms,
317 						     (void*)&state,
318 						     smbcli_list_old_callback);
319 
320 			if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
321 				break;
322 			}
323 			if (!NT_STATUS_IS_OK(status)) {
324 				talloc_free(state.mem_ctx);
325 				return -1;
326 			}
327 			received = next_parms.search_next.out.count;
328 			if (received <= 0) break;
329 		}
330 
331 		num_received += received;
332 	}
333 
334 	for (i=0;i<state.total_received;i++) {
335 		fn(&state.dirlist[i], Mask, caller_state);
336 	}
337 
338 	talloc_free(state.mem_ctx);
339 
340 	return state.total_received;
341 }
342 
343 /****************************************************************************
344  Do a directory listing, calling fn on each file found.
345  This auto-switches between old and new style.
346 ****************************************************************************/
347 
smbcli_list(struct smbcli_tree * tree,const char * Mask,uint16_t attribute,void (* fn)(struct clilist_file_info *,const char *,void *),void * state)348 int smbcli_list(struct smbcli_tree *tree, const char *Mask,uint16_t attribute,
349 		void (*fn)(struct clilist_file_info *, const char *, void *), void *state)
350 {
351 	if (tree->session->transport->negotiate.protocol <= PROTOCOL_LANMAN1)
352 		return smbcli_list_old(tree, Mask, attribute, fn, state);
353 	return smbcli_list_new(tree, Mask, attribute, RAW_SEARCH_DATA_GENERIC, fn, state);
354 }
355