Merge branch 'master' of ssh://git.samba.org/data/git/samba
[jra/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 (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_size(mem_ctx, 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(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(iconv_convenience, d, c2);
128                 }
129
130                 p1 += c_size1;
131                 p2 += c_size2;
132         }
133
134         *d = 0;
135
136         return dest;
137 }
138
139 /*
140   resolve a wildcard rename pattern.
141 */
142 static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx, 
143                                          struct smb_iconv_convenience *iconv_convenience,
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, iconv_convenience, base1, base2);
177         ext1 = pvfs_resolve_wildcard_component(mem_ctx, iconv_convenience, 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 = ntvfs->private_data;
251         struct timeval end_time;
252
253         if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
254                 end_time = timeval_add(&req->statistics.request_time,
255                                        0, pvfs->sharing_violation_delay);
256         } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
257                 end_time = timeval_add(&req->statistics.request_time,
258                                        pvfs->oplock_break_timeout, 0);
259         } else {
260                 return NT_STATUS_INTERNAL_ERROR;
261         }
262
263         return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
264                                     pvfs_retry_rename);
265 }
266
267 /*
268   rename one file from a wildcard set
269 */
270 static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs, 
271                                 struct ntvfs_request *req, 
272                                 const char *dir_path,
273                                 const char *fname1,
274                                 const char *fname2,
275                                 uint16_t attrib)
276 {
277         struct pvfs_filename *name1, *name2;
278         TALLOC_CTX *mem_ctx = talloc_new(req);
279         struct odb_lock *lck = NULL;
280         NTSTATUS status;
281
282         /* resolve the wildcard pattern for this name */
283         fname2 = pvfs_resolve_wildcard(mem_ctx, lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx), fname1, fname2);
284         if (fname2 == NULL) {
285                 return NT_STATUS_NO_MEMORY;
286         }
287
288         /* get a pvfs_filename source object */
289         status = pvfs_resolve_partial(pvfs, mem_ctx, 
290                                       dir_path, fname1,
291                                       PVFS_RESOLVE_NO_OPENDB,
292                                       &name1);
293         if (!NT_STATUS_IS_OK(status)) {
294                 goto failed;
295         }
296
297         /* make sure its matches the given attributes */
298         status = pvfs_match_attrib(pvfs, name1, attrib, 0);
299         if (!NT_STATUS_IS_OK(status)) {
300                 goto failed;
301         }
302
303         status = pvfs_can_rename(pvfs, req, name1, &lck);
304         if (!NT_STATUS_IS_OK(status)) {
305                 talloc_free(lck);
306                 goto failed;
307         }
308
309         /* get a pvfs_filename dest object */
310         status = pvfs_resolve_partial(pvfs, mem_ctx, 
311                                       dir_path, fname2,
312                                       PVFS_RESOLVE_NO_OPENDB,
313                                       &name2);
314         if (NT_STATUS_IS_OK(status)) {
315                 status = pvfs_can_delete(pvfs, req, name2, NULL);
316                 if (!NT_STATUS_IS_OK(status)) {
317                         goto failed;
318                 }
319         }
320
321         status = NT_STATUS_OK;
322
323         fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2);
324         if (fname2 == NULL) {
325                 return NT_STATUS_NO_MEMORY;
326         }
327
328         status = pvfs_do_rename(pvfs, lck, name1, fname2);
329
330 failed:
331         talloc_free(mem_ctx);
332         return status;
333 }
334
335
336 /*
337   rename a set of files with wildcards
338 */
339 static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs, 
340                                      struct ntvfs_request *req, 
341                                      union smb_rename *ren, 
342                                      struct pvfs_filename *name1, 
343                                      struct pvfs_filename *name2)
344 {
345         struct pvfs_dir *dir;
346         NTSTATUS status;
347         off_t ofs = 0;
348         const char *fname, *fname2, *dir_path;
349         uint16_t attrib = ren->rename.in.attrib;
350         int total_renamed = 0;
351
352         /* get list of matching files */
353         status = pvfs_list_start(pvfs, name1, req, &dir);
354         if (!NT_STATUS_IS_OK(status)) {
355                 return status;
356         }
357
358         status = NT_STATUS_NO_SUCH_FILE;
359
360         dir_path = pvfs_list_unix_path(dir);
361
362         /* only allow wildcard renames within a directory */
363         if (strncmp(dir_path, name2->full_name, strlen(dir_path)) != 0 ||
364             name2->full_name[strlen(dir_path)] != '/' ||
365             strchr(name2->full_name + strlen(dir_path) + 1, '/')) {
366                 return NT_STATUS_INVALID_PARAMETER;
367         }
368
369         fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1);
370         if (fname2 == NULL) {
371                 return NT_STATUS_NO_MEMORY;
372         }
373
374         while ((fname = pvfs_list_next(dir, &ofs))) {
375                 status = pvfs_rename_one(pvfs, req, 
376                                          dir_path,
377                                          fname, fname2, attrib);
378                 if (NT_STATUS_IS_OK(status)) {
379                         total_renamed++;
380                 }
381         }
382
383         if (total_renamed == 0) {
384                 return status;
385         }
386
387         return NT_STATUS_OK;
388 }
389
390 /*
391   rename a set of files - SMBmv interface
392 */
393 static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs,
394                                struct ntvfs_request *req, union smb_rename *ren)
395 {
396         struct pvfs_state *pvfs = ntvfs->private_data;
397         NTSTATUS status;
398         struct pvfs_filename *name1, *name2;
399         struct odb_lock *lck = NULL;
400
401         /* resolve the cifs name to a posix name */
402         status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern1, 
403                                    PVFS_RESOLVE_WILDCARD, &name1);
404         if (!NT_STATUS_IS_OK(status)) {
405                 return status;
406         }
407
408         status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2, 
409                                    PVFS_RESOLVE_WILDCARD, &name2);
410         if (!NT_STATUS_IS_OK(status)) {
411                 return status;
412         }
413
414         if (name1->has_wildcard || name2->has_wildcard) {
415                 return pvfs_rename_wildcard(pvfs, req, ren, name1, name2);
416         }
417
418         if (!name1->exists) {
419                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
420         }
421
422         if (strcmp(name1->full_name, name2->full_name) == 0) {
423                 return NT_STATUS_OK;
424         }
425
426         if (name2->exists) {
427                 return NT_STATUS_OBJECT_NAME_COLLISION;
428         }
429
430         status = pvfs_match_attrib(pvfs, name1, ren->rename.in.attrib, 0);
431         if (!NT_STATUS_IS_OK(status)) {
432                 return status;
433         }
434
435         status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
436         if (!NT_STATUS_IS_OK(status)) {
437                 return status;
438         }
439
440         status = pvfs_can_rename(pvfs, req, name1, &lck);
441         /*
442          * on a sharing violation we need to retry when the file is closed by
443          * the other user, or after 1 second
444          * on a non granted oplock we need to retry when the file is closed by
445          * the other user, or after 30 seconds
446          */
447         if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
448              NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
449             (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
450                 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
451         }
452
453         if (!NT_STATUS_IS_OK(status)) {
454                 return status;
455         }
456
457         status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
458         if (!NT_STATUS_IS_OK(status)) {
459                 return status;
460         }
461         
462         return NT_STATUS_OK;
463 }
464
465
466 /*
467   rename a stream
468 */
469 static NTSTATUS pvfs_rename_stream(struct ntvfs_module_context *ntvfs,
470                                    struct ntvfs_request *req, union smb_rename *ren,
471                                    struct pvfs_filename *name1)
472 {
473         struct pvfs_state *pvfs = ntvfs->private_data;
474         NTSTATUS status;
475         struct odb_lock *lck = NULL;
476
477         if (name1->has_wildcard) {
478                 return NT_STATUS_INVALID_PARAMETER;
479         }
480
481         if (ren->ntrename.in.new_name[0] != ':') {
482                 return NT_STATUS_INVALID_PARAMETER;
483         }
484
485         if (!name1->exists) {
486                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
487         }
488
489         if (ren->ntrename.in.flags != RENAME_FLAG_RENAME) {
490                 return NT_STATUS_INVALID_PARAMETER;
491         }
492
493         status = pvfs_can_rename(pvfs, req, name1, &lck);
494         /*
495          * on a sharing violation we need to retry when the file is closed by
496          * the other user, or after 1 second
497          * on a non granted oplock we need to retry when the file is closed by
498          * the other user, or after 30 seconds
499          */
500         if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
501              NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
502             (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
503                 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
504         }
505         if (!NT_STATUS_IS_OK(status)) {
506                 return status;
507         }
508
509         status = pvfs_access_check_simple(pvfs, req, name1, SEC_FILE_WRITE_ATTRIBUTE);
510         NT_STATUS_NOT_OK_RETURN(status);
511
512         status = pvfs_stream_rename(pvfs, name1, -1, 
513                                     ren->ntrename.in.new_name+1);
514         NT_STATUS_NOT_OK_RETURN(status);
515         
516         return NT_STATUS_OK;
517 }
518
519 /*
520   rename a set of files - ntrename interface
521 */
522 static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
523                                struct ntvfs_request *req, union smb_rename *ren)
524 {
525         struct pvfs_state *pvfs = ntvfs->private_data;
526         NTSTATUS status;
527         struct pvfs_filename *name1, *name2;
528         struct odb_lock *lck = NULL;
529
530         switch (ren->ntrename.in.flags) {
531         case RENAME_FLAG_RENAME:
532         case RENAME_FLAG_HARD_LINK:
533         case RENAME_FLAG_COPY:
534         case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
535                 break;
536         default:
537                 return NT_STATUS_ACCESS_DENIED;
538         }
539
540         /* resolve the cifs name to a posix name */
541         status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name, 
542                                    PVFS_RESOLVE_WILDCARD | PVFS_RESOLVE_STREAMS, &name1);
543         if (!NT_STATUS_IS_OK(status)) {
544                 return status;
545         }
546
547         if (name1->stream_name) {
548                 /* stream renames need to be handled separately */
549                 return pvfs_rename_stream(ntvfs, req, ren, name1);
550         }
551
552         status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name, 
553                                    PVFS_RESOLVE_WILDCARD, &name2);
554         if (!NT_STATUS_IS_OK(status)) {
555                 return status;
556         }
557
558         if (name1->has_wildcard || name2->has_wildcard) {
559                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
560         }
561
562         if (!name1->exists) {
563                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
564         }
565
566         if (strcmp(name1->full_name, name2->full_name) == 0) {
567                 return NT_STATUS_OK;
568         }
569
570         if (name2->exists) {
571                 return NT_STATUS_OBJECT_NAME_COLLISION;
572         }
573
574         status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
575         if (!NT_STATUS_IS_OK(status)) {
576                 return status;
577         }
578
579         status = pvfs_can_rename(pvfs, req, name1, &lck);
580         /*
581          * on a sharing violation we need to retry when the file is closed by
582          * the other user, or after 1 second
583          * on a non granted oplock we need to retry when the file is closed by
584          * the other user, or after 30 seconds
585          */
586         if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
587              NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
588             (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
589                 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
590         }
591         if (!NT_STATUS_IS_OK(status)) {
592                 return status;
593         }
594
595         switch (ren->ntrename.in.flags) {
596         case RENAME_FLAG_RENAME:
597                 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
598                 NT_STATUS_NOT_OK_RETURN(status);
599                 status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
600                 NT_STATUS_NOT_OK_RETURN(status);
601                 break;
602
603         case RENAME_FLAG_HARD_LINK:
604                 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
605                 NT_STATUS_NOT_OK_RETURN(status);
606                 if (link(name1->full_name, name2->full_name) == -1) {
607                         return pvfs_map_errno(pvfs, errno);
608                 }
609                 break;
610
611         case RENAME_FLAG_COPY:
612                 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
613                 NT_STATUS_NOT_OK_RETURN(status);
614                 return pvfs_copy_file(pvfs, name1, name2);
615
616         case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
617                 return NT_STATUS_INVALID_PARAMETER;
618
619         default:
620                 return NT_STATUS_ACCESS_DENIED;
621         }
622
623         
624         return NT_STATUS_OK;
625 }
626
627 /*
628   rename a set of files - ntrename interface
629 */
630 NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
631                      struct ntvfs_request *req, union smb_rename *ren)
632 {
633         struct pvfs_state *pvfs = ntvfs->private_data;
634         struct pvfs_file *f;
635
636         switch (ren->generic.level) {
637         case RAW_RENAME_RENAME:
638                 return pvfs_rename_mv(ntvfs, req, ren);
639
640         case RAW_RENAME_NTRENAME:
641                 return pvfs_rename_nt(ntvfs, req, ren);
642
643         case RAW_RENAME_NTTRANS:
644                 f = pvfs_find_fd(pvfs, req, ren->nttrans.in.file.ntvfs);
645                 if (!f) {
646                         return NT_STATUS_INVALID_HANDLE;
647                 }
648
649                 /* wk23 ignores the request */
650                 return NT_STATUS_OK;
651
652         default:
653                 break;
654         }
655
656         return NT_STATUS_INVALID_LEVEL;
657 }
658