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