s4: update setntacl and getntacl to select the adaquate backend (fs/tdb) for storing...
[Samba/ekacnet.git] / source4 / ntvfs / posix / pvfs_rename.c
blobd963357cbabd2ad4d0cd18537d6ae153c2746689
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 (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;
49 } else {
50 mask = FILE_NOTIFY_CHANGE_FILE_NAME;
52 /*
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,
63 mask,
64 name1->full_name);
65 notify_trigger(pvfs->notify_context,
66 NOTIFY_ACTION_ADDED,
67 mask,
68 name2);
69 } else {
70 notify_trigger(pvfs->notify_context,
71 NOTIFY_ACTION_OLD_NAME,
72 mask,
73 name1->full_name);
74 notify_trigger(pvfs->notify_context,
75 NOTIFY_ACTION_NEW_NAME,
76 mask,
77 name2);
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
82 directories */
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,
87 name2);
90 return NT_STATUS_OK;
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,
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_convenience(iconv_convenience, p1, &c_size1);
119 c2 = next_codepoint_convenience(iconv_convenience, p2, &c_size2);
120 if (c2 == '?') {
121 d += push_codepoint_convenience(iconv_convenience, d, c1);
122 } else if (c2 == '*') {
123 memcpy(d, p1, strlen(p1));
124 d += strlen(p1);
125 break;
126 } else {
127 d += push_codepoint_convenience(iconv_convenience, 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 struct smb_iconv_convenience *iconv_convenience,
146 const char *fname,
147 const char *pattern)
149 const char *base1, *base2;
150 const char *ext1, *ext2;
151 char *p;
153 /* break into base part plus extension */
154 p = strrchr_m(fname, '.');
155 if (p == NULL) {
156 ext1 = "";
157 base1 = fname;
158 } else {
159 ext1 = talloc_strdup(mem_ctx, p+1);
160 base1 = talloc_strndup(mem_ctx, fname, p-fname);
162 if (ext1 == NULL || base1 == NULL) {
163 return NULL;
166 p = strrchr_m(pattern, '.');
167 if (p == NULL) {
168 ext2 = "";
169 base2 = fname;
170 } else {
171 ext2 = talloc_strdup(mem_ctx, p+1);
172 base2 = talloc_strndup(mem_ctx, pattern, p-pattern);
174 if (ext2 == NULL || base2 == NULL) {
175 return 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) {
181 return NULL;
184 if (*ext1 == 0) {
185 return base1;
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,
197 void *_io,
198 void *private_data,
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;
204 talloc_free(r);
206 switch (reason) {
207 case PVFS_WAIT_CANCEL:
208 /*TODO*/
209 status = NT_STATUS_CANCELLED;
210 break;
211 case PVFS_WAIT_TIMEOUT:
212 /* if it timed out, then give the failure
213 immediately */
214 /*TODO*/
215 status = NT_STATUS_SHARING_VIOLATION;
216 break;
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
227 the reply yet */
228 return;
231 /* re-mark it async, just in case someone up the chain does
232 paranoid checking */
233 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
234 break;
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,
250 NTSTATUS status)
252 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
253 struct pvfs_state);
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);
262 } else {
263 return NT_STATUS_INTERNAL_ERROR;
266 return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
267 pvfs_retry_rename);
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,
276 const char *fname1,
277 const char *fname2,
278 uint16_t attrib)
280 struct pvfs_filename *name1, *name2;
281 TALLOC_CTX *mem_ctx = talloc_new(req);
282 struct odb_lock *lck = NULL;
283 NTSTATUS status;
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,
293 dir_path, fname1,
294 PVFS_RESOLVE_NO_OPENDB,
295 &name1);
296 if (!NT_STATUS_IS_OK(status)) {
297 goto failed;
300 /* make sure its matches the given attributes */
301 status = pvfs_match_attrib(pvfs, name1, attrib, 0);
302 if (!NT_STATUS_IS_OK(status)) {
303 goto failed;
306 status = pvfs_can_rename(pvfs, req, name1, &lck);
307 if (!NT_STATUS_IS_OK(status)) {
308 talloc_free(lck);
309 goto failed;
312 /* get a pvfs_filename dest object */
313 status = pvfs_resolve_partial(pvfs, mem_ctx,
314 dir_path, fname2,
315 PVFS_RESOLVE_NO_OPENDB,
316 &name2);
317 if (NT_STATUS_IS_OK(status)) {
318 status = pvfs_can_delete(pvfs, req, name2, NULL);
319 if (!NT_STATUS_IS_OK(status)) {
320 goto failed;
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);
333 failed:
334 talloc_free(mem_ctx);
335 return status;
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;
349 NTSTATUS status;
350 off_t ofs = 0;
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)) {
358 return 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,
379 dir_path,
380 fname, fname2, attrib);
381 if (NT_STATUS_IS_OK(status)) {
382 total_renamed++;
386 if (total_renamed == 0) {
387 return status;
390 return NT_STATUS_OK;
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,
400 struct pvfs_state);
401 NTSTATUS status;
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)) {
409 return status;
412 status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2,
413 PVFS_RESOLVE_WILDCARD, &name2);
414 if (!NT_STATUS_IS_OK(status)) {
415 return 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) {
427 return NT_STATUS_OK;
430 if (name2->exists) {
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)) {
436 return status;
439 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
440 if (!NT_STATUS_IS_OK(status)) {
441 return 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)) {
458 return status;
461 status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
462 if (!NT_STATUS_IS_OK(status)) {
463 return status;
466 return NT_STATUS_OK;
471 rename a stream
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,
478 struct pvfs_state);
479 NTSTATUS status;
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)) {
511 return 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,
519 true);
520 NT_STATUS_NOT_OK_RETURN(status);
522 return NT_STATUS_OK;
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,
532 struct pvfs_state);
533 NTSTATUS status;
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:
542 break;
543 default:
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)) {
551 return 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)) {
562 return 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) {
574 return NT_STATUS_OK;
577 if (name2->exists) {
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)) {
583 return 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)) {
599 return 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);
608 break;
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);
616 break;
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;
626 default:
627 return NT_STATUS_ACCESS_DENIED;
631 return NT_STATUS_OK;
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,
641 struct pvfs_state);
642 struct pvfs_file *f;
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);
653 if (!f) {
654 return NT_STATUS_INVALID_HANDLE;
657 /* wk23 ignores the request */
658 return NT_STATUS_OK;
660 default:
661 break;
664 return NT_STATUS_INVALID_LEVEL;