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