02b7cb8db52252a89d3e57ae9900329229fa75fd
[amitay/samba.git] / source4 / ntvfs / unixuid / vfs_unixuid.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    a pass-thru NTVFS module to setup a security context using unix
5    uid/gid
6
7    Copyright (C) Andrew Tridgell 2004
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "system/filesys.h"
25 #include "system/passwd.h"
26 #include "auth/auth.h"
27 #include "ntvfs/ntvfs.h"
28 #include "libcli/wbclient/wbclient.h"
29 #define TEVENT_DEPRECATED
30 #include <tevent.h>
31
32 #if defined(UID_WRAPPER)
33 #if !defined(UID_WRAPPER_REPLACE) && !defined(UID_WRAPPER_NOT_REPLACE)
34 #define UID_WRAPPER_REPLACE
35 #include "../uid_wrapper/uid_wrapper.h"
36 #endif
37 #else
38 #define uwrap_enabled() 0
39 #endif
40
41
42 NTSTATUS ntvfs_unixuid_init(void);
43
44 struct unixuid_private {
45         struct wbc_context *wbc_ctx;
46         struct security_unix_token *last_sec_ctx;
47         struct security_token *last_token;
48 };
49
50
51 /*
52   pull the current security context into a security_unix_token
53 */
54 static struct security_unix_token *save_unix_security(TALLOC_CTX *mem_ctx)
55 {
56         struct security_unix_token *sec = talloc(mem_ctx, struct security_unix_token);
57         if (sec == NULL) {
58                 return NULL;
59         }
60         sec->uid = geteuid();
61         sec->gid = getegid();
62         sec->ngroups = getgroups(0, NULL);
63         if (sec->ngroups == -1) {
64                 talloc_free(sec);
65                 return NULL;
66         }
67         sec->groups = talloc_array(sec, gid_t, sec->ngroups);
68         if (sec->groups == NULL) {
69                 talloc_free(sec);
70                 return NULL;
71         }
72
73         if (getgroups(sec->ngroups, sec->groups) != sec->ngroups) {
74                 talloc_free(sec);
75                 return NULL;
76         }
77
78         return sec;
79 }
80
81 /*
82   set the current security context from a security_unix_token
83 */
84 static NTSTATUS set_unix_security(struct security_unix_token *sec)
85 {
86         seteuid(0);
87
88         if (setgroups(sec->ngroups, sec->groups) != 0) {
89                 return NT_STATUS_ACCESS_DENIED;
90         }
91         if (setegid(sec->gid) != 0) {
92                 return NT_STATUS_ACCESS_DENIED;
93         }
94         if (seteuid(sec->uid) != 0) {
95                 return NT_STATUS_ACCESS_DENIED;
96         }
97         return NT_STATUS_OK;
98 }
99
100 static int unixuid_nesting_level;
101
102 /*
103   called at the start and end of a tevent nesting loop. Needs to save/restore
104   unix security context
105  */
106 static int unixuid_event_nesting_hook(struct tevent_context *ev,
107                                       void *private_data,
108                                       uint32_t level,
109                                       bool begin,
110                                       void *stack_ptr,
111                                       const char *location)
112 {
113         struct security_unix_token *sec_ctx;
114
115         if (unixuid_nesting_level == 0) {
116                 /* we don't need to do anything unless we are nested
117                    inside of a call in this module */
118                 return 0;
119         }
120
121         if (begin) {
122                 sec_ctx = save_unix_security(ev);
123                 if (sec_ctx == NULL) {
124                         DEBUG(0,("%s: Failed to save security context\n", location));
125                         return -1;
126                 }
127                 *(struct security_unix_token **)stack_ptr = sec_ctx;
128                 if (seteuid(0) != 0 || setegid(0) != 0) {
129                         DEBUG(0,("%s: Failed to change to root\n", location));
130                         return -1;                      
131                 }
132         } else {
133                 /* called when we come out of a nesting level */
134                 NTSTATUS status;
135
136                 sec_ctx = *(struct security_unix_token **)stack_ptr;
137                 if (sec_ctx == NULL) {
138                         /* this happens the first time this function
139                            is called, as we install the hook while
140                            inside an event in unixuid_connect() */
141                         return 0;
142                 }
143
144                 sec_ctx = talloc_get_type_abort(sec_ctx, struct security_unix_token);
145                 status = set_unix_security(sec_ctx);
146                 talloc_free(sec_ctx);
147                 if (!NT_STATUS_IS_OK(status)) {
148                         DEBUG(0,("%s: Failed to revert security context (%s)\n", 
149                                  location, nt_errstr(status)));
150                         return -1;
151                 }
152         }
153
154         return 0;
155 }
156
157
158 /*
159   form a security_unix_token from the current security_token
160 */
161 static NTSTATUS nt_token_to_unix_security(struct ntvfs_module_context *ntvfs,
162                                           struct ntvfs_request *req,
163                                           struct security_token *token,
164                                           struct security_unix_token **sec)
165 {
166         struct unixuid_private *priv = ntvfs->private_data;
167         int i;
168         NTSTATUS status;
169         struct id_map *ids;
170         struct composite_context *ctx;
171         *sec = talloc(req, struct security_unix_token);
172
173         /* we can't do unix security without a user and group */
174         if (token->num_sids < 2) {
175                 return NT_STATUS_ACCESS_DENIED;
176         }
177
178         ids = talloc_array(req, struct id_map, token->num_sids);
179         NT_STATUS_HAVE_NO_MEMORY(ids);
180
181         (*sec)->ngroups = token->num_sids - 2;
182         (*sec)->groups = talloc_array(*sec, gid_t, (*sec)->ngroups);
183         NT_STATUS_HAVE_NO_MEMORY((*sec)->groups);
184
185         for (i=0;i<token->num_sids;i++) {
186                 ZERO_STRUCT(ids[i].xid);
187                 ids[i].sid = &token->sids[i];
188                 ids[i].status = ID_UNKNOWN;
189         }
190
191         ctx = wbc_sids_to_xids_send(priv->wbc_ctx, ids, token->num_sids, ids);
192         NT_STATUS_HAVE_NO_MEMORY(ctx);
193
194         status = wbc_sids_to_xids_recv(ctx, &ids);
195         NT_STATUS_NOT_OK_RETURN(status);
196
197         if (ids[0].xid.type == ID_TYPE_BOTH ||
198             ids[0].xid.type == ID_TYPE_UID) {
199                 (*sec)->uid = ids[0].xid.id;
200         } else {
201                 return NT_STATUS_INVALID_SID;
202         }
203
204         if (ids[1].xid.type == ID_TYPE_BOTH ||
205             ids[1].xid.type == ID_TYPE_GID) {
206                 (*sec)->gid = ids[1].xid.id;
207         } else {
208                 return NT_STATUS_INVALID_SID;
209         }
210
211         for (i=0;i<(*sec)->ngroups;i++) {
212                 if (ids[i+2].xid.type == ID_TYPE_BOTH ||
213                     ids[i+2].xid.type == ID_TYPE_GID) {
214                         (*sec)->groups[i] = ids[i+2].xid.id;
215                 } else {
216                         return NT_STATUS_INVALID_SID;
217                 }
218         }
219
220         return NT_STATUS_OK;
221 }
222
223 /*
224   setup our unix security context according to the session authentication info
225 */
226 static NTSTATUS unixuid_setup_security(struct ntvfs_module_context *ntvfs,
227                                        struct ntvfs_request *req, struct security_unix_token **sec)
228 {
229         struct unixuid_private *priv = ntvfs->private_data;
230         struct security_token *token;
231         struct security_unix_token *newsec;
232         NTSTATUS status;
233
234         /* If we are asked to set up, but have not had a successful
235          * session setup or tree connect, then these may not be filled
236          * in.  ACCESS_DENIED is the right error code here */
237         if (req->session_info == NULL || priv == NULL) {
238                 return NT_STATUS_ACCESS_DENIED;
239         }
240
241         token = req->session_info->security_token;
242
243         *sec = save_unix_security(ntvfs);
244         if (*sec == NULL) {
245                 return NT_STATUS_NO_MEMORY;
246         }
247
248         if (token == priv->last_token) {
249                 newsec = priv->last_sec_ctx;
250         } else {
251                 status = nt_token_to_unix_security(ntvfs, req, token, &newsec);
252                 if (!NT_STATUS_IS_OK(status)) {
253                         talloc_free(*sec);
254                         return status;
255                 }
256                 if (priv->last_sec_ctx) {
257                         talloc_free(priv->last_sec_ctx);
258                 }
259                 priv->last_sec_ctx = newsec;
260                 priv->last_token = token;
261                 talloc_steal(priv, newsec);
262         }
263
264         status = set_unix_security(newsec);
265         if (!NT_STATUS_IS_OK(status)) {
266                 talloc_free(*sec);
267                 return status;
268         }
269
270         return NT_STATUS_OK;
271 }
272
273 /*
274   this pass through macro operates on request contexts
275 */
276 #define PASS_THRU_REQ(ntvfs, req, op, args) do { \
277         NTSTATUS status2; \
278         struct security_unix_token *sec; \
279         status = unixuid_setup_security(ntvfs, req, &sec); \
280         NT_STATUS_NOT_OK_RETURN(status); \
281         unixuid_nesting_level++; \
282         status = ntvfs_next_##op args; \
283         unixuid_nesting_level--; \
284         status2 = set_unix_security(sec); \
285         talloc_free(sec); \
286         if (!NT_STATUS_IS_OK(status2)) smb_panic("Unable to reset security context"); \
287 } while (0)
288
289
290
291 /*
292   connect to a share - used when a tree_connect operation comes in.
293 */
294 static NTSTATUS unixuid_connect(struct ntvfs_module_context *ntvfs,
295                                 struct ntvfs_request *req, union smb_tcon *tcon)
296 {
297         struct unixuid_private *priv;
298         NTSTATUS status;
299
300         priv = talloc(ntvfs, struct unixuid_private);
301         if (!priv) {
302                 return NT_STATUS_NO_MEMORY;
303         }
304
305         priv->wbc_ctx = wbc_init(priv, ntvfs->ctx->msg_ctx,
306                                     ntvfs->ctx->event_ctx);
307         if (priv->wbc_ctx == NULL) {
308                 talloc_free(priv);
309                 return NT_STATUS_INTERNAL_ERROR;
310         }
311
312         priv->last_sec_ctx = NULL;
313         priv->last_token = NULL;
314         ntvfs->private_data = priv;
315
316         tevent_loop_set_nesting_hook(ntvfs->ctx->event_ctx, 
317                                      unixuid_event_nesting_hook,
318                                      &unixuid_nesting_level);
319
320         /* we don't use PASS_THRU_REQ here, as the connect operation runs with 
321            root privileges. This allows the backends to setup any database
322            links they might need during the connect. */
323         status = ntvfs_next_connect(ntvfs, req, tcon);
324
325         return status;
326 }
327
328 /*
329   disconnect from a share
330 */
331 static NTSTATUS unixuid_disconnect(struct ntvfs_module_context *ntvfs)
332 {
333         struct unixuid_private *priv = ntvfs->private_data;
334         NTSTATUS status;
335
336         talloc_free(priv);
337         ntvfs->private_data = NULL;
338
339         status = ntvfs_next_disconnect(ntvfs);
340  
341         return status;
342 }
343
344
345 /*
346   delete a file
347 */
348 static NTSTATUS unixuid_unlink(struct ntvfs_module_context *ntvfs,
349                               struct ntvfs_request *req,
350                               union smb_unlink *unl)
351 {
352         NTSTATUS status;
353
354         PASS_THRU_REQ(ntvfs, req, unlink, (ntvfs, req, unl));
355
356         return status;
357 }
358
359 /*
360   ioctl interface
361 */
362 static NTSTATUS unixuid_ioctl(struct ntvfs_module_context *ntvfs,
363                              struct ntvfs_request *req, union smb_ioctl *io)
364 {
365         NTSTATUS status;
366
367         PASS_THRU_REQ(ntvfs, req, ioctl, (ntvfs, req, io));
368
369         return status;
370 }
371
372 /*
373   check if a directory exists
374 */
375 static NTSTATUS unixuid_chkpath(struct ntvfs_module_context *ntvfs,
376                                 struct ntvfs_request *req,
377                                 union smb_chkpath *cp)
378 {
379         NTSTATUS status;
380
381         PASS_THRU_REQ(ntvfs, req, chkpath, (ntvfs, req, cp));
382
383         return status;
384 }
385
386 /*
387   return info on a pathname
388 */
389 static NTSTATUS unixuid_qpathinfo(struct ntvfs_module_context *ntvfs,
390                                  struct ntvfs_request *req, union smb_fileinfo *info)
391 {
392         NTSTATUS status;
393
394         PASS_THRU_REQ(ntvfs, req, qpathinfo, (ntvfs, req, info));
395
396         return status;
397 }
398
399 /*
400   query info on a open file
401 */
402 static NTSTATUS unixuid_qfileinfo(struct ntvfs_module_context *ntvfs,
403                                  struct ntvfs_request *req, union smb_fileinfo *info)
404 {
405         NTSTATUS status;
406
407         PASS_THRU_REQ(ntvfs, req, qfileinfo, (ntvfs, req, info));
408
409         return status;
410 }
411
412
413 /*
414   set info on a pathname
415 */
416 static NTSTATUS unixuid_setpathinfo(struct ntvfs_module_context *ntvfs,
417                                    struct ntvfs_request *req, union smb_setfileinfo *st)
418 {
419         NTSTATUS status;
420
421         PASS_THRU_REQ(ntvfs, req, setpathinfo, (ntvfs, req, st));
422
423         return status;
424 }
425
426 /*
427   open a file
428 */
429 static NTSTATUS unixuid_open(struct ntvfs_module_context *ntvfs,
430                              struct ntvfs_request *req, union smb_open *io)
431 {
432         NTSTATUS status;
433
434         PASS_THRU_REQ(ntvfs, req, open, (ntvfs, req, io));
435
436         return status;
437 }
438
439 /*
440   create a directory
441 */
442 static NTSTATUS unixuid_mkdir(struct ntvfs_module_context *ntvfs,
443                              struct ntvfs_request *req, union smb_mkdir *md)
444 {
445         NTSTATUS status;
446
447         PASS_THRU_REQ(ntvfs, req, mkdir, (ntvfs, req, md));
448
449         return status;
450 }
451
452 /*
453   remove a directory
454 */
455 static NTSTATUS unixuid_rmdir(struct ntvfs_module_context *ntvfs,
456                              struct ntvfs_request *req, struct smb_rmdir *rd)
457 {
458         NTSTATUS status;
459
460         PASS_THRU_REQ(ntvfs, req, rmdir, (ntvfs, req, rd));
461
462         return status;
463 }
464
465 /*
466   rename a set of files
467 */
468 static NTSTATUS unixuid_rename(struct ntvfs_module_context *ntvfs,
469                               struct ntvfs_request *req, union smb_rename *ren)
470 {
471         NTSTATUS status;
472
473         PASS_THRU_REQ(ntvfs, req, rename, (ntvfs, req, ren));
474
475         return status;
476 }
477
478 /*
479   copy a set of files
480 */
481 static NTSTATUS unixuid_copy(struct ntvfs_module_context *ntvfs,
482                             struct ntvfs_request *req, struct smb_copy *cp)
483 {
484         NTSTATUS status;
485
486         PASS_THRU_REQ(ntvfs, req, copy, (ntvfs, req, cp));
487
488         return status;
489 }
490
491 /*
492   read from a file
493 */
494 static NTSTATUS unixuid_read(struct ntvfs_module_context *ntvfs,
495                             struct ntvfs_request *req, union smb_read *rd)
496 {
497         NTSTATUS status;
498
499         PASS_THRU_REQ(ntvfs, req, read, (ntvfs, req, rd));
500
501         return status;
502 }
503
504 /*
505   write to a file
506 */
507 static NTSTATUS unixuid_write(struct ntvfs_module_context *ntvfs,
508                              struct ntvfs_request *req, union smb_write *wr)
509 {
510         NTSTATUS status;
511
512         PASS_THRU_REQ(ntvfs, req, write, (ntvfs, req, wr));
513
514         return status;
515 }
516
517 /*
518   seek in a file
519 */
520 static NTSTATUS unixuid_seek(struct ntvfs_module_context *ntvfs,
521                              struct ntvfs_request *req,
522                              union smb_seek *io)
523 {
524         NTSTATUS status;
525
526         PASS_THRU_REQ(ntvfs, req, seek, (ntvfs, req, io));
527
528         return status;
529 }
530
531 /*
532   flush a file
533 */
534 static NTSTATUS unixuid_flush(struct ntvfs_module_context *ntvfs,
535                               struct ntvfs_request *req,
536                               union smb_flush *io)
537 {
538         NTSTATUS status;
539
540         PASS_THRU_REQ(ntvfs, req, flush, (ntvfs, req, io));
541
542         return status;
543 }
544
545 /*
546   close a file
547 */
548 static NTSTATUS unixuid_close(struct ntvfs_module_context *ntvfs,
549                              struct ntvfs_request *req, union smb_close *io)
550 {
551         NTSTATUS status;
552
553         PASS_THRU_REQ(ntvfs, req, close, (ntvfs, req, io));
554
555         return status;
556 }
557
558 /*
559   exit - closing files
560 */
561 static NTSTATUS unixuid_exit(struct ntvfs_module_context *ntvfs,
562                             struct ntvfs_request *req)
563 {
564         NTSTATUS status;
565
566         PASS_THRU_REQ(ntvfs, req, exit, (ntvfs, req));
567
568         return status;
569 }
570
571 /*
572   logoff - closing files
573 */
574 static NTSTATUS unixuid_logoff(struct ntvfs_module_context *ntvfs,
575                               struct ntvfs_request *req)
576 {
577         struct unixuid_private *priv = ntvfs->private_data;
578         NTSTATUS status;
579
580         PASS_THRU_REQ(ntvfs, req, logoff, (ntvfs, req));
581
582         priv->last_token = NULL;
583
584         return status;
585 }
586
587 /*
588   async setup
589 */
590 static NTSTATUS unixuid_async_setup(struct ntvfs_module_context *ntvfs,
591                                     struct ntvfs_request *req, 
592                                     void *private_data)
593 {
594         NTSTATUS status;
595
596         PASS_THRU_REQ(ntvfs, req, async_setup, (ntvfs, req, private_data));
597
598         return status;
599 }
600
601 /*
602   cancel an async request
603 */
604 static NTSTATUS unixuid_cancel(struct ntvfs_module_context *ntvfs,
605                                struct ntvfs_request *req)
606 {
607         NTSTATUS status;
608
609         PASS_THRU_REQ(ntvfs, req, cancel, (ntvfs, req));
610
611         return status;
612 }
613
614 /*
615   change notify
616 */
617 static NTSTATUS unixuid_notify(struct ntvfs_module_context *ntvfs,
618                                struct ntvfs_request *req, union smb_notify *info)
619 {
620         NTSTATUS status;
621
622         PASS_THRU_REQ(ntvfs, req, notify, (ntvfs, req, info));
623
624         return status;
625 }
626
627 /*
628   lock a byte range
629 */
630 static NTSTATUS unixuid_lock(struct ntvfs_module_context *ntvfs,
631                             struct ntvfs_request *req, union smb_lock *lck)
632 {
633         NTSTATUS status;
634
635         PASS_THRU_REQ(ntvfs, req, lock, (ntvfs, req, lck));
636
637         return status;
638 }
639
640 /*
641   set info on a open file
642 */
643 static NTSTATUS unixuid_setfileinfo(struct ntvfs_module_context *ntvfs,
644                                    struct ntvfs_request *req, 
645                                    union smb_setfileinfo *info)
646 {
647         NTSTATUS status;
648
649         PASS_THRU_REQ(ntvfs, req, setfileinfo, (ntvfs, req, info));
650
651         return status;
652 }
653
654
655 /*
656   return filesystem space info
657 */
658 static NTSTATUS unixuid_fsinfo(struct ntvfs_module_context *ntvfs,
659                               struct ntvfs_request *req, union smb_fsinfo *fs)
660 {
661         NTSTATUS status;
662
663         PASS_THRU_REQ(ntvfs, req, fsinfo, (ntvfs, req, fs));
664
665         return status;
666 }
667
668 /*
669   return print queue info
670 */
671 static NTSTATUS unixuid_lpq(struct ntvfs_module_context *ntvfs,
672                            struct ntvfs_request *req, union smb_lpq *lpq)
673 {
674         NTSTATUS status;
675
676         PASS_THRU_REQ(ntvfs, req, lpq, (ntvfs, req, lpq));
677
678         return status;
679 }
680
681 /* 
682    list files in a directory matching a wildcard pattern
683 */
684 static NTSTATUS unixuid_search_first(struct ntvfs_module_context *ntvfs,
685                                     struct ntvfs_request *req, union smb_search_first *io, 
686                                     void *search_private, 
687                                     bool (*callback)(void *, const union smb_search_data *))
688 {
689         NTSTATUS status;
690
691         PASS_THRU_REQ(ntvfs, req, search_first, (ntvfs, req, io, search_private, callback));
692
693         return status;
694 }
695
696 /* continue a search */
697 static NTSTATUS unixuid_search_next(struct ntvfs_module_context *ntvfs,
698                                    struct ntvfs_request *req, union smb_search_next *io, 
699                                    void *search_private, 
700                                    bool (*callback)(void *, const union smb_search_data *))
701 {
702         NTSTATUS status;
703
704         PASS_THRU_REQ(ntvfs, req, search_next, (ntvfs, req, io, search_private, callback));
705
706         return status;
707 }
708
709 /* close a search */
710 static NTSTATUS unixuid_search_close(struct ntvfs_module_context *ntvfs,
711                                     struct ntvfs_request *req, union smb_search_close *io)
712 {
713         NTSTATUS status;
714
715         PASS_THRU_REQ(ntvfs, req, search_close, (ntvfs, req, io));
716
717         return status;
718 }
719
720 /* SMBtrans - not used on file shares */
721 static NTSTATUS unixuid_trans(struct ntvfs_module_context *ntvfs,
722                              struct ntvfs_request *req, struct smb_trans2 *trans2)
723 {
724         NTSTATUS status;
725
726         PASS_THRU_REQ(ntvfs, req, trans, (ntvfs, req, trans2));
727
728         return status;
729 }
730
731 /*
732   initialise the unixuid backend, registering ourselves with the ntvfs subsystem
733  */
734 NTSTATUS ntvfs_unixuid_init(void)
735 {
736         NTSTATUS ret;
737         struct ntvfs_ops ops;
738         NTVFS_CURRENT_CRITICAL_SIZES(vers);
739
740         ZERO_STRUCT(ops);
741
742         /* fill in all the operations */
743         ops.connect = unixuid_connect;
744         ops.disconnect = unixuid_disconnect;
745         ops.unlink = unixuid_unlink;
746         ops.chkpath = unixuid_chkpath;
747         ops.qpathinfo = unixuid_qpathinfo;
748         ops.setpathinfo = unixuid_setpathinfo;
749         ops.open = unixuid_open;
750         ops.mkdir = unixuid_mkdir;
751         ops.rmdir = unixuid_rmdir;
752         ops.rename = unixuid_rename;
753         ops.copy = unixuid_copy;
754         ops.ioctl = unixuid_ioctl;
755         ops.read = unixuid_read;
756         ops.write = unixuid_write;
757         ops.seek = unixuid_seek;
758         ops.flush = unixuid_flush;      
759         ops.close = unixuid_close;
760         ops.exit = unixuid_exit;
761         ops.lock = unixuid_lock;
762         ops.setfileinfo = unixuid_setfileinfo;
763         ops.qfileinfo = unixuid_qfileinfo;
764         ops.fsinfo = unixuid_fsinfo;
765         ops.lpq = unixuid_lpq;
766         ops.search_first = unixuid_search_first;
767         ops.search_next = unixuid_search_next;
768         ops.search_close = unixuid_search_close;
769         ops.trans = unixuid_trans;
770         ops.logoff = unixuid_logoff;
771         ops.async_setup = unixuid_async_setup;
772         ops.cancel = unixuid_cancel;
773         ops.notify = unixuid_notify;
774
775         ops.name = "unixuid";
776
777         /* we register under all 3 backend types, as we are not type specific */
778         ops.type = NTVFS_DISK;  
779         ret = ntvfs_register(&ops, &vers);
780         if (!NT_STATUS_IS_OK(ret)) goto failed;
781
782         ops.type = NTVFS_PRINT; 
783         ret = ntvfs_register(&ops, &vers);
784         if (!NT_STATUS_IS_OK(ret)) goto failed;
785
786         ops.type = NTVFS_IPC;   
787         ret = ntvfs_register(&ops, &vers);
788         if (!NT_STATUS_IS_OK(ret)) goto failed;
789         
790 failed:
791         return ret;
792 }