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