~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/ntvfs/posix/pvfs_search.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
   Unix SMB/CIFS implementation.
 
3
 
 
4
   POSIX NTVFS backend - directory search functions
 
5
 
 
6
   Copyright (C) Andrew Tridgell 2004
 
7
 
 
8
   This program is free software; you can redistribute it and/or modify
 
9
   it under the terms of the GNU General Public License as published by
 
10
   the Free Software Foundation; either version 3 of the License, or
 
11
   (at your option) any later version.
 
12
   
 
13
   This program is distributed in the hope that it will be useful,
 
14
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
   GNU General Public License for more details.
 
17
   
 
18
   You should have received a copy of the GNU General Public License
 
19
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
*/
 
21
 
 
22
#include "includes.h"
 
23
#include "vfs_posix.h"
 
24
#include "system/time.h"
 
25
#include "librpc/gen_ndr/security.h"
 
26
#include "smbd/service_stream.h"
 
27
#include "lib/events/events.h"
 
28
#include "../lib/util/dlinklist.h"
 
29
 
 
30
/* place a reasonable limit on old-style searches as clients tend to
 
31
   not send search close requests */
 
32
#define MAX_OLD_SEARCHES 2000
 
33
#define MAX_SEARCH_HANDLES (UINT16_MAX - 1)
 
34
#define INVALID_SEARCH_HANDLE UINT16_MAX
 
35
 
 
36
/*
 
37
  destroy an open search
 
38
*/
 
39
static int pvfs_search_destructor(struct pvfs_search_state *search)
 
40
{
 
41
        DLIST_REMOVE(search->pvfs->search.list, search);
 
42
        idr_remove(search->pvfs->search.idtree, search->handle);
 
43
        return 0;
 
44
}
 
45
 
 
46
/*
 
47
  called when a search timer goes off
 
48
*/
 
49
static void pvfs_search_timer(struct tevent_context *ev, struct tevent_timer *te, 
 
50
                                      struct timeval t, void *ptr)
 
51
{
 
52
        struct pvfs_search_state *search = talloc_get_type(ptr, struct pvfs_search_state);
 
53
        talloc_free(search);
 
54
}
 
55
 
 
56
/*
 
57
  setup a timer to destroy a open search after a inactivity period
 
58
*/
 
59
static void pvfs_search_setup_timer(struct pvfs_search_state *search)
 
60
{
 
61
        struct tevent_context *ev = search->pvfs->ntvfs->ctx->event_ctx;
 
62
        if (search->handle == INVALID_SEARCH_HANDLE) return;
 
63
        talloc_free(search->te);
 
64
        search->te = event_add_timed(ev, search, 
 
65
                                     timeval_current_ofs(search->pvfs->search.inactivity_time, 0), 
 
66
                                     pvfs_search_timer, search);
 
67
}
 
68
 
 
69
/*
 
70
  fill in a single search result for a given info level
 
71
*/
 
72
static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
 
73
                                 enum smb_search_data_level level,
 
74
                                 const char *unix_path,
 
75
                                 const char *fname, 
 
76
                                 struct pvfs_search_state *search,
 
77
                                 off_t dir_offset,
 
78
                                 union smb_search_data *file)
 
79
{
 
80
        struct pvfs_filename *name;
 
81
        NTSTATUS status;
 
82
        const char *shortname;
 
83
        uint32_t dir_index = (uint32_t)dir_offset; /* truncated - see the code 
 
84
                                                      in pvfs_list_seek_ofs() for 
 
85
                                                      how we cope with this */
 
86
 
 
87
        status = pvfs_resolve_partial(pvfs, file, unix_path, fname, 0, &name);
 
88
        if (!NT_STATUS_IS_OK(status)) {
 
89
                return status;
 
90
        }
 
91
 
 
92
        status = pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib);
 
93
        if (!NT_STATUS_IS_OK(status)) {
 
94
                return status;
 
95
        }
 
