r13923: make the state_flags per dcesrv_connection
[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 "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                                                 0,
231                                                 &p->dce_conn);
232         if (!NT_STATUS_IS_OK(status)) {
233                 idr_remove(private->idtree_fnum, p->fnum);
234                 return status;
235         }
236
237         DLIST_ADD(private->pipe_list, p);
238
239         p->smbpid = req->smbpid;
240         p->session = req->session;
241         p->private = private;
242
243         *ps = p;
244
245         talloc_steal(private, p);
246
247         talloc_set_destructor(p, ipc_fd_destructor);
248
249         return NT_STATUS_OK;
250 }
251
252 /*
253   open a file with ntcreatex - used for MSRPC pipes
254 */
255 static NTSTATUS ipc_open_ntcreatex(struct ntvfs_module_context *ntvfs,
256                                    struct smbsrv_request *req, union smb_open *oi)
257 {
258         struct pipe_state *p;
259         NTSTATUS status;
260
261         status = ipc_open_generic(ntvfs, req, oi->ntcreatex.in.fname, &p);
262         if (!NT_STATUS_IS_OK(status)) {
263                 return status;
264         }
265
266         ZERO_STRUCT(oi->ntcreatex.out);
267         oi->ntcreatex.out.fnum = p->fnum;
268         oi->ntcreatex.out.ipc_state = p->ipc_state;
269         oi->ntcreatex.out.file_type = FILE_TYPE_MESSAGE_MODE_PIPE;
270
271         return status;
272 }
273
274 /*
275   open a file with openx - used for MSRPC pipes
276 */
277 static NTSTATUS ipc_open_openx(struct ntvfs_module_context *ntvfs,
278                                struct smbsrv_request *req, union smb_open *oi)
279 {
280         struct pipe_state *p;
281         NTSTATUS status;
282         const char *fname = oi->openx.in.fname;
283
284         status = ipc_open_generic(ntvfs, req, fname, &p);
285         if (!NT_STATUS_IS_OK(status)) {
286                 return status;
287         }
288
289         ZERO_STRUCT(oi->openx.out);
290         oi->openx.out.fnum = p->fnum;
291         oi->openx.out.ftype = 2;
292         oi->openx.out.devstate = p->ipc_state;
293         
294         return status;
295 }
296
297 /*
298   open a file - used for MSRPC pipes
299 */
300 static NTSTATUS ipc_open(struct ntvfs_module_context *ntvfs,
301                                 struct smbsrv_request *req, union smb_open *oi)
302 {
303         NTSTATUS status;
304
305         switch (oi->generic.level) {
306         case RAW_OPEN_NTCREATEX:
307                 status = ipc_open_ntcreatex(ntvfs, req, oi);
308                 break;
309         case RAW_OPEN_OPENX:
310                 status = ipc_open_openx(ntvfs, req, oi);
311                 break;
312         default:
313                 status = NT_STATUS_NOT_SUPPORTED;
314                 break;
315         }
316
317         return status;
318 }
319
320 /*
321   create a directory
322 */
323 static NTSTATUS ipc_mkdir(struct ntvfs_module_context *ntvfs,
324                           struct smbsrv_request *req, union smb_mkdir *md)
325 {
326         return NT_STATUS_ACCESS_DENIED;
327 }
328
329 /*
330   remove a directory
331 */
332 static NTSTATUS ipc_rmdir(struct ntvfs_module_context *ntvfs,
333                           struct smbsrv_request *req, struct smb_rmdir *rd)
334 {
335         return NT_STATUS_ACCESS_DENIED;
336 }
337
338 /*
339   rename a set of files
340 */
341 static NTSTATUS ipc_rename(struct ntvfs_module_context *ntvfs,
342                            struct smbsrv_request *req, union smb_rename *ren)
343 {
344         return NT_STATUS_ACCESS_DENIED;
345 }
346
347 /*
348   copy a set of files
349 */
350 static NTSTATUS ipc_copy(struct ntvfs_module_context *ntvfs,
351                          struct smbsrv_request *req, struct smb_copy *cp)
352 {
353         return NT_STATUS_ACCESS_DENIED;
354 }
355
356 static NTSTATUS ipc_readx_dcesrv_output(void *private_data, DATA_BLOB *out, size_t *nwritten)
357 {
358         DATA_BLOB *blob = private_data;
359
360         if (out->length < blob->length) {
361                 blob->length = out->length;
362         }
363         memcpy(blob->data, out->data, blob->length);
364         *nwritten = blob->length;
365         return NT_STATUS_OK;
366 }
367
368 /*
369   read from a file
370 */
371 static NTSTATUS ipc_read(struct ntvfs_module_context *ntvfs,
372                          struct smbsrv_request *req, union smb_read *rd)
373 {
374         struct ipc_private *private = ntvfs->private_data;
375         DATA_BLOB data;
376         uint16_t fnum;
377         struct pipe_state *p;
378         NTSTATUS status = NT_STATUS_OK;
379
380         if (rd->generic.level != RAW_READ_GENERIC) {
381                 return ntvfs_map_read(req, rd, ntvfs);
382         }
383
384         fnum = rd->readx.in.fnum;
385
386         p = pipe_state_find(private, fnum);
387         if (!p) {
388                 return NT_STATUS_INVALID_HANDLE;
389         }
390
391         data.length = rd->readx.in.maxcnt;
392         data.data = rd->readx.out.data;
393         if (data.length > UINT16_MAX) {
394                 data.length = UINT16_MAX;
395         }
396
397         if (data.length != 0) {
398                 status = dcesrv_output(p->dce_conn, &data, ipc_readx_dcesrv_output);
399                 if (NT_STATUS_IS_ERR(status)) {
400                         return status;
401                 }
402         }
403
404         rd->readx.out.remaining = 0;
405         rd->readx.out.compaction_mode = 0;
406         rd->readx.out.nread = data.length;
407
408         return status;
409 }
410
411 /*
412   write to a file
413 */
414 static NTSTATUS ipc_write(struct ntvfs_module_context *ntvfs,
415                           struct smbsrv_request *req, union smb_write *wr)
416 {
417         struct ipc_private *private = ntvfs->private_data;
418         DATA_BLOB data;
419         uint16_t fnum;
420         struct pipe_state *p;
421         NTSTATUS status;
422
423         if (wr->generic.level != RAW_WRITE_GENERIC) {
424                 return ntvfs_map_write(req, wr, ntvfs);
425         }
426
427         fnum = wr->writex.in.fnum;
428         data.data = discard_const_p(void, wr->writex.in.data);
429         data.length = wr->writex.in.count;
430
431         p = pipe_state_find(private, fnum);
432         if (!p) {
433                 return NT_STATUS_INVALID_HANDLE;
434         }
435
436         status = dcesrv_input(p->dce_conn, &data);
437         if (!NT_STATUS_IS_OK(status)) {
438                 return status;
439         }
440
441         wr->writex.out.nwritten = data.length;
442         wr->writex.out.remaining = 0;
443
444         return NT_STATUS_OK;
445 }
446
447 /*
448   seek in a file
449 */
450 static NTSTATUS ipc_seek(struct ntvfs_module_context *ntvfs,
451                          struct smbsrv_request *req, struct smb_seek *io)
452 {
453         return NT_STATUS_ACCESS_DENIED;
454 }
455
456 /*
457   flush a file
458 */
459 static NTSTATUS ipc_flush(struct ntvfs_module_context *ntvfs,
460                           struct smbsrv_request *req, struct smb_flush *io)
461 {
462         return NT_STATUS_ACCESS_DENIED;
463 }
464
465 /*
466   close a file
467 */
468 static NTSTATUS ipc_close(struct ntvfs_module_context *ntvfs,
469                           struct smbsrv_request *req, union smb_close *io)
470 {
471         struct ipc_private *private = ntvfs->private_data;
472         struct pipe_state *p;
473
474         if (io->generic.level != RAW_CLOSE_CLOSE) {
475                 return ntvfs_map_close(req, io, ntvfs);
476         }
477
478         p = pipe_state_find(private, io->close.in.fnum);
479         if (!p) {
480                 return NT_STATUS_INVALID_HANDLE;
481         }
482
483         talloc_free(p);
484
485         return NT_STATUS_OK;
486 }
487
488 /*
489   exit - closing files
490 */
491 static NTSTATUS ipc_exit(struct ntvfs_module_context *ntvfs,
492                          struct smbsrv_request *req)
493 {
494         struct ipc_private *private = ntvfs->private_data;
495         struct pipe_state *p, *next;
496         
497         for (p=private->pipe_list; p; p=next) {
498                 next = p->next;
499                 if (p->smbpid == req->smbpid) {
500                         talloc_free(p);
501                 }
502         }
503
504         return NT_STATUS_OK;
505 }
506
507 /*
508   logoff - closing files open by the user
509 */
510 static NTSTATUS ipc_logoff(struct ntvfs_module_context *ntvfs,
511                            struct smbsrv_request *req)
512 {
513         struct ipc_private *private = ntvfs->private_data;
514         struct pipe_state *p, *next;
515         
516         for (p=private->pipe_list; p; p=next) {
517                 next = p->next;
518                 if (p->session == req->session) {
519                         talloc_free(p);
520                 }
521         }
522
523         return NT_STATUS_OK;
524 }
525
526 /*
527   setup for an async call
528 */
529 static NTSTATUS ipc_async_setup(struct ntvfs_module_context *ntvfs,
530                                 struct smbsrv_request *req,
531                                 void *private)
532 {
533         return NT_STATUS_OK;
534 }
535
536 /*
537   cancel an async call
538 */
539 static NTSTATUS ipc_cancel(struct ntvfs_module_context *ntvfs,
540                            struct smbsrv_request *req)
541 {
542         return NT_STATUS_UNSUCCESSFUL;
543 }
544
545 /*
546   lock a byte range
547 */
548 static NTSTATUS ipc_lock(struct ntvfs_module_context *ntvfs,
549                          struct smbsrv_request *req, union smb_lock *lck)
550 {
551         return NT_STATUS_ACCESS_DENIED;
552 }
553
554 /*
555   set info on a open file
556 */
557 static NTSTATUS ipc_setfileinfo(struct ntvfs_module_context *ntvfs,
558                                 struct smbsrv_request *req, union smb_setfileinfo *info)
559 {
560         return NT_STATUS_ACCESS_DENIED;
561 }
562
563 /*
564   query info on a open file
565 */
566 static NTSTATUS ipc_qfileinfo(struct ntvfs_module_context *ntvfs,
567                               struct smbsrv_request *req, union smb_fileinfo *info)
568 {
569         return NT_STATUS_ACCESS_DENIED;
570 }
571
572
573 /*
574   return filesystem info
575 */
576 static NTSTATUS ipc_fsinfo(struct ntvfs_module_context *ntvfs,
577                            struct smbsrv_request *req, union smb_fsinfo *fs)
578 {
579         return NT_STATUS_ACCESS_DENIED;
580 }
581
582 /*
583   return print queue info
584 */
585 static NTSTATUS ipc_lpq(struct ntvfs_module_context *ntvfs,
586                         struct smbsrv_request *req, union smb_lpq *lpq)
587 {
588         return NT_STATUS_ACCESS_DENIED;
589 }
590
591 /* 
592    list files in a directory matching a wildcard pattern
593 */
594 static NTSTATUS ipc_search_first(struct ntvfs_module_context *ntvfs,
595                           struct smbsrv_request *req, union smb_search_first *io,
596                           void *search_private, 
597                           BOOL (*callback)(void *, union smb_search_data *))
598 {
599         return NT_STATUS_ACCESS_DENIED;
600 }
601
602 /* 
603    continue listing files in a directory 
604 */
605 static NTSTATUS ipc_search_next(struct ntvfs_module_context *ntvfs,
606                          struct smbsrv_request *req, union smb_search_next *io,
607                          void *search_private, 
608                          BOOL (*callback)(void *, union smb_search_data *))
609 {
610         return NT_STATUS_ACCESS_DENIED;
611 }
612
613 /* 
614    end listing files in a directory 
615 */
616 static NTSTATUS ipc_search_close(struct ntvfs_module_context *ntvfs,
617                           struct smbsrv_request *req, union smb_search_close *io)
618 {
619         return NT_STATUS_ACCESS_DENIED;
620 }
621
622 static NTSTATUS ipc_trans_dcesrv_output(void *private_data, DATA_BLOB *out, size_t *nwritten)
623 {
624         NTSTATUS status = NT_STATUS_OK;
625         DATA_BLOB *blob = private_data;
626
627         if (out->length > blob->length) {
628                 status = STATUS_BUFFER_OVERFLOW;
629         }
630
631         if (out->length < blob->length) {
632                 blob->length = out->length;
633         }
634         memcpy(blob->data, out->data, blob->length);
635         *nwritten = blob->length;
636         return status;
637 }
638
639 /* SMBtrans - handle a DCERPC command */
640 static NTSTATUS ipc_dcerpc_cmd(struct ntvfs_module_context *ntvfs,
641                                struct smbsrv_request *req, struct smb_trans2 *trans)
642 {
643         struct pipe_state *p;
644         struct ipc_private *private = ntvfs->private_data;
645         NTSTATUS status;
646
647         /* the fnum is in setup[1] */
648         p = pipe_state_find(private, trans->in.setup[1]);
649         if (!p) {
650                 return NT_STATUS_INVALID_HANDLE;
651         }
652
653         trans->out.data = data_blob_talloc(req, NULL, trans->in.max_data);
654         if (!trans->out.data.data) {
655                 return NT_STATUS_NO_MEMORY;
656         }
657
658         /* pass the data to the dcerpc server. Note that we don't
659            expect this to fail, and things like NDR faults are not
660            reported at this stage. Those sorts of errors happen in the
661            dcesrv_output stage */
662         status = dcesrv_input(p->dce_conn, &trans->in.data);
663         if (!NT_STATUS_IS_OK(status)) {
664                 return status;
665         }
666
667         /*
668           now ask the dcerpc system for some output. This doesn't yet handle
669           async calls. Again, we only expect NT_STATUS_OK. If the call fails then
670           the error is encoded at the dcerpc level
671         */
672         status = dcesrv_output(p->dce_conn, &trans->out.data, ipc_trans_dcesrv_output);
673         if (NT_STATUS_IS_ERR(status)) {
674                 return status;
675         }
676
677         trans->out.setup_count = 0;
678         trans->out.setup = NULL;
679         trans->out.params = data_blob(NULL, 0);
680
681         return status;
682 }
683
684
685 /* SMBtrans - set named pipe state */
686 static NTSTATUS ipc_set_nm_pipe_state(struct ntvfs_module_context *ntvfs,
687                                 struct smbsrv_request *req, struct smb_trans2 *trans)
688 {
689         struct ipc_private *private = ntvfs->private_data;
690         struct pipe_state *p;
691
692         /* the fnum is in setup[1] */
693         p = pipe_state_find(private, trans->in.setup[1]);
694         if (!p) {
695                 return NT_STATUS_INVALID_HANDLE;
696         }
697
698         if (trans->in.params.length != 2) {
699                 return NT_STATUS_INVALID_PARAMETER;
700         }
701         p->ipc_state = SVAL(trans->in.params.data, 0);
702
703         trans->out.setup_count = 0;
704         trans->out.setup = NULL;
705         trans->out.params = data_blob(NULL, 0);
706         trans->out.data = data_blob(NULL, 0);
707
708         return NT_STATUS_OK;
709 }
710
711
712 /* SMBtrans - used to provide access to SMB pipes */
713 static NTSTATUS ipc_trans(struct ntvfs_module_context *ntvfs,
714                                 struct smbsrv_request *req, struct smb_trans2 *trans)
715 {
716         NTSTATUS status;
717
718         if (strequal(trans->in.trans_name, "\\PIPE\\LANMAN"))
719                 return ipc_rap_call(req, trans);
720
721         if (trans->in.setup_count != 2) {
722                 return NT_STATUS_INVALID_PARAMETER;
723         }
724
725         switch (trans->in.setup[0]) {
726         case TRANSACT_SETNAMEDPIPEHANDLESTATE:
727                 status = ipc_set_nm_pipe_state(ntvfs, req, trans);
728                 break;
729         case TRANSACT_DCERPCCMD:
730                 status = ipc_dcerpc_cmd(ntvfs, req, trans);
731                 break;
732         default:
733                 status = NT_STATUS_INVALID_PARAMETER;
734                 break;
735         }
736
737         return status;
738 }
739
740
741
742 /*
743   initialialise the IPC backend, registering ourselves with the ntvfs subsystem
744  */
745 NTSTATUS ntvfs_ipc_init(void)
746 {
747         NTSTATUS ret;
748         struct ntvfs_ops ops;
749
750         ZERO_STRUCT(ops);
751         
752         /* fill in the name and type */
753         ops.name = "default";
754         ops.type = NTVFS_IPC;
755
756         /* fill in all the operations */
757         ops.connect = ipc_connect;
758         ops.disconnect = ipc_disconnect;
759         ops.unlink = ipc_unlink;
760         ops.chkpath = ipc_chkpath;
761         ops.qpathinfo = ipc_qpathinfo;
762         ops.setpathinfo = ipc_setpathinfo;
763         ops.openfile = ipc_open;
764         ops.mkdir = ipc_mkdir;
765         ops.rmdir = ipc_rmdir;
766         ops.rename = ipc_rename;
767         ops.copy = ipc_copy;
768         ops.ioctl = ipc_ioctl;
769         ops.read = ipc_read;
770         ops.write = ipc_write;
771         ops.seek = ipc_seek;
772         ops.flush = ipc_flush;  
773         ops.close = ipc_close;
774         ops.exit = ipc_exit;
775         ops.lock = ipc_lock;
776         ops.setfileinfo = ipc_setfileinfo;
777         ops.qfileinfo = ipc_qfileinfo;
778         ops.fsinfo = ipc_fsinfo;
779         ops.lpq = ipc_lpq;
780         ops.search_first = ipc_search_first;
781         ops.search_next = ipc_search_next;
782         ops.search_close = ipc_search_close;
783         ops.trans = ipc_trans;
784         ops.logoff = ipc_logoff;
785         ops.async_setup = ipc_async_setup;
786         ops.cancel = ipc_cancel;
787
788         /* register ourselves with the NTVFS subsystem. */
789         ret = ntvfs_register(&ops);
790
791         if (!NT_STATUS_IS_OK(ret)) {
792                 DEBUG(0,("Failed to register IPC backend!\n"));
793                 return ret;
794         }
795
796         return ret;
797 }