1/* 2 Unix SMB/CIFS implementation. 3 4 CIFS-to-SMB2 NTVFS filesystem backend 5 6 Copyright (C) Andrew Tridgell 2008 7 8 largely based on vfs_cifs.c which was 9 Copyright (C) Andrew Tridgell 2003 10 Copyright (C) James J Myers 2003 <myersjj@samba.org> 11 12 This program is free software; you can redistribute it and/or modify 13 it under the terms of the GNU General Public License as published by 14 the Free Software Foundation; either version 3 of the License, or 15 (at your option) any later version. 16 17 This program is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU General Public License for more details. 21 22 You should have received a copy of the GNU General Public License 23 along with this program. If not, see <http://www.gnu.org/licenses/>. 24*/ 25/* 26 this implements a CIFS->CIFS NTVFS filesystem backend. 27 28*/ 29 30#include "includes.h" 31#include "libcli/raw/libcliraw.h" 32#include "libcli/raw/raw_proto.h" 33#include "libcli/composite/composite.h" 34#include "libcli/smb_composite/smb_composite.h" 35#include "auth/auth.h" 36#include "auth/credentials/credentials.h" 37#include "ntvfs/ntvfs.h" 38#include "../lib/util/dlinklist.h" 39#include "param/param.h" 40#include "libcli/resolve/resolve.h" 41#include "libcli/smb2/smb2.h" 42#include "libcli/smb2/smb2_calls.h" 43 44struct cvfs_file { 45 struct cvfs_file *prev, *next; 46 uint16_t fnum; 47 struct ntvfs_handle *h; 48}; 49 50/* this is stored in ntvfs_private */ 51struct cvfs_private { 52 struct smb2_tree *tree; 53 struct smb2_transport *transport; 54 struct ntvfs_module_context *ntvfs; 55 struct async_info *pending; 56 struct cvfs_file *files; 57 58 /* a handle on the root of the share */ 59 /* TODO: leaving this handle open could prevent other users 60 from opening the share with exclusive access. We probably 61 need to open it on demand */ 62 struct smb2_handle roothandle; 63}; 64 65 66/* a structure used to pass information to an async handler */ 67struct async_info { 68 struct async_info *next, *prev; 69 struct cvfs_private *cvfs; 70 struct ntvfs_request *req; 71 void *c_req; 72 struct composite_context *c_comp; 73 struct cvfs_file *f; 74 void *parms; 75}; 76 77#define SETUP_FILE_HERE(f) do { \ 78 f = ntvfs_handle_get_backend_data(io->generic.in.file.ntvfs, ntvfs); \ 79 if (!f) return NT_STATUS_INVALID_HANDLE; \ 80 io->generic.in.file.fnum = f->fnum; \ 81} while (0) 82 83#define SETUP_FILE do { \ 84 struct cvfs_file *f; \ 85 SETUP_FILE_HERE(f); \ 86} while (0) 87 88#define SMB2_SERVER "smb2:server" 89#define SMB2_USER "smb2:user" 90#define SMB2_PASSWORD "smb2:password" 91#define SMB2_DOMAIN "smb2:domain" 92#define SMB2_SHARE "smb2:share" 93#define SMB2_USE_MACHINE_ACCT "smb2:use-machine-account" 94 95#define SMB2_USE_MACHINE_ACCT_DEFAULT false 96 97/* 98 a handler for oplock break events from the server - these need to be passed 99 along to the client 100 */ 101static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *p_private) 102{ 103 struct cvfs_private *p = p_private; 104 NTSTATUS status; 105 struct ntvfs_handle *h = NULL; 106 struct cvfs_file *f; 107 108 for (f=p->files; f; f=f->next) { 109 if (f->fnum != fnum) continue; 110 h = f->h; 111 break; 112 } 113 114 if (!h) { 115 DEBUG(5,("vfs_smb2: ignoring oplock break level %d for fnum %d\n", level, fnum)); 116 return true; 117 } 118 119 DEBUG(5,("vfs_smb2: sending oplock break level %d for fnum %d\n", level, fnum)); 120 status = ntvfs_send_oplock_break(p->ntvfs, h, level); 121 if (!NT_STATUS_IS_OK(status)) return false; 122 return true; 123} 124 125/* 126 return a handle to the root of the share 127*/ 128static NTSTATUS smb2_get_roothandle(struct smb2_tree *tree, struct smb2_handle *handle) 129{ 130 struct smb2_create io; 131 NTSTATUS status; 132 133 ZERO_STRUCT(io); 134 io.in.oplock_level = 0; 135 io.in.desired_access = SEC_STD_SYNCHRONIZE | SEC_DIR_READ_ATTRIBUTE | SEC_DIR_LIST; 136 io.in.file_attributes = 0; 137 io.in.create_disposition = NTCREATEX_DISP_OPEN; 138 io.in.share_access = 139 NTCREATEX_SHARE_ACCESS_READ | 140 NTCREATEX_SHARE_ACCESS_WRITE| 141 NTCREATEX_SHARE_ACCESS_DELETE; 142 io.in.create_options = 0; 143 io.in.fname = NULL; 144 145 status = smb2_create(tree, tree, &io); 146 NT_STATUS_NOT_OK_RETURN(status); 147 148 *handle = io.out.file.handle; 149 150 return NT_STATUS_OK; 151} 152 153/* 154 connect to a share - used when a tree_connect operation comes in. 155*/ 156static NTSTATUS cvfs_connect(struct ntvfs_module_context *ntvfs, 157 struct ntvfs_request *req, 158 union smb_tcon* tcon) 159{ 160 NTSTATUS status; 161 struct cvfs_private *p; 162 const char *host, *user, *pass, *domain, *remote_share, *sharename; 163 struct composite_context *creq; 164 struct share_config *scfg = ntvfs->ctx->config; 165 struct smb2_tree *tree; 166 struct cli_credentials *credentials; 167 bool machine_account; 168 struct smbcli_options options; 169 170 switch (tcon->generic.level) { 171 case RAW_TCON_TCON: 172 sharename = tcon->tcon.in.service; 173 break; 174 case RAW_TCON_TCONX: 175 sharename = tcon->tconx.in.path; 176 break; 177 case RAW_TCON_SMB2: 178 sharename = tcon->smb2.in.path; 179 break; 180 default: 181 return NT_STATUS_INVALID_LEVEL; 182 } 183 184 if (strncmp(sharename, "\\\\", 2) == 0) { 185 char *str = strchr(sharename+2, '\\'); 186 if (str) { 187 sharename = str + 1; 188 } 189 } 190 191 /* Here we need to determine which server to connect to. 192 * For now we use parametric options, type cifs. 193 * Later we will use security=server and auth_server.c. 194 */ 195 host = share_string_option(scfg, SMB2_SERVER, NULL); 196 user = share_string_option(scfg, SMB2_USER, NULL); 197 pass = share_string_option(scfg, SMB2_PASSWORD, NULL); 198 domain = share_string_option(scfg, SMB2_DOMAIN, NULL); 199 remote_share = share_string_option(scfg, SMB2_SHARE, NULL); 200 if (!remote_share) { 201 remote_share = sharename; 202 } 203 204 machine_account = share_bool_option(scfg, SMB2_USE_MACHINE_ACCT, SMB2_USE_MACHINE_ACCT_DEFAULT); 205 206 p = talloc_zero(ntvfs, struct cvfs_private); 207 if (!p) { 208 return NT_STATUS_NO_MEMORY; 209 } 210 211 ntvfs->private_data = p; 212 213 if (!host) { 214 DEBUG(1,("CIFS backend: You must supply server\n")); 215 return NT_STATUS_INVALID_PARAMETER; 216 } 217 218 if (user && pass) { 219 DEBUG(5, ("CIFS backend: Using specified password\n")); 220 credentials = cli_credentials_init(p); 221 if (!credentials) { 222 return NT_STATUS_NO_MEMORY; 223 } 224 cli_credentials_set_conf(credentials, ntvfs->ctx->lp_ctx); 225 cli_credentials_set_username(credentials, user, CRED_SPECIFIED); 226 if (domain) { 227 cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED); 228 } 229 cli_credentials_set_password(credentials, pass, CRED_SPECIFIED); 230 } else if (machine_account) { 231 DEBUG(5, ("CIFS backend: Using machine account\n")); 232 credentials = cli_credentials_init(p); 233 cli_credentials_set_conf(credentials, ntvfs->ctx->lp_ctx); 234 if (domain) { 235 cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED); 236 } 237 status = cli_credentials_set_machine_account(credentials, ntvfs->ctx->lp_ctx); 238 if (!NT_STATUS_IS_OK(status)) { 239 return status; 240 } 241 } else if (req->session_info->credentials) { 242 DEBUG(5, ("CIFS backend: Using delegated credentials\n")); 243 credentials = req->session_info->credentials; 244 } else { 245 DEBUG(1,("CIFS backend: NO delegated credentials found: You must supply server, user and password or the client must supply delegated credentials\n")); 246 return NT_STATUS_INVALID_PARAMETER; 247 } 248 249 lp_smbcli_options(ntvfs->ctx->lp_ctx, &options); 250 251 creq = smb2_connect_send(p, host, 252 lp_parm_string_list(p, ntvfs->ctx->lp_ctx, NULL, "smb2", "ports", NULL), 253 remote_share, 254 lp_resolve_context(ntvfs->ctx->lp_ctx), 255 credentials, 256 ntvfs->ctx->event_ctx, &options, 257 lp_socket_options(ntvfs->ctx->lp_ctx), 258 lp_gensec_settings(p, ntvfs->ctx->lp_ctx) 259 ); 260 261 status = smb2_connect_recv(creq, p, &tree); 262 NT_STATUS_NOT_OK_RETURN(status); 263 264 status = smb2_get_roothandle(tree, &p->roothandle); 265 NT_STATUS_NOT_OK_RETURN(status); 266 267 p->tree = tree; 268 p->transport = p->tree->session->transport; 269 p->ntvfs = ntvfs; 270 271 ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS"); 272 NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type); 273 ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:"); 274 NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type); 275 276 if (tcon->generic.level == RAW_TCON_TCONX) { 277 tcon->tconx.out.fs_type = ntvfs->ctx->fs_type; 278 tcon->tconx.out.dev_type = ntvfs->ctx->dev_type; 279 } 280 281 /* we need to receive oplock break requests from the server */ 282 /* TODO: enable oplocks 283 smbcli_oplock_handler(p->transport, oplock_handler, p); 284 */ 285 return NT_STATUS_OK; 286} 287 288/* 289 disconnect from a share 290*/ 291static NTSTATUS cvfs_disconnect(struct ntvfs_module_context *ntvfs) 292{ 293 struct cvfs_private *p = ntvfs->private_data; 294 struct async_info *a, *an; 295 296 /* first cleanup pending requests */ 297 for (a=p->pending; a; a = an) { 298 an = a->next; 299 talloc_free(a->c_req); 300 talloc_free(a); 301 } 302 303 talloc_free(p); 304 ntvfs->private_data = NULL; 305 306 return NT_STATUS_OK; 307} 308 309/* 310 destroy an async info structure 311*/ 312static int async_info_destructor(struct async_info *async) 313{ 314 DLIST_REMOVE(async->cvfs->pending, async); 315 return 0; 316} 317 318/* 319 a handler for simple async SMB2 replies 320 this handler can only be used for functions that don't return any 321 parameters (those that just return a status code) 322 */ 323static void async_simple_smb2(struct smb2_request *c_req) 324{ 325 struct async_info *async = c_req->async.private_data; 326 struct ntvfs_request *req = async->req; 327 328 smb2_request_receive(c_req); 329 req->async_states->status = smb2_request_destroy(c_req); 330 talloc_free(async); 331 req->async_states->send_fn(req); 332} 333 334/* 335 a handler for simple async composite replies 336 this handler can only be used for functions that don't return any 337 parameters (those that just return a status code) 338 */ 339static void async_simple_composite(struct composite_context *c_req) 340{ 341 struct async_info *async = c_req->async.private_data; 342 struct ntvfs_request *req = async->req; 343 344 req->async_states->status = composite_wait_free(c_req); 345 talloc_free(async); 346 req->async_states->send_fn(req); 347} 348 349 350/* save some typing for the simple functions */ 351#define ASYNC_RECV_TAIL_F(io, async_fn, file) do { \ 352 if (!c_req) return NT_STATUS_UNSUCCESSFUL; \ 353 { \ 354 struct async_info *async; \ 355 async = talloc(req, struct async_info); \ 356 if (!async) return NT_STATUS_NO_MEMORY; \ 357 async->parms = io; \ 358 async->req = req; \ 359 async->f = file; \ 360 async->cvfs = p; \ 361 async->c_req = c_req; \ 362 DLIST_ADD(p->pending, async); \ 363 c_req->async.private_data = async; \ 364 talloc_set_destructor(async, async_info_destructor); \ 365 } \ 366 c_req->async.fn = async_fn; \ 367 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC; \ 368 return NT_STATUS_OK; \ 369} while (0) 370 371#define ASYNC_RECV_TAIL(io, async_fn) ASYNC_RECV_TAIL_F(io, async_fn, NULL) 372 373#define SIMPLE_ASYNC_TAIL ASYNC_RECV_TAIL(NULL, async_simple_smb2) 374#define SIMPLE_COMPOSITE_TAIL ASYNC_RECV_TAIL(NULL, async_simple_composite) 375 376#define CHECK_ASYNC(req) do { \ 377 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { \ 378 DEBUG(0,("SMB2 proxy backend does not support sync operation at %s\n", \ 379 __location__)); \ 380 return NT_STATUS_NOT_IMPLEMENTED; \ 381 }} while (0) 382 383/* 384 delete a file - the dirtype specifies the file types to include in the search. 385 The name can contain CIFS wildcards, but rarely does (except with OS/2 clients) 386 387 BUGS: 388 - doesn't handle wildcards 389 - doesn't obey attrib restrictions 390*/ 391static NTSTATUS cvfs_unlink(struct ntvfs_module_context *ntvfs, 392 struct ntvfs_request *req, union smb_unlink *unl) 393{ 394 struct cvfs_private *p = ntvfs->private_data; 395 struct composite_context *c_req; 396 397 CHECK_ASYNC(req); 398 399 c_req = smb2_composite_unlink_send(p->tree, unl); 400 401 SIMPLE_COMPOSITE_TAIL; 402} 403 404/* 405 ioctl interface 406*/ 407static NTSTATUS cvfs_ioctl(struct ntvfs_module_context *ntvfs, 408 struct ntvfs_request *req, union smb_ioctl *io) 409{ 410 return NT_STATUS_NOT_IMPLEMENTED; 411} 412 413/* 414 check if a directory exists 415*/ 416static NTSTATUS cvfs_chkpath(struct ntvfs_module_context *ntvfs, 417 struct ntvfs_request *req, union smb_chkpath *cp) 418{ 419 struct cvfs_private *p = ntvfs->private_data; 420 struct smb2_request *c_req; 421 struct smb2_find f; 422 423 CHECK_ASYNC(req); 424 425 /* SMB2 doesn't have a chkpath operation, and also doesn't 426 have a query path info call, so the best seems to be to do a 427 find call, using the roothandle we established at connect 428 time */ 429 ZERO_STRUCT(f); 430 f.in.file.handle = p->roothandle; 431 f.in.level = SMB2_FIND_DIRECTORY_INFO; 432 f.in.pattern = cp->chkpath.in.path; 433 /* SMB2 find doesn't accept \ or the empty string - this is the best 434 approximation */ 435 if (strcmp(f.in.pattern, "\\") == 0 || 436 strcmp(f.in.pattern, "") == 0) { 437 f.in.pattern = "?"; 438 } 439 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE | SMB2_CONTINUE_FLAG_RESTART; 440 f.in.max_response_size = 0x1000; 441 442 c_req = smb2_find_send(p->tree, &f); 443 444 SIMPLE_ASYNC_TAIL; 445} 446 447/* 448 return info on a pathname 449*/ 450static NTSTATUS cvfs_qpathinfo(struct ntvfs_module_context *ntvfs, 451 struct ntvfs_request *req, union smb_fileinfo *info) 452{ 453 return NT_STATUS_NOT_IMPLEMENTED; 454} 455 456/* 457 query info on a open file 458*/ 459static NTSTATUS cvfs_qfileinfo(struct ntvfs_module_context *ntvfs, 460 struct ntvfs_request *req, union smb_fileinfo *io) 461{ 462 return NT_STATUS_NOT_IMPLEMENTED; 463} 464 465 466/* 467 set info on a pathname 468*/ 469static NTSTATUS cvfs_setpathinfo(struct ntvfs_module_context *ntvfs, 470 struct ntvfs_request *req, union smb_setfileinfo *st) 471{ 472 return NT_STATUS_NOT_IMPLEMENTED; 473} 474 475 476/* 477 open a file 478*/ 479static NTSTATUS cvfs_open(struct ntvfs_module_context *ntvfs, 480 struct ntvfs_request *req, union smb_open *io) 481{ 482 return NT_STATUS_NOT_IMPLEMENTED; 483} 484 485/* 486 create a directory 487*/ 488static NTSTATUS cvfs_mkdir(struct ntvfs_module_context *ntvfs, 489 struct ntvfs_request *req, union smb_mkdir *md) 490{ 491 struct cvfs_private *p = ntvfs->private_data; 492 struct composite_context *c_req; 493 494 CHECK_ASYNC(req); 495 496 c_req = smb2_composite_mkdir_send(p->tree, md); 497 498 SIMPLE_COMPOSITE_TAIL; 499} 500 501/* 502 remove a directory 503*/ 504static NTSTATUS cvfs_rmdir(struct ntvfs_module_context *ntvfs, 505 struct ntvfs_request *req, struct smb_rmdir *rd) 506{ 507 struct cvfs_private *p = ntvfs->private_data; 508 struct composite_context *c_req; 509 510 CHECK_ASYNC(req); 511 512 c_req = smb2_composite_rmdir_send(p->tree, rd); 513 514 SIMPLE_COMPOSITE_TAIL; 515} 516 517/* 518 rename a set of files 519*/ 520static NTSTATUS cvfs_rename(struct ntvfs_module_context *ntvfs, 521 struct ntvfs_request *req, union smb_rename *ren) 522{ 523 return NT_STATUS_NOT_IMPLEMENTED; 524} 525 526/* 527 copy a set of files 528*/ 529static NTSTATUS cvfs_copy(struct ntvfs_module_context *ntvfs, 530 struct ntvfs_request *req, struct smb_copy *cp) 531{ 532 return NT_STATUS_NOT_SUPPORTED; 533} 534 535/* 536 read from a file 537*/ 538static NTSTATUS cvfs_read(struct ntvfs_module_context *ntvfs, 539 struct ntvfs_request *req, union smb_read *io) 540{ 541 return NT_STATUS_NOT_IMPLEMENTED; 542} 543 544/* 545 write to a file 546*/ 547static NTSTATUS cvfs_write(struct ntvfs_module_context *ntvfs, 548 struct ntvfs_request *req, union smb_write *io) 549{ 550 return NT_STATUS_NOT_IMPLEMENTED; 551} 552 553/* 554 seek in a file 555*/ 556static NTSTATUS cvfs_seek(struct ntvfs_module_context *ntvfs, 557 struct ntvfs_request *req, 558 union smb_seek *io) 559{ 560 return NT_STATUS_NOT_IMPLEMENTED; 561} 562 563/* 564 flush a file 565*/ 566static NTSTATUS cvfs_flush(struct ntvfs_module_context *ntvfs, 567 struct ntvfs_request *req, 568 union smb_flush *io) 569{ 570 return NT_STATUS_NOT_IMPLEMENTED; 571} 572 573/* 574 close a file 575*/ 576static NTSTATUS cvfs_close(struct ntvfs_module_context *ntvfs, 577 struct ntvfs_request *req, union smb_close *io) 578{ 579 return NT_STATUS_NOT_IMPLEMENTED; 580} 581 582/* 583 exit - closing files open by the pid 584*/ 585static NTSTATUS cvfs_exit(struct ntvfs_module_context *ntvfs, 586 struct ntvfs_request *req) 587{ 588 return NT_STATUS_NOT_IMPLEMENTED; 589} 590 591/* 592 logoff - closing files open by the user 593*/ 594static NTSTATUS cvfs_logoff(struct ntvfs_module_context *ntvfs, 595 struct ntvfs_request *req) 596{ 597 /* we can't do this right in the cifs backend .... */ 598 return NT_STATUS_OK; 599} 600 601/* 602 setup for an async call - nothing to do yet 603*/ 604static NTSTATUS cvfs_async_setup(struct ntvfs_module_context *ntvfs, 605 struct ntvfs_request *req, 606 void *private_data) 607{ 608 return NT_STATUS_OK; 609} 610 611/* 612 cancel an async call 613*/ 614static NTSTATUS cvfs_cancel(struct ntvfs_module_context *ntvfs, 615 struct ntvfs_request *req) 616{ 617 return NT_STATUS_NOT_IMPLEMENTED; 618} 619 620/* 621 lock a byte range 622*/ 623static NTSTATUS cvfs_lock(struct ntvfs_module_context *ntvfs, 624 struct ntvfs_request *req, union smb_lock *io) 625{ 626 return NT_STATUS_NOT_IMPLEMENTED; 627} 628 629/* 630 set info on a open file 631*/ 632static NTSTATUS cvfs_setfileinfo(struct ntvfs_module_context *ntvfs, 633 struct ntvfs_request *req, 634 union smb_setfileinfo *io) 635{ 636 return NT_STATUS_NOT_IMPLEMENTED; 637} 638 639 640/* 641 a handler for async fsinfo replies 642 */ 643static void async_fsinfo(struct smb2_request *c_req) 644{ 645 struct async_info *async = c_req->async.private_data; 646 struct ntvfs_request *req = async->req; 647 req->async_states->status = smb2_getinfo_fs_recv(c_req, req, async->parms); 648 talloc_free(async); 649 req->async_states->send_fn(req); 650} 651 652/* 653 return filesystem space info 654*/ 655static NTSTATUS cvfs_fsinfo(struct ntvfs_module_context *ntvfs, 656 struct ntvfs_request *req, union smb_fsinfo *fs) 657{ 658 struct cvfs_private *p = ntvfs->private_data; 659 struct smb2_request *c_req; 660 enum smb_fsinfo_level level = fs->generic.level; 661 662 CHECK_ASYNC(req); 663 664 switch (level) { 665 /* some levels go straight through */ 666 case RAW_QFS_VOLUME_INFORMATION: 667 case RAW_QFS_SIZE_INFORMATION: 668 case RAW_QFS_DEVICE_INFORMATION: 669 case RAW_QFS_ATTRIBUTE_INFORMATION: 670 case RAW_QFS_QUOTA_INFORMATION: 671 case RAW_QFS_FULL_SIZE_INFORMATION: 672 case RAW_QFS_OBJECTID_INFORMATION: 673 break; 674 675 /* some get mapped */ 676 case RAW_QFS_VOLUME_INFO: 677 level = RAW_QFS_VOLUME_INFORMATION; 678 break; 679 case RAW_QFS_SIZE_INFO: 680 level = RAW_QFS_SIZE_INFORMATION; 681 break; 682 case RAW_QFS_DEVICE_INFO: 683 level = RAW_QFS_DEVICE_INFORMATION; 684 break; 685 case RAW_QFS_ATTRIBUTE_INFO: 686 level = RAW_QFS_ATTRIBUTE_INFO; 687 break; 688 689 default: 690 /* the rest get refused for now */ 691 DEBUG(0,("fsinfo level %u not possible on SMB2\n", 692 (unsigned)fs->generic.level)); 693 break; 694 } 695 696 fs->generic.level = level; 697 fs->generic.handle = p->roothandle; 698 699 c_req = smb2_getinfo_fs_send(p->tree, fs); 700 701 ASYNC_RECV_TAIL(fs, async_fsinfo); 702} 703 704/* 705 return print queue info 706*/ 707static NTSTATUS cvfs_lpq(struct ntvfs_module_context *ntvfs, 708 struct ntvfs_request *req, union smb_lpq *lpq) 709{ 710 return NT_STATUS_NOT_SUPPORTED; 711} 712 713/* 714 list files in a directory matching a wildcard pattern 715*/ 716static NTSTATUS cvfs_search_first(struct ntvfs_module_context *ntvfs, 717 struct ntvfs_request *req, union smb_search_first *io, 718 void *search_private, 719 bool (*callback)(void *, const union smb_search_data *)) 720{ 721 struct cvfs_private *p = ntvfs->private_data; 722 struct smb2_find f; 723 enum smb_search_data_level smb2_level; 724 uint_t count, i; 725 union smb_search_data *data; 726 NTSTATUS status; 727 728 if (io->generic.level != RAW_SEARCH_TRANS2) { 729 DEBUG(0,("We only support trans2 search in smb2 backend\n")); 730 return NT_STATUS_NOT_SUPPORTED; 731 } 732 733 switch (io->generic.data_level) { 734 case RAW_SEARCH_DATA_DIRECTORY_INFO: 735 smb2_level = SMB2_FIND_DIRECTORY_INFO; 736 break; 737 case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO: 738 smb2_level = SMB2_FIND_FULL_DIRECTORY_INFO; 739 break; 740 case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO: 741 smb2_level = SMB2_FIND_BOTH_DIRECTORY_INFO; 742 break; 743 case RAW_SEARCH_DATA_NAME_INFO: 744 smb2_level = SMB2_FIND_NAME_INFO; 745 break; 746 case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO: 747 smb2_level = SMB2_FIND_ID_FULL_DIRECTORY_INFO; 748 break; 749 case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO: 750 smb2_level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO; 751 break; 752 default: 753 DEBUG(0,("Unsupported search level %u for smb2 backend\n", 754 (unsigned)io->generic.data_level)); 755 return NT_STATUS_INVALID_INFO_CLASS; 756 } 757 758 /* we do the search on the roothandle. This only works because 759 search is synchronous, otherwise we'd have no way to 760 distinguish multiple searches happening at once 761 */ 762 ZERO_STRUCT(f); 763 f.in.file.handle = p->roothandle; 764 f.in.level = smb2_level; 765 f.in.pattern = io->t2ffirst.in.pattern; 766 while (f.in.pattern[0] == '\\') { 767 f.in.pattern++; 768 } 769 f.in.continue_flags = 0; 770 f.in.max_response_size = 0x10000; 771 772 status = smb2_find_level(p->tree, req, &f, &count, &data); 773 NT_STATUS_NOT_OK_RETURN(status); 774 775 for (i=0;i<count;i++) { 776 if (!callback(search_private, &data[i])) break; 777 } 778 779 io->t2ffirst.out.handle = 0; 780 io->t2ffirst.out.count = i; 781 /* TODO: fix end_of_file */ 782 io->t2ffirst.out.end_of_search = 1; 783 784 talloc_free(data); 785 786 return NT_STATUS_OK; 787} 788 789/* continue a search */ 790static NTSTATUS cvfs_search_next(struct ntvfs_module_context *ntvfs, 791 struct ntvfs_request *req, union smb_search_next *io, 792 void *search_private, 793 bool (*callback)(void *, const union smb_search_data *)) 794{ 795 return NT_STATUS_NOT_IMPLEMENTED; 796} 797 798/* close a search */ 799static NTSTATUS cvfs_search_close(struct ntvfs_module_context *ntvfs, 800 struct ntvfs_request *req, union smb_search_close *io) 801{ 802 return NT_STATUS_NOT_IMPLEMENTED; 803} 804 805/* SMBtrans - not used on file shares */ 806static NTSTATUS cvfs_trans(struct ntvfs_module_context *ntvfs, 807 struct ntvfs_request *req, 808 struct smb_trans2 *trans2) 809{ 810 return NT_STATUS_ACCESS_DENIED; 811} 812 813/* change notify request - always async */ 814static NTSTATUS cvfs_notify(struct ntvfs_module_context *ntvfs, 815 struct ntvfs_request *req, 816 union smb_notify *io) 817{ 818 return NT_STATUS_NOT_IMPLEMENTED; 819} 820 821/* 822 initialise the CIFS->CIFS backend, registering ourselves with the ntvfs subsystem 823 */ 824NTSTATUS ntvfs_smb2_init(void) 825{ 826 NTSTATUS ret; 827 struct ntvfs_ops ops; 828 NTVFS_CURRENT_CRITICAL_SIZES(vers); 829 830 ZERO_STRUCT(ops); 831 832 /* fill in the name and type */ 833 ops.name = "smb2"; 834 ops.type = NTVFS_DISK; 835 836 /* fill in all the operations */ 837 ops.connect = cvfs_connect; 838 ops.disconnect = cvfs_disconnect; 839 ops.unlink = cvfs_unlink; 840 ops.chkpath = cvfs_chkpath; 841 ops.qpathinfo = cvfs_qpathinfo; 842 ops.setpathinfo = cvfs_setpathinfo; 843 ops.open = cvfs_open; 844 ops.mkdir = cvfs_mkdir; 845 ops.rmdir = cvfs_rmdir; 846 ops.rename = cvfs_rename; 847 ops.copy = cvfs_copy; 848 ops.ioctl = cvfs_ioctl; 849 ops.read = cvfs_read; 850 ops.write = cvfs_write; 851 ops.seek = cvfs_seek; 852 ops.flush = cvfs_flush; 853 ops.close = cvfs_close; 854 ops.exit = cvfs_exit; 855 ops.lock = cvfs_lock; 856 ops.setfileinfo = cvfs_setfileinfo; 857 ops.qfileinfo = cvfs_qfileinfo; 858 ops.fsinfo = cvfs_fsinfo; 859 ops.lpq = cvfs_lpq; 860 ops.search_first = cvfs_search_first; 861 ops.search_next = cvfs_search_next; 862 ops.search_close = cvfs_search_close; 863 ops.trans = cvfs_trans; 864 ops.logoff = cvfs_logoff; 865 ops.async_setup = cvfs_async_setup; 866 ops.cancel = cvfs_cancel; 867 ops.notify = cvfs_notify; 868 869 /* register ourselves with the NTVFS subsystem. We register 870 under the name 'smb2'. */ 871 ret = ntvfs_register(&ops, &vers); 872 873 if (!NT_STATUS_IS_OK(ret)) { 874 DEBUG(0,("Failed to register SMB2 backend\n")); 875 } 876 877 return ret; 878} 879