96
 
 
97
        switch (level) {
 
98
        case RAW_SEARCH_DATA_SEARCH:
 
99
                shortname = pvfs_short_name(pvfs, name, name);
 
100
                file->search.attrib           = name->dos.attrib;
 
101
                file->search.write_time       = nt_time_to_unix(name->dos.write_time);
 
102
                file->search.size             = name->st.st_size;
 
103
                file->search.name             = shortname;
 
104
                file->search.id.reserved      = search->handle >> 8;
 
105
                memset(file->search.id.name, ' ', sizeof(file->search.id.name));
 
106
                memcpy(file->search.id.name, shortname, 
 
107
                       MIN(strlen(shortname)+1, sizeof(file->search.id.name)));
 
108
                file->search.id.handle        = search->handle & 0xFF;
 
109
                file->search.id.server_cookie = dir_index;
 
110
                file->search.id.client_cookie = 0;
 
111
                return NT_STATUS_OK;
 
112
 
 
113
        case RAW_SEARCH_DATA_STANDARD:
 
114
                file->standard.resume_key   = dir_index;
 
115
                file->standard.create_time  = nt_time_to_unix(name->dos.create_time);
 
116
                file->standard.access_time  = nt_time_to_unix(name->dos.access_time);
 
117
                file->standard.write_time   = nt_time_to_unix(name->dos.write_time);
 
118
                file->standard.size         = name->st.st_size;
 
119
                file->standard.alloc_size   = name->dos.alloc_size;
 
120
                file->standard.attrib       = name->dos.attrib;
 
121
                file->standard.name.s       = fname;
 
122
                return NT_STATUS_OK;
 
123
 
 
124
        case RAW_SEARCH_DATA_EA_SIZE:
 
125
                file->ea_size.resume_key   = dir_index;
 
126
                file->ea_size.create_time  = nt_time_to_unix(name->dos.create_time);
 
127
                file->ea_size.access_time  = nt_time_to_unix(name->dos.access_time);
 
128
                file->ea_size.write_time   = nt_time_to_unix(name->dos.write_time);
 
129
                file->ea_size.size         = name->st.st_size;
 
130
                file->ea_size.alloc_size   = name->dos.alloc_size;
 
131
                file->ea_size.attrib       = name->dos.attrib;
 
132
                file->ea_size.ea_size      = name->dos.ea_size;
 
133
                file->ea_size.name.s       = fname;
 
134
                return NT_STATUS_OK;
 
135
 
 
136
        case RAW_SEARCH_DATA_EA_LIST:
 
137
                file->ea_list.resume_key   = dir_index;
 
138
                file->ea_list.create_time  = nt_time_to_unix(name->dos.create_time);
 
139
                file->ea_list.access_time  = nt_time_to_unix(name->dos.access_time);
 
140
                file->ea_list.write_time   = nt_time_to_unix(name->dos.write_time);
 
141
                file->ea_list.size         = name->st.st_size;
 
142
                file->ea_list.alloc_size   = name->dos.alloc_size;
 
143
                file->ea_list.attrib       = name->dos.attrib;
 
144
                file->ea_list.name.s       = fname;
 
145
                return pvfs_query_ea_list(pvfs, file, name, -1, 
 
146
                                          search->num_ea_names,
 
147
                                          search->ea_names,
 
148
                                          &file->ea_list.eas);
 
149
 
 
150
        case RAW_SEARCH_DATA_DIRECTORY_INFO:
 
151
                file->directory_info.file_index   = dir_index;
 
152
                file->directory_info.create_time  = name->dos.create_time;
 
153
                file->directory_info.access_time  = name->dos.access_time;
 
154
                file->directory_info.write_time   = name->dos.write_time;
 
155
                file->directory_info.change_time  = name->dos.change_time;
 
156
                file->directory_info.size         = name->st.st_size;
 
157
                file->directory_info.alloc_size   = name->dos.alloc_size;
 
158
                file->directory_info.attrib       = name->dos.attrib;
 
159
                file->directory_info.name.s       = fname;
 
160
                return NT_STATUS_OK;
 
161
 
 
162
        case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
 
163
                file->full_directory_info.file_index   = dir_index;
 
164
                file->full_directory_info.create_time  = name->dos.create_time;
 
165
                file->full_directory_info.access_time  = name->dos.access_time;
 
166
                file->full_directory_info.write_time   = name->dos.write_time;
 
167
                file->full_directory_info.change_time  = name->dos.change_time;
 
168
                file->full_directory_info.size         = name->st.st_size;
 
169
                file->full_directory_info.alloc_size   = name->dos.alloc_size;
 
170
                file->full_directory_info.attrib       = name->dos.attrib;
 
171
                file->full_directory_info.ea_size      = name->dos.ea_size;
 
172
                file->full_directory_info.name.s       = fname;
 
173
                return NT_STATUS_OK;
 
174
 
 
175
        case RAW_SEARCH_DATA_NAME_INFO:
 
176
                file->name_info.file_index   = dir_index;
 
177
                file->name_info.name.s       = fname;
 
178
                return NT_STATUS_OK;
 
179
 
 
180
        case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
 
181
                file->both_directory_info.file_index   = dir_index;
 
182
                file->both_directory_info.create_time  = name->dos.create_time;
 
183
                file->both_directory_info.access_time  = name->dos.access_time;
 
184
                file->both_directory_info.write_time   = name->dos.write_time;
 
185
                file->both_directory_info.change_time  = name->dos.change_time;
 
186
                file->both_directory_info.size         = name->st.st_size;
 
187
                file->both_directory_info.alloc_size   = name->dos.alloc_size;
 
188
                file->both_directory_info.attrib       = name->dos.attrib;
 
189
                file->both_directory_info.ea_size      = name->dos.ea_size;
 
190
                file->both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
 
191
                file->both_directory_info.name.s       = fname;
 
192
                return NT_STATUS_OK;
 
193
 
 
194
        case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
 
195
                file->id_full_directory_info.file_index   = dir_index;
 
196
                file->id_full_directory_info.create_time  = name->dos.create_time;
 
197
                file->id_full_directory_info.access_time  = name->dos.access_time;
 
198
                file->id_full_directory_info.write_time   = name->dos.write_time;
 
199
                file->id_full_directory_info.change_time  = name->dos.change_time;
 
200
                file->id_full_directory_info.size         = name->st.st_size;
 
201
                file->id_full_directory_info.alloc_size   = name->dos.alloc_size;
 
202
                file->id_full_directory_info.attrib       = name->dos.attrib;
 
203
                file->id_full_directory_info.ea_size      = name->dos.ea_size;
 
204
                file->id_full_directory_info.file_id      = name->dos.file_id;
 
205
                file->id_full_directory_info.name.s       = fname;
 
206
                return NT_STATUS_OK;
 
207
 
 
208
        case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
 
209
                file->id_both_directory_info.file_index   = dir_index;
 
210
                file->id_both_directory_info.create_time  = name->dos.create_time;
 
211
                file->id_both_directory_info.access_time  = name->dos.access_time;
 
212
                file->id_both_directory_info.write_time   = name->dos.write_time;
 
213
                file->id_both_directory_info.change_time  = name->dos.change_time;
 
214
                file->id_both_directory_info.size         = name->st.st_size;
 
215
                file->id_both_directory_info.alloc_size   = name->dos.alloc_size;
 
216
                file->id_both_directory_info.attrib       = name->dos.attrib;
 
217
                file->id_both_directory_info.ea_size      = name->dos.ea_size;
 
218
                file->id_both_directory_info.file_id      = name->dos.file_id;
 
219
                file->id_both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
 
220
                file->id_both_directory_info.name.s       = fname;
 
221
                return NT_STATUS_OK;
 
222
 
 
223
        case RAW_SEARCH_DATA_GENERIC:
 
224
                break;
 
225
        }
 
