pvfs: add PVFS_RESOLVE_NO_OPENDB flag and get the write time from the opendb
[Samba/ekacnet.git] / source4 / ntvfs / posix / pvfs_rename.c
blobd8ea5896e560f417870d9709e8dc76559ac0743d
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_size(mem_ctx, 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(iconv_convenience, p1, &c_size1);
119 c2 = next_codepoint(iconv_convenience, p2, &c_size2);
120 if (c2 == '?') {
121 d += push_codepoint(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(iconv_convenience, d, c2);
130 p1 += c_size1;
131 p2 += c_size2;
134 *d = 0;
136 return dest;
140 resolve a wildcard rename pattern.
142 static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx,
143 struct smb_iconv_convenience *iconv_convenience,
144 const char *fname,
145 const char *pattern)
147 const char *base1, *base2;
148 const char *ext1, *ext2;
149 char *p;
151 /* break into base part plus extension */
152 p = strrchr_m(fname, '.');
153 if (p == NULL) {
154 ext1 = "";
155 base1 = fname;
156 } else {
157 ext1 = talloc_strdup(mem_ctx, p+1);
158 base1 = talloc_strndup(mem_ctx, fname, p-fname);
160 if (ext1 == NULL || base1 == NULL) {
161 return NULL;
164 p = strrchr_m(pattern, '.');
165 if (p == NULL) {
166 ext2 = "";
167 base2 = fname;
168 } else {
169 ext2 = talloc_strdup(mem_ctx, p+1);
170 base2 = talloc_strndup(mem_ctx, pattern, p-pattern);
172 if (ext2 == NULL || base2 == NULL) {
173 return 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) {
179 return NULL;
182 if (*ext1 == 0) {
183 return base1;
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,
195 void *_io,
196 void *private_data,
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;
202 talloc_free(r);
204 switch (reason) {
205 case PVFS_WAIT_CANCEL:
206 /*TODO*/
207 status = NT_STATUS_CANCELLED;
208 break;
209 case PVFS_WAIT_TIMEOUT:
210 /* if it timed out, then give the failure
211 immediately */
212 /*TODO*/
213 status = NT_STATUS_SHARING_VIOLATION;
214 break;
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
225 the reply yet */
226 return;
229 /* re-mark it async, just in case someone up the chain does
230 paranoid checking */
231 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
232 break;
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,
248 NTSTATUS status)
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);
259 } else {
260 return NT_STATUS_INTERNAL_ERROR;
263 return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
264 pvfs_retry_rename);
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,
273 const char *fname1,
274 const char *fname2,
275 uint16_t attrib)
277 struct pvfs_filename *name1, *name2;
278 TALLOC_CTX *mem_ctx = talloc_new(req);
279 struct odb_lock *lck = NULL;
280 NTSTATUS status;
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,
291 PVFS_RESOLVE_NO_OPENDB,
292 &name1);
293 if (!NT_STATUS_IS_OK(status)) {
294 goto failed;
297 /* make sure its matches the given attributes */
298 status = pvfs_match_attrib(pvfs, name1, attrib, 0);
299 if (!NT_STATUS_IS_OK(status)) {
300 goto failed;
303 status = pvfs_can_rename(pvfs, req, name1, &lck);
304 if (!NT_STATUS_IS_OK(status)) {
305 talloc_free(lck);
306 goto failed;
309 /* get a pvfs_filename dest object */
310 status = pvfs_resolve_partial(pvfs, mem_ctx,
311 dir_path, fname2,
312 PVFS_RESOLVE_NO_OPENDB,
313 &name2);
314 if (NT_STATUS_IS_OK(status)) {
315 status = pvfs_can_delete(pvfs, req, name2, NULL);
316 if (!NT_STATUS_IS_OK(status)) {
317 goto failed;
321 status = NT_STATUS_OK;
323 fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2);
324 if (fname2 == NULL) {
325 return NT_STATUS_NO_MEMORY;
328 status = pvfs_do_rename(pvfs, lck, name1, fname2);
330 failed:
331 talloc_free(mem_ctx);
332 return status;
337 rename a set of files with wildcards
339 static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs,
340 struct ntvfs_request *req,
341 union smb_rename *ren,
342 struct pvfs_filename *name1,
343 struct pvfs_filename *name2)
345 struct pvfs_dir *dir;
346 NTSTATUS status;
347 off_t ofs = 0;
348 const char *fname, *fname2, *dir_path;
349 uint16_t attrib = ren->rename.in.attrib;
350 int total_renamed = 0;
352 /* get list of matching files */
353 status = pvfs_list_start(pvfs, name1, req, &dir);
354 if (!NT_STATUS_IS_OK(status)) {
355 return status;
358 status = NT_STATUS_NO_SUCH_FILE;
360 dir_path = pvfs_list_unix_path(dir);
362 /* only allow wildcard renames within a directory */
363 if (strncmp(dir_path, name2->full_name, strlen(dir_path)) != 0 ||
364 name2->full_name[strlen(dir_path)] != '/' ||
365 strchr(name2->full_name + strlen(dir_path) + 1, '/')) {
366 return NT_STATUS_INVALID_PARAMETER;
369 fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1);
370 if (fname2 == NULL) {
371 return NT_STATUS_NO_MEMORY;
374 while ((fname = pvfs_list_next(dir, &ofs))) {
375 status = pvfs_rename_one(pvfs, req,
376 dir_path,
377 fname, fname2, attrib);
378 if (NT_STATUS_IS_OK(status)) {
379 total_renamed++;
383 if (total_renamed == 0) {
384 return status;
387 return NT_STATUS_OK;
391 rename a set of files - SMBmv interface
393 static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs,
394 struct ntvfs_request *req, union smb_rename *ren)
396 struct pvfs_state *pvfs = ntvfs->private_data;
397 NTSTATUS status;
398 struct pvfs_filename *name1, *name2;
399 struct odb_lock *lck = NULL;
401 /* resolve the cifs name to a posix name */
402 status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern1,
403 PVFS_RESOLVE_WILDCARD, &name1);
404 if (!NT_STATUS_IS_OK(status)) {
405 return status;
408 status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2,
409 PVFS_RESOLVE_WILDCARD, &name2);
410 if (!NT_STATUS_IS_OK(status)) {
411 return status;
414 if (name1->has_wildcard || name2->has_wildcard) {
415 return pvfs_rename_wildcard(pvfs, req, ren, name1, name2);
418 if (!name1->exists) {
419 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
422 if (strcmp(name1->full_name, name2->full_name) == 0) {
423 return NT_STATUS_OK;
426 if (name2->exists) {
427 return NT_STATUS_OBJECT_NAME_COLLISION;
430 status = pvfs_match_attrib(pvfs, name1, ren->rename.in.attrib, 0);
431 if (!NT_STATUS_IS_OK(status)) {
432 return status;
435 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
436 if (!NT_STATUS_IS_OK(status)) {
437 return status;
440 status = pvfs_can_rename(pvfs, req, name1, &lck);
442 * on a sharing violation we need to retry when the file is closed by
443 * the other user, or after 1 second
444 * on a non granted oplock we need to retry when the file is closed by
445 * the other user, or after 30 seconds
447 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
448 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
449 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
450 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
453 if (!NT_STATUS_IS_OK(status)) {
454 return status;
457 status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
458 if (!NT_STATUS_IS_OK(status)) {
459 return status;
462 return NT_STATUS_OK;
467 rename a set of files - ntrename interface
469 static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
470 struct ntvfs_request *req, union smb_rename *ren)
472 struct pvfs_state *pvfs = ntvfs->private_data;
473 NTSTATUS status;
474 struct pvfs_filename *name1, *name2;
475 struct odb_lock *lck = NULL;
477 switch (ren->ntrename.in.flags) {
478 case RENAME_FLAG_RENAME:
479 case RENAME_FLAG_HARD_LINK:
480 case RENAME_FLAG_COPY:
481 case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
482 break;
483 default:
484 return NT_STATUS_ACCESS_DENIED;
487 /* resolve the cifs name to a posix name */
488 status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name,
489 PVFS_RESOLVE_WILDCARD, &name1);
490 if (!NT_STATUS_IS_OK(status)) {
491 return status;
494 status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name,
495 PVFS_RESOLVE_WILDCARD, &name2);
496 if (!NT_STATUS_IS_OK(status)) {
497 return status;
500 if (name1->has_wildcard || name2->has_wildcard) {
501 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
504 if (!name1->exists) {
505 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
508 if (strcmp(name1->full_name, name2->full_name) == 0) {
509 return NT_STATUS_OK;
512 if (name2->exists) {
513 return NT_STATUS_OBJECT_NAME_COLLISION;
516 status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
517 if (!NT_STATUS_IS_OK(status)) {
518 return status;
521 status = pvfs_can_rename(pvfs, req, name1, &lck);
523 * on a sharing violation we need to retry when the file is closed by
524 * the other user, or after 1 second
525 * on a non granted oplock we need to retry when the file is closed by
526 * the other user, or after 30 seconds
528 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
529 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
530 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
531 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
533 if (!NT_STATUS_IS_OK(status)) {
534 return status;
537 switch (ren->ntrename.in.flags) {
538 case RENAME_FLAG_RENAME:
539 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
540 NT_STATUS_NOT_OK_RETURN(status);
541 status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
542 NT_STATUS_NOT_OK_RETURN(status);
543 break;
545 case RENAME_FLAG_HARD_LINK:
546 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
547 NT_STATUS_NOT_OK_RETURN(status);
548 if (link(name1->full_name, name2->full_name) == -1) {
549 return pvfs_map_errno(pvfs, errno);
551 break;
553 case RENAME_FLAG_COPY:
554 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
555 NT_STATUS_NOT_OK_RETURN(status);
556 return pvfs_copy_file(pvfs, name1, name2);
558 case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
559 return NT_STATUS_INVALID_PARAMETER;
561 default:
562 return NT_STATUS_ACCESS_DENIED;
566 return NT_STATUS_OK;
570 rename a set of files - ntrename interface
572 NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
573 struct ntvfs_request *req, union smb_rename *ren)
575 struct pvfs_state *pvfs = ntvfs->private_data;
576 struct pvfs_file *f;
578 switch (ren->generic.level) {
579 case RAW_RENAME_RENAME:
580 return pvfs_rename_mv(ntvfs, req, ren);
582 case RAW_RENAME_NTRENAME:
583 return pvfs_rename_nt(ntvfs, req, ren);
585 case RAW_RENAME_NTTRANS:
586 f = pvfs_find_fd(pvfs, req, ren->nttrans.in.file.ntvfs);
587 if (!f) {
588 return NT_STATUS_INVALID_HANDLE;
591 /* wk23 ignores the request */
592 return NT_STATUS_OK;
594 default:
595 break;
598 return NT_STATUS_INVALID_LEVEL;