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_size(mem_ctx
, strlen(fname
) + strlen(pattern
) + 1);
117 size_t c_size1
, c_size2
;
118 c1
= next_codepoint(iconv_convenience
, p1
, &c_size1
);
119 c2
= next_codepoint(iconv_convenience
, p2
, &c_size2
);
121 d
+= push_codepoint(iconv_convenience
, d
, c1
);
122 } else if (c2
== '*') {
123 memcpy(d
, p1
, strlen(p1
));
127 d
+= push_codepoint(iconv_convenience
, d
, c2
);
140 resolve a wildcard rename pattern.
142 static const char *pvfs_resolve_wildcard(TALLOC_CTX
*mem_ctx
,
143 struct smb_iconv_convenience
*iconv_convenience
,
147 const char *base1
, *base2
;
148 const char *ext1
, *ext2
;
151 /* break into base part plus extension */
152 p
= strrchr_m(fname
, '.');
157 ext1
= talloc_strdup(mem_ctx
, p
+1);
158 base1
= talloc_strndup(mem_ctx
, fname
, p
-fname
);
160 if (ext1
== NULL
|| base1
== NULL
) {
164 p
= strrchr_m(pattern
, '.');
169 ext2
= talloc_strdup(mem_ctx
, p
+1);
170 base2
= talloc_strndup(mem_ctx
, pattern
, p
-pattern
);
172 if (ext2
== NULL
|| base2
== NULL
) {
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
) {
186 return talloc_asprintf(mem_ctx
, "%s.%s", base1
, ext1
);
190 retry an rename after a sharing violation
192 static void pvfs_retry_rename(struct pvfs_odb_retry
*r
,
193 struct ntvfs_module_context
*ntvfs
,
194 struct ntvfs_request
*req
,
197 enum pvfs_wait_notice reason
)
199 union smb_rename
*io
= talloc_get_type(_io
, union smb_rename
);
200 NTSTATUS status
= NT_STATUS_INTERNAL_ERROR
;
205 case PVFS_WAIT_CANCEL
:
207 status
= NT_STATUS_CANCELLED
;
209 case PVFS_WAIT_TIMEOUT
:
210 /* if it timed out, then give the failure
213 status
= NT_STATUS_SHARING_VIOLATION
;
215 case PVFS_WAIT_EVENT
:
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
;
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
229 /* re-mark it async, just in case someone up the chain does
231 req
->async_states
->state
|= NTVFS_ASYNC_STATE_ASYNC
;
235 /* send the reply up the chain */
236 req
->async_states
->status
= status
;
237 req
->async_states
->send_fn(req
);
241 setup for a rename retry after a sharing violation
242 or a non granted oplock
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
,
250 struct pvfs_state
*pvfs
= ntvfs
->private_data
;
251 struct timeval end_time
;
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);
260 return NT_STATUS_INTERNAL_ERROR
;
263 return pvfs_odb_retry_setup(ntvfs
, req
, lck
, end_time
, io
, NULL
,
268 rename one file from a wildcard set
270 static NTSTATUS
pvfs_rename_one(struct pvfs_state
*pvfs
,
271 struct ntvfs_request
*req
,
272 const char *dir_path
,
277 struct pvfs_filename
*name1
, *name2
;
278 TALLOC_CTX
*mem_ctx
= talloc_new(req
);
279 struct odb_lock
*lck
= NULL
;
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
;
288 /* get a pvfs_filename source object */
289 status
= pvfs_resolve_partial(pvfs
, mem_ctx
,
290 dir_path
, fname1
, &name1
);
291 if (!NT_STATUS_IS_OK(status
)) {
295 /* make sure its matches the given attributes */
296 status
= pvfs_match_attrib(pvfs
, name1
, attrib
, 0);
297 if (!NT_STATUS_IS_OK(status
)) {
301 status
= pvfs_can_rename(pvfs
, req
, name1
, &lck
);
302 if (!NT_STATUS_IS_OK(status
)) {
307 /* get a pvfs_filename dest object */
308 status
= pvfs_resolve_partial(pvfs
, mem_ctx
,
309 dir_path
, fname2
, &name2
);
310 if (NT_STATUS_IS_OK(status
)) {
311 status
= pvfs_can_delete(pvfs
, req
, name2
, NULL
);
312 if (!NT_STATUS_IS_OK(status
)) {
317 status
= NT_STATUS_OK
;
319 fname2
= talloc_asprintf(mem_ctx
, "%s/%s", dir_path
, fname2
);
320 if (fname2
== NULL
) {
321 return NT_STATUS_NO_MEMORY
;
324 status
= pvfs_do_rename(pvfs
, lck
, name1
, fname2
);
327 talloc_free(mem_ctx
);
333 rename a set of files with wildcards
335 static NTSTATUS
pvfs_rename_wildcard(struct pvfs_state
*pvfs
,
336 struct ntvfs_request
*req
,
337 union smb_rename
*ren
,
338 struct pvfs_filename
*name1
,
339 struct pvfs_filename
*name2
)
341 struct pvfs_dir
*dir
;
344 const char *fname
, *fname2
, *dir_path
;
345 uint16_t attrib
= ren
->rename
.in
.attrib
;
346 int total_renamed
= 0;
348 /* get list of matching files */
349 status
= pvfs_list_start(pvfs
, name1
, req
, &dir
);
350 if (!NT_STATUS_IS_OK(status
)) {
354 status
= NT_STATUS_NO_SUCH_FILE
;
356 dir_path
= pvfs_list_unix_path(dir
);
358 /* only allow wildcard renames within a directory */
359 if (strncmp(dir_path
, name2
->full_name
, strlen(dir_path
)) != 0 ||
360 name2
->full_name
[strlen(dir_path
)] != '/' ||
361 strchr(name2
->full_name
+ strlen(dir_path
) + 1, '/')) {
362 return NT_STATUS_INVALID_PARAMETER
;
365 fname2
= talloc_strdup(name2
, name2
->full_name
+ strlen(dir_path
) + 1);
366 if (fname2
== NULL
) {
367 return NT_STATUS_NO_MEMORY
;
370 while ((fname
= pvfs_list_next(dir
, &ofs
))) {
371 status
= pvfs_rename_one(pvfs
, req
,
373 fname
, fname2
, attrib
);
374 if (NT_STATUS_IS_OK(status
)) {
379 if (total_renamed
== 0) {
387 rename a set of files - SMBmv interface
389 static NTSTATUS
pvfs_rename_mv(struct ntvfs_module_context
*ntvfs
,
390 struct ntvfs_request
*req
, union smb_rename
*ren
)
392 struct pvfs_state
*pvfs
= ntvfs
->private_data
;
394 struct pvfs_filename
*name1
, *name2
;
395 struct odb_lock
*lck
= NULL
;
397 /* resolve the cifs name to a posix name */
398 status
= pvfs_resolve_name(pvfs
, req
, ren
->rename
.in
.pattern1
,
399 PVFS_RESOLVE_WILDCARD
, &name1
);
400 if (!NT_STATUS_IS_OK(status
)) {
404 status
= pvfs_resolve_name(pvfs
, req
, ren
->rename
.in
.pattern2
,
405 PVFS_RESOLVE_WILDCARD
, &name2
);
406 if (!NT_STATUS_IS_OK(status
)) {
410 if (name1
->has_wildcard
|| name2
->has_wildcard
) {
411 return pvfs_rename_wildcard(pvfs
, req
, ren
, name1
, name2
);
414 if (!name1
->exists
) {
415 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
418 if (strcmp(name1
->full_name
, name2
->full_name
) == 0) {
423 return NT_STATUS_OBJECT_NAME_COLLISION
;
426 status
= pvfs_match_attrib(pvfs
, name1
, ren
->rename
.in
.attrib
, 0);
427 if (!NT_STATUS_IS_OK(status
)) {
431 status
= pvfs_access_check_parent(pvfs
, req
, name2
, SEC_DIR_ADD_FILE
);
432 if (!NT_STATUS_IS_OK(status
)) {
436 status
= pvfs_can_rename(pvfs
, req
, name1
, &lck
);
438 * on a sharing violation we need to retry when the file is closed by
439 * the other user, or after 1 second
440 * on a non granted oplock we need to retry when the file is closed by
441 * the other user, or after 30 seconds
443 if ((NT_STATUS_EQUAL(status
, NT_STATUS_SHARING_VIOLATION
) ||
444 NT_STATUS_EQUAL(status
, NT_STATUS_OPLOCK_NOT_GRANTED
)) &&
445 (req
->async_states
->state
& NTVFS_ASYNC_STATE_MAY_ASYNC
)) {
446 return pvfs_rename_setup_retry(pvfs
->ntvfs
, req
, ren
, lck
, status
);
449 if (!NT_STATUS_IS_OK(status
)) {
453 status
= pvfs_do_rename(pvfs
, lck
, name1
, name2
->full_name
);
454 if (!NT_STATUS_IS_OK(status
)) {
463 rename a set of files - ntrename interface
465 static NTSTATUS
pvfs_rename_nt(struct ntvfs_module_context
*ntvfs
,
466 struct ntvfs_request
*req
, union smb_rename
*ren
)
468 struct pvfs_state
*pvfs
= ntvfs
->private_data
;
470 struct pvfs_filename
*name1
, *name2
;
471 struct odb_lock
*lck
= NULL
;
473 switch (ren
->ntrename
.in
.flags
) {
474 case RENAME_FLAG_RENAME
:
475 case RENAME_FLAG_HARD_LINK
:
476 case RENAME_FLAG_COPY
:
477 case RENAME_FLAG_MOVE_CLUSTER_INFORMATION
:
480 return NT_STATUS_ACCESS_DENIED
;
483 /* resolve the cifs name to a posix name */
484 status
= pvfs_resolve_name(pvfs
, req
, ren
->ntrename
.in
.old_name
,
485 PVFS_RESOLVE_WILDCARD
, &name1
);
486 if (!NT_STATUS_IS_OK(status
)) {
490 status
= pvfs_resolve_name(pvfs
, req
, ren
->ntrename
.in
.new_name
,
491 PVFS_RESOLVE_WILDCARD
, &name2
);
492 if (!NT_STATUS_IS_OK(status
)) {
496 if (name1
->has_wildcard
|| name2
->has_wildcard
) {
497 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD
;
500 if (!name1
->exists
) {
501 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
504 if (strcmp(name1
->full_name
, name2
->full_name
) == 0) {
509 return NT_STATUS_OBJECT_NAME_COLLISION
;
512 status
= pvfs_match_attrib(pvfs
, name1
, ren
->ntrename
.in
.attrib
, 0);
513 if (!NT_STATUS_IS_OK(status
)) {
517 status
= pvfs_can_rename(pvfs
, req
, name1
, &lck
);
519 * on a sharing violation we need to retry when the file is closed by
520 * the other user, or after 1 second
521 * on a non granted oplock we need to retry when the file is closed by
522 * the other user, or after 30 seconds
524 if ((NT_STATUS_EQUAL(status
, NT_STATUS_SHARING_VIOLATION
) ||
525 NT_STATUS_EQUAL(status
, NT_STATUS_OPLOCK_NOT_GRANTED
)) &&
526 (req
->async_states
->state
& NTVFS_ASYNC_STATE_MAY_ASYNC
)) {
527 return pvfs_rename_setup_retry(pvfs
->ntvfs
, req
, ren
, lck
, status
);
529 if (!NT_STATUS_IS_OK(status
)) {
533 switch (ren
->ntrename
.in
.flags
) {
534 case RENAME_FLAG_RENAME
:
535 status
= pvfs_access_check_parent(pvfs
, req
, name2
, SEC_DIR_ADD_FILE
);
536 NT_STATUS_NOT_OK_RETURN(status
);
537 status
= pvfs_do_rename(pvfs
, lck
, name1
, name2
->full_name
);
538 NT_STATUS_NOT_OK_RETURN(status
);
541 case RENAME_FLAG_HARD_LINK
:
542 status
= pvfs_access_check_parent(pvfs
, req
, name2
, SEC_DIR_ADD_FILE
);
543 NT_STATUS_NOT_OK_RETURN(status
);
544 if (link(name1
->full_name
, name2
->full_name
) == -1) {
545 return pvfs_map_errno(pvfs
, errno
);
549 case RENAME_FLAG_COPY
:
550 status
= pvfs_access_check_parent(pvfs
, req
, name2
, SEC_DIR_ADD_FILE
);
551 NT_STATUS_NOT_OK_RETURN(status
);
552 return pvfs_copy_file(pvfs
, name1
, name2
);
554 case RENAME_FLAG_MOVE_CLUSTER_INFORMATION
:
555 return NT_STATUS_INVALID_PARAMETER
;
558 return NT_STATUS_ACCESS_DENIED
;
566 rename a set of files - ntrename interface
568 NTSTATUS
pvfs_rename(struct ntvfs_module_context
*ntvfs
,
569 struct ntvfs_request
*req
, union smb_rename
*ren
)
571 switch (ren
->generic
.level
) {
572 case RAW_RENAME_RENAME
:
573 return pvfs_rename_mv(ntvfs
, req
, ren
);
575 case RAW_RENAME_NTRENAME
:
576 return pvfs_rename_nt(ntvfs
, req
, ren
);
578 case RAW_RENAME_NTTRANS
:
579 return NT_STATUS_NOT_IMPLEMENTED
;
585 return NT_STATUS_INVALID_LEVEL
;