226
 
 
227
        return NT_STATUS_INVALID_LEVEL;
 
228
}
 
229
 
 
230
 
 
231
/*
 
232
  the search fill loop
 
233
*/
 
234
static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, 
 
235
                                 uint_t max_count, 
 
236
                                 struct pvfs_search_state *search,
 
237
                                 enum smb_search_data_level level,
 
238
                                 uint_t *reply_count,
 
239
                                 void *search_private, 
 
240
                                 bool (*callback)(void *, const union smb_search_data *))
 
241
{
 
242
        struct pvfs_dir *dir = search->dir;
 
243
        NTSTATUS status;
 
244
 
 
245
        *reply_count = 0;
 
246
 
 
247
        if (max_count == 0) {
 
248
                max_count = 1;
 
249
        }
 
250
 
 
251
        while ((*reply_count) < max_count) {
 
252
                union smb_search_data *file;
 
253
                const char *name;
 
254
                off_t ofs = search->current_index;
 
255
 
 
256
                name = pvfs_list_next(dir, &search->current_index);
 
257
                if (name == NULL) break;
 
258
 
 
259
                file = talloc(mem_ctx, union smb_search_data);
 
260
                if (!file) {
 
261
                        return NT_STATUS_NO_MEMORY;
 
262
                }
 
263
 
 
264
                status = fill_search_info(pvfs, level, 
 
265
                                          pvfs_list_unix_path(dir), name, 
 
266
                                          search, search->current_index, file);
 
267
                if (!NT_STATUS_IS_OK(status)) {
 
268
                        talloc_free(file);
 
269
                        continue;
 
270
                }
 
271
 
 
272
                if (!callback(search_private, file)) {
 
273
                        talloc_free(file);
 
274
                        search->current_index = ofs;
 
275
                        break;
 
276
                }
 
277
 
 
278
                (*reply_count)++;
 
279
                talloc_free(file);
 
280
        }
 
281
 
 
282
        pvfs_search_setup_timer(search);
 
283
 
 
284
        return NT_STATUS_OK;
 
285
}
 
