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