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*/ 39static 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*/ 49static 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*/ 59static 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*/ 72static 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*/ 234static 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*/ 291static 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*/ 317static 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 */ 408static 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*/ 461static 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 */ 558static 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 623static 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 727static 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*/ 781NTSTATUS 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 */ 803NTSTATUS 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 */ 828NTSTATUS 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 863