286
 
 
287
/*
 
288
  we've run out of search handles - cleanup those that the client forgot
 
289
  to close
 
290
*/
 
291
static void pvfs_search_cleanup(struct pvfs_state *pvfs)
 
292
{
 
293
        int i;
 
294
        time_t t = time(NULL);
 
295
 
 
296
        for (i=0;i<MAX_OLD_SEARCHES;i++) {
 
297
                struct pvfs_search_state *search;
 
298
                void *p = idr_find(pvfs->search.idtree, i);
 
299
 
 
300
                if (p == NULL) return;
 
301
 
 
302
                search = talloc_get_type(p, struct pvfs_search_state);
 
303
                if (pvfs_list_eos(search->dir, search->current_index) &&
 
304
                    search->last_used != 0 &&
 
305
                    t > search->last_used + 30) {
 
306
                        /* its almost certainly been forgotten
 
307
                         about */
 
308
                        talloc_free(search);
 
309
                }
 
310
        }
 
311
}
 
312
 
 
313
 
 
314
/* 
 
315
   list files in a directory matching a wildcard pattern - old SMBsearch interface
 
316
*/
 
317
static NTSTATUS pvfs_search_first_old(struct ntvfs_module_context *ntvfs,
 
318
                                      struct ntvfs_request *req, union smb_search_first *io, 
 
319
                                      void *search_private, 
 
320
                                      bool (*callback)(void *, const union smb_search_data *))
 
321
{
 
322
        struct pvfs_dir *dir;
 
323
        struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
 
324
                                  struct pvfs_state);
 
325
        struct pvfs_search_state *search;
 
326
        uint_t reply_count;
 
327
        uint16_t search_attrib;
 
328
        const char *pattern;
 
329
        NTSTATUS status;
 
330
        struct pvfs_filename *name;
 
331
        int id;
 
332
 
 
333
        search_attrib = io->search_first.in.search_attrib;
 
334
        pattern       = io->search_first.in.pattern;
 
335
 
 
336
        /* resolve the cifs name to a posix name */
 
337
        status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
 
338
        if (!NT_STATUS_IS_OK(status)) {
 
339
                return status;
 
340
        }
 
341
 
 
342
        if (!name->has_wildcard && !name->exists) {
 
343
                return STATUS_NO_MORE_FILES;
 
344
        }
 
345
 
 
346
        status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
 
347
        if (!NT_STATUS_IS_OK(status)) {
 
348
                return status;
 
349
        }
 
350
 
 
351
        /* we initially make search a child of the request, then if we
 
352
           need to keep it long term we steal it for the private
 
353
           structure */
 
354
        search = talloc(req, struct pvfs_search_state);
 
355
        if (!search) {
 
356
                return NT_STATUS_NO_MEMORY;
 
357
        }
 
358
 
 
359
        /* do the actual directory listing */
 
360
        status = pvfs_list_start(pvfs, name, search, &dir);
 
361
        if (!NT_STATUS_IS_OK(status)) {
 
362
                return status;
 
363
        }
 
364
 
 
365
        /* we need to give a handle back to the client so it
 
366
           can continue a search */
 
367
        id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
 
368
        if (id == -1) {
 
369
                pvfs_search_cleanup(pvfs);
 
370
                id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
 
371
        }
 
372
        if (id == -1) {
 
373
                return NT_STATUS_INSUFFICIENT_RESOURCES;
 
374
        }
 
375
 
 
376
        search->pvfs = pvfs;
 
377
        search->handle = id;
 
378
        search->dir = dir;
 
379
        search->current_index = 0;
 
380
        search->search_attrib = search_attrib & 0xFF;
 
381
        search->must_attrib = (search_attrib>>8) & 0xFF;
 
382
        search->last_used = time(NULL);
 
383
        search->te = NULL;
 
384
 
 
385
        DLIST_ADD(pvfs->search.list, search);
 
386
 
 
387
        talloc_set_destructor(search, pvfs_search_destructor);
 
388
 
 
389
        status = pvfs_search_fill(pvfs, req, io->search_first.in.max_count, search, io->generic.data_level,
 
390
                                  &reply_count, search_private, callback);
 
391
        if (!NT_STATUS_IS_OK(status)) {
 
392
                return status;
 
393
        }
 
394
 
 
395
        io->search_first.out.count = reply_count;
 
396
 
 
397
        /* not matching any entries is an error */
 
398
        if (reply_count == 0) {
 
399
                return STATUS_NO_MORE_FILES;
 
400
        }
 
401
 
 
402
        talloc_steal(pvfs, search);
 
403
 
 
404
        return NT_STATUS_OK;
 
405
}
 
