smbd: Remove superfluous ()
[Samba.git] / source4 / ntvfs / posix / pvfs_rename.c
blob432716130c6c3ab4101097ca5de22512782dbe91
1 /*
2 Unix SMB/CIFS implementation.
4 POSIX NTVFS backend - rename
6 Copyright (C) Andrew Tridgell 2004
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.
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.
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/>.
22 #include "includes.h"
23 #include "vfs_posix.h"
24 #include "librpc/gen_ndr/security.h"
25 #include "param/param.h"
29 do a file rename, and send any notify triggers
31 NTSTATUS pvfs_do_rename(struct pvfs_state *pvfs,
32 struct odb_lock *lck,
33 const struct pvfs_filename *name1,
34 const char *name2)
36 const char *r1, *r2;
37 uint32_t mask;
38 NTSTATUS status;
40 if (pvfs_sys_rename(pvfs, name1->full_name, name2,
41 name1->allow_override) == -1) {
42 return pvfs_map_errno(pvfs, errno);
45 status = odb_rename(lck, name2);
46 NT_STATUS_NOT_OK_RETURN(status);
48 if (name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
49 mask = FILE_NOTIFY_CHANGE_DIR_NAME;
50 } else {
51 mask = FILE_NOTIFY_CHANGE_FILE_NAME;
53 /*
54 renames to the same directory cause a OLD_NAME->NEW_NAME notify.
55 renames to a different directory are considered a remove/add
57 r1 = strrchr_m(name1->full_name, '/');
58 r2 = strrchr_m(name2, '/');
60 if ((r1-name1->full_name) != (r2-name2) ||
61 strncmp(name1->full_name, name2, r1-name1->full_name) != 0) {
62 notify_trigger(pvfs->notify_context,
63 NOTIFY_ACTION_REMOVED,
64 mask,
65 name1->full_name);
66 notify_trigger(pvfs->notify_context,
67 NOTIFY_ACTION_ADDED,
68 mask,
69 name2);
70 } else {
71 notify_trigger(pvfs->notify_context,
72 NOTIFY_ACTION_OLD_NAME,
73 mask,
74 name1->full_name);
75 notify_trigger(pvfs->notify_context,
76 NOTIFY_ACTION_NEW_NAME,
77 mask,
78 name2);
81 /* this is a strange one. w2k3 gives an additional event for CHANGE_ATTRIBUTES
82 and CHANGE_CREATION on the new file when renaming files, but not
83 directories */
84 if ((name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) == 0) {
85 notify_trigger(pvfs->notify_context,
86 NOTIFY_ACTION_MODIFIED,
87 FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_CREATION,
88 name2);
91 return NT_STATUS_OK;
96 resolve a wildcard rename pattern. This works on one component of the name
98 static const char *pvfs_resolve_wildcard_component(TALLOC_CTX *mem_ctx,
99 const char *fname,
100 const char *pattern)
102 const char *p1, *p2;
103 char *dest, *d;
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;
111 p1 = fname;
112 p2 = pattern;
113 d = dest;
115 while (*p2) {
116 codepoint_t c1, c2;
117 size_t c_size1, c_size2;
118 c1 = next_codepoint(p1, &c_size1);
119 c2 = next_codepoint(p2, &c_size2);
120 if (c2 == '?') {
121 d += push_codepoint(d, c1);
122 } else if (c2 == '*') {
123 memcpy(d, p1, strlen(p1));
124 d += strlen(p1);
125 break;
126 } else {
127 d += push_codepoint(d, c2);
130 p1 += c_size1;
131 p2 += c_size2;
134 *d = 0;
136 talloc_set_name_const(dest, dest);
138 return dest;
142 resolve a wildcard rename pattern.
144 static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx,
145 const char *fname,
146 const char *pattern)
148 const char *base1, *base2;
149 const char *ext1, *ext2;
150 char *p;
152 /* break into base part plus extension */
153 p = strrchr_m(fname, '.');
154 if (p == NULL) {
155 ext1 = "";
156 base1 = fname;
157 } else {
158 ext1 = talloc_strdup(mem_ctx, p+1);
159 base1 = talloc_strndup(mem_ctx, fname, p-fname);
161 if (ext1 == NULL || base1 == NULL) {
162 return NULL;
165 p = strrchr_m(pattern, '.');
166 if (p == NULL) {
167 ext2 = "";
168 base2 = fname;
169 } else {
170 ext2 = talloc_strdup(mem_ctx, p+1);
171 base2 = talloc_strndup(mem_ctx, pattern, p-pattern);
173 if (ext2 == NULL || base2 == NULL) {
174 return NULL;
177 base1 = pvfs_resolve_wildcard_component(mem_ctx, base1, base2);
178 ext1 = pvfs_resolve_wildcard_component(mem_ctx, ext1, ext2);
179 if (base1 == NULL || ext1 == NULL) {
180 return NULL;
183 if (*ext1 == 0) {
184 return base1;
187 return talloc_asprintf(mem_ctx, "%s.%s", base1, ext1);
191 retry an rename after a sharing violation
193 static void pvfs_retry_rename(struct pvfs_odb_retry *r,
194 struct ntvfs_module_context *ntvfs,
195 struct ntvfs_request *req,
196 void *_io,
197 void *private_data,
198 enum pvfs_wait_notice reason)
200 union smb_rename *io = talloc_get_type(_io, union smb_rename);
201 NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
203 talloc_free(r);
205 switch (reason) {
206 case PVFS_WAIT_CANCEL:
207 /*TODO*/
208 status = NT_STATUS_CANCELLED;
209 break;
210 case PVFS_WAIT_TIMEOUT:
211 /* if it timed out, then give the failure
212 immediately */
213 /*TODO*/
214 status = NT_STATUS_SHARING_VIOLATION;
215 break;
216 case PVFS_WAIT_EVENT:
218 /* try the open again, which could trigger another retry setup
219 if it wants to, so we have to unmark the async flag so we
220 will know if it does a second async reply */
221 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
223 status = pvfs_rename(ntvfs, req, io);
224 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
225 /* the 2nd try also replied async, so we don't send
226 the reply yet */
227 return;
230 /* re-mark it async, just in case someone up the chain does
231 paranoid checking */
232 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
233 break;
236 /* send the reply up the chain */
237 req->async_states->status = status;
238 req->async_states->send_fn(req);
242 setup for a rename retry after a sharing violation
243 or a non granted oplock
245 static NTSTATUS pvfs_rename_setup_retry(struct ntvfs_module_context *ntvfs,
246 struct ntvfs_request *req,
247 union smb_rename *io,
248 struct odb_lock *lck,
249 NTSTATUS status)
251 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
252 struct pvfs_state);
253 struct timeval end_time;
255 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
256 end_time = timeval_add(&req->statistics.request_time,
257 0, pvfs->sharing_violation_delay);
258 } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
259 end_time = timeval_add(&req->statistics.request_time,
260 pvfs->oplock_break_timeout, 0);
261 } else {
262 return NT_STATUS_INTERNAL_ERROR;
265 return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
266 pvfs_retry_rename);
270 rename one file from a wildcard set
272 static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs,
273 struct ntvfs_request *req,
274 const char *dir_path,
275 const char *fname1,
276 const char *fname2,
277 uint16_t attrib)
279 struct pvfs_filename *name1, *name2;
280 TALLOC_CTX *mem_ctx = talloc_new(req);
281 struct odb_lock *lck = NULL;
282 NTSTATUS status;
284 /* resolve the wildcard pattern for this name */
285 fname2 = pvfs_resolve_wildcard(mem_ctx, fname1, fname2);
286 if (fname2 == NULL) {
287 return NT_STATUS_NO_MEMORY;
290 /* get a pvfs_filename source object */
291 status = pvfs_resolve_partial(pvfs, mem_ctx,
292 dir_path, fname1,
293 PVFS_RESOLVE_NO_OPENDB,
294 &name1);
295 if (!NT_STATUS_IS_OK(status)) {
296 goto failed;
299 /* make sure its matches the given attributes */
300 status = pvfs_match_attrib(pvfs, name1, attrib, 0);
301 if (!NT_STATUS_IS_OK(status)) {
302 goto failed;
305 status = pvfs_can_rename(pvfs, req, name1, &lck);
306 if (!NT_STATUS_IS_OK(status)) {
307 talloc_free(lck);
308 goto failed;
311 /* get a pvfs_filename dest object */
312 status = pvfs_resolve_partial(pvfs, mem_ctx,
313 dir_path, fname2,
314 PVFS_RESOLVE_NO_OPENDB,
315 &name2);
316 if (NT_STATUS_IS_OK(status)) {
317 status = pvfs_can_delete(pvfs, req, name2, NULL);
318 if (!NT_STATUS_IS_OK(status)) {
319 goto failed;
323 status = NT_STATUS_OK;
325 fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2);
326 if (fname2 == NULL) {
327 return NT_STATUS_NO_MEMORY;
330 status = pvfs_do_rename(pvfs, lck, name1, fname2);
332 failed:
333 talloc_free(mem_ctx);
334 return status;
339 rename a set of files with wildcards
341 static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs,
342 struct ntvfs_request *req,
343 union smb_rename *ren,
344 struct pvfs_filename *name1,
345 struct pvfs_filename *name2)
347 struct pvfs_dir *dir;
348 NTSTATUS status;
349 off_t ofs = 0;
350 const char *fname, *fname2, *dir_path;
351 uint16_t attrib = ren->rename.in.attrib;
352 int total_renamed = 0;
354 /* get list of matching files */
355 status = pvfs_list_start(pvfs, name1, req, &dir);
356 if (!NT_STATUS_IS_OK(status)) {
357 return status;
360 status = NT_STATUS_NO_SUCH_FILE;
362 dir_path = pvfs_list_unix_path(dir);
364 /* only allow wildcard renames within a directory */
365 if (strncmp(dir_path, name2->full_name, strlen(dir_path)) != 0 ||
366 name2->full_name[strlen(dir_path)] != '/' ||
367 strchr(name2->full_name + strlen(dir_path) + 1, '/')) {
368 DEBUG(3,(__location__ ": Invalid rename for %s -> %s\n",
369 name1->original_name, name2->original_name));
370 return NT_STATUS_INVALID_PARAMETER;
373 fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1);
374 if (fname2 == NULL) {
375 return NT_STATUS_NO_MEMORY;
378 while ((fname = pvfs_list_next(dir, &ofs))) {
379 status = pvfs_rename_one(pvfs, req,
380 dir_path,
381 fname, fname2, attrib);
382 if (NT_STATUS_IS_OK(status)) {
383 total_renamed++;
387 if (total_renamed == 0) {
388 return status;
391 return NT_STATUS_OK;
395 rename a set of files - SMBmv interface
397 static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs,
398 struct ntvfs_request *req, union smb_rename *ren)
400 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
401 struct pvfs_state);
402 NTSTATUS status;
403 struct pvfs_filename *name1, *name2;
404 struct odb_lock *lck = NULL;
406 /* resolve the cifs name to a posix name */
407 status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern1,
408 PVFS_RESOLVE_WILDCARD, &name1);
409 if (!NT_STATUS_IS_OK(status)) {
410 return status;
413 status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2,
414 PVFS_RESOLVE_WILDCARD, &name2);
415 if (!NT_STATUS_IS_OK(status)) {
416 return status;
419 if (name1->has_wildcard || name2->has_wildcard) {
420 return pvfs_rename_wildcard(pvfs, req, ren, name1, name2);
423 if (!name1->exists) {
424 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
427 if (strcmp(name1->full_name, name2->full_name) == 0) {
428 return NT_STATUS_OK;
431 if (name2->exists) {
432 return NT_STATUS_OBJECT_NAME_COLLISION;
435 status = pvfs_match_attrib(pvfs, name1, ren->rename.in.attrib, 0);
436 if (!NT_STATUS_IS_OK(status)) {
437 return status;
440 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
441 if (!NT_STATUS_IS_OK(status)) {
442 return status;
445 status = pvfs_can_rename(pvfs, req, name1, &lck);
447 * on a sharing violation we need to retry when the file is closed by
448 * the other user, or after 1 second
449 * on a non granted oplock we need to retry when the file is closed by
450 * the other user, or after 30 seconds
452 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
453 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
454 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
455 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
458 if (!NT_STATUS_IS_OK(status)) {
459 return status;
462 status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
463 if (!NT_STATUS_IS_OK(status)) {
464 return status;
467 return NT_STATUS_OK;
472 rename a stream
474 static NTSTATUS pvfs_rename_stream(struct ntvfs_module_context *ntvfs,
475 struct ntvfs_request *req, union smb_rename *ren,
476 struct pvfs_filename *name1)
478 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
479 struct pvfs_state);
480 NTSTATUS status;
481 struct odb_lock *lck = NULL;
483 if (name1->has_wildcard) {
484 DEBUG(3,(__location__ ": Invalid wildcard rename for %s\n",
485 name1->original_name));
486 return NT_STATUS_INVALID_PARAMETER;
489 if (ren->ntrename.in.new_name[0] != ':') {
490 DEBUG(3,(__location__ ": Invalid rename for %s\n",
491 ren->ntrename.in.new_name));
492 return NT_STATUS_INVALID_PARAMETER;
495 if (!name1->exists) {
496 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
499 if (ren->ntrename.in.flags != RENAME_FLAG_RENAME) {
500 DEBUG(3,(__location__ ": Invalid rename flags 0x%x for %s\n",
501 ren->ntrename.in.flags, ren->ntrename.in.new_name));
502 return NT_STATUS_INVALID_PARAMETER;
505 status = pvfs_can_rename(pvfs, req, name1, &lck);
507 * on a sharing violation we need to retry when the file is closed by
508 * the other user, or after 1 second
509 * on a non granted oplock we need to retry when the file is closed by
510 * the other user, or after 30 seconds
512 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
513 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
514 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
515 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
517 if (!NT_STATUS_IS_OK(status)) {
518 return status;
521 status = pvfs_access_check_simple(pvfs, req, name1, SEC_FILE_WRITE_ATTRIBUTE);
522 NT_STATUS_NOT_OK_RETURN(status);
524 status = pvfs_stream_rename(pvfs, name1, -1,
525 ren->ntrename.in.new_name+1,
526 true);
527 NT_STATUS_NOT_OK_RETURN(status);
529 return NT_STATUS_OK;
533 rename a set of files - ntrename interface
535 static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
536 struct ntvfs_request *req, union smb_rename *ren)
538 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
539 struct pvfs_state);
540 NTSTATUS status;
541 struct pvfs_filename *name1, *name2;
542 struct odb_lock *lck = NULL;
544 switch (ren->ntrename.in.flags) {
545 case RENAME_FLAG_RENAME:
546 case RENAME_FLAG_HARD_LINK:
547 case RENAME_FLAG_COPY:
548 case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
549 break;
550 default:
551 return NT_STATUS_ACCESS_DENIED;
554 /* resolve the cifs name to a posix name */
555 status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name,
556 PVFS_RESOLVE_WILDCARD | PVFS_RESOLVE_STREAMS, &name1);
557 if (!NT_STATUS_IS_OK(status)) {
558 return status;
561 if (name1->stream_name) {
562 /* stream renames need to be handled separately */
563 return pvfs_rename_stream(ntvfs, req, ren, name1);
566 status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name,
567 PVFS_RESOLVE_WILDCARD, &name2);
568 if (!NT_STATUS_IS_OK(status)) {
569 return status;
572 if (name1->has_wildcard || name2->has_wildcard) {
573 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
576 if (!name1->exists) {
577 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
580 if (strcmp(name1->full_name, name2->full_name) == 0) {
581 return NT_STATUS_OK;
584 if (name2->exists) {
585 return NT_STATUS_OBJECT_NAME_COLLISION;
588 status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
589 if (!NT_STATUS_IS_OK(status)) {
590 return status;
593 status = pvfs_can_rename(pvfs, req, name1, &lck);
595 * on a sharing violation we need to retry when the file is closed by
596 * the other user, or after 1 second
597 * on a non granted oplock we need to retry when the file is closed by
598 * the other user, or after 30 seconds
600 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
601 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
602 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
603 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
605 if (!NT_STATUS_IS_OK(status)) {
606 return status;
609 switch (ren->ntrename.in.flags) {
610 case RENAME_FLAG_RENAME:
611 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
612 NT_STATUS_NOT_OK_RETURN(status);
613 status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
614 NT_STATUS_NOT_OK_RETURN(status);
615 break;
617 case RENAME_FLAG_HARD_LINK:
618 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
619 NT_STATUS_NOT_OK_RETURN(status);
620 if (link(name1->full_name, name2->full_name) == -1) {
621 return pvfs_map_errno(pvfs, errno);
623 break;
625 case RENAME_FLAG_COPY:
626 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
627 NT_STATUS_NOT_OK_RETURN(status);
628 return pvfs_copy_file(pvfs, name1, name2, name1->allow_override && name2->allow_override);
630 case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
631 DEBUG(3,(__location__ ": Invalid rename cluster for %s\n",
632 name1->original_name));
633 return NT_STATUS_INVALID_PARAMETER;
635 default:
636 return NT_STATUS_ACCESS_DENIED;
640 return NT_STATUS_OK;
644 rename a set of files - ntrename interface
646 NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
647 struct ntvfs_request *req, union smb_rename *ren)
649 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
650 struct pvfs_state);
651 struct pvfs_file *f;
653 switch (ren->generic.level) {
654 case RAW_RENAME_RENAME:
655 return pvfs_rename_mv(ntvfs, req, ren);
657 case RAW_RENAME_NTRENAME:
658 return pvfs_rename_nt(ntvfs, req, ren);
660 case RAW_RENAME_NTTRANS:
661 f = pvfs_find_fd(pvfs, req, ren->nttrans.in.file.ntvfs);
662 if (!f) {
663 return NT_STATUS_INVALID_HANDLE;
666 /* wk23 ignores the request */
667 return NT_STATUS_OK;
669 default:
670 break;
673 return NT_STATUS_INVALID_LEVEL;