0616d38bee59e226e05ad0c6c8c909fd6885c7c2
[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                 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                 return NT_STATUS_INVALID_PARAMETER;
484         }
485
486         if (ren->ntrename.in.new_name[0] != ':') {
487                 return NT_STATUS_INVALID_PARAMETER;
488         }
489
490         if (!name1->exists) {
491                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
492         }
493
494         if (ren->ntrename.in.flags != RENAME_FLAG_RENAME) {
495                 return NT_STATUS_INVALID_PARAMETER;
496         }
497
498         status = pvfs_can_rename(pvfs, req, name1, &lck);
499         /*
500          * on a sharing violation we need to retry when the file is closed by
501          * the other user, or after 1 second
502          * on a non granted oplock we need to retry when the file is closed by
503          * the other user, or after 30 seconds
504          */
505         if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
506              NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
507             (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
508                 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
509         }
510         if (!NT_STATUS_IS_OK(status)) {
511                 return status;
512         }
513
514         status = pvfs_access_check_simple(pvfs, req, name1, SEC_FILE_WRITE_ATTRIBUTE);
515         NT_STATUS_NOT_OK_RETURN(status);
516
517         status = pvfs_stream_rename(pvfs, name1, -1, 
518                                     ren->ntrename.in.new_name+1);
519         NT_STATUS_NOT_OK_RETURN(status);
520         
521         return NT_STATUS_OK;
522 }
523
524 /*
525   rename a set of files - ntrename interface
526 */
527 static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
528                                struct ntvfs_request *req, union smb_rename *ren)
529 {
530         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
531                                   struct pvfs_state);
532         NTSTATUS status;
533         struct pvfs_filename *name1, *name2;
534         struct odb_lock *lck = NULL;
535
536         switch (ren->ntrename.in.flags) {
537         case RENAME_FLAG_RENAME:
538         case RENAME_FLAG_HARD_LINK:
539         case RENAME_FLAG_COPY:
540         case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
541                 break;
542         default:
543                 return NT_STATUS_ACCESS_DENIED;
544         }
545
546         /* resolve the cifs name to a posix name */
547         status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name, 
548                                    PVFS_RESOLVE_WILDCARD | PVFS_RESOLVE_STREAMS, &name1);
549         if (!NT_STATUS_IS_OK(status)) {
550                 return status;
551         }
552
553         if (name1->stream_name) {
554                 /* stream renames need to be handled separately */
555                 return pvfs_rename_stream(ntvfs, req, ren, name1);
556         }
557
558         status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name, 
559                                    PVFS_RESOLVE_WILDCARD, &name2);
560         if (!NT_STATUS_IS_OK(status)) {
561                 return status;
562         }
563
564         if (name1->has_wildcard || name2->has_wildcard) {
565                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
566         }
567
568         if (!name1->exists) {
569                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
570         }
571
572         if (strcmp(name1->full_name, name2->full_name) == 0) {
573                 return NT_STATUS_OK;
574         }
575
576         if (name2->exists) {
577                 return NT_STATUS_OBJECT_NAME_COLLISION;
578         }
579
580         status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
581         if (!NT_STATUS_IS_OK(status)) {
582                 return status;
583         }
584
585         status = pvfs_can_rename(pvfs, req, name1, &lck);
586         /*
587          * on a sharing violation we need to retry when the file is closed by
588          * the other user, or after 1 second
589          * on a non granted oplock we need to retry when the file is closed by
590          * the other user, or after 30 seconds
591          */
592         if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
593              NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
594             (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
595                 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
596         }
597         if (!NT_STATUS_IS_OK(status)) {
598                 return status;
599         }
600
601         switch (ren->ntrename.in.flags) {
602         case RENAME_FLAG_RENAME:
603                 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
604                 NT_STATUS_NOT_OK_RETURN(status);
605                 status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
606                 NT_STATUS_NOT_OK_RETURN(status);
607                 break;
608
609         case RENAME_FLAG_HARD_LINK:
610                 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
611                 NT_STATUS_NOT_OK_RETURN(status);
612                 if (link(name1->full_name, name2->full_name) == -1) {
613                         return pvfs_map_errno(pvfs, errno);
614                 }
615                 break;
616
617         case RENAME_FLAG_COPY:
618                 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
619                 NT_STATUS_NOT_OK_RETURN(status);
620                 return pvfs_copy_file(pvfs, name1, name2);
621
622         case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
623                 return NT_STATUS_INVALID_PARAMETER;
624
625         default:
626                 return NT_STATUS_ACCESS_DENIED;
627         }
628
629         
630         return NT_STATUS_OK;
631 }
632
633 /*
634   rename a set of files - ntrename interface
635 */
636 NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
637                      struct ntvfs_request *req, union smb_rename *ren)
638 {
639         struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
640                                   struct pvfs_state);
641         struct pvfs_file *f;
642
643         switch (ren->generic.level) {
644         case RAW_RENAME_RENAME:
645                 return pvfs_rename_mv(ntvfs, req, ren);
646
647         case RAW_RENAME_NTRENAME:
648                 return pvfs_rename_nt(ntvfs, req, ren);
649
650         case RAW_RENAME_NTTRANS:
651                 f = pvfs_find_fd(pvfs, req, ren->nttrans.in.file.ntvfs);
652                 if (!f) {
653                         return NT_STATUS_INVALID_HANDLE;
654                 }
655
656                 /* wk23 ignores the request */
657                 return NT_STATUS_OK;
658
659         default:
660                 break;
661         }
662
663         return NT_STATUS_INVALID_LEVEL;
664 }
665