406
 
 
407
/* continue a old style search */
 
408
static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs,
 
409
                                     struct ntvfs_request *req, union smb_search_next *io, 
 
410
                                     void *search_private, 
 
411
                                     bool (*callback)(void *, const union smb_search_data *))
 
412
{
 
413
        struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
 
414
                                  struct pvfs_state);
 
415
        void *p;
 
416
        struct pvfs_search_state *search;
 
417
        struct pvfs_dir *dir;
 
418
        uint_t reply_count, max_count;
 
419
        uint16_t handle;
 
420
        NTSTATUS status;
 
421
 
 
422
        handle    = io->search_next.in.id.handle | (io->search_next.in.id.reserved<<8);
 
423
        max_count = io->search_next.in.max_count;
 
424
 
 
425
        p = idr_find(pvfs->search.idtree, handle);
 
426
        if (p == NULL) {
 
427
                /* we didn't find the search handle */
 
428
                return NT_STATUS_INVALID_HANDLE;
 
429
        }
 
430
 
 
431
        search = talloc_get_type(p, struct pvfs_search_state);
 
432
 
 
433
        dir = search->dir;
 
434
 
 
435
        status = pvfs_list_seek_ofs(dir, io->search_next.in.id.server_cookie, 
 
436
                                    &search->current_index);
 
437
        if (!NT_STATUS_IS_OK(status)) {
 
438
                return status;
 
439
        }
 
440
        search->last_used = time(NULL);
 
441
 
 
442
        status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
 
443
                                  &reply_count, search_private, callback);
 
444
        if (!NT_STATUS_IS_OK(status)) {
 
445
                return status;
 
446
        }
 
447
 
 
448
        io->search_next.out.count = reply_count;
 
449
 
 
450
        /* not matching any entries means end of search */
 
451
        if (reply_count == 0) {
 
452
                talloc_free(search);
 
453
        }
 
454
 
 
455
        return NT_STATUS_OK;
 
456
}
 
457
 
 
458
/* 
 
459
   list files in a directory matching a wildcard pattern
 
460
*/
 
461
static NTSTATUS pvfs_search_first_trans2(struct ntvfs_module_context *ntvfs,
 
462
                                         struct ntvfs_request *req, union smb_search_first *io, 
 
463
                                         void *search_private, 
 
464
                                         bool (*callback)(void *, const union smb_search_data *))
 
465
{
 
466
        struct pvfs_dir *dir;
 
467
        struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
 
468
                                  struct pvfs_state);
 
469
        struct pvfs_search_state *search;
 
470
        uint_t reply_count;
 
471
        uint16_t search_attrib, max_count;
 
472
        const char *pattern;
 
473
        NTSTATUS status;
 
474
        struct pvfs_filename *name;
 
475
        int id;
 
476
 
 
477
        search_attrib = io->t2ffirst.in.search_attrib;
 
478
        pattern       = io->t2ffirst.in.pattern;
 
479
        max_count     = io->t2ffirst.in.max_count;
 
480
 
 
481
        /* resolve the cifs name to a posix name */
 
482
        status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
 
483
        if (!NT_STATUS_IS_OK(status)) {
 
484
                return status;
 
485
        }
 
486
 
 
487
        if (!name->has_wildcard && !name->exists) {
 
488
                return NT_STATUS_NO_SUCH_FILE;
 
489
        }
 
490
 
 
491
        status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
 
492
        if (!NT_STATUS_IS_OK(status)) {
 
493
                return status;
 
494
        }
 
495
 
 
496
        /* we initially make search a child of the request, then if we
 
497
           need to keep it long term we steal it for the private
 
498
           structure */
 
499
        search = talloc(req, struct pvfs_search_state);
 
500
        if (!search) {
 
501
                return NT_STATUS_NO_MEMORY;
 
502
        }
 
503
 
 
504
        /* do the actual directory listing */
 
505
        status = pvfs_list_start(pvfs, name, search, &dir);
 
506
        if (!NT_STATUS_IS_OK(status)) {
 
507
                return status;
 
508
        }
 
509
 
 
510
        id = idr_get_new(pvfs->search.idtree, search, MAX_SEARCH_HANDLES);
 
511
        if (id == -1) {
 
512
                return NT_STATUS_INSUFFICIENT_RESOURCES;
 
513
        }
 
514
 
 
515
        search->pvfs = pvfs;
 
516
        search->handle = id;
 
517
        search->dir = dir;
 
518
        search->current_index = 0;
 
519
        search->search_attrib = search_attrib;
 
520
        search->must_attrib = 0;
 
521
        search->last_used = 0;
 
