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/>.
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
,
33 const struct pvfs_filename
*name1
,
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
;
51 mask
= FILE_NOTIFY_CHANGE_FILE_NAME
;
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
,
66 notify_trigger(pvfs
->notify_context
,
71 notify_trigger(pvfs
->notify_context
,
72 NOTIFY_ACTION_OLD_NAME
,
75 notify_trigger(pvfs
->notify_context
,
76 NOTIFY_ACTION_NEW_NAME
,
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
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
,
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
,
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);
117 size_t c_size1
, c_size2
;
118 c1
= next_codepoint(p1
, &c_size1
);
119 c2
= next_codepoint(p2
, &c_size2
);
121 d
+= push_codepoint(d
, c1
);
122 } else if (c2
== '*') {
123 memcpy(d
, p1
, strlen(p1
));
127 d
+= push_codepoint(d
, c2
);
136 talloc_set_name_const(dest
, dest
);
142 resolve a wildcard rename pattern.
144 static const char *pvfs_resolve_wildcard(TALLOC_CTX
*mem_ctx
,
148 const char *base1
, *base2
;
149 const char *ext1
, *ext2
;
152 /* break into base part plus extension */
153 p
= strrchr_m(fname
, '.');
158 ext1
= talloc_strdup(mem_ctx
, p
+1);
159 base1
= talloc_strndup(mem_ctx
, fname
, p
-fname
);
161 if (ext1
== NULL
|| base1
== NULL
) {
165 p
= strrchr_m(pattern
, '.');
170 ext2
= talloc_strdup(mem_ctx
, p
+1);
171 base2
= talloc_strndup(mem_ctx
, pattern
, p
-pattern
);
173 if (ext2
== NULL
|| base2
== 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
) {
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
,
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
;
206 case PVFS_WAIT_CANCEL
:
208 status
= NT_STATUS_CANCELLED
;
210 case PVFS_WAIT_TIMEOUT
:
211 /* if it timed out, then give the failure
214 status
= NT_STATUS_SHARING_VIOLATION
;
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
230 /* re-mark it async, just in case someone up the chain does
232 req
->async_states
->state
|= NTVFS_ASYNC_STATE_ASYNC
;
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
,
251 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
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);
262 return NT_STATUS_INTERNAL_ERROR
;
265 return pvfs_odb_retry_setup(ntvfs
, req
, lck
, end_time
, io
, NULL
,
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
,
279 struct pvfs_filename
*name1
, *name2
;
280 TALLOC_CTX
*mem_ctx
= talloc_new(req
);
281 struct odb_lock
*lck
= NULL
;
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
,
293 PVFS_RESOLVE_NO_OPENDB
,
295 if (!NT_STATUS_IS_OK(status
)) {
299 /* make sure its matches the given attributes */
300 status
= pvfs_match_attrib(pvfs
, name1
, attrib
, 0);
301 if (!NT_STATUS_IS_OK(status
)) {
305 status
= pvfs_can_rename(pvfs
, req
, name1
, &lck
);
306 if (!NT_STATUS_IS_OK(status
)) {
311 /* get a pvfs_filename dest object */
312 status
= pvfs_resolve_partial(pvfs
, mem_ctx
,
314 PVFS_RESOLVE_NO_OPENDB
,
316 if (NT_STATUS_IS_OK(status
)) {
317 status
= pvfs_can_delete(pvfs
, req
, name2
, NULL
);
318 if (!NT_STATUS_IS_OK(status
)) {
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
);
333 talloc_free(mem_ctx
);
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
;
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
)) {
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
,
381 fname
, fname2
, attrib
);
382 if (NT_STATUS_IS_OK(status
)) {
387 if (total_renamed
== 0) {
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
,
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
)) {
413 status
= pvfs_resolve_name(pvfs
, req
, ren
->rename
.in
.pattern2
,
414 PVFS_RESOLVE_WILDCARD
, &name2
);
415 if (!NT_STATUS_IS_OK(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) {
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
)) {
440 status
= pvfs_access_check_parent(pvfs
, req
, name2
, SEC_DIR_ADD_FILE
);
441 if (!NT_STATUS_IS_OK(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
)) {
462 status
= pvfs_do_rename(pvfs
, lck
, name1
, name2
->full_name
);
463 if (!NT_STATUS_IS_OK(status
)) {
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
,
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
)) {
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,
527 NT_STATUS_NOT_OK_RETURN(status
);
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
,
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
:
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
)) {
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
)) {
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) {
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
)) {
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
)) {
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
);
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
);
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
;
636 return NT_STATUS_ACCESS_DENIED
;
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
,
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
);
663 return NT_STATUS_INVALID_HANDLE
;
666 /* wk23 ignores the request */
673 return NT_STATUS_INVALID_LEVEL
;