Finish removal of iconv_convenience in public API's.
[amitay/samba.git] / source4 / ntvfs / posix / pvfs_rename.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - rename
5
6    Copyright (C) Andrew Tridgell 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 3 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, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "vfs_posix.h"
24 #include "librpc/gen_ndr/security.h"
25 #include "param/param.h"
26
27
28 /*
29   do a file rename, and send any notify triggers
30 */
31 NTSTATUS pvfs_do_rename(struct pvfs_state *pvfs,
32                         struct odb_lock *lck,
33                         const struct pvfs_filename *name1,
34                         const char *name2)
35 {
36         const char *r1, *r2;
37         uint32_t mask;
38         NTSTATUS status;
39
40         if (pvfs_sys_rename(pvfs, name1->full_name, name2) == -1) {
41                 return pvfs_map_errno(pvfs, errno);
42         }
43
44         status = odb_rename(lck, name2);
45         NT_STATUS_NOT_OK_RETURN(status);
46
47         if (name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
48                 mask = FILE_NOTIFY_CHANGE_DIR_NAME;
49         } else {
50                 mask = FILE_NOTIFY_CHANGE_FILE_NAME;
51         }
52         /* 
53            renames to the same directory cause a OLD_NAME->NEW_NAME notify.
54            renames to a different directory are considered a remove/add 
55         */
56         r1 = strrchr_m(name1->full_name, '/');
57         r2 = strrchr_m(name2, '/');
58
59         if ((r1-name1->full_name) != (r2-name2) ||
60             strncmp(name1->full_name, name2, r1-name1->full_name) != 0) {
61                 notify_trigger(pvfs->notify_context, 
62                                NOTIFY_ACTION_REMOVED, 
63                                mask,
64                                name1->full_name);
65                 notify_trigger(pvfs->notify_context, 
66                                NOTIFY_ACTION_ADDED, 
67                                mask,
68                                name2);
69         } else {
70                 notify_trigger(pvfs->notify_context, 
71                                NOTIFY_ACTION_OLD_NAME, 
72                                mask,
73                                name1->full_name);
74                 notify_trigger(pvfs->notify_context, 
75                                NOTIFY_ACTION_NEW_NAME, 
76                                mask,
77                                name2);
78         }
79
80         /* this is a strange one. w2k3 gives an additional event for CHANGE_ATTRIBUTES
81            and CHANGE_CREATION on the new file when renaming files, but not 
82            directories */
83         if ((name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) == 0) {
84                 notify_trigger(pvfs->notify_context, 
85                                NOTIFY_ACTION_MODIFIED, 
86                                FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_CREATION,
87                                name2);
88         }
89         
90         return NT_STATUS_OK;
91 }
92
93
94 /*
95   resolve a wildcard rename pattern. This works on one component of the name
96 */
97 static const char *pvfs_resolve_wildcard_component(TALLOC_CTX *mem_ctx, 
98                                                    const char *fname, 
99                                                    const char *pattern)
100 {
101         const char *p1, *p2;
102         char *dest, *d;
103
104         /* the length is bounded by the length of the two strings combined */
105         dest = talloc_array(mem_ctx, char, strlen(fname) + strlen(pattern) + 1);
106         if (dest == NULL) {
107                 return NULL;
108         }
109
110         p1 = fname;
111         p2 = pattern;
112         d = dest;
113
114         while (*p2) {
115                 codepoint_t c1, c2;
116                 size_t c_size1, c_size2;
117                 c1 = next_codepoint(p1, &c_size1);
118                 c2 = next_codepoint(p2, &c_size2);
119                 if (c2 == '?') {
120                         d += push_codepoint(d, c1);
121                 } else if (c2 == '*') {
122                         memcpy(d, p1, strlen(p1));
123                         d += strlen(p1);
124                         break;
125                 } else {
126                         d += push_codepoint(d, c2);
127                 }
128
129                 p1 += c_size1;
130                 p2 += c_size2;
131         }
132
133         *d = 0;
134
135         talloc_set_name_const(dest, dest);
136
137         return dest;
138 }
139
140 /*
141   resolve a wildcard rename pattern.
142 */
143 static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx, 
144                                          const char *fname, 
145                                          const char *pattern)
146 {
147         const char *base1, *base2;
148         const char *ext1, *ext2;
149         char *p;
150
151         /* break into base part plus extension */
152         p = strrchr_m(fname, '.');
153         if (p == NULL) {
154                 ext1 = "";
155                 base1 = fname;
156         } else {
157                 ext1 = talloc_strdup(mem_ctx, p+1);
158                 base1 = talloc_strndup(mem_ctx, fname, p-fname);
159         }
160         if (ext1 == NULL || base1 == NULL) {
161                 return NULL;
162         }
163
164         p = strrchr_m(pattern, '.');
165         if (p == NULL) {
166                 ext2 = "";
167                 base2 = fname;
168         } else {
169                 ext2 = talloc_strdup(mem_ctx, p+1);
170                 base2 = talloc_strndup(mem_ctx, pattern, p-pattern);
171         }
172         if (ext2 == NULL || base2 == NULL) {
173                 return NULL;
174         }
175
176         base1 = pvfs_resolve_wildcard_component(mem_ctx, base1, base2);
177         ext1 = pvfs_resolve_wildcard_component(mem_ctx, ext1, ext2);
178         if (base1 == NULL || ext1 == NULL) {
179                 return NULL;
180         }
181
182         if (*ext1 == 0) {
183                 return base1;
184         }
185
186         return talloc_asprintf(mem_ctx, "%s.%s", base1, ext1);
187 }
188
189 /*
190   retry an rename after a sharing violation
191 */
192 static void pvfs_retry_rename(struct pvfs_odb_retry *r,
193                               struct ntvfs_module_context *ntvfs,
194                               struct ntvfs_request *req,
195                               void *_io,
196                               void *private_data,
197                               enum pvfs_wait_notice reason)
198 {
199         union smb_rename *io = talloc_get_type(_io, union smb_rename);
200         NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
201
202         talloc_free(r);
203
204         switch (reason) {
205         case PVFS_WAIT_CANCEL:
206 /*TODO*/
207                 status = NT_STATUS_CANCELLED;
208                 break;
209         case PVFS_WAIT_TIMEOUT:
210                 /* if it timed out, then give the failure
211                    immediately */
212 /*TODO*/
213                 status = NT_STATUS_SHARING_VIOLATION;
214                 break;
215         case PVFS_WAIT_EVENT:
216
217                 /* try the open again, which could trigger another retry setup
218                    if it wants to, so we have to unmark the async flag so we
219                    will know if it does a second async reply */
220                 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
221
222                 status = pvfs_rename(ntvfs, req, io);
223                 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
224                         /* the 2nd try also replied async, so we don't send
225                            the reply yet */
226                         return;
227                 }
228
229                 /* re-mark it async, just in case someone up the chain does
230                    paranoid checking */
231                 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
232                 break;
233         }
234
235         /* send the reply up the chain */
236         req->async_states->status = status;
237         req->async_states->send_fn(req);
238 }
239
240 /*
241   setup for a rename retry after a sharing violation
242   or a non granted oplock
243 */
244 static NTSTATUS pvfs_rename_setup_retry(struct ntvfs_module_context *ntvfs,
245                                         struct ntvfs_request *req,
246                                         union smb_rename *io,
247                                         struct odb_lock *lck,
248                                         NTSTATUS status)
249 {
250         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
251                                   struct pvfs_state);
252         struct timeval end_time;
253
254         if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
255                 end_time = timeval_add(&req->statistics.request_time,
256                                        0, pvfs->sharing_violation_delay);
257         } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
258                 end_time = timeval_add(&req->statistics.request_time,
259                                        pvfs->oplock_break_timeout, 0);
260         } else {
261                 return NT_STATUS_INTERNAL_ERROR;
262         }
263
264         return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
265                                     pvfs_retry_rename);
266 }
267
268 /*
269   rename one file from a wildcard set
270 */
271 static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs, 
272                                 struct ntvfs_request *req, 
273                                 const char *dir_path,
274                                 const char *fname1,
275                                 const char *fname2,
276                                 uint16_t attrib)
277 {
278         struct pvfs_filename *name1, *name2;
279         TALLOC_CTX *mem_ctx = talloc_new(req);
280         struct odb_lock *lck = NULL;
281         NTSTATUS status;
282
283         /* resolve the wildcard pattern for this name */
284         fname2 = pvfs_resolve_wildcard(mem_ctx, fname1, fname2);
285         if (fname2 == NULL) {
286                 return NT_STATUS_NO_MEMORY;
287         }
288
289         /* get a pvfs_filename source object */
290         status = pvfs_resolve_partial(pvfs, mem_ctx, 
291                                       dir_path, fname1,
292                                       PVFS_RESOLVE_NO_OPENDB,
293                                       &name1);
294         if (!NT_STATUS_IS_OK(status)) {
295                 goto failed;
296         }
297
298         /* make sure its matches the given attributes */
299         status = pvfs_match_attrib(pvfs, name1, attrib, 0);
300         if (!NT_STATUS_IS_OK(status)) {
301                 goto failed;
302         }
303
304         status = pvfs_can_rename(pvfs, req, name1, &lck);
305         if (!NT_STATUS_IS_OK(status)) {
306                 talloc_free(lck);
307                 goto failed;
308         }
309
310         /* get a pvfs_filename dest object */
311         status = pvfs_resolve_partial(pvfs, mem_ctx, 
312                                       dir_path, fname2,
313                                       PVFS_RESOLVE_NO_OPENDB,
314                                       &name2);
315         if (NT_STATUS_IS_OK(status)) {
316                 status = pvfs_can_delete(pvfs, req, name2, NULL);
317                 if (!NT_STATUS_IS_OK(status)) {
318                         goto failed;
319                 }
320         }
321
322         status = NT_STATUS_OK;
323
324         fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2);
325         if (fname2 == NULL) {
326                 return NT_STATUS_NO_MEMORY;
327         }
328
329         status = pvfs_do_rename(pvfs, lck, name1, fname2);
330
331 failed:
332         talloc_free(mem_ctx);
333         return status;
334 }
335
336
337 /*
338   rename a set of files with wildcards
339 */
340 static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs, 
341                                      struct ntvfs_request *req, 
342                                      union smb_rename *ren, 
343                                      struct pvfs_filename *name1, 
344                                      struct pvfs_filename *name2)
345 {
346         struct pvfs_dir *dir;
347         NTSTATUS status;
348         off_t ofs = 0;
349         const char *fname, *fname2, *dir_path;
350         uint16_t attrib = ren->rename.in.attrib;
351         int total_renamed = 0;
352
353         /* get list of matching files */
354         status = pvfs_list_start(pvfs, name1, req, &dir);
355         if (!NT_STATUS_IS_OK(status)) {
356                 return status;
357         }
358
359         status = NT_STATUS_NO_SUCH_FILE;
360
361         dir_path = pvfs_list_unix_path(dir);
362
363         /* only allow wildcard renames within a directory */
364         if (strncmp(dir_path, name2->full_name, strlen(dir_path)) != 0 ||
365             name2->full_name[strlen(dir_path)] != '/' ||
366             strchr(name2->full_name + strlen(dir_path) + 1, '/')) {
367                 DEBUG(3,(__location__ ": Invalid rename for %s -> %s\n",
368                          name1->original_name, name2->original_name));
369                 return NT_STATUS_INVALID_PARAMETER;
370         }
371
372         fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1);
373         if (fname2 == NULL) {
374                 return NT_STATUS_NO_MEMORY;
375         }
376
377         while ((fname = pvfs_list_next(dir, &ofs))) {
378                 status = pvfs_rename_one(pvfs, req, 
379                                          dir_path,
380                                          fname, fname2, attrib);
381                 if (NT_STATUS_IS_OK(status)) {
382                         total_renamed++;
383                 }
384         }
385
386         if (total_renamed == 0) {
387                 return status;
388         }
389
390         return NT_STATUS_OK;
391 }
392
393 /*
394   rename a set of files - SMBmv interface
395 */
396 static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs,
397                                struct ntvfs_request *req, union smb_rename *ren)
398 {
399         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
400                                   struct pvfs_state);
401         NTSTATUS status;
402         struct pvfs_filename *name1, *name2;
403         struct odb_lock *lck = NULL;
404
405         /* resolve the cifs name to a posix name */
406         status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern1, 
407                                    PVFS_RESOLVE_WILDCARD, &name1);
408         if (!NT_STATUS_IS_OK(status)) {
409                 return status;
410         }
411
412         status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2, 
413                                    PVFS_RESOLVE_WILDCARD, &name2);
414         if (!NT_STATUS_IS_OK(status)) {
415                 return status;
416         }
417
418         if (name1->has_wildcard || name2->has_wildcard) {
419                 return pvfs_rename_wildcard(pvfs, req, ren, name1, name2);
420         }
421
422         if (!name1->exists) {
423                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
424         }
425
426         if (strcmp(name1->full_name, name2->full_name) == 0) {
427                 return NT_STATUS_OK;
428         }
429
430         if (name2->exists) {
431                 return NT_STATUS_OBJECT_NAME_COLLISION;
432         }
433
434         status = pvfs_match_attrib(pvfs, name1, ren->rename.in.attrib, 0);
435         if (!NT_STATUS_IS_OK(status)) {
436                 return status;
437         }
438
439         status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
440         if (!NT_STATUS_IS_OK(status)) {
441                 return status;
442         }
443
444         status = pvfs_can_rename(pvfs, req, name1, &lck);
445         /*
446          * on a sharing violation we need to retry when the file is closed by
447          * the other user, or after 1 second
448          * on a non granted oplock we need to retry when the file is closed by
449          * the other user, or after 30 seconds
450          */
451         if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
452              NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
453             (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
454                 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
455         }
456
457         if (!NT_STATUS_IS_OK(status)) {
458                 return status;
459         }
460
461         status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
462         if (!NT_STATUS_IS_OK(status)) {
463                 return status;
464         }
465         
466         return NT_STATUS_OK;
467 }
468
469
470 /*
471   rename a stream
472 */
473 static NTSTATUS pvfs_rename_stream(struct ntvfs_module_context *ntvfs,
474                                    struct ntvfs_request *req, union smb_rename *ren,
475                                    struct pvfs_filename *name1)
476 {
477         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
478                                   struct pvfs_state);
479         NTSTATUS status;
480         struct odb_lock *lck = NULL;
481
482         if (name1->has_wildcard) {
483                 DEBUG(3,(__location__ ": Invalid wildcard rename for %s\n",
484                          name1->original_name));
485                 return NT_STATUS_INVALID_PARAMETER;
486         }
487
488         if (ren->ntrename.in.new_name[0] != ':') {
489                 DEBUG(3,(__location__ ": Invalid rename for %s\n",
490                          ren->ntrename.in.new_name));
491                 return NT_STATUS_INVALID_PARAMETER;
492         }
493
494         if (!name1->exists) {
495                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
496         }
497
498         if (ren->ntrename.in.flags != RENAME_FLAG_RENAME) {
499                 DEBUG(3,(__location__ ": Invalid rename flags 0x%x for %s\n",
500                          ren->ntrename.in.flags, ren->ntrename.in.new_name));
501                 return NT_STATUS_INVALID_PARAMETER;
502         }
503
504         status = pvfs_can_rename(pvfs, req, name1, &lck);
505         /*
506          * on a sharing violation we need to retry when the file is closed by
507          * the other user, or after 1 second
508          * on a non granted oplock we need to retry when the file is closed by
509          * the other user, or after 30 seconds
510          */
511         if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
512              NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
513             (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
514                 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
515         }
516         if (!NT_STATUS_IS_OK(status)) {
517                 return status;
518         }
519
520         status = pvfs_access_check_simple(pvfs, req, name1, SEC_FILE_WRITE_ATTRIBUTE);
521         NT_STATUS_NOT_OK_RETURN(status);
522
523         status = pvfs_stream_rename(pvfs, name1, -1, 
524                                     ren->ntrename.in.new_name+1, 
525                                     true);
526         NT_STATUS_NOT_OK_RETURN(status);
527         
528         return NT_STATUS_OK;
529 }
530
531 /*
532   rename a set of files - ntrename interface
533 */
534 static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
535                                struct ntvfs_request *req, union smb_rename *ren)
536 {
537         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
538                                   struct pvfs_state);
539         NTSTATUS status;
540         struct pvfs_filename *name1, *name2;
541         struct odb_lock *lck = NULL;
542
543         switch (ren->ntrename.in.flags) {
544         case RENAME_FLAG_RENAME:
545         case RENAME_FLAG_HARD_LINK:
546         case RENAME_FLAG_COPY:
547         case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
548                 break;
549         default:
550                 return NT_STATUS_ACCESS_DENIED;
551         }
552
553         /* resolve the cifs name to a posix name */
554         status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name, 
555                                    PVFS_RESOLVE_WILDCARD | PVFS_RESOLVE_STREAMS, &name1);
556         if (!NT_STATUS_IS_OK(status)) {
557                 return status;
558         }
559
560         if (name1->stream_name) {
561                 /* stream renames need to be handled separately */
562                 return pvfs_rename_stream(ntvfs, req, ren, name1);
563         }
564
565         status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name, 
566                                    PVFS_RESOLVE_WILDCARD, &name2);
567         if (!NT_STATUS_IS_OK(status)) {
568                 return status;
569         }
570
571         if (name1->has_wildcard || name2->has_wildcard) {
572                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
573         }
574
575         if (!name1->exists) {
576                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
577         }
578
579         if (strcmp(name1->full_name, name2->full_name) == 0) {
580                 return NT_STATUS_OK;
581         }
582
583         if (name2->exists) {
584                 return NT_STATUS_OBJECT_NAME_COLLISION;
585         }
586
587         status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
588         if (!NT_STATUS_IS_OK(status)) {
589                 return status;
590         }
591
592         status = pvfs_can_rename(pvfs, req, name1, &lck);
593         /*
594          * on a sharing violation we need to retry when the file is closed by
595          * the other user, or after 1 second
596          * on a non granted oplock we need to retry when the file is closed by
597          * the other user, or after 30 seconds
598          */
599         if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
600              NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
601             (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
602                 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
603         }
604         if (!NT_STATUS_IS_OK(status)) {
605                 return status;
606         }
607
608         switch (ren->ntrename.in.flags) {
609         case RENAME_FLAG_RENAME:
610                 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
611                 NT_STATUS_NOT_OK_RETURN(status);
612                 status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
613                 NT_STATUS_NOT_OK_RETURN(status);
614                 break;
615
616         case RENAME_FLAG_HARD_LINK:
617                 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
618                 NT_STATUS_NOT_OK_RETURN(status);
619                 if (link(name1->full_name, name2->full_name) == -1) {
620                         return pvfs_map_errno(pvfs, errno);
621                 }
622                 break;
623
624         case RENAME_FLAG_COPY:
625                 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
626                 NT_STATUS_NOT_OK_RETURN(status);
627                 return pvfs_copy_file(pvfs, name1, name2);
628
629         case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
630                 DEBUG(3,(__location__ ": Invalid rename cluster for %s\n",
631                          name1->original_name));
632                 return NT_STATUS_INVALID_PARAMETER;
633
634         default:
635                 return NT_STATUS_ACCESS_DENIED;
636         }
637
638         
639         return NT_STATUS_OK;
640 }
641
642 /*
643   rename a set of files - ntrename interface
644 */
645 NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
646                      struct ntvfs_request *req, union smb_rename *ren)
647 {
648         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
649                                   struct pvfs_state);
650         struct pvfs_file *f;
651
652         switch (ren->generic.level) {
653         case RAW_RENAME_RENAME:
654                 return pvfs_rename_mv(ntvfs, req, ren);
655
656         case RAW_RENAME_NTRENAME:
657                 return pvfs_rename_nt(ntvfs, req, ren);
658
659         case RAW_RENAME_NTTRANS:
660                 f = pvfs_find_fd(pvfs, req, ren->nttrans.in.file.ntvfs);
661                 if (!f) {
662                         return NT_STATUS_INVALID_HANDLE;
663                 }
664
665                 /* wk23 ignores the request */
666                 return NT_STATUS_OK;
667
668         default:
669                 break;
670         }
671
672         return NT_STATUS_INVALID_LEVEL;
673 }
674