522
        search->num_ea_names = io->t2ffirst.in.num_names;
 
523
        search->ea_names = io->t2ffirst.in.ea_names;
 
524
        search->te = NULL;
 
525
 
 
526
        DLIST_ADD(pvfs->search.list, search);
 
527
        talloc_set_destructor(search, pvfs_search_destructor);
 
528
 
 
529
        status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
 
530
                                  &reply_count, search_private, callback);
 
531
        if (!NT_STATUS_IS_OK(status)) {
 
532
                return status;
 
533
        }
 
534
 
 
535
        /* not matching any entries is an error */
 
536
        if (reply_count == 0) {
 
537
                return NT_STATUS_NO_SUCH_FILE;
 
538
        }
 
539
 
 
540
        io->t2ffirst.out.count = reply_count;
 
541
        io->t2ffirst.out.handle = search->handle;
 
542
        io->t2ffirst.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
 
543
 
 
544
        /* work out if we are going to keep the search state
 
545
           and allow for a search continue */
 
546
        if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
 
547
            ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && 
 
548
             io->t2ffirst.out.end_of_search)) {
 
549
                talloc_free(search);
 
550
        } else {
 
551
                talloc_steal(pvfs, search);
 
552
        }
 
553
 
 
554
        return NT_STATUS_OK;
 
555
}
 
556
 
 
557
/* continue a search */
 
558
static NTSTATUS pvfs_search_next_trans2(struct ntvfs_module_context *ntvfs,
 
559
                                        struct ntvfs_request *req, union smb_search_next *io, 
 
560
                                        void *search_private, 
 
561
                                        bool (*callback)(void *, const union smb_search_data *))
 
562
{
 
563
        struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
 
564
                                  struct pvfs_state);
 
565
        void *p;
 
566
        struct pvfs_search_state *search;
 
567
        struct pvfs_dir *dir;
 
568
        uint_t reply_count;
 
569
        uint16_t handle;
 
570
        NTSTATUS status;
 
571
 
 
572
        handle = io->t2fnext.in.handle;
 
573
 
 
574
        p = idr_find(pvfs->search.idtree, handle);
 
575
        if (p == NULL) {
 
576
                /* we didn't find the search handle */
 
577
                return NT_STATUS_INVALID_HANDLE;
 
578
        }
 
579
 
 
580
        search = talloc_get_type(p, struct pvfs_search_state);
 
581
 
 
582
        dir = search->dir;
 
583
        
 
584
        status = NT_STATUS_OK;
 
585
 
 
586
        /* work out what type of continuation is being used */
 
587
        if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
 
588
                status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
 
589
                if (!NT_STATUS_IS_OK(status) && io->t2fnext.in.resume_key) {
 
590
                        status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key, 
 
591
                                                    &search->current_index);
 
592
                }
 
593
        } else if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE)) {
 
594
                status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key, 
 
595
                                            &search->current_index);
 
596
        }
 
597
        if (!NT_STATUS_IS_OK(status)) {
 
598
                return status;
 
599
        }
 
600
 
 
601
        search->num_ea_names = io->t2fnext.in.num_names;
 
602
        search->ea_names = io->t2fnext.in.ea_names;
 
603
 
 
604
        status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.data_level,
 
605
                                  &reply_count, search_private, callback);
 
606
        if (!NT_STATUS_IS_OK(status)) {
 
607
                return status;
 
608
        }
 
609
 
 
610
        io->t2fnext.out.count = reply_count;
 
611
        io->t2fnext.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
 
612
 
 
613
        /* work out if we are going to keep the search state */
 
614
        if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
 
615
            ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && 
 
616
             io->t2fnext.out.end_of_search)) {
 
617
                talloc_free(search);
 
618
        }
 
619
 
 
620
        return NT_STATUS_OK;
 
621
}
 
622
 
 
623
static NTSTATUS pvfs_search_first_smb2(struct ntvfs_module_context *ntvfs,
 
624
                                       struct ntvfs_request *req, const struct smb2_find *io, 
 
625
                                       void *search_private, 
 
626
                                       bool (*callback)(void *, const union smb_search_data *))
 
627
{
 
628
        struct pvfs_dir *dir;
 
629
        struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
 
630
                                  struct pvfs_state);
 
631
        struct pvfs_search_state *search;
 
632
        uint_t reply_count;
 
633
        uint16_t max_count;
 
634
        const char *pattern;
 
635
        NTSTATUS status;
 
636
        struct pvfs_filename *name;
 
637
        struct pvfs_file *f;
 
638
 
 
639
        f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
 
640
        if (!f) {
 
641
                return NT_STATUS_FILE_CLOSED;
 
642
        }
 
