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