878dbf2357f1315972fa49ac4ec4efec6d543e17
[tprouty/samba.git] / source4 / ntvfs / cifs / vfs_cifs.c
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 2 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, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23 /*
24   this implements a CIFS->CIFS NTVFS filesystem backend. 
25   
26 */
27
28 #include "includes.h"
29
30 /* this is stored in ntvfs_private */
31 struct cvfs_private {
32         struct cli_tree *tree;
33         struct cli_transport *transport;
34         struct tcon_context *conn;
35         const char *map_calls;
36 };
37
38
39 /* a structure used to pass information to an async handler */
40 struct async_info {
41         struct request_context *req;
42         void *parms;
43 };
44
45 /*
46   an idle function to cope with messages from the smbd client while 
47   waiting for a reply from the server
48   this function won't be needed once all of the cifs backend
49   and the core of smbd is converted to use async calls
50 */
51 static void idle_func(struct cli_transport *transport, void *p_private)
52 {
53         struct cvfs_private *private = p_private;
54         if (socket_pending(private->conn->smb->socket.fd)) {
55                 smbd_process_async(private->conn->smb);
56         }
57 }
58
59 /*
60   a handler for oplock break events from the server - these need to be passed
61   along to the client
62  */
63 static BOOL oplock_handler(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *p_private)
64 {
65         struct cvfs_private *private = p_private;
66         
67         DEBUG(5,("vfs_cifs: sending oplock break level %d for fnum %d\n", level, fnum));
68         return req_send_oplock_break(private->conn, fnum, level);
69 }
70
71 /*
72   a handler for read events on a connection to a backend server
73 */
74 static void cifs_socket_handler(struct event_context *ev, struct fd_event *fde, time_t t, uint16 flags)
75 {
76         struct tcon_context *conn = fde->private;
77         struct cvfs_private *private = conn->ntvfs_private;
78
79         DEBUG(5,("cifs_socket_handler event on fd %d\n", fde->fd));
80
81         if (!cli_request_receive_next(private->transport)) {
82                 /* the connection to our server is dead */
83                 close_cnum(conn);
84         }
85 }
86
87 /*
88   connect to a share - used when a tree_connect operation comes in.
89 */
90 static NTSTATUS cvfs_connect(struct request_context *req, const char *sharename)
91 {
92         struct tcon_context *conn = req->conn;
93         NTSTATUS status;
94         struct cvfs_private *private;
95         char *map_calls;
96         struct fd_event fde;
97         const char *host, *user, *pass, *domain, *remote_share;
98
99         /* Here we need to determine which server to connect to.
100          * For now we use parametric options, type cifs.
101          * Later we will use security=server and auth_server.c.
102          */
103         host = lp_parm_string(req->conn->service, "cifs", "server");
104         user = lp_parm_string(req->conn->service, "cifs", "user");
105         pass = lp_parm_string(req->conn->service, "cifs", "password");
106         domain = lp_parm_string(req->conn->service, "cifs", "domain");
107         remote_share = lp_parm_string(req->conn->service, "cifs", "share");
108         if (!remote_share) {
109                 remote_share = sharename;
110         }
111
112         if (!host || !user || !pass || !domain) {
113                 DEBUG(1,("CIFS backend: You must supply server, user, password and domain\n"));
114                 return NT_STATUS_INVALID_PARAMETER;
115         }
116         
117         private = talloc(req->conn->mem_ctx, sizeof(struct cvfs_private));
118         if (!private) {
119                 return NT_STATUS_NO_MEMORY;
120         }
121         ZERO_STRUCTP(private);
122
123         req->conn->ntvfs_private = (void *)private;
124
125         status = cli_tree_full_connection(&private->tree, 
126                                           "vfs_cifs",
127                                           host,
128                                           0,
129                                           remote_share, "?????",
130                                           user, domain,
131                                           pass);
132         if (!NT_STATUS_IS_OK(status)) {
133                 return status;
134         }
135
136         private->transport = private->tree->session->transport;
137         private->tree->session->pid = SVAL(req->in.hdr, HDR_PID);
138         private->conn = req->conn;
139
140         conn->fs_type = talloc_strdup(conn->mem_ctx, "NTFS");
141         conn->dev_type = talloc_strdup(conn->mem_ctx, "A:");
142         
143         map_calls = lp_parm_string(req->conn->service, "cifs", "map calls");
144         if (map_calls) {
145                 private->map_calls = talloc_strdup(conn->mem_ctx, map_calls);
146         }
147
148         /* if we are mapping trans2, then we need to not give a trans2
149            pointer in the operations structure */
150         if (private->map_calls && in_list("trans2", private->map_calls, True)) {
151                 struct ntvfs_ops *ops = talloc_memdup(conn->mem_ctx,conn->ntvfs_ops,sizeof(*ops));
152                 if (!ops) {
153                         return NT_STATUS_NO_MEMORY;
154                 }
155                 ops->trans2 = NULL;
156                 conn->ntvfs_ops = ops;
157         }         
158
159         /* we need to tell the event loop that we wish to receive read events
160            on our SMB connection to the server */
161         fde.fd = private->transport->socket->fd;
162         fde.flags = EVENT_FD_READ;
163         fde.private = req->conn;
164         fde.handler = cifs_socket_handler;
165
166         event_add_fd(conn->smb->events, &fde);
167
168         /* we need to receive oplock break requests from the server */
169         cli_oplock_handler(private->transport, oplock_handler, private);
170         cli_transport_idle_handler(private->transport, idle_func, 100, private);
171
172         return NT_STATUS_OK;
173 }
174
175 /*
176   disconnect from a share
177 */
178 static NTSTATUS cvfs_disconnect(struct tcon_context *conn)
179 {
180         struct cvfs_private *private = conn->ntvfs_private;
181
182         event_remove_fd_all(conn->smb->events, private->transport->socket->fd);
183         smb_tree_disconnect(private->tree);
184         cli_tree_close(private->tree);
185
186         return NT_STATUS_OK;
187 }
188
189 /*
190   a handler for simple async replies
191   this handler can only be used for functions that don't return any
192   parameters (those that just return a status code)
193  */
194 static void async_simple(struct cli_request *c_req)
195 {
196         struct async_info *async = c_req->async.private;
197         struct request_context *req = async->req;
198         req->async.status = cli_request_simple_recv(c_req);
199         req->async.send_fn(req);
200 }
201
202
203 /* save some typing for the simple functions */
204 #define ASYNC_RECV_TAIL(io, async_fn) do { \
205         if (!c_req) return NT_STATUS_UNSUCCESSFUL; \
206         { \
207                 struct async_info *async; \
208                 async = talloc(req->mem_ctx, sizeof(*async)); \
209                 if (!async) return NT_STATUS_NO_MEMORY; \
210                 async->parms = io; \
211                 async->req = req; \
212                 c_req->async.private = async; \
213         } \
214         c_req->async.fn = async_fn; \
215         req->control_flags |= REQ_CONTROL_ASYNC; \
216         return NT_STATUS_OK; \
217 } while (0)
218
219 #define SIMPLE_ASYNC_TAIL ASYNC_RECV_TAIL(NULL, async_simple)
220
221 /*
222   delete a file - the dirtype specifies the file types to include in the search. 
223   The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
224 */
225 static NTSTATUS cvfs_unlink(struct request_context *req, struct smb_unlink *unl)
226 {
227         struct cvfs_private *private = req->conn->ntvfs_private;
228         struct cli_request *c_req;
229
230         /* see if the front end will allow us to perform this
231            function asynchronously.  */
232         if (!req->async.send_fn) {
233                 return smb_raw_unlink(private->tree, unl);
234         }
235
236         c_req = smb_raw_unlink_send(private->tree, unl);
237
238         SIMPLE_ASYNC_TAIL;
239 }
240
241 /*
242   a handler for async ioctl replies
243  */
244 static void async_ioctl(struct cli_request *c_req)
245 {
246         struct async_info *async = c_req->async.private;
247         struct request_context *req = async->req;
248         req->async.status = smb_raw_ioctl_recv(c_req, req->mem_ctx, async->parms);
249         req->async.send_fn(req);
250 }
251
252 /*
253   ioctl interface
254 */
255 static NTSTATUS cvfs_ioctl(struct request_context *req, union smb_ioctl *io)
256 {
257         struct cvfs_private *private = req->conn->ntvfs_private;
258         struct cli_request *c_req;
259
260         /* see if the front end will allow us to perform this
261            function asynchronously.  */
262         if (!req->async.send_fn) {
263                 return smb_raw_ioctl(private->tree, req->mem_ctx, io);
264         }
265
266         c_req = smb_raw_ioctl_send(private->tree, io);
267
268         ASYNC_RECV_TAIL(io, async_ioctl);
269 }
270
271 /*
272   check if a directory exists
273 */
274 static NTSTATUS cvfs_chkpath(struct request_context *req, struct smb_chkpath *cp)
275 {
276         struct cvfs_private *private = req->conn->ntvfs_private;
277         struct cli_request *c_req;
278
279         if (!req->async.send_fn) {
280                 return smb_raw_chkpath(private->tree, cp);
281         }
282
283         c_req = smb_raw_chkpath_send(private->tree, cp);
284
285         SIMPLE_ASYNC_TAIL;
286 }
287
288 /*
289   a handler for async qpathinfo replies
290  */
291 static void async_qpathinfo(struct cli_request *c_req)
292 {
293         struct async_info *async = c_req->async.private;
294         struct request_context *req = async->req;
295         req->async.status = smb_raw_pathinfo_recv(c_req, req->mem_ctx, async->parms);
296         req->async.send_fn(req);
297 }
298
299 /*
300   return info on a pathname
301 */
302 static NTSTATUS cvfs_qpathinfo(struct request_context *req, union smb_fileinfo *info)
303 {
304         struct cvfs_private *private = req->conn->ntvfs_private;
305         struct cli_request *c_req;
306
307         if (!req->async.send_fn) {
308                 return smb_raw_pathinfo(private->tree, req->mem_ctx, info);
309         }
310
311         c_req = smb_raw_pathinfo_send(private->tree, info);
312
313         ASYNC_RECV_TAIL(info, async_qpathinfo);
314 }
315
316 /*
317   a handler for async qfileinfo replies
318  */
319 static void async_qfileinfo(struct cli_request *c_req)
320 {
321         struct async_info *async = c_req->async.private;
322         struct request_context *req = async->req;
323         req->async.status = smb_raw_fileinfo_recv(c_req, req->mem_ctx, async->parms);
324         req->async.send_fn(req);
325 }
326
327 /*
328   query info on a open file
329 */
330 static NTSTATUS cvfs_qfileinfo(struct request_context *req, union smb_fileinfo *info)
331 {
332         struct cvfs_private *private = req->conn->ntvfs_private;
333         struct cli_request *c_req;
334
335         if (!req->async.send_fn) {
336                 return smb_raw_fileinfo(private->tree, req->mem_ctx, info);
337         }
338
339         c_req = smb_raw_fileinfo_send(private->tree, info);
340
341         ASYNC_RECV_TAIL(info, async_qfileinfo);
342 }
343
344
345 /*
346   set info on a pathname
347 */
348 static NTSTATUS cvfs_setpathinfo(struct request_context *req, union smb_setfileinfo *st)
349 {
350         struct cvfs_private *private = req->conn->ntvfs_private;
351         struct cli_request *c_req;
352
353         if (!req->async.send_fn) {
354                 return smb_raw_setpathinfo(private->tree, st);
355         }
356
357         c_req = smb_raw_setpathinfo_send(private->tree, st);
358
359         SIMPLE_ASYNC_TAIL;
360 }
361
362
363 /*
364   a handler for async open replies
365  */
366 static void async_open(struct cli_request *c_req)
367 {
368         struct async_info *async = c_req->async.private;
369         struct request_context *req = async->req;
370         req->async.status = smb_raw_open_recv(c_req, req->mem_ctx, async->parms);
371         req->async.send_fn(req);
372 }
373
374 /*
375   open a file
376 */
377 static NTSTATUS cvfs_open(struct request_context *req, union smb_open *io)
378 {
379         struct cvfs_private *private = req->conn->ntvfs_private;
380         struct cli_request *c_req;
381
382         if (private->map_calls && in_list("open", private->map_calls, True) &&
383             io->generic.level != RAW_OPEN_GENERIC) {
384                 return ntvfs_map_open(req, io);
385         }
386
387         if (!req->async.send_fn) {
388                 return smb_raw_open(private->tree, req->mem_ctx, io);
389         }
390
391         c_req = smb_raw_open_send(private->tree, io);
392
393         ASYNC_RECV_TAIL(io, async_open);
394 }
395
396 /*
397   create a directory
398 */
399 static NTSTATUS cvfs_mkdir(struct request_context *req, union smb_mkdir *md)
400 {
401         struct cvfs_private *private = req->conn->ntvfs_private;
402         struct cli_request *c_req;
403
404         if (!req->async.send_fn) {
405                 return smb_raw_mkdir(private->tree, md);
406         }
407
408         c_req = smb_raw_mkdir_send(private->tree, md);
409
410         SIMPLE_ASYNC_TAIL;
411 }
412
413 /*
414   remove a directory
415 */
416 static NTSTATUS cvfs_rmdir(struct request_context *req, struct smb_rmdir *rd)
417 {
418         struct cvfs_private *private = req->conn->ntvfs_private;
419         struct cli_request *c_req;
420
421         if (!req->async.send_fn) {
422                 return smb_raw_rmdir(private->tree, rd);
423         }
424         c_req = smb_raw_rmdir_send(private->tree, rd);
425
426         SIMPLE_ASYNC_TAIL;
427 }
428
429 /*
430   rename a set of files
431 */
432 static NTSTATUS cvfs_rename(struct request_context *req, union smb_rename *ren)
433 {
434         struct cvfs_private *private = req->conn->ntvfs_private;
435         struct cli_request *c_req;
436
437         if (!req->async.send_fn) {
438                 return smb_raw_rename(private->tree, ren);
439         }
440
441         c_req = smb_raw_rename_send(private->tree, ren);
442
443         SIMPLE_ASYNC_TAIL;
444 }
445
446 /*
447   copy a set of files
448 */
449 static NTSTATUS cvfs_copy(struct request_context *req, struct smb_copy *cp)
450 {
451         return NT_STATUS_NOT_SUPPORTED;
452 }
453
454 /*
455   a handler for async read replies
456  */
457 static void async_read(struct cli_request *c_req)
458 {
459         struct async_info *async = c_req->async.private;
460         struct request_context *req = async->req;
461         req->async.status = smb_raw_read_recv(c_req, async->parms);
462         req->async.send_fn(req);
463 }
464
465 /*
466   read from a file
467 */
468 static NTSTATUS cvfs_read(struct request_context *req, union smb_read *rd)
469 {
470         struct cvfs_private *private = req->conn->ntvfs_private;
471         struct cli_request *c_req;
472
473         if (!req->async.send_fn) {
474                 return smb_raw_read(private->tree, rd);
475         }
476
477         c_req = smb_raw_read_send(private->tree, rd);
478
479         ASYNC_RECV_TAIL(rd, async_read);
480 }
481
482 /*
483   a handler for async write replies
484  */
485 static void async_write(struct cli_request *c_req)
486 {
487         struct async_info *async = c_req->async.private;
488         struct request_context *req = async->req;
489         req->async.status = smb_raw_write_recv(c_req, async->parms);
490         req->async.send_fn(req);
491 }
492
493 /*
494   write to a file
495 */
496 static NTSTATUS cvfs_write(struct request_context *req, union smb_write *wr)
497 {
498         struct cvfs_private *private = req->conn->ntvfs_private;
499         struct cli_request *c_req;
500
501         if (!req->async.send_fn) {
502                 return smb_raw_write(private->tree, wr);
503         }
504
505         c_req = smb_raw_write_send(private->tree, wr);
506
507         ASYNC_RECV_TAIL(wr, async_write);
508 }
509
510 /*
511   seek in a file
512 */
513 static NTSTATUS cvfs_seek(struct request_context *req, struct smb_seek *io)
514 {
515         return NT_STATUS_NOT_SUPPORTED;
516 }
517
518 /*
519   flush a file
520 */
521 static NTSTATUS cvfs_flush(struct request_context *req, struct smb_flush *io)
522 {
523         return NT_STATUS_OK;
524 }
525
526 /*
527   close a file
528 */
529 static NTSTATUS cvfs_close(struct request_context *req, union smb_close *io)
530 {
531         struct cvfs_private *private = req->conn->ntvfs_private;
532         struct cli_request *c_req;
533
534         if (!req->async.send_fn) {
535                 return smb_raw_close(private->tree, io);
536         }
537
538         c_req = smb_raw_close_send(private->tree, io);
539
540         SIMPLE_ASYNC_TAIL;
541 }
542
543 /*
544   exit - closing files?
545 */
546 static NTSTATUS cvfs_exit(struct request_context *req)
547 {
548         return NT_STATUS_NOT_SUPPORTED;
549 }
550
551 /*
552   lock a byte range
553 */
554 static NTSTATUS cvfs_lock(struct request_context *req, union smb_lock *lck)
555 {
556         struct cvfs_private *private = req->conn->ntvfs_private;
557         struct cli_request *c_req;
558
559         if (!req->async.send_fn) {
560                 return smb_raw_lock(private->tree, lck);
561         }
562
563         c_req = smb_raw_lock_send(private->tree, lck);
564         SIMPLE_ASYNC_TAIL;
565 }
566
567 /*
568   set info on a open file
569 */
570 static NTSTATUS cvfs_setfileinfo(struct request_context *req, 
571                                  union smb_setfileinfo *info)
572 {
573         struct cvfs_private *private = req->conn->ntvfs_private;
574         struct cli_request *c_req;
575
576         if (!req->async.send_fn) {
577                 return smb_raw_setfileinfo(private->tree, info);
578         }
579         c_req = smb_raw_setfileinfo_send(private->tree, info);
580
581         SIMPLE_ASYNC_TAIL;
582 }
583
584
585 /*
586   a handler for async fsinfo replies
587  */
588 static void async_fsinfo(struct cli_request *c_req)
589 {
590         struct async_info *async = c_req->async.private;
591         struct request_context *req = async->req;
592         req->async.status = smb_raw_fsinfo_recv(c_req, req->mem_ctx, async->parms);
593         req->async.send_fn(req);
594 }
595
596 /*
597   return filesystem space info
598 */
599 static NTSTATUS cvfs_fsinfo(struct request_context *req, union smb_fsinfo *fs)
600 {
601         struct cvfs_private *private = req->conn->ntvfs_private;
602         struct cli_request *c_req;
603
604         if (!req->async.send_fn) {
605                 return smb_raw_fsinfo(private->tree, req->mem_ctx, fs);
606         }
607
608         c_req = smb_raw_fsinfo_send(private->tree, req->mem_ctx, fs);
609
610         ASYNC_RECV_TAIL(fs, async_fsinfo);
611 }
612
613 /*
614   return print queue info
615 */
616 static NTSTATUS cvfs_lpq(struct request_context *req, union smb_lpq *lpq)
617 {
618         return NT_STATUS_NOT_SUPPORTED;
619 }
620
621 /* 
622    list files in a directory matching a wildcard pattern
623 */
624 static NTSTATUS cvfs_search_first(struct request_context *req, union smb_search_first *io, 
625                                   void *search_private, 
626                                   BOOL (*callback)(void *, union smb_search_data *))
627 {
628         struct cvfs_private *private = req->conn->ntvfs_private;
629
630         return smb_raw_search_first(private->tree, req->mem_ctx, io, search_private, callback);
631 }
632
633 /* continue a search */
634 static NTSTATUS cvfs_search_next(struct request_context *req, union smb_search_next *io, 
635                                  void *search_private, 
636                                  BOOL (*callback)(void *, union smb_search_data *))
637 {
638         struct cvfs_private *private = req->conn->ntvfs_private;
639
640         return smb_raw_search_next(private->tree, req->mem_ctx, io, search_private, callback);
641 }
642
643 /* close a search */
644 static NTSTATUS cvfs_search_close(struct request_context *req, union smb_search_close *io)
645 {
646         struct cvfs_private *private = req->conn->ntvfs_private;
647
648         return smb_raw_search_close(private->tree, io);
649 }
650
651 /*
652   a handler for async trans2 replies
653  */
654 static void async_trans2(struct cli_request *c_req)
655 {
656         struct async_info *async = c_req->async.private;
657         struct request_context *req = async->req;
658         req->async.status = smb_raw_trans2_recv(c_req, req->mem_ctx, async->parms);
659         req->async.send_fn(req);
660 }
661
662 /* raw trans2 */
663 static NTSTATUS cvfs_trans2(struct request_context *req, struct smb_trans2 *trans2)
664 {
665         struct cvfs_private *private = req->conn->ntvfs_private;
666         struct cli_request *c_req;
667
668         if (!req->async.send_fn) {
669                 return smb_raw_trans2(private->tree, req->mem_ctx, trans2);
670         }
671
672         c_req = smb_raw_trans2_send(private->tree, trans2);
673
674         ASYNC_RECV_TAIL(trans2, async_trans2);
675 }
676
677
678 /* SMBtrans - not used on file shares */
679 static NTSTATUS cvfs_trans(struct request_context *req, struct smb_trans2 *trans2)
680 {
681         return NT_STATUS_ACCESS_DENIED;
682 }
683
684 /*
685   initialise the CIFS->CIFS backend, registering ourselves with the ntvfs subsystem
686  */
687 NTSTATUS ntvfs_cifs_init(void)
688 {
689         NTSTATUS ret;
690         struct ntvfs_ops ops;
691
692         ZERO_STRUCT(ops);
693
694         /* fill in the name and type */
695         ops.name = "cifs";
696         ops.type = NTVFS_DISK;
697         
698         /* fill in all the operations */
699         ops.connect = cvfs_connect;
700         ops.disconnect = cvfs_disconnect;
701         ops.unlink = cvfs_unlink;
702         ops.chkpath = cvfs_chkpath;
703         ops.qpathinfo = cvfs_qpathinfo;
704         ops.setpathinfo = cvfs_setpathinfo;
705         ops.open = cvfs_open;
706         ops.mkdir = cvfs_mkdir;
707         ops.rmdir = cvfs_rmdir;
708         ops.rename = cvfs_rename;
709         ops.copy = cvfs_copy;
710         ops.ioctl = cvfs_ioctl;
711         ops.read = cvfs_read;
712         ops.write = cvfs_write;
713         ops.seek = cvfs_seek;
714         ops.flush = cvfs_flush; 
715         ops.close = cvfs_close;
716         ops.exit = cvfs_exit;
717         ops.lock = cvfs_lock;
718         ops.setfileinfo = cvfs_setfileinfo;
719         ops.qfileinfo = cvfs_qfileinfo;
720         ops.fsinfo = cvfs_fsinfo;
721         ops.lpq = cvfs_lpq;
722         ops.search_first = cvfs_search_first;
723         ops.search_next = cvfs_search_next;
724         ops.search_close = cvfs_search_close;
725         ops.trans = cvfs_trans;
726
727         /* only define this one for trans2 testing */
728         ops.trans2 = cvfs_trans2;
729
730         /* register ourselves with the NTVFS subsystem. We register under the name 'cifs'. */
731
732         ret = register_backend("ntvfs", &ops);
733
734         if (!NT_STATUS_IS_OK(ret)) {
735                 DEBUG(0,("Failed to register CIFS backend!\n"));
736         }
737         
738         return ret;
739 }