643
 
 
644
        /* its only valid for directories */
 
645
        if (f->handle->fd != -1) {
 
646
                return NT_STATUS_INVALID_PARAMETER;
 
647
        }
 
648
 
 
649
        if (!(f->access_mask & SEC_DIR_LIST)) {
 
650
                return NT_STATUS_ACCESS_DENIED;
 
651
        }
 
652
 
 
653
        if (f->search) {
 
654
                talloc_free(f->search);
 
655
                f->search = NULL;
 
656
        }
 
657
 
 
658
        if (strequal(io->in.pattern, "")) {
 
659
                return NT_STATUS_OBJECT_NAME_INVALID;
 
660
        }
 
661
        if (strchr_m(io->in.pattern, '\\')) {
 
662
                return NT_STATUS_OBJECT_NAME_INVALID;
 
663
        }
 
664
        if (strchr_m(io->in.pattern, '/')) {
 
665
                return NT_STATUS_OBJECT_NAME_INVALID;
 
666
        }
 
667
 
 
668
        if (strequal("", f->handle->name->original_name)) {
 
669
                pattern = talloc_asprintf(req, "\\%s", io->in.pattern);
 
670
                NT_STATUS_HAVE_NO_MEMORY(pattern);
 
671
        } else {
 
672
                pattern = talloc_asprintf(req, "\\%s\\%s",
 
673
                                          f->handle->name->original_name,
 
674
                                          io->in.pattern);
 
675
                NT_STATUS_HAVE_NO_MEMORY(pattern);
 
676
        }
 
677
 
 
678
        /* resolve the cifs name to a posix name */
 
679
        status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
 
680
        NT_STATUS_NOT_OK_RETURN(status);
 
681
 
 
682
        if (!name->has_wildcard && !name->exists) {
 
683
                return NT_STATUS_NO_SUCH_FILE;
 
684
        }
 
685
 
 
686
        /* we initially make search a child of the request, then if we
 
687
           need to keep it long term we steal it for the private
 
688
           structure */
 
689
        search = talloc(req, struct pvfs_search_state);
 
690
        NT_STATUS_HAVE_NO_MEMORY(search);
 
691
 
 
692
        /* do the actual directory listing */
 
693
        status = pvfs_list_start(pvfs, name, search, &dir);
 
694
        NT_STATUS_NOT_OK_RETURN(status);
 
695
 
 
696
        search->pvfs            = pvfs;
 
697
        search->handle          = INVALID_SEARCH_HANDLE;
 
698
        search->dir             = dir;
 
699
        search->current_index   = 0;
 
700
        search->search_attrib   = 0x0000FFFF;
 
701
        search->must_attrib     = 0;
 
702
        search->last_used       = 0;
 
703
        search->num_ea_names    = 0;
 
704
        search->ea_names        = NULL;
 
705
        search->te              = NULL;
 
706
 
 
707
        if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
 
708
                max_count = 1;
 
709
        } else {
 
710
                max_count = UINT16_MAX;
 
711
        }
 
712
 
 
713
        status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
 
714
                                  &reply_count, search_private, callback);
 
715
        NT_STATUS_NOT_OK_RETURN(status);
 
716
 
 
717
        /* not matching any entries is an error */
 
718
        if (reply_count == 0) {
 
719
                return NT_STATUS_NO_SUCH_FILE;
 
720
        }
 
721
 
 
722
        f->search = talloc_steal(f, search);
 
723
 
 
724
        return NT_STATUS_OK;
 
725
}
 
726
 
 
727
static NTSTATUS pvfs_search_next_smb2(struct ntvfs_module_context *ntvfs,
 
728
                                      struct ntvfs_request *req, const struct smb2_find *io, 
 
729
                                      void *search_private, 
 
730
                                      bool (*callback)(void *, const union smb_search_data *))
 
731
{
 
732
        struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
 
733
                                  struct pvfs_state);
 
734
        struct pvfs_search_state *search;
 
735
        uint_t reply_count;
 
736
        uint16_t max_count;
 
737
        NTSTATUS status;
 
738
        struct pvfs_file *f;
 
739
 
 
740
        f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
 
741
        if (!f) {
 
742
                return NT_STATUS_FILE_CLOSED;
 
743
        }
 
744
 
 
745
        /* its only valid for directories */
 
746
        if (f->handle->fd != -1) {
 
747
                return NT_STATUS_INVALID_PARAMETER;
 
748
        }
 
749
 
 
750
        /* if there's no search started on the dir handle, it's like a search_first */
 
751
        search = f->search;
 
