1/* 2 Unix SMB/CIFS implementation. 3 4 CIFS-on-CIFS NTVFS filesystem backend 5 6 Copyright (C) Andrew Tridgell 2003 7 Copyright (C) James J Myers 2003 <myersjj@samba.org> 8 9 This program is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 3 of the License, or 12 (at your option) any later version. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program. If not, see <http://www.gnu.org/licenses/>. 21*/ 22/* 23 this implements a CIFS->CIFS NTVFS filesystem backend. 24 25*/ 26 27#include "includes.h" 28#include "libcli/raw/libcliraw.h" 29#include "libcli/raw/raw_proto.h" 30#include "libcli/smb_composite/smb_composite.h" 31#include "auth/auth.h" 32#include "auth/credentials/credentials.h" 33#include "ntvfs/ntvfs.h" 34#include "../lib/util/dlinklist.h" 35#include "param/param.h" 36#include "libcli/resolve/resolve.h" 37 38struct cvfs_file { 39 struct cvfs_file *prev, *next; 40 uint16_t fnum; 41 struct ntvfs_handle *h; 42}; 43 44/* this is stored in ntvfs_private */ 45struct cvfs_private { 46 struct smbcli_tree *tree; 47 struct smbcli_transport *transport; 48 struct ntvfs_module_context *ntvfs; 49 struct async_info *pending; 50 struct cvfs_file *files; 51 bool map_generic; 52 bool map_trans2; 53}; 54 55 56/* a structure used to pass information to an async handler */ 57struct async_info { 58 struct async_info *next, *prev; 59 struct cvfs_private *cvfs; 60 struct ntvfs_request *req; 61 struct smbcli_request *c_req; 62 struct cvfs_file *f; 63 void *parms; 64}; 65 66#define CHECK_UPSTREAM_OPEN do { \ 67 if (! p->transport->socket->sock) { \ 68 req->async_states->state|=NTVFS_ASYNC_STATE_CLOSE; \ 69 return NT_STATUS_CONNECTION_DISCONNECTED; \ 70 } \ 71} while(0) 72 73#define SETUP_PID do { \ 74 p->tree->session->pid = req->smbpid; \ 75 CHECK_UPSTREAM_OPEN; \ 76} while(0) 77 78#define SETUP_FILE_HERE(f) do { \ 79 f = ntvfs_handle_get_backend_data(io->generic.in.file.ntvfs, ntvfs); \ 80 if (!f) return NT_STATUS_INVALID_HANDLE; \ 81 io->generic.in.file.fnum = f->fnum; \ 82} while (0) 83 84#define SETUP_FILE do { \ 85 struct cvfs_file *f; \ 86 SETUP_FILE_HERE(f); \ 87} while (0) 88 89#define SETUP_PID_AND_FILE do { \ 90 SETUP_PID; \ 91 SETUP_FILE; \ 92} while (0) 93 94#define CIFS_SERVER "cifs:server" 95#define CIFS_USER "cifs:user" 96#define CIFS_PASSWORD "cifs:password" 97#define CIFS_DOMAIN "cifs:domain" 98#define CIFS_SHARE "cifs:share" 99#define CIFS_USE_MACHINE_ACCT "cifs:use-machine-account" 100#define CIFS_MAP_GENERIC "cifs:map-generic" 101#define CIFS_MAP_TRANS2 "cifs:map-trans2" 102 103#define CIFS_USE_MACHINE_ACCT_DEFAULT false 104#define CIFS_MAP_GENERIC_DEFAULT false 105#define CIFS_MAP_TRANS2_DEFAULT true 106 107/* 108 a handler for oplock break events from the server - these need to be passed 109 along to the client 110 */ 111static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *p_private) 112{ 113 struct cvfs_private *p = p_private; 114 NTSTATUS status; 115 struct ntvfs_handle *h = NULL; 116 struct cvfs_file *f; 117 118 for (f=p->files; f; f=f->next) { 119 if (f->fnum != fnum) continue; 120 h = f->h; 121 break; 122 } 123 124 if (!h) { 125 DEBUG(5,("vfs_cifs: ignoring oplock break level %d for fnum %d\n", level, fnum)); 126 return true; 127 } 128 129 DEBUG(5,("vfs_cifs: sending oplock break level %d for fnum %d\n", level, fnum)); 130 status = ntvfs_send_oplock_break(p->ntvfs, h, level); 131 if (!NT_STATUS_IS_OK(status)) return false; 132 return true; 133} 134 135/* 136 connect to a share - used when a tree_connect operation comes in. 137*/ 138static NTSTATUS cvfs_connect(struct ntvfs_module_context *ntvfs, 139 struct ntvfs_request *req, 140 union smb_tcon *tcon) 141{ 142 NTSTATUS status; 143 struct cvfs_private *p; 144 const char *host, *user, *pass, *domain, *remote_share; 145 struct smb_composite_connect io; 146 struct composite_context *creq; 147 struct share_config *scfg = ntvfs->ctx->config; 148 149 struct cli_credentials *credentials; 150 bool machine_account; 151 const char* sharename; 152 153 switch (tcon->generic.level) { 154 case RAW_TCON_TCON: 155 sharename = tcon->tcon.in.service; 156 break; 157 case RAW_TCON_TCONX: 158 sharename = tcon->tconx.in.path; 159 break; 160 case RAW_TCON_SMB2: 161 sharename = tcon->smb2.in.path; 162 break; 163 default: 164 return NT_STATUS_INVALID_LEVEL; 165 } 166 167 if (strncmp(sharename, "\\\\", 2) == 0) { 168 char *str = strchr(sharename+2, '\\'); 169 if (str) { 170 sharename = str + 1; 171 } 172 } 173 174 /* Here we need to determine which server to connect to. 175 * For now we use parametric options, type cifs. 176 * Later we will use security=server and auth_server.c. 177 */ 178 host = share_string_option(scfg, CIFS_SERVER, NULL); 179 user = share_string_option(scfg, CIFS_USER, NULL); 180 pass = share_string_option(scfg, CIFS_PASSWORD, NULL); 181 domain = share_string_option(scfg, CIFS_DOMAIN, NULL); 182 remote_share = share_string_option(scfg, CIFS_SHARE, NULL); 183 if (!remote_share) { 184 remote_share = sharename; 185 } 186 187 machine_account = share_bool_option(scfg, CIFS_USE_MACHINE_ACCT, CIFS_USE_MACHINE_ACCT_DEFAULT); 188 189 p = talloc_zero(ntvfs, struct cvfs_private); 190 if (!p) { 191 return NT_STATUS_NO_MEMORY; 192 } 193 194 ntvfs->private_data = p; 195 196 if (!host) { 197 DEBUG(1,("CIFS backend: You must supply server\n")); 198 return NT_STATUS_INVALID_PARAMETER; 199 } 200 201 if (user && pass) { 202 DEBUG(5, ("CIFS backend: Using specified password\n")); 203 credentials = cli_credentials_init(p); 204 if (!credentials) { 205 return NT_STATUS_NO_MEMORY; 206 } 207 cli_credentials_set_conf(credentials, ntvfs->ctx->lp_ctx); 208 cli_credentials_set_username(credentials, user, CRED_SPECIFIED); 209 if (domain) { 210 cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED); 211 } 212 cli_credentials_set_password(credentials, pass, CRED_SPECIFIED); 213 } else if (machine_account) { 214 DEBUG(5, ("CIFS backend: Using machine account\n")); 215 credentials = cli_credentials_init(p); 216 cli_credentials_set_conf(credentials, ntvfs->ctx->lp_ctx); 217 if (domain) { 218 cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED); 219 } 220 status = cli_credentials_set_machine_account(credentials, ntvfs->ctx->lp_ctx); 221 if (!NT_STATUS_IS_OK(status)) { 222 return status; 223 } 224 } else if (req->session_info->credentials) { 225 DEBUG(5, ("CIFS backend: Using delegated credentials\n")); 226 credentials = req->session_info->credentials; 227 } else { 228 DEBUG(1,("CIFS backend: NO delegated credentials found: You must supply server, user and password or the client must supply delegated credentials\n")); 229 return NT_STATUS_INVALID_PARAMETER; 230 } 231 232 /* connect to the server, using the smbd event context */ 233 io.in.dest_host = host; 234 io.in.dest_ports = lp_smb_ports(ntvfs->ctx->lp_ctx); 235 io.in.socket_options = lp_socket_options(ntvfs->ctx->lp_ctx); 236 io.in.called_name = host; 237 io.in.credentials = credentials; 238 io.in.fallback_to_anonymous = false; 239 io.in.workgroup = lp_workgroup(ntvfs->ctx->lp_ctx); 240 io.in.service = remote_share; 241 io.in.service_type = "?????"; 242 io.in.iconv_convenience = lp_iconv_convenience(ntvfs->ctx->lp_ctx); 243 io.in.gensec_settings = lp_gensec_settings(p, ntvfs->ctx->lp_ctx); 244 lp_smbcli_options(ntvfs->ctx->lp_ctx, &io.in.options); 245 lp_smbcli_session_options(ntvfs->ctx->lp_ctx, &io.in.session_options); 246 247 if (!(ntvfs->ctx->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS)) { 248 io.in.options.use_level2_oplocks = false; 249 } 250 251 creq = smb_composite_connect_send(&io, p, 252 lp_resolve_context(ntvfs->ctx->lp_ctx), 253 ntvfs->ctx->event_ctx); 254 status = smb_composite_connect_recv(creq, p); 255 NT_STATUS_NOT_OK_RETURN(status); 256 257 p->tree = io.out.tree; 258 259 p->transport = p->tree->session->transport; 260 SETUP_PID; 261 p->ntvfs = ntvfs; 262 263 ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS"); 264 NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type); 265 ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:"); 266 NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type); 267 268 if (tcon->generic.level == RAW_TCON_TCONX) { 269 tcon->tconx.out.fs_type = ntvfs->ctx->fs_type; 270 tcon->tconx.out.dev_type = ntvfs->ctx->dev_type; 271 } 272 273 /* we need to receive oplock break requests from the server */ 274 smbcli_oplock_handler(p->transport, oplock_handler, p); 275 276 p->map_generic = share_bool_option(scfg, CIFS_MAP_GENERIC, CIFS_MAP_GENERIC_DEFAULT); 277 278 p->map_trans2 = share_bool_option(scfg, CIFS_MAP_TRANS2, CIFS_MAP_TRANS2_DEFAULT); 279 280 return NT_STATUS_OK; 281} 282 283/* 284 disconnect from a share 285*/ 286static NTSTATUS cvfs_disconnect(struct ntvfs_module_context *ntvfs) 287{ 288 struct cvfs_private *p = ntvfs->private_data; 289 struct async_info *a, *an; 290 291 /* first cleanup pending requests */ 292 for (a=p->pending; a; a = an) { 293 an = a->next; 294 smbcli_request_destroy(a->c_req); 295 talloc_free(a); 296 } 297 298 talloc_free(p); 299 ntvfs->private_data = NULL; 300 301 return NT_STATUS_OK; 302} 303 304/* 305 destroy an async info structure 306*/ 307static int async_info_destructor(struct async_info *async) 308{ 309 DLIST_REMOVE(async->cvfs->pending, async); 310 return 0; 311} 312 313/* 314 a handler for simple async replies 315 this handler can only be used for functions that don't return any 316 parameters (those that just return a status code) 317 */ 318static void async_simple(struct smbcli_request *c_req) 319{ 320 struct async_info *async = c_req->async.private_data; 321 struct ntvfs_request *req = async->req; 322 req->async_states->status = smbcli_request_simple_recv(c_req); 323 talloc_free(async); 324 req->async_states->send_fn(req); 325} 326 327 328/* save some typing for the simple functions */ 329#define ASYNC_RECV_TAIL_F(io, async_fn, file) do { \ 330 if (!c_req) return NT_STATUS_UNSUCCESSFUL; \ 331 { \ 332 struct async_info *async; \ 333 async = talloc(req, struct async_info); \ 334 if (!async) return NT_STATUS_NO_MEMORY; \ 335 async->parms = io; \ 336 async->req = req; \ 337 async->f = file; \ 338 async->cvfs = p; \ 339 async->c_req = c_req; \ 340 DLIST_ADD(p->pending, async); \ 341 c_req->async.private_data = async; \ 342 talloc_set_destructor(async, async_info_destructor); \ 343 } \ 344 c_req->async.fn = async_fn; \ 345 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC; \ 346 return NT_STATUS_OK; \ 347} while (0) 348 349#define ASYNC_RECV_TAIL(io, async_fn) ASYNC_RECV_TAIL_F(io, async_fn, NULL) 350 351#define SIMPLE_ASYNC_TAIL ASYNC_RECV_TAIL(NULL, async_simple) 352 353/* 354 delete a file - the dirtype specifies the file types to include in the search. 355 The name can contain CIFS wildcards, but rarely does (except with OS/2 clients) 356*/ 357static NTSTATUS cvfs_unlink(struct ntvfs_module_context *ntvfs, 358 struct ntvfs_request *req, union smb_unlink *unl) 359{ 360 struct cvfs_private *p = ntvfs->private_data; 361 struct smbcli_request *c_req; 362 363 SETUP_PID; 364 365 /* see if the front end will allow us to perform this 366 function asynchronously. */ 367 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 368 return smb_raw_unlink(p->tree, unl); 369 } 370 371 c_req = smb_raw_unlink_send(p->tree, unl); 372 373 SIMPLE_ASYNC_TAIL; 374} 375 376/* 377 a handler for async ioctl replies 378 */ 379static void async_ioctl(struct smbcli_request *c_req) 380{ 381 struct async_info *async = c_req->async.private_data; 382 struct ntvfs_request *req = async->req; 383 req->async_states->status = smb_raw_ioctl_recv(c_req, req, async->parms); 384 talloc_free(async); 385 req->async_states->send_fn(req); 386} 387 388/* 389 ioctl interface 390*/ 391static NTSTATUS cvfs_ioctl(struct ntvfs_module_context *ntvfs, 392 struct ntvfs_request *req, union smb_ioctl *io) 393{ 394 struct cvfs_private *p = ntvfs->private_data; 395 struct smbcli_request *c_req; 396 397 SETUP_PID_AND_FILE; 398 399 /* see if the front end will allow us to perform this 400 function asynchronously. */ 401 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 402 return smb_raw_ioctl(p->tree, req, io); 403 } 404 405 c_req = smb_raw_ioctl_send(p->tree, io); 406 407 ASYNC_RECV_TAIL(io, async_ioctl); 408} 409 410/* 411 check if a directory exists 412*/ 413static NTSTATUS cvfs_chkpath(struct ntvfs_module_context *ntvfs, 414 struct ntvfs_request *req, union smb_chkpath *cp) 415{ 416 struct cvfs_private *p = ntvfs->private_data; 417 struct smbcli_request *c_req; 418 419 SETUP_PID; 420 421 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 422 return smb_raw_chkpath(p->tree, cp); 423 } 424 425 c_req = smb_raw_chkpath_send(p->tree, cp); 426 427 SIMPLE_ASYNC_TAIL; 428} 429 430/* 431 a handler for async qpathinfo replies 432 */ 433static void async_qpathinfo(struct smbcli_request *c_req) 434{ 435 struct async_info *async = c_req->async.private_data; 436 struct ntvfs_request *req = async->req; 437 req->async_states->status = smb_raw_pathinfo_recv(c_req, req, async->parms); 438 talloc_free(async); 439 req->async_states->send_fn(req); 440} 441 442/* 443 return info on a pathname 444*/ 445static NTSTATUS cvfs_qpathinfo(struct ntvfs_module_context *ntvfs, 446 struct ntvfs_request *req, union smb_fileinfo *info) 447{ 448 struct cvfs_private *p = ntvfs->private_data; 449 struct smbcli_request *c_req; 450 451 SETUP_PID; 452 453 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 454 return smb_raw_pathinfo(p->tree, req, info); 455 } 456 457 c_req = smb_raw_pathinfo_send(p->tree, info); 458 459 ASYNC_RECV_TAIL(info, async_qpathinfo); 460} 461 462/* 463 a handler for async qfileinfo replies 464 */ 465static void async_qfileinfo(struct smbcli_request *c_req) 466{ 467 struct async_info *async = c_req->async.private_data; 468 struct ntvfs_request *req = async->req; 469 req->async_states->status = smb_raw_fileinfo_recv(c_req, req, async->parms); 470 talloc_free(async); 471 req->async_states->send_fn(req); 472} 473 474/* 475 query info on a open file 476*/ 477static NTSTATUS cvfs_qfileinfo(struct ntvfs_module_context *ntvfs, 478 struct ntvfs_request *req, union smb_fileinfo *io) 479{ 480 struct cvfs_private *p = ntvfs->private_data; 481 struct smbcli_request *c_req; 482 483 SETUP_PID_AND_FILE; 484 485 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 486 return smb_raw_fileinfo(p->tree, req, io); 487 } 488 489 c_req = smb_raw_fileinfo_send(p->tree, io); 490 491 ASYNC_RECV_TAIL(io, async_qfileinfo); 492} 493 494 495/* 496 set info on a pathname 497*/ 498static NTSTATUS cvfs_setpathinfo(struct ntvfs_module_context *ntvfs, 499 struct ntvfs_request *req, union smb_setfileinfo *st) 500{ 501 struct cvfs_private *p = ntvfs->private_data; 502 struct smbcli_request *c_req; 503 504 SETUP_PID; 505 506 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 507 return smb_raw_setpathinfo(p->tree, st); 508 } 509 510 c_req = smb_raw_setpathinfo_send(p->tree, st); 511 512 SIMPLE_ASYNC_TAIL; 513} 514 515 516/* 517 a handler for async open replies 518 */ 519static void async_open(struct smbcli_request *c_req) 520{ 521 struct async_info *async = c_req->async.private_data; 522 struct cvfs_private *cvfs = async->cvfs; 523 struct ntvfs_request *req = async->req; 524 struct cvfs_file *f = async->f; 525 union smb_open *io = async->parms; 526 union smb_handle *file; 527 talloc_free(async); 528 req->async_states->status = smb_raw_open_recv(c_req, req, io); 529 SMB_OPEN_OUT_FILE(io, file); 530 f->fnum = file->fnum; 531 file->ntvfs = NULL; 532 if (!NT_STATUS_IS_OK(req->async_states->status)) goto failed; 533 req->async_states->status = ntvfs_handle_set_backend_data(f->h, cvfs->ntvfs, f); 534 if (!NT_STATUS_IS_OK(req->async_states->status)) goto failed; 535 file->ntvfs = f->h; 536 DLIST_ADD(cvfs->files, f); 537failed: 538 req->async_states->send_fn(req); 539} 540 541/* 542 open a file 543*/ 544static NTSTATUS cvfs_open(struct ntvfs_module_context *ntvfs, 545 struct ntvfs_request *req, union smb_open *io) 546{ 547 struct cvfs_private *p = ntvfs->private_data; 548 struct smbcli_request *c_req; 549 struct ntvfs_handle *h; 550 struct cvfs_file *f; 551 NTSTATUS status; 552 553 SETUP_PID; 554 555 if (io->generic.level != RAW_OPEN_GENERIC && 556 p->map_generic) { 557 return ntvfs_map_open(ntvfs, req, io); 558 } 559 560 status = ntvfs_handle_new(ntvfs, req, &h); 561 NT_STATUS_NOT_OK_RETURN(status); 562 563 f = talloc_zero(h, struct cvfs_file); 564 NT_STATUS_HAVE_NO_MEMORY(f); 565 f->h = h; 566 567 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 568 union smb_handle *file; 569 570 status = smb_raw_open(p->tree, req, io); 571 NT_STATUS_NOT_OK_RETURN(status); 572 573 SMB_OPEN_OUT_FILE(io, file); 574 f->fnum = file->fnum; 575 file->ntvfs = NULL; 576 status = ntvfs_handle_set_backend_data(f->h, p->ntvfs, f); 577 NT_STATUS_NOT_OK_RETURN(status); 578 file->ntvfs = f->h; 579 DLIST_ADD(p->files, f); 580 581 return NT_STATUS_OK; 582 } 583 584 c_req = smb_raw_open_send(p->tree, io); 585 586 ASYNC_RECV_TAIL_F(io, async_open, f); 587} 588 589/* 590 create a directory 591*/ 592static NTSTATUS cvfs_mkdir(struct ntvfs_module_context *ntvfs, 593 struct ntvfs_request *req, union smb_mkdir *md) 594{ 595 struct cvfs_private *p = ntvfs->private_data; 596 struct smbcli_request *c_req; 597 598 SETUP_PID; 599 600 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 601 return smb_raw_mkdir(p->tree, md); 602 } 603 604 c_req = smb_raw_mkdir_send(p->tree, md); 605 606 SIMPLE_ASYNC_TAIL; 607} 608 609/* 610 remove a directory 611*/ 612static NTSTATUS cvfs_rmdir(struct ntvfs_module_context *ntvfs, 613 struct ntvfs_request *req, struct smb_rmdir *rd) 614{ 615 struct cvfs_private *p = ntvfs->private_data; 616 struct smbcli_request *c_req; 617 618 SETUP_PID; 619 620 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 621 return smb_raw_rmdir(p->tree, rd); 622 } 623 c_req = smb_raw_rmdir_send(p->tree, rd); 624 625 SIMPLE_ASYNC_TAIL; 626} 627 628/* 629 rename a set of files 630*/ 631static NTSTATUS cvfs_rename(struct ntvfs_module_context *ntvfs, 632 struct ntvfs_request *req, union smb_rename *ren) 633{ 634 struct cvfs_private *p = ntvfs->private_data; 635 struct smbcli_request *c_req; 636 637 SETUP_PID; 638 639 if (ren->nttrans.level == RAW_RENAME_NTTRANS) { 640 struct cvfs_file *f; 641 f = ntvfs_handle_get_backend_data(ren->nttrans.in.file.ntvfs, ntvfs); 642 if (!f) return NT_STATUS_INVALID_HANDLE; 643 ren->nttrans.in.file.fnum = f->fnum; 644 } 645 646 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 647 return smb_raw_rename(p->tree, ren); 648 } 649 650 c_req = smb_raw_rename_send(p->tree, ren); 651 652 SIMPLE_ASYNC_TAIL; 653} 654 655/* 656 copy a set of files 657*/ 658static NTSTATUS cvfs_copy(struct ntvfs_module_context *ntvfs, 659 struct ntvfs_request *req, struct smb_copy *cp) 660{ 661 return NT_STATUS_NOT_SUPPORTED; 662} 663 664/* 665 a handler for async read replies 666 */ 667static void async_read(struct smbcli_request *c_req) 668{ 669 struct async_info *async = c_req->async.private_data; 670 struct ntvfs_request *req = async->req; 671 req->async_states->status = smb_raw_read_recv(c_req, async->parms); 672 talloc_free(async); 673 req->async_states->send_fn(req); 674} 675 676/* 677 read from a file 678*/ 679static NTSTATUS cvfs_read(struct ntvfs_module_context *ntvfs, 680 struct ntvfs_request *req, union smb_read *io) 681{ 682 struct cvfs_private *p = ntvfs->private_data; 683 struct smbcli_request *c_req; 684 685 SETUP_PID; 686 687 if (io->generic.level != RAW_READ_GENERIC && 688 p->map_generic) { 689 return ntvfs_map_read(ntvfs, req, io); 690 } 691 692 SETUP_FILE; 693 694 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 695 return smb_raw_read(p->tree, io); 696 } 697 698 c_req = smb_raw_read_send(p->tree, io); 699 700 ASYNC_RECV_TAIL(io, async_read); 701} 702 703/* 704 a handler for async write replies 705 */ 706static void async_write(struct smbcli_request *c_req) 707{ 708 struct async_info *async = c_req->async.private_data; 709 struct ntvfs_request *req = async->req; 710 req->async_states->status = smb_raw_write_recv(c_req, async->parms); 711 talloc_free(async); 712 req->async_states->send_fn(req); 713} 714 715/* 716 write to a file 717*/ 718static NTSTATUS cvfs_write(struct ntvfs_module_context *ntvfs, 719 struct ntvfs_request *req, union smb_write *io) 720{ 721 struct cvfs_private *p = ntvfs->private_data; 722 struct smbcli_request *c_req; 723 724 SETUP_PID; 725 726 if (io->generic.level != RAW_WRITE_GENERIC && 727 p->map_generic) { 728 return ntvfs_map_write(ntvfs, req, io); 729 } 730 SETUP_FILE; 731 732 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 733 return smb_raw_write(p->tree, io); 734 } 735 736 c_req = smb_raw_write_send(p->tree, io); 737 738 ASYNC_RECV_TAIL(io, async_write); 739} 740 741/* 742 a handler for async seek replies 743 */ 744static void async_seek(struct smbcli_request *c_req) 745{ 746 struct async_info *async = c_req->async.private_data; 747 struct ntvfs_request *req = async->req; 748 req->async_states->status = smb_raw_seek_recv(c_req, async->parms); 749 talloc_free(async); 750 req->async_states->send_fn(req); 751} 752 753/* 754 seek in a file 755*/ 756static NTSTATUS cvfs_seek(struct ntvfs_module_context *ntvfs, 757 struct ntvfs_request *req, 758 union smb_seek *io) 759{ 760 struct cvfs_private *p = ntvfs->private_data; 761 struct smbcli_request *c_req; 762 763 SETUP_PID_AND_FILE; 764 765 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 766 return smb_raw_seek(p->tree, io); 767 } 768 769 c_req = smb_raw_seek_send(p->tree, io); 770 771 ASYNC_RECV_TAIL(io, async_seek); 772} 773 774/* 775 flush a file 776*/ 777static NTSTATUS cvfs_flush(struct ntvfs_module_context *ntvfs, 778 struct ntvfs_request *req, 779 union smb_flush *io) 780{ 781 struct cvfs_private *p = ntvfs->private_data; 782 struct smbcli_request *c_req; 783 784 SETUP_PID; 785 switch (io->generic.level) { 786 case RAW_FLUSH_FLUSH: 787 SETUP_FILE; 788 break; 789 case RAW_FLUSH_ALL: 790 io->generic.in.file.fnum = 0xFFFF; 791 break; 792 case RAW_FLUSH_SMB2: 793 return NT_STATUS_INVALID_LEVEL; 794 } 795 796 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 797 return smb_raw_flush(p->tree, io); 798 } 799 800 c_req = smb_raw_flush_send(p->tree, io); 801 802 SIMPLE_ASYNC_TAIL; 803} 804 805/* 806 close a file 807*/ 808static NTSTATUS cvfs_close(struct ntvfs_module_context *ntvfs, 809 struct ntvfs_request *req, union smb_close *io) 810{ 811 struct cvfs_private *p = ntvfs->private_data; 812 struct smbcli_request *c_req; 813 struct cvfs_file *f; 814 union smb_close io2; 815 816 SETUP_PID; 817 818 if (io->generic.level != RAW_CLOSE_GENERIC && 819 p->map_generic) { 820 return ntvfs_map_close(ntvfs, req, io); 821 } 822 823 if (io->generic.level == RAW_CLOSE_GENERIC) { 824 ZERO_STRUCT(io2); 825 io2.close.level = RAW_CLOSE_CLOSE; 826 io2.close.in.file = io->generic.in.file; 827 io2.close.in.write_time = io->generic.in.write_time; 828 io = &io2; 829 } 830 831 SETUP_FILE_HERE(f); 832 /* Note, we aren't free-ing f, or it's h here. Should we? 833 even if file-close fails, we'll remove it from the list, 834 what else would we do? Maybe we should not remove until 835 after the proxied call completes? */ 836 DLIST_REMOVE(p->files, f); 837 838 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 839 return smb_raw_close(p->tree, io); 840 } 841 842 c_req = smb_raw_close_send(p->tree, io); 843 844 SIMPLE_ASYNC_TAIL; 845} 846 847/* 848 exit - closing files open by the pid 849*/ 850static NTSTATUS cvfs_exit(struct ntvfs_module_context *ntvfs, 851 struct ntvfs_request *req) 852{ 853 struct cvfs_private *p = ntvfs->private_data; 854 struct smbcli_request *c_req; 855 856 SETUP_PID; 857 858 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 859 return smb_raw_exit(p->tree->session); 860 } 861 862 c_req = smb_raw_exit_send(p->tree->session); 863 864 SIMPLE_ASYNC_TAIL; 865} 866 867/* 868 logoff - closing files open by the user 869*/ 870static NTSTATUS cvfs_logoff(struct ntvfs_module_context *ntvfs, 871 struct ntvfs_request *req) 872{ 873 /* we can't do this right in the cifs backend .... */ 874 return NT_STATUS_OK; 875} 876 877/* 878 setup for an async call - nothing to do yet 879*/ 880static NTSTATUS cvfs_async_setup(struct ntvfs_module_context *ntvfs, 881 struct ntvfs_request *req, 882 void *private_data) 883{ 884 return NT_STATUS_OK; 885} 886 887/* 888 cancel an async call 889*/ 890static NTSTATUS cvfs_cancel(struct ntvfs_module_context *ntvfs, 891 struct ntvfs_request *req) 892{ 893 struct cvfs_private *p = ntvfs->private_data; 894 struct async_info *a; 895 896 /* find the matching request */ 897 for (a=p->pending;a;a=a->next) { 898 if (a->req == req) { 899 break; 900 } 901 } 902 903 if (a == NULL) { 904 return NT_STATUS_INVALID_PARAMETER; 905 } 906 907 return smb_raw_ntcancel(a->c_req); 908} 909 910/* 911 lock a byte range 912*/ 913static NTSTATUS cvfs_lock(struct ntvfs_module_context *ntvfs, 914 struct ntvfs_request *req, union smb_lock *io) 915{ 916 struct cvfs_private *p = ntvfs->private_data; 917 struct smbcli_request *c_req; 918 919 SETUP_PID; 920 921 if (io->generic.level != RAW_LOCK_GENERIC && 922 p->map_generic) { 923 return ntvfs_map_lock(ntvfs, req, io); 924 } 925 SETUP_FILE; 926 927 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 928 return smb_raw_lock(p->tree, io); 929 } 930 931 c_req = smb_raw_lock_send(p->tree, io); 932 SIMPLE_ASYNC_TAIL; 933} 934 935/* 936 set info on a open file 937*/ 938static NTSTATUS cvfs_setfileinfo(struct ntvfs_module_context *ntvfs, 939 struct ntvfs_request *req, 940 union smb_setfileinfo *io) 941{ 942 struct cvfs_private *p = ntvfs->private_data; 943 struct smbcli_request *c_req; 944 945 SETUP_PID_AND_FILE; 946 947 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 948 return smb_raw_setfileinfo(p->tree, io); 949 } 950 c_req = smb_raw_setfileinfo_send(p->tree, io); 951 952 SIMPLE_ASYNC_TAIL; 953} 954 955 956/* 957 a handler for async fsinfo replies 958 */ 959static void async_fsinfo(struct smbcli_request *c_req) 960{ 961 struct async_info *async = c_req->async.private_data; 962 struct ntvfs_request *req = async->req; 963 req->async_states->status = smb_raw_fsinfo_recv(c_req, req, async->parms); 964 talloc_free(async); 965 req->async_states->send_fn(req); 966} 967 968/* 969 return filesystem space info 970*/ 971static NTSTATUS cvfs_fsinfo(struct ntvfs_module_context *ntvfs, 972 struct ntvfs_request *req, union smb_fsinfo *fs) 973{ 974 struct cvfs_private *p = ntvfs->private_data; 975 struct smbcli_request *c_req; 976 977 SETUP_PID; 978 979 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 980 return smb_raw_fsinfo(p->tree, req, fs); 981 } 982 983 c_req = smb_raw_fsinfo_send(p->tree, req, fs); 984 985 ASYNC_RECV_TAIL(fs, async_fsinfo); 986} 987 988/* 989 return print queue info 990*/ 991static NTSTATUS cvfs_lpq(struct ntvfs_module_context *ntvfs, 992 struct ntvfs_request *req, union smb_lpq *lpq) 993{ 994 return NT_STATUS_NOT_SUPPORTED; 995} 996 997/* 998 list files in a directory matching a wildcard pattern 999*/ 1000static NTSTATUS cvfs_search_first(struct ntvfs_module_context *ntvfs, 1001 struct ntvfs_request *req, union smb_search_first *io, 1002 void *search_private, 1003 bool (*callback)(void *, const union smb_search_data *)) 1004{ 1005 struct cvfs_private *p = ntvfs->private_data; 1006 1007 SETUP_PID; 1008 1009 return smb_raw_search_first(p->tree, req, io, search_private, callback); 1010} 1011 1012/* continue a search */ 1013static NTSTATUS cvfs_search_next(struct ntvfs_module_context *ntvfs, 1014 struct ntvfs_request *req, union smb_search_next *io, 1015 void *search_private, 1016 bool (*callback)(void *, const union smb_search_data *)) 1017{ 1018 struct cvfs_private *p = ntvfs->private_data; 1019 1020 SETUP_PID; 1021 1022 return smb_raw_search_next(p->tree, req, io, search_private, callback); 1023} 1024 1025/* close a search */ 1026static NTSTATUS cvfs_search_close(struct ntvfs_module_context *ntvfs, 1027 struct ntvfs_request *req, union smb_search_close *io) 1028{ 1029 struct cvfs_private *p = ntvfs->private_data; 1030 1031 SETUP_PID; 1032 1033 return smb_raw_search_close(p->tree, io); 1034} 1035 1036/* 1037 a handler for async trans2 replies 1038 */ 1039static void async_trans2(struct smbcli_request *c_req) 1040{ 1041 struct async_info *async = c_req->async.private_data; 1042 struct ntvfs_request *req = async->req; 1043 req->async_states->status = smb_raw_trans2_recv(c_req, req, async->parms); 1044 talloc_free(async); 1045 req->async_states->send_fn(req); 1046} 1047 1048/* raw trans2 */ 1049static NTSTATUS cvfs_trans2(struct ntvfs_module_context *ntvfs, 1050 struct ntvfs_request *req, 1051 struct smb_trans2 *trans2) 1052{ 1053 struct cvfs_private *p = ntvfs->private_data; 1054 struct smbcli_request *c_req; 1055 1056 if (p->map_trans2) { 1057 return NT_STATUS_NOT_IMPLEMENTED; 1058 } 1059 1060 SETUP_PID; 1061 1062 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 1063 return smb_raw_trans2(p->tree, req, trans2); 1064 } 1065 1066 c_req = smb_raw_trans2_send(p->tree, trans2); 1067 1068 ASYNC_RECV_TAIL(trans2, async_trans2); 1069} 1070 1071 1072/* SMBtrans - not used on file shares */ 1073static NTSTATUS cvfs_trans(struct ntvfs_module_context *ntvfs, 1074 struct ntvfs_request *req, 1075 struct smb_trans2 *trans2) 1076{ 1077 return NT_STATUS_ACCESS_DENIED; 1078} 1079 1080/* 1081 a handler for async change notify replies 1082 */ 1083static void async_changenotify(struct smbcli_request *c_req) 1084{ 1085 struct async_info *async = c_req->async.private_data; 1086 struct ntvfs_request *req = async->req; 1087 req->async_states->status = smb_raw_changenotify_recv(c_req, req, async->parms); 1088 talloc_free(async); 1089 req->async_states->send_fn(req); 1090} 1091 1092/* change notify request - always async */ 1093static NTSTATUS cvfs_notify(struct ntvfs_module_context *ntvfs, 1094 struct ntvfs_request *req, 1095 union smb_notify *io) 1096{ 1097 struct cvfs_private *p = ntvfs->private_data; 1098 struct smbcli_request *c_req; 1099 int saved_timeout = p->transport->options.request_timeout; 1100 struct cvfs_file *f; 1101 1102 if (io->nttrans.level != RAW_NOTIFY_NTTRANS) { 1103 return NT_STATUS_NOT_IMPLEMENTED; 1104 } 1105 1106 SETUP_PID; 1107 1108 f = ntvfs_handle_get_backend_data(io->nttrans.in.file.ntvfs, ntvfs); 1109 if (!f) return NT_STATUS_INVALID_HANDLE; 1110 io->nttrans.in.file.fnum = f->fnum; 1111 1112 /* this request doesn't make sense unless its async */ 1113 if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { 1114 return NT_STATUS_INVALID_PARAMETER; 1115 } 1116 1117 /* we must not timeout on notify requests - they wait 1118 forever */ 1119 p->transport->options.request_timeout = 0; 1120 1121 c_req = smb_raw_changenotify_send(p->tree, io); 1122 1123 p->transport->options.request_timeout = saved_timeout; 1124 1125 ASYNC_RECV_TAIL(io, async_changenotify); 1126} 1127 1128/* 1129 initialise the CIFS->CIFS backend, registering ourselves with the ntvfs subsystem 1130 */ 1131NTSTATUS ntvfs_cifs_init(void) 1132{ 1133 NTSTATUS ret; 1134 struct ntvfs_ops ops; 1135 NTVFS_CURRENT_CRITICAL_SIZES(vers); 1136 1137 ZERO_STRUCT(ops); 1138 1139 /* fill in the name and type */ 1140 ops.name = "cifs"; 1141 ops.type = NTVFS_DISK; 1142 1143 /* fill in all the operations */ 1144 ops.connect = cvfs_connect; 1145 ops.disconnect = cvfs_disconnect; 1146 ops.unlink = cvfs_unlink; 1147 ops.chkpath = cvfs_chkpath; 1148 ops.qpathinfo = cvfs_qpathinfo; 1149 ops.setpathinfo = cvfs_setpathinfo; 1150 ops.open = cvfs_open; 1151 ops.mkdir = cvfs_mkdir; 1152 ops.rmdir = cvfs_rmdir; 1153 ops.rename = cvfs_rename; 1154 ops.copy = cvfs_copy; 1155 ops.ioctl = cvfs_ioctl; 1156 ops.read = cvfs_read; 1157 ops.write = cvfs_write; 1158 ops.seek = cvfs_seek; 1159 ops.flush = cvfs_flush; 1160 ops.close = cvfs_close; 1161 ops.exit = cvfs_exit; 1162 ops.lock = cvfs_lock; 1163 ops.setfileinfo = cvfs_setfileinfo; 1164 ops.qfileinfo = cvfs_qfileinfo; 1165 ops.fsinfo = cvfs_fsinfo; 1166 ops.lpq = cvfs_lpq; 1167 ops.search_first = cvfs_search_first; 1168 ops.search_next = cvfs_search_next; 1169 ops.search_close = cvfs_search_close; 1170 ops.trans = cvfs_trans; 1171 ops.logoff = cvfs_logoff; 1172 ops.async_setup = cvfs_async_setup; 1173 ops.cancel = cvfs_cancel; 1174 ops.notify = cvfs_notify; 1175 ops.trans2 = cvfs_trans2; 1176 1177 /* register ourselves with the NTVFS subsystem. We register 1178 under the name 'cifs'. */ 1179 ret = ntvfs_register(&ops, &vers); 1180 1181 if (!NT_STATUS_IS_OK(ret)) { 1182 DEBUG(0,("Failed to register CIFS backend!\n")); 1183 } 1184 1185 return ret; 1186} 1187