r14380: Reduce the size of structs.h
[kamenim/samba.git] / source4 / ntvfs / ipc / vfs_ipc.c
1 /* 
2    Unix SMB/CIFS implementation.
3    default IPC$ NTVFS backend
4
5    Copyright (C) Andrew Tridgell 2003
6    Copyright (C) Stefan (metze) Metzmacher 2004-2005
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 2 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, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22 /*
23   this implements the IPC$ backend, called by the NTVFS subsystem to
24   handle requests on IPC$ shares
25 */
26
27
28 #include "includes.h"
29 #include "dlinklist.h"
30 #include "smb_server/smb_server.h"
31 #include "ntvfs/ntvfs.h"
32 #include "libcli/rap/rap.h"
33 #include "ntvfs/ipc/proto.h"
34 #include "rpc_server/dcerpc_server.h"
35
36 #define IPC_BASE_FNUM 0x400
37
38 /* this is the private structure used to keep the state of an open
39    ipc$ connection. It needs to keep information about all open
40    pipes */
41 struct ipc_private {
42         struct idr_context *idtree_fnum;
43
44         struct dcesrv_context *dcesrv;
45
46         /* a list of open pipes */
47         struct pipe_state {
48                 struct pipe_state *next, *prev;
49                 struct ipc_private *private;
50                 const char *pipe_name;
51                 uint16_t fnum;
52                 struct dcesrv_connection *dce_conn;
53                 uint16_t ipc_state;
54                 /* we need to remember the session it was opened on,
55                    as it is illegal to operate on someone elses fnum */
56                 struct smbsrv_session *session;
57
58                 /* we need to remember the client pid that 
59                    opened the file so SMBexit works */
60                 uint16_t smbpid;
61         } *pipe_list;
62
63 };
64
65
66 /*
67   find a open pipe give a file descriptor
68 */
69 static struct pipe_state *pipe_state_find(struct ipc_private *private, uint16_t fnum)
70 {
71         return idr_find(private->idtree_fnum, fnum);
72 }
73
74
75 /*
76   connect to a share - always works 
77 */
78 static NTSTATUS ipc_connect(struct ntvfs_module_context *ntvfs,
79                             struct ntvfs_request *req, const char *sharename)
80 {
81         NTSTATUS status;
82         struct smbsrv_tcon *tcon = req->tcon;
83         struct ipc_private *private;
84
85         tcon->fs_type = talloc_strdup(tcon, "IPC");
86         NT_STATUS_HAVE_NO_MEMORY(tcon->fs_type);
87
88         tcon->dev_type = talloc_strdup(tcon, "IPC");
89         NT_STATUS_HAVE_NO_MEMORY(tcon->dev_type);
90
91         /* prepare the private state for this connection */
92         private = talloc(ntvfs, struct ipc_private);
93         NT_STATUS_HAVE_NO_MEMORY(private);
94
95         ntvfs->private_data = private;
96
97         private->pipe_list = NULL;
98
99         private->idtree_fnum = idr_init(private);
100         NT_STATUS_HAVE_NO_MEMORY(private->idtree_fnum);
101
102         /* setup the DCERPC server subsystem */
103         status = dcesrv_init_ipc_context(private, &private->dcesrv);
104         NT_STATUS_NOT_OK_RETURN(status);
105
106         return NT_STATUS_OK;
107 }
108
109 /*
110   disconnect from a share
111 */
112 static NTSTATUS ipc_disconnect(struct ntvfs_module_context *ntvfs)
113 {
114         return NT_STATUS_OK;
115 }
116
117 /*
118   delete a file
119 */
120 static NTSTATUS ipc_unlink(struct ntvfs_module_context *ntvfs,
121                            struct ntvfs_request *req,
122                            union smb_unlink *unl)
123 {
124         return NT_STATUS_ACCESS_DENIED;
125 }
126
127
128 /*
129   ioctl interface - we don't do any
130 */
131 static NTSTATUS ipc_ioctl(struct ntvfs_module_context *ntvfs,
132                           struct ntvfs_request *req, union smb_ioctl *io)
133 {
134         return NT_STATUS_ACCESS_DENIED;
135 }
136
137 /*
138   check if a directory exists
139 */
140 static NTSTATUS ipc_chkpath(struct ntvfs_module_context *ntvfs,
141                             struct ntvfs_request *req,
142                             union smb_chkpath *cp)
143 {
144         return NT_STATUS_ACCESS_DENIED;
145 }
146
147 /*
148   return info on a pathname
149 */
150 static NTSTATUS ipc_qpathinfo(struct ntvfs_module_context *ntvfs,
151                               struct ntvfs_request *req, union smb_fileinfo *info)
152 {
153         return NT_STATUS_ACCESS_DENIED;
154 }
155
156 /*
157   set info on a pathname
158 */
159 static NTSTATUS ipc_setpathinfo(struct ntvfs_module_context *ntvfs,
160                                 struct ntvfs_request *req, union smb_setfileinfo *st)
161 {
162         return NT_STATUS_ACCESS_DENIED;
163 }
164
165
166 /*
167   destroy a open pipe structure
168 */
169 static int ipc_fd_destructor(void *ptr)
170 {
171         struct pipe_state *p = ptr;
172         idr_remove(p->private->idtree_fnum, p->fnum);
173         DLIST_REMOVE(p->private->pipe_list, p);
174         return 0;
175 }
176
177
178 /*
179   open a file backend - used for MSRPC pipes
180 */
181 static NTSTATUS ipc_open_generic(struct ntvfs_module_context *ntvfs,
182                                  struct ntvfs_request *req, const char *fname, 
183                                  struct pipe_state **ps)
184 {
185         struct pipe_state *p;
186         NTSTATUS status;
187         struct dcerpc_binding *ep_description;
188         struct ipc_private *private = ntvfs->private_data;
189         int fnum;
190         struct stream_connection *srv_conn = req->smb_conn->connection;
191
192         if (!req->session || !req->session->session_info) {
193                 return NT_STATUS_ACCESS_DENIED;
194         }
195
196         p = talloc(req, struct pipe_state);
197         NT_STATUS_HAVE_NO_MEMORY(p);
198
199         ep_description = talloc(req, struct dcerpc_binding);
200         NT_STATUS_HAVE_NO_MEMORY(ep_description);
201
202         while (fname[0] == '\\') fname++;
203
204         p->pipe_name = talloc_asprintf(p, "\\pipe\\%s", fname);
205         NT_STATUS_HAVE_NO_MEMORY(p->pipe_name);
206
207         fnum = idr_get_new_above(private->idtree_fnum, p, IPC_BASE_FNUM, UINT16_MAX);
208         if (fnum == -1) {
209                 return NT_STATUS_TOO_MANY_OPENED_FILES;
210         }
211
212         p->fnum = fnum;
213         p->ipc_state = 0x5ff;
214
215         /*
216           we're all set, now ask the dcerpc server subsystem to open the 
217           endpoint. At this stage the pipe isn't bound, so we don't
218           know what interface the user actually wants, just that they want
219           one of the interfaces attached to this pipe endpoint.
220         */
221         ep_description->transport = NCACN_NP;
222         ep_description->endpoint = talloc_reference(ep_description, p->pipe_name);
223
224         /* The session info is refcount-increased in the 
225          * dcesrv_endpoint_search_connect() function
226          */
227         status = dcesrv_endpoint_search_connect(private->dcesrv,
228                                                 p,
229                                                 ep_description, 
230                                                 req->session->session_info,
231                                                 srv_conn,
232                                                 0,
233                                                 &p->dce_conn);
234         if (!NT_STATUS_IS_OK(status)) {
235                 idr_remove(private->idtree_fnum, p->fnum);
236                 return status;
237         }
238
239         DLIST_ADD(private->pipe_list, p);
240
241         p->smbpid = req->smbpid;
242         p->session = req->session;
243         p->private = private;
244
245         *ps = p;
246
247         talloc_steal(private, p);
248
249         talloc_set_destructor(p, ipc_fd_destructor);
250
251         return NT_STATUS_OK;
252 }
253
254 /*
255   open a file with ntcreatex - used for MSRPC pipes
256 */
257 static NTSTATUS ipc_open_ntcreatex(struct ntvfs_module_context *ntvfs,
258                                    struct ntvfs_request *req, union smb_open *oi)
259 {
260         struct pipe_state *p;
261         NTSTATUS status;
262
263         status = ipc_open_generic(ntvfs, req, oi->ntcreatex.in.fname, &p);
264         if (!NT_STATUS_IS_OK(status)) {
265                 return status;
266         }
267
268         ZERO_STRUCT(oi->ntcreatex.out);
269         oi->ntcreatex.out.file.fnum = p->fnum;
270         oi->ntcreatex.out.ipc_state = p->ipc_state;
271         oi->ntcreatex.out.file_type = FILE_TYPE_MESSAGE_MODE_PIPE;
272
273         return status;
274 }
275
276 /*
277   open a file with openx - used for MSRPC pipes
278 */
279 static NTSTATUS ipc_open_openx(struct ntvfs_module_context *ntvfs,
280                                struct ntvfs_request *req, union smb_open *oi)
281 {
282         struct pipe_state *p;
283         NTSTATUS status;
284         const char *fname = oi->openx.in.fname;
285
286         status = ipc_open_generic(ntvfs, req, fname, &p);
287         if (!NT_STATUS_IS_OK(status)) {
288                 return status;
289         }
290
291         ZERO_STRUCT(oi->openx.out);
292         oi->openx.out.file.fnum = p->fnum;
293         oi->openx.out.ftype     = 2;
294         oi->openx.out.devstate  = p->ipc_state;
295         
296         return status;
297 }
298
299 /*
300   open a file - used for MSRPC pipes
301 */
302 static NTSTATUS ipc_open(struct ntvfs_module_context *ntvfs,
303                                 struct ntvfs_request *req, union smb_open *oi)
304 {
305         NTSTATUS status;
306
307         switch (oi->generic.level) {
308         case RAW_OPEN_NTCREATEX:
309                 status = ipc_open_ntcreatex(ntvfs, req, oi);
310                 break;
311         case RAW_OPEN_OPENX:
312                 status = ipc_open_openx(ntvfs, req, oi);
313                 break;
314         default:
315                 status = NT_STATUS_NOT_SUPPORTED;
316                 break;
317         }
318
319         return status;
320 }
321
322 /*
323   create a directory
324 */
325 static NTSTATUS ipc_mkdir(struct ntvfs_module_context *ntvfs,
326                           struct ntvfs_request *req, union smb_mkdir *md)
327 {
328         return NT_STATUS_ACCESS_DENIED;
329 }
330
331 /*
332   remove a directory
333 */
334 static NTSTATUS ipc_rmdir(struct ntvfs_module_context *ntvfs,
335                           struct ntvfs_request *req, struct smb_rmdir *rd)
336 {
337         return NT_STATUS_ACCESS_DENIED;
338 }
339
340 /*
341   rename a set of files
342 */
343 static NTSTATUS ipc_rename(struct ntvfs_module_context *ntvfs,
344                            struct ntvfs_request *req, union smb_rename *ren)
345 {
346         return NT_STATUS_ACCESS_DENIED;
347 }
348
349 /*
350   copy a set of files
351 */
352 static NTSTATUS ipc_copy(struct ntvfs_module_context *ntvfs,
353                          struct ntvfs_request *req, struct smb_copy *cp)
354 {
355         return NT_STATUS_ACCESS_DENIED;
356 }
357
358 static NTSTATUS ipc_readx_dcesrv_output(void *private_data, DATA_BLOB *out, size_t *nwritten)
359 {
360         DATA_BLOB *blob = private_data;
361
362         if (out->length < blob->length) {
363                 blob->length = out->length;
364         }
365         memcpy(blob->data, out->data, blob->length);
366         *nwritten = blob->length;
367         return NT_STATUS_OK;
368 }
369
370 /*
371   read from a file
372 */
373 static NTSTATUS ipc_read(struct ntvfs_module_context *ntvfs,
374                          struct ntvfs_request *req, union smb_read *rd)
375 {
376         struct ipc_private *private = ntvfs->private_data;
377         DATA_BLOB data;
378         uint16_t fnum;
379         struct pipe_state *p;
380         NTSTATUS status = NT_STATUS_OK;
381
382         if (rd->generic.level != RAW_READ_GENERIC) {
383                 return ntvfs_map_read(ntvfs, req, rd);
384         }
385
386         fnum = rd->readx.in.file.fnum;
387
388         p = pipe_state_find(private, fnum);
389         if (!p) {
390                 return NT_STATUS_INVALID_HANDLE;
391         }
392
393         data.length = rd->readx.in.maxcnt;
394         data.data = rd->readx.out.data;
395         if (data.length > UINT16_MAX) {
396                 data.length = UINT16_MAX;
397         }
398
399         if (data.length != 0) {
400                 status = dcesrv_output(p->dce_conn, &data, ipc_readx_dcesrv_output);
401                 if (NT_STATUS_IS_ERR(status)) {
402                         return status;
403                 }
404         }
405
406         rd->readx.out.remaining = 0;
407         rd->readx.out.compaction_mode = 0;
408         rd->readx.out.nread = data.length;
409
410         return status;
411 }
412
413 /*
414   write to a file
415 */
416 static NTSTATUS ipc_write(struct ntvfs_module_context *ntvfs,
417                           struct ntvfs_request *req, union smb_write *wr)
418 {
419         struct ipc_private *private = ntvfs->private_data;
420         DATA_BLOB data;
421         uint16_t fnum;
422         struct pipe_state *p;
423         NTSTATUS status;
424
425         if (wr->generic.level != RAW_WRITE_GENERIC) {
426                 return ntvfs_map_write(ntvfs, req, wr);
427         }
428
429         fnum = wr->writex.in.file.fnum;
430         data.data = discard_const_p(void, wr->writex.in.data);
431         data.length = wr->writex.in.count;
432
433         p = pipe_state_find(private, fnum);
434         if (!p) {
435                 return NT_STATUS_INVALID_HANDLE;
436         }
437
438         status = dcesrv_input(p->dce_conn, &data);
439         if (!NT_STATUS_IS_OK(status)) {
440                 return status;
441         }
442
443         wr->writex.out.nwritten = data.length;
444         wr->writex.out.remaining = 0;
445
446         return NT_STATUS_OK;
447 }
448
449 /*
450   seek in a file
451 */
452 static NTSTATUS ipc_seek(struct ntvfs_module_context *ntvfs,
453                          struct ntvfs_request *req,
454                          union smb_seek *io)
455 {
456         return NT_STATUS_ACCESS_DENIED;
457 }
458
459 /*
460   flush a file
461 */
462 static NTSTATUS ipc_flush(struct ntvfs_module_context *ntvfs,
463                           struct ntvfs_request *req,
464                           union smb_flush *io)
465 {
466         return NT_STATUS_ACCESS_DENIED;
467 }
468
469 /*
470   close a file
471 */
472 static NTSTATUS ipc_close(struct ntvfs_module_context *ntvfs,
473                           struct ntvfs_request *req, union smb_close *io)
474 {
475         struct ipc_private *private = ntvfs->private_data;
476         struct pipe_state *p;
477
478         if (io->generic.level != RAW_CLOSE_CLOSE) {
479                 return ntvfs_map_close(ntvfs, req, io);
480         }
481
482         p = pipe_state_find(private, io->close.in.file.fnum);
483         if (!p) {
484                 return NT_STATUS_INVALID_HANDLE;
485         }
486
487         talloc_free(p);
488
489         return NT_STATUS_OK;
490 }
491
492 /*
493   exit - closing files
494 */
495 static NTSTATUS ipc_exit(struct ntvfs_module_context *ntvfs,
496                          struct ntvfs_request *req)
497 {
498         struct ipc_private *private = ntvfs->private_data;
499         struct pipe_state *p, *next;
500         
501         for (p=private->pipe_list; p; p=next) {
502                 next = p->next;
503                 if (p->smbpid == req->smbpid) {
504                         talloc_free(p);
505                 }
506         }
507
508         return NT_STATUS_OK;
509 }
510
511 /*
512   logoff - closing files open by the user
513 */
514 static NTSTATUS ipc_logoff(struct ntvfs_module_context *ntvfs,
515                            struct ntvfs_request *req)
516 {
517         struct ipc_private *private = ntvfs->private_data;
518         struct pipe_state *p, *next;
519         
520         for (p=private->pipe_list; p; p=next) {
521                 next = p->next;
522                 if (p->session == req->session) {
523                         talloc_free(p);
524                 }
525         }
526
527         return NT_STATUS_OK;
528 }
529
530 /*
531   setup for an async call
532 */
533 static NTSTATUS ipc_async_setup(struct ntvfs_module_context *ntvfs,
534                                 struct ntvfs_request *req,
535                                 void *private)
536 {
537         return NT_STATUS_OK;
538 }
539
540 /*
541   cancel an async call
542 */
543 static NTSTATUS ipc_cancel(struct ntvfs_module_context *ntvfs,
544                            struct ntvfs_request *req)
545 {
546         return NT_STATUS_UNSUCCESSFUL;
547 }
548
549 /*
550   lock a byte range
551 */
552 static NTSTATUS ipc_lock(struct ntvfs_module_context *ntvfs,
553                          struct ntvfs_request *req, union smb_lock *lck)
554 {
555         return NT_STATUS_ACCESS_DENIED;
556 }
557
558 /*
559   set info on a open file
560 */
561 static NTSTATUS ipc_setfileinfo(struct ntvfs_module_context *ntvfs,
562                                 struct ntvfs_request *req, union smb_setfileinfo *info)
563 {
564         return NT_STATUS_ACCESS_DENIED;
565 }
566
567 /*
568   query info on a open file
569 */
570 static NTSTATUS ipc_qfileinfo(struct ntvfs_module_context *ntvfs,
571                               struct ntvfs_request *req, union smb_fileinfo *info)
572 {
573         return NT_STATUS_ACCESS_DENIED;
574 }
575
576
577 /*
578   return filesystem info
579 */
580 static NTSTATUS ipc_fsinfo(struct ntvfs_module_context *ntvfs,
581                            struct ntvfs_request *req, union smb_fsinfo *fs)
582 {
583         return NT_STATUS_ACCESS_DENIED;
584 }
585
586 /*
587   return print queue info
588 */
589 static NTSTATUS ipc_lpq(struct ntvfs_module_context *ntvfs,
590                         struct ntvfs_request *req, union smb_lpq *lpq)
591 {
592         return NT_STATUS_ACCESS_DENIED;
593 }
594
595 /* 
596    list files in a directory matching a wildcard pattern
597 */
598 static NTSTATUS ipc_search_first(struct ntvfs_module_context *ntvfs,
599                           struct ntvfs_request *req, union smb_search_first *io,
600                           void *search_private, 
601                           BOOL (*callback)(void *, union smb_search_data *))
602 {
603         return NT_STATUS_ACCESS_DENIED;
604 }
605
606 /* 
607    continue listing files in a directory 
608 */
609 static NTSTATUS ipc_search_next(struct ntvfs_module_context *ntvfs,
610                          struct ntvfs_request *req, union smb_search_next *io,
611                          void *search_private, 
612                          BOOL (*callback)(void *, union smb_search_data *))
613 {
614         return NT_STATUS_ACCESS_DENIED;
615 }
616
617 /* 
618    end listing files in a directory 
619 */
620 static NTSTATUS ipc_search_close(struct ntvfs_module_context *ntvfs,
621                           struct ntvfs_request *req, union smb_search_close *io)
622 {
623         return NT_STATUS_ACCESS_DENIED;
624 }
625
626 static NTSTATUS ipc_trans_dcesrv_output(void *private_data, DATA_BLOB *out, size_t *nwritten)
627 {
628         NTSTATUS status = NT_STATUS_OK;
629         DATA_BLOB *blob = private_data;
630
631         if (out->length > blob->length) {
632                 status = STATUS_BUFFER_OVERFLOW;
633         }
634
635         if (out->length < blob->length) {
636                 blob->length = out->length;
637         }
638         memcpy(blob->data, out->data, blob->length);
639         *nwritten = blob->length;
640         return status;
641 }
642
643 /* SMBtrans - handle a DCERPC command */
644 static NTSTATUS ipc_dcerpc_cmd(struct ntvfs_module_context *ntvfs,
645                                struct ntvfs_request *req, struct smb_trans2 *trans)
646 {
647         struct pipe_state *p;
648         struct ipc_private *private = ntvfs->private_data;
649         NTSTATUS status;
650
651         /* the fnum is in setup[1] */
652         p = pipe_state_find(private, trans->in.setup[1]);
653         if (!p) {
654                 return NT_STATUS_INVALID_HANDLE;
655         }
656
657         trans->out.data = data_blob_talloc(req, NULL, trans->in.max_data);
658         if (!trans->out.data.data) {
659                 return NT_STATUS_NO_MEMORY;
660         }
661
662         /* pass the data to the dcerpc server. Note that we don't
663            expect this to fail, and things like NDR faults are not
664            reported at this stage. Those sorts of errors happen in the
665            dcesrv_output stage */
666         status = dcesrv_input(p->dce_conn, &trans->in.data);
667         if (!NT_STATUS_IS_OK(status)) {
668                 return status;
669         }
670
671         /*
672           now ask the dcerpc system for some output. This doesn't yet handle
673           async calls. Again, we only expect NT_STATUS_OK. If the call fails then
674           the error is encoded at the dcerpc level
675         */
676         status = dcesrv_output(p->dce_conn, &trans->out.data, ipc_trans_dcesrv_output);
677         if (NT_STATUS_IS_ERR(status)) {
678                 return status;
679         }
680
681         trans->out.setup_count = 0;
682         trans->out.setup = NULL;
683         trans->out.params = data_blob(NULL, 0);
684
685         return status;
686 }
687
688
689 /* SMBtrans - set named pipe state */
690 static NTSTATUS ipc_set_nm_pipe_state(struct ntvfs_module_context *ntvfs,
691                                 struct ntvfs_request *req, struct smb_trans2 *trans)
692 {
693         struct ipc_private *private = ntvfs->private_data;
694         struct pipe_state *p;
695
696         /* the fnum is in setup[1] */
697         p = pipe_state_find(private, trans->in.setup[1]);
698         if (!p) {
699                 return NT_STATUS_INVALID_HANDLE;
700         }
701
702         if (trans->in.params.length != 2) {
703                 return NT_STATUS_INVALID_PARAMETER;
704         }
705         p->ipc_state = SVAL(trans->in.params.data, 0);
706
707         trans->out.setup_count = 0;
708         trans->out.setup = NULL;
709         trans->out.params = data_blob(NULL, 0);
710         trans->out.data = data_blob(NULL, 0);
711
712         return NT_STATUS_OK;
713 }
714
715
716 /* SMBtrans - used to provide access to SMB pipes */
717 static NTSTATUS ipc_trans(struct ntvfs_module_context *ntvfs,
718                                 struct ntvfs_request *req, struct smb_trans2 *trans)
719 {
720         NTSTATUS status;
721
722         if (strequal(trans->in.trans_name, "\\PIPE\\LANMAN"))
723                 return ipc_rap_call(req, trans);
724
725         if (trans->in.setup_count != 2) {
726                 return NT_STATUS_INVALID_PARAMETER;
727         }
728
729         switch (trans->in.setup[0]) {
730         case TRANSACT_SETNAMEDPIPEHANDLESTATE:
731                 status = ipc_set_nm_pipe_state(ntvfs, req, trans);
732                 break;
733         case TRANSACT_DCERPCCMD:
734                 status = ipc_dcerpc_cmd(ntvfs, req, trans);
735                 break;
736         default:
737                 status = NT_STATUS_INVALID_PARAMETER;
738                 break;
739         }
740
741         return status;
742 }
743
744
745
746 /*
747   initialialise the IPC backend, registering ourselves with the ntvfs subsystem
748  */
749 NTSTATUS ntvfs_ipc_init(void)
750 {
751         NTSTATUS ret;
752         struct ntvfs_ops ops;
753
754         ZERO_STRUCT(ops);
755         
756         /* fill in the name and type */
757         ops.name = "default";
758         ops.type = NTVFS_IPC;
759
760         /* fill in all the operations */
761         ops.connect = ipc_connect;
762         ops.disconnect = ipc_disconnect;
763         ops.unlink = ipc_unlink;
764         ops.chkpath = ipc_chkpath;
765         ops.qpathinfo = ipc_qpathinfo;
766         ops.setpathinfo = ipc_setpathinfo;
767         ops.open = ipc_open;
768         ops.mkdir = ipc_mkdir;
769         ops.rmdir = ipc_rmdir;
770         ops.rename = ipc_rename;
771         ops.copy = ipc_copy;
772         ops.ioctl = ipc_ioctl;
773         ops.read = ipc_read;
774         ops.write = ipc_write;
775         ops.seek = ipc_seek;
776         ops.flush = ipc_flush;  
777         ops.close = ipc_close;
778         ops.exit = ipc_exit;
779         ops.lock = ipc_lock;
780         ops.setfileinfo = ipc_setfileinfo;
781         ops.qfileinfo = ipc_qfileinfo;
782         ops.fsinfo = ipc_fsinfo;
783         ops.lpq = ipc_lpq;
784         ops.search_first = ipc_search_first;
785         ops.search_next = ipc_search_next;
786         ops.search_close = ipc_search_close;
787         ops.trans = ipc_trans;
788         ops.logoff = ipc_logoff;
789         ops.async_setup = ipc_async_setup;
790         ops.cancel = ipc_cancel;
791
792         /* register ourselves with the NTVFS subsystem. */
793         ret = ntvfs_register(&ops);
794
795         if (!NT_STATUS_IS_OK(ret)) {
796                 DEBUG(0,("Failed to register IPC backend!\n"));
797                 return ret;
798         }
799
800         return ret;
801 }