752
        if (!search) {
 
753
                return pvfs_search_first_smb2(ntvfs, req, io, search_private, callback);
 
754
        }
 
755
 
 
756
        if (io->in.continue_flags & SMB2_CONTINUE_FLAG_RESTART) {
 
757
                search->current_index = 0;
 
758
        }
 
759
 
 
760
        if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
 
761
                max_count = 1;
 
762
        } else {
 
763
                max_count = UINT16_MAX;
 
764
        }
 
765
 
 
766
        status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
 
767
                                  &reply_count, search_private, callback);
 
768
        NT_STATUS_NOT_OK_RETURN(status);
 
769
 
 
770
        /* not matching any entries is an error */
 
771
        if (reply_count == 0) {
 
772
                return STATUS_NO_MORE_FILES;
 
773
        }
 
774
 
 
775
        return NT_STATUS_OK;
 
776
}
 
777
 
 
778
/* 
 
779
   list files in a directory matching a wildcard pattern
 
780
*/
 
781
NTSTATUS pvfs_search_first(struct ntvfs_module_context *ntvfs,
 
782
                           struct ntvfs_request *req, union smb_search_first *io, 
 
783
                           void *search_private, 
 
784
                           bool (*callback)(void *, const union smb_search_data *))
 
785
{
 
786
        switch (io->generic.level) {
 
787
        case RAW_SEARCH_SEARCH:
 
788
        case RAW_SEARCH_FFIRST:
 
789
        case RAW_SEARCH_FUNIQUE:
 
790
                return pvfs_search_first_old(ntvfs, req, io, search_private, callback);
 
791
 
 
792
        case RAW_SEARCH_TRANS2:
 
793
                return pvfs_search_first_trans2(ntvfs, req, io, search_private, callback);
 
794
 
 
795
        case RAW_SEARCH_SMB2:
 
796
                return pvfs_search_first_smb2(ntvfs, req, &io->smb2, search_private, callback);
 
797
        }
 
798
 
 
799
        return NT_STATUS_INVALID_LEVEL;
 
800
}
 
801
 
 
802
/* continue a search */
 
803
NTSTATUS pvfs_search_next(struct ntvfs_module_context *ntvfs,
 
804
                          struct ntvfs_request *req, union smb_search_next *io, 
 
805
                          void *search_private, 
 
806
                          bool (*callback)(void *, const union smb_search_data *))
 
807
{
 
808
        switch (io->generic.level) {
 
809
        case RAW_SEARCH_SEARCH:
 
810
        case RAW_SEARCH_FFIRST:
 
811
                return pvfs_search_next_old(ntvfs, req, io, search_private, callback);
 
812
 
 
813
        case RAW_SEARCH_FUNIQUE:
 
814
                return NT_STATUS_INVALID_LEVEL;
 
815
 
 
816
        case RAW_SEARCH_TRANS2:
 
817
                return pvfs_search_next_trans2(ntvfs, req, io, search_private, callback);
 
818
 
 
819
        case RAW_SEARCH_SMB2:
 
820
                return pvfs_search_next_smb2(ntvfs, req, &io->smb2, search_private, callback);
 
821
        }
 
822
 
 
823
        return NT_STATUS_INVALID_LEVEL;
 
824
}
 
825
 
 
826
 
 
827
/* close a search */
 
828
NTSTATUS pvfs_search_close(struct ntvfs_module_context *ntvfs,
 
829
                           struct ntvfs_request *req, union smb_search_close *io)
 
830
{
 
831
        struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
 
832
                                  struct pvfs_state);
 
833
        void *p;
 
834
        struct pvfs_search_state *search;
 
835
        uint16_t handle = INVALID_SEARCH_HANDLE;
 
836
 
 
837
        switch (io->generic.level) {
 
838
        case RAW_FINDCLOSE_GENERIC:
 
839
                return NT_STATUS_INVALID_LEVEL;
 
840
 
 
841
        case RAW_FINDCLOSE_FCLOSE:
 
842
                handle = io->fclose.in.id.handle;
 
843
                break;
 
844
 
 
845
        case RAW_FINDCLOSE_FINDCLOSE:
 
846
                handle = io->findclose.in.handle;
 
847
                break;
 
848
        }
 
849
 
 
850
        p = idr_find(pvfs->search.idtree, handle);
 
851
        if (p == NULL) {
 
852
                /* we didn't find the search handle */
 
853
                return NT_STATUS_INVALID_HANDLE;
 
854
        }
 
855
 
 
856
        search = talloc_get_type(p, struct pvfs_search_state);
 
857
 
 
858
        talloc_free(search);
 
859
 
 
860
        return NT_STATUS_OK;
 
861
}
 
862