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, const struct pvfs_filename *name1,
37 if (rename(name1->full_name, name2) == -1) {
38 return pvfs_map_errno(pvfs, errno);
41 if (name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
42 mask = FILE_NOTIFY_CHANGE_DIR_NAME;
44 mask = FILE_NOTIFY_CHANGE_FILE_NAME;
47 renames to the same directory cause a OLD_NAME->NEW_NAME notify.
48 renames to a different directory are considered a remove/add
50 r1 = strrchr_m(name1->full_name, '/');
51 r2 = strrchr_m(name2, '/');
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,
59 notify_trigger(pvfs->notify_context,
64 notify_trigger(pvfs->notify_context,
65 NOTIFY_ACTION_OLD_NAME,
68 notify_trigger(pvfs->notify_context,
69 NOTIFY_ACTION_NEW_NAME,
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
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,
89 resolve a wildcard rename pattern. This works on one component of the name
91 static const char *pvfs_resolve_wildcard_component(TALLOC_CTX *mem_ctx,
92 struct smb_iconv_convenience *iconv_convenience,
99 /* the length is bounded by the length of the two strings combined */
100 dest = talloc_size(mem_ctx, strlen(fname) + strlen(pattern) + 1);
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);
115 d += push_codepoint(iconv_convenience, d, c1);
116 } else if (c2 == '*') {
117 memcpy(d, p1, strlen(p1));
121 d += push_codepoint(iconv_convenience, d, c2);
134 resolve a wildcard rename pattern.
136 static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx,
137 struct smb_iconv_convenience *iconv_convenience,
141 const char *base1, *base2;
142 const char *ext1, *ext2;
145 /* break into base part plus extension */
146 p = strrchr_m(fname, '.');
151 ext1 = talloc_strdup(mem_ctx, p+1);
152 base1 = talloc_strndup(mem_ctx, fname, p-fname);
154 if (ext1 == NULL || base1 == NULL) {
158 p = strrchr_m(pattern, '.');
163 ext2 = talloc_strdup(mem_ctx, p+1);
164 base2 = talloc_strndup(mem_ctx, pattern, p-pattern);
166 if (ext2 == NULL || base2 == NULL) {
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) {
180 return talloc_asprintf(mem_ctx, "%s.%s", base1, ext1);
184 retry an rename after a sharing violation
186 static void pvfs_retry_rename(struct pvfs_odb_retry *r,
187 struct ntvfs_module_context *ntvfs,
188 struct ntvfs_request *req,
191 enum pvfs_wait_notice reason)
193 union smb_rename *io = talloc_get_type(_io, union smb_rename);
194 NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
199 case PVFS_WAIT_CANCEL:
201 status = NT_STATUS_CANCELLED;
203 case PVFS_WAIT_TIMEOUT:
204 /* if it timed out, then give the failure
207 status = NT_STATUS_SHARING_VIOLATION;
209 case PVFS_WAIT_EVENT:
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;
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
223 /* re-mark it async, just in case someone up the chain does
225 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
229 /* send the reply up the chain */
230 req->async_states->status = status;
231 req->async_states->send_fn(req);
235 setup for a rename retry after a sharing violation
236 or a non granted oplock
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,
244 struct pvfs_state *pvfs = ntvfs->private_data;
245 struct timeval end_time;
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);
254 return NT_STATUS_INTERNAL_ERROR;
257 return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
262 rename one file from a wildcard set
264 static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs,
265 struct ntvfs_request *req,
266 const char *dir_path,
271 struct pvfs_filename *name1, *name2;
272 TALLOC_CTX *mem_ctx = talloc_new(req);
273 struct odb_lock *lck = NULL;
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;
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)) {
289 /* make sure its matches the given attributes */
290 status = pvfs_match_attrib(pvfs, name1, attrib, 0);
291 if (!NT_STATUS_IS_OK(status)) {
295 status = pvfs_can_rename(pvfs, req, name1, &lck);
296 if (!NT_STATUS_IS_OK(status)) {
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)) {
311 status = NT_STATUS_OK;
313 fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2);
314 if (fname2 == NULL) {
315 return NT_STATUS_NO_MEMORY;
318 status = pvfs_do_rename(pvfs, name1, fname2);
320 if (NT_STATUS_IS_OK(status)) {
321 status = odb_rename(lck, fname2);
325 talloc_free(mem_ctx);
331 rename a set of files with wildcards
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)
339 struct pvfs_dir *dir;
342 const char *fname, *fname2, *dir_path;
343 uint16_t attrib = ren->rename.in.attrib;
344 int total_renamed = 0;
346 /* get list of matching files */
347 status = pvfs_list_start(pvfs, name1, req, &dir);
348 if (!NT_STATUS_IS_OK(status)) {
352 status = NT_STATUS_NO_SUCH_FILE;
354 dir_path = pvfs_list_unix_path(dir);
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;
363 fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1);
364 if (fname2 == NULL) {
365 return NT_STATUS_NO_MEMORY;
368 while ((fname = pvfs_list_next(dir, &ofs))) {
369 status = pvfs_rename_one(pvfs, req,
371 fname, fname2, attrib);
372 if (NT_STATUS_IS_OK(status)) {
377 if (total_renamed == 0) {
385 rename a set of files - SMBmv interface
387 static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs,
388 struct ntvfs_request *req, union smb_rename *ren)
390 struct pvfs_state *pvfs = ntvfs->private_data;
392 struct pvfs_filename *name1, *name2;
393 struct odb_lock *lck = NULL;
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)) {
402 status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2,
403 PVFS_RESOLVE_WILDCARD, &name2);
404 if (!NT_STATUS_IS_OK(status)) {
408 if (name1->has_wildcard || name2->has_wildcard) {
409 return pvfs_rename_wildcard(pvfs, req, ren, name1, name2);
412 if (!name1->exists) {
413 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
416 if (strcmp(name1->full_name, name2->full_name) == 0) {
421 return NT_STATUS_OBJECT_NAME_COLLISION;
424 status = pvfs_match_attrib(pvfs, name1, ren->rename.in.attrib, 0);
425 if (!NT_STATUS_IS_OK(status)) {
429 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
430 if (!NT_STATUS_IS_OK(status)) {
434 status = pvfs_can_rename(pvfs, req, name1, &lck);
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
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);
447 if (!NT_STATUS_IS_OK(status)) {
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);
461 rename a set of files - ntrename interface
463 static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
464 struct ntvfs_request *req, union smb_rename *ren)
466 struct pvfs_state *pvfs = ntvfs->private_data;
468 struct pvfs_filename *name1, *name2;
469 struct odb_lock *lck = NULL;
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:
478 return NT_STATUS_ACCESS_DENIED;
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)) {
488 status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name,
489 PVFS_RESOLVE_WILDCARD, &name2);
490 if (!NT_STATUS_IS_OK(status)) {
494 if (name1->has_wildcard || name2->has_wildcard) {
495 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
498 if (!name1->exists) {
499 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
502 if (strcmp(name1->full_name, name2->full_name) == 0) {
507 return NT_STATUS_OBJECT_NAME_COLLISION;
510 status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
511 if (!NT_STATUS_IS_OK(status)) {
515 status = pvfs_can_rename(pvfs, req, name1, &lck);
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
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);
527 if (!NT_STATUS_IS_OK(status)) {
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);
539 NT_STATUS_NOT_OK_RETURN(status);
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);
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);
555 case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
556 return NT_STATUS_INVALID_PARAMETER;
559 return NT_STATUS_ACCESS_DENIED;
567 rename a set of files - ntrename interface
569 NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
570 struct ntvfs_request *req, union smb_rename *ren)
572 switch (ren->generic.level) {
573 case RAW_RENAME_RENAME:
574 return pvfs_rename_mv(ntvfs, req, ren);
576 case RAW_RENAME_NTRENAME:
577 return pvfs_rename_nt(ntvfs, req, ren);
583 return NT_STATUS_INVALID_LEVEL;