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