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 (rename(name1
->full_name
, name2
) == -1) {
41 return pvfs_map_errno(pvfs
, errno
);
44 status
= odb_rename(lck
, name2
);
45 NT_STATUS_NOT_OK_RETURN(status
);
47 if (name1
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
) {
48 mask
= FILE_NOTIFY_CHANGE_DIR_NAME
;
50 mask
= FILE_NOTIFY_CHANGE_FILE_NAME
;
53 renames to the same directory cause a OLD_NAME->NEW_NAME notify.
54 renames to a different directory are considered a remove/add
56 r1
= strrchr_m(name1
->full_name
, '/');
57 r2
= strrchr_m(name2
, '/');
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
,
65 notify_trigger(pvfs
->notify_context
,
70 notify_trigger(pvfs
->notify_context
,
71 NOTIFY_ACTION_OLD_NAME
,
74 notify_trigger(pvfs
->notify_context
,
75 NOTIFY_ACTION_NEW_NAME
,
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
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
,
95 resolve a wildcard rename pattern. This works on one component of the name
97 static const char *pvfs_resolve_wildcard_component(TALLOC_CTX
*mem_ctx
,
98 struct smb_iconv_convenience
*iconv_convenience
,
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_convenience(iconv_convenience
, p1
, &c_size1
);
119 c2
= next_codepoint_convenience(iconv_convenience
, p2
, &c_size2
);
121 d
+= push_codepoint_convenience(iconv_convenience
, d
, c1
);
122 } else if (c2
== '*') {
123 memcpy(d
, p1
, strlen(p1
));
127 d
+= push_codepoint_convenience(iconv_convenience
, 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
,
145 struct smb_iconv_convenience
*iconv_convenience
,
149 const char *base1
, *base2
;
150 const char *ext1
, *ext2
;
153 /* break into base part plus extension */
154 p
= strrchr_m(fname
, '.');
159 ext1
= talloc_strdup(mem_ctx
, p
+1);
160 base1
= talloc_strndup(mem_ctx
, fname
, p
-fname
);
162 if (ext1
== NULL
|| base1
== NULL
) {
166 p
= strrchr_m(pattern
, '.');
171 ext2
= talloc_strdup(mem_ctx
, p
+1);
172 base2
= talloc_strndup(mem_ctx
, pattern
, p
-pattern
);
174 if (ext2
== NULL
|| base2
== NULL
) {
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
) {
188 return talloc_asprintf(mem_ctx
, "%s.%s", base1
, ext1
);
192 retry an rename after a sharing violation
194 static void pvfs_retry_rename(struct pvfs_odb_retry
*r
,
195 struct ntvfs_module_context
*ntvfs
,
196 struct ntvfs_request
*req
,
199 enum pvfs_wait_notice reason
)
201 union smb_rename
*io
= talloc_get_type(_io
, union smb_rename
);
202 NTSTATUS status
= NT_STATUS_INTERNAL_ERROR
;
207 case PVFS_WAIT_CANCEL
:
209 status
= NT_STATUS_CANCELLED
;
211 case PVFS_WAIT_TIMEOUT
:
212 /* if it timed out, then give the failure
215 status
= NT_STATUS_SHARING_VIOLATION
;
217 case PVFS_WAIT_EVENT
:
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
;
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
231 /* re-mark it async, just in case someone up the chain does
233 req
->async_states
->state
|= NTVFS_ASYNC_STATE_ASYNC
;
237 /* send the reply up the chain */
238 req
->async_states
->status
= status
;
239 req
->async_states
->send_fn(req
);
243 setup for a rename retry after a sharing violation
244 or a non granted oplock
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
,
252 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
254 struct timeval end_time
;
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);
263 return NT_STATUS_INTERNAL_ERROR
;
266 return pvfs_odb_retry_setup(ntvfs
, req
, lck
, end_time
, io
, NULL
,
271 rename one file from a wildcard set
273 static NTSTATUS
pvfs_rename_one(struct pvfs_state
*pvfs
,
274 struct ntvfs_request
*req
,
275 const char *dir_path
,
280 struct pvfs_filename
*name1
, *name2
;
281 TALLOC_CTX
*mem_ctx
= talloc_new(req
);
282 struct odb_lock
*lck
= NULL
;
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
;
291 /* get a pvfs_filename source object */
292 status
= pvfs_resolve_partial(pvfs
, mem_ctx
,
294 PVFS_RESOLVE_NO_OPENDB
,
296 if (!NT_STATUS_IS_OK(status
)) {
300 /* make sure its matches the given attributes */
301 status
= pvfs_match_attrib(pvfs
, name1
, attrib
, 0);
302 if (!NT_STATUS_IS_OK(status
)) {
306 status
= pvfs_can_rename(pvfs
, req
, name1
, &lck
);
307 if (!NT_STATUS_IS_OK(status
)) {
312 /* get a pvfs_filename dest object */
313 status
= pvfs_resolve_partial(pvfs
, mem_ctx
,
315 PVFS_RESOLVE_NO_OPENDB
,
317 if (NT_STATUS_IS_OK(status
)) {
318 status
= pvfs_can_delete(pvfs
, req
, name2
, NULL
);
319 if (!NT_STATUS_IS_OK(status
)) {
324 status
= NT_STATUS_OK
;
326 fname2
= talloc_asprintf(mem_ctx
, "%s/%s", dir_path
, fname2
);
327 if (fname2
== NULL
) {
328 return NT_STATUS_NO_MEMORY
;
331 status
= pvfs_do_rename(pvfs
, lck
, name1
, fname2
);
334 talloc_free(mem_ctx
);
340 rename a set of files with wildcards
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
)
348 struct pvfs_dir
*dir
;
351 const char *fname
, *fname2
, *dir_path
;
352 uint16_t attrib
= ren
->rename
.in
.attrib
;
353 int total_renamed
= 0;
355 /* get list of matching files */
356 status
= pvfs_list_start(pvfs
, name1
, req
, &dir
);
357 if (!NT_STATUS_IS_OK(status
)) {
361 status
= NT_STATUS_NO_SUCH_FILE
;
363 dir_path
= pvfs_list_unix_path(dir
);
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
;
372 fname2
= talloc_strdup(name2
, name2
->full_name
+ strlen(dir_path
) + 1);
373 if (fname2
== NULL
) {
374 return NT_STATUS_NO_MEMORY
;
377 while ((fname
= pvfs_list_next(dir
, &ofs
))) {
378 status
= pvfs_rename_one(pvfs
, req
,
380 fname
, fname2
, attrib
);
381 if (NT_STATUS_IS_OK(status
)) {
386 if (total_renamed
== 0) {
394 rename a set of files - SMBmv interface
396 static NTSTATUS
pvfs_rename_mv(struct ntvfs_module_context
*ntvfs
,
397 struct ntvfs_request
*req
, union smb_rename
*ren
)
399 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
402 struct pvfs_filename
*name1
, *name2
;
403 struct odb_lock
*lck
= NULL
;
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
)) {
412 status
= pvfs_resolve_name(pvfs
, req
, ren
->rename
.in
.pattern2
,
413 PVFS_RESOLVE_WILDCARD
, &name2
);
414 if (!NT_STATUS_IS_OK(status
)) {
418 if (name1
->has_wildcard
|| name2
->has_wildcard
) {
419 return pvfs_rename_wildcard(pvfs
, req
, ren
, name1
, name2
);
422 if (!name1
->exists
) {
423 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
426 if (strcmp(name1
->full_name
, name2
->full_name
) == 0) {
431 return NT_STATUS_OBJECT_NAME_COLLISION
;
434 status
= pvfs_match_attrib(pvfs
, name1
, ren
->rename
.in
.attrib
, 0);
435 if (!NT_STATUS_IS_OK(status
)) {
439 status
= pvfs_access_check_parent(pvfs
, req
, name2
, SEC_DIR_ADD_FILE
);
440 if (!NT_STATUS_IS_OK(status
)) {
444 status
= pvfs_can_rename(pvfs
, req
, name1
, &lck
);
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
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
);
457 if (!NT_STATUS_IS_OK(status
)) {
461 status
= pvfs_do_rename(pvfs
, lck
, name1
, name2
->full_name
);
462 if (!NT_STATUS_IS_OK(status
)) {
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
)
477 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
480 struct odb_lock
*lck
= NULL
;
482 if (name1
->has_wildcard
) {
483 return NT_STATUS_INVALID_PARAMETER
;
486 if (ren
->ntrename
.in
.new_name
[0] != ':') {
487 return NT_STATUS_INVALID_PARAMETER
;
490 if (!name1
->exists
) {
491 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
494 if (ren
->ntrename
.in
.flags
!= RENAME_FLAG_RENAME
) {
495 return NT_STATUS_INVALID_PARAMETER
;
498 status
= pvfs_can_rename(pvfs
, req
, name1
, &lck
);
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
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
);
510 if (!NT_STATUS_IS_OK(status
)) {
514 status
= pvfs_access_check_simple(pvfs
, req
, name1
, SEC_FILE_WRITE_ATTRIBUTE
);
515 NT_STATUS_NOT_OK_RETURN(status
);
517 status
= pvfs_stream_rename(pvfs
, name1
, -1,
518 ren
->ntrename
.in
.new_name
+1,
520 NT_STATUS_NOT_OK_RETURN(status
);
526 rename a set of files - ntrename interface
528 static NTSTATUS
pvfs_rename_nt(struct ntvfs_module_context
*ntvfs
,
529 struct ntvfs_request
*req
, union smb_rename
*ren
)
531 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
534 struct pvfs_filename
*name1
, *name2
;
535 struct odb_lock
*lck
= NULL
;
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
:
544 return NT_STATUS_ACCESS_DENIED
;
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
)) {
554 if (name1
->stream_name
) {
555 /* stream renames need to be handled separately */
556 return pvfs_rename_stream(ntvfs
, req
, ren
, name1
);
559 status
= pvfs_resolve_name(pvfs
, req
, ren
->ntrename
.in
.new_name
,
560 PVFS_RESOLVE_WILDCARD
, &name2
);
561 if (!NT_STATUS_IS_OK(status
)) {
565 if (name1
->has_wildcard
|| name2
->has_wildcard
) {
566 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD
;
569 if (!name1
->exists
) {
570 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
573 if (strcmp(name1
->full_name
, name2
->full_name
) == 0) {
578 return NT_STATUS_OBJECT_NAME_COLLISION
;
581 status
= pvfs_match_attrib(pvfs
, name1
, ren
->ntrename
.in
.attrib
, 0);
582 if (!NT_STATUS_IS_OK(status
)) {
586 status
= pvfs_can_rename(pvfs
, req
, name1
, &lck
);
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
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
);
598 if (!NT_STATUS_IS_OK(status
)) {
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
);
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
);
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
);
623 case RENAME_FLAG_MOVE_CLUSTER_INFORMATION
:
624 return NT_STATUS_INVALID_PARAMETER
;
627 return NT_STATUS_ACCESS_DENIED
;
635 rename a set of files - ntrename interface
637 NTSTATUS
pvfs_rename(struct ntvfs_module_context
*ntvfs
,
638 struct ntvfs_request
*req
, union smb_rename
*ren
)
640 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
644 switch (ren
->generic
.level
) {
645 case RAW_RENAME_RENAME
:
646 return pvfs_rename_mv(ntvfs
, req
, ren
);
648 case RAW_RENAME_NTRENAME
:
649 return pvfs_rename_nt(ntvfs
, req
, ren
);
651 case RAW_RENAME_NTTRANS
:
652 f
= pvfs_find_fd(pvfs
, req
, ren
->nttrans
.in
.file
.ntvfs
);
654 return NT_STATUS_INVALID_HANDLE
;
657 /* wk23 ignores the request */
664 return NT_STATUS_INVALID_LEVEL
;