source: vendor/current/source4/ntvfs/posix/pvfs_rename.c@ 740

Last change on this file since 740 was 740, checked in by Silvan Scherrer, 12 years ago

Samba Server: update vendor to 3.6.0

File size: 17.7 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3
4 POSIX NTVFS backend - rename
5
6 Copyright (C) Andrew Tridgell 2004
7
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.
12
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.
17
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/>.
20*/
21
22#include "includes.h"
23#include "vfs_posix.h"
24#include "librpc/gen_ndr/security.h"
25#include "param/param.h"
26
27
28/*
29 do a file rename, and send any notify triggers
30*/
31NTSTATUS pvfs_do_rename(struct pvfs_state *pvfs,
32 struct odb_lock *lck,
33 const struct pvfs_filename *name1,
34 const char *name2)
35{
36 const char *r1, *r2;
37 uint32_t mask;
38 NTSTATUS status;
39
40 if (pvfs_sys_rename(pvfs, name1->full_name, name2) == -1) {
41 return pvfs_map_errno(pvfs, errno);
42 }
43
44 status = odb_rename(lck, name2);
45 NT_STATUS_NOT_OK_RETURN(status);
46
47 if (name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
48 mask = FILE_NOTIFY_CHANGE_DIR_NAME;
49 } else {
50 mask = FILE_NOTIFY_CHANGE_FILE_NAME;
51 }
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
55 */
56 r1 = strrchr_m(name1->full_name, '/');
57 r2 = strrchr_m(name2, '/');
58
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);
78 }
79
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);
88 }
89
90 return NT_STATUS_OK;
91}
92
93
94/*
95 resolve a wildcard rename pattern. This works on one component of the name
96*/
97static const char *pvfs_resolve_wildcard_component(TALLOC_CTX *mem_ctx,
98 const char *fname,
99 const char *pattern)
100{
101 const char *p1, *p2;
102 char *dest, *d;
103
104 /* the length is bounded by the length of the two strings combined */
105 dest = talloc_array(mem_ctx, char, strlen(fname) + strlen(pattern) + 1);
106 if (dest == NULL) {
107 return NULL;
108 }
109
110 p1 = fname;
111 p2 = pattern;
112 d = dest;
113
114 while (*p2) {
115 codepoint_t c1, c2;
116 size_t c_size1, c_size2;
117 c1 = next_codepoint(p1, &c_size1);
118 c2 = next_codepoint(p2, &c_size2);
119 if (c2 == '?') {
120 d += push_codepoint(d, c1);
121 } else if (c2 == '*') {
122 memcpy(d, p1, strlen(p1));
123 d += strlen(p1);
124 break;
125 } else {
126 d += push_codepoint(d, c2);
127 }
128
129 p1 += c_size1;
130 p2 += c_size2;
131 }
132
133 *d = 0;
134
135 talloc_set_name_const(dest, dest);
136
137 return dest;
138}
139
140/*
141 resolve a wildcard rename pattern.
142*/
143static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx,
144 const char *fname,
145 const char *pattern)
146{
147 const char *base1, *base2;
148 const char *ext1, *ext2;
149 char *p;
150
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);
159 }
160 if (ext1 == NULL || base1 == NULL) {
161 return NULL;
162 }
163
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);
171 }
172 if (ext2 == NULL || base2 == NULL) {
173 return NULL;
174 }
175
176 base1 = pvfs_resolve_wildcard_component(mem_ctx, base1, base2);
177 ext1 = pvfs_resolve_wildcard_component(mem_ctx, ext1, ext2);
178 if (base1 == NULL || ext1 == NULL) {
179 return NULL;
180 }
181
182 if (*ext1 == 0) {
183 return base1;
184 }
185
186 return talloc_asprintf(mem_ctx, "%s.%s", base1, ext1);
187}
188
189/*
190 retry an rename after a sharing violation
191*/
192static 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)
198{
199 union smb_rename *io = talloc_get_type(_io, union smb_rename);
200 NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
201
202 talloc_free(r);
203
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:
216
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;
221
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;
227 }
228
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;
233 }
234
235 /* send the reply up the chain */
236 req->async_states->status = status;
237 req->async_states->send_fn(req);
238}
239
240/*
241 setup for a rename retry after a sharing violation
242 or a non granted oplock
243*/
244static 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)
249{
250 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
251 struct pvfs_state);
252 struct timeval end_time;
253
254 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
255 end_time = timeval_add(&req->statistics.request_time,
256 0, pvfs->sharing_violation_delay);
257 } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
258 end_time = timeval_add(&req->statistics.request_time,
259 pvfs->oplock_break_timeout, 0);
260 } else {
261 return NT_STATUS_INTERNAL_ERROR;
262 }
263
264 return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
265 pvfs_retry_rename);
266}
267
268/*
269 rename one file from a wildcard set
270*/
271static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs,
272 struct ntvfs_request *req,
273 const char *dir_path,
274 const char *fname1,
275 const char *fname2,
276 uint16_t attrib)
277{
278 struct pvfs_filename *name1, *name2;
279 TALLOC_CTX *mem_ctx = talloc_new(req);
280 struct odb_lock *lck = NULL;
281 NTSTATUS status;
282
283 /* resolve the wildcard pattern for this name */
284 fname2 = pvfs_resolve_wildcard(mem_ctx, fname1, fname2);
285 if (fname2 == NULL) {
286 return NT_STATUS_NO_MEMORY;
287 }
288
289 /* get a pvfs_filename source object */
290 status = pvfs_resolve_partial(pvfs, mem_ctx,
291 dir_path, fname1,
292 PVFS_RESOLVE_NO_OPENDB,
293 &name1);
294 if (!NT_STATUS_IS_OK(status)) {
295 goto failed;
296 }
297
298 /* make sure its matches the given attributes */
299 status = pvfs_match_attrib(pvfs, name1, attrib, 0);
300 if (!NT_STATUS_IS_OK(status)) {
301 goto failed;
302 }
303
304 status = pvfs_can_rename(pvfs, req, name1, &lck);
305 if (!NT_STATUS_IS_OK(status)) {
306 talloc_free(lck);
307 goto failed;
308 }
309
310 /* get a pvfs_filename dest object */
311 status = pvfs_resolve_partial(pvfs, mem_ctx,
312 dir_path, fname2,
313 PVFS_RESOLVE_NO_OPENDB,
314 &name2);
315 if (NT_STATUS_IS_OK(status)) {
316 status = pvfs_can_delete(pvfs, req, name2, NULL);
317 if (!NT_STATUS_IS_OK(status)) {
318 goto failed;
319 }
320 }
321
322 status = NT_STATUS_OK;
323
324 fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2);
325 if (fname2 == NULL) {
326 return NT_STATUS_NO_MEMORY;
327 }
328
329 status = pvfs_do_rename(pvfs, lck, name1, fname2);
330
331failed:
332 talloc_free(mem_ctx);
333 return status;
334}
335
336
337/*
338 rename a set of files with wildcards
339*/
340static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs,
341 struct ntvfs_request *req,
342 union smb_rename *ren,
343 struct pvfs_filename *name1,
344 struct pvfs_filename *name2)
345{
346 struct pvfs_dir *dir;
347 NTSTATUS status;
348 off_t ofs = 0;
349 const char *fname, *fname2, *dir_path;
350 uint16_t attrib = ren->rename.in.attrib;
351 int total_renamed = 0;
352
353 /* get list of matching files */
354 status = pvfs_list_start(pvfs, name1, req, &dir);
355 if (!NT_STATUS_IS_OK(status)) {
356 return status;
357 }
358
359 status = NT_STATUS_NO_SUCH_FILE;
360
361 dir_path = pvfs_list_unix_path(dir);
362
363 /* only allow wildcard renames within a directory */
364 if (strncmp(dir_path, name2->full_name, strlen(dir_path)) != 0 ||
365 name2->full_name[strlen(dir_path)] != '/' ||
366 strchr(name2->full_name + strlen(dir_path) + 1, '/')) {
367 DEBUG(3,(__location__ ": Invalid rename for %s -> %s\n",
368 name1->original_name, name2->original_name));
369 return NT_STATUS_INVALID_PARAMETER;
370 }
371
372 fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1);
373 if (fname2 == NULL) {
374 return NT_STATUS_NO_MEMORY;
375 }
376
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++;
383 }
384 }
385
386 if (total_renamed == 0) {
387 return status;
388 }
389
390 return NT_STATUS_OK;
391}
392
393/*
394 rename a set of files - SMBmv interface
395*/
396static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs,
397 struct ntvfs_request *req, union smb_rename *ren)
398{
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;
404
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;
410 }
411
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;
416 }
417
418 if (name1->has_wildcard || name2->has_wildcard) {
419 return pvfs_rename_wildcard(pvfs, req, ren, name1, name2);
420 }
421
422 if (!name1->exists) {
423 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
424 }
425
426 if (strcmp(name1->full_name, name2->full_name) == 0) {
427 return NT_STATUS_OK;
428 }
429
430 if (name2->exists) {
431 return NT_STATUS_OBJECT_NAME_COLLISION;
432 }
433
434 status = pvfs_match_attrib(pvfs, name1, ren->rename.in.attrib, 0);
435 if (!NT_STATUS_IS_OK(status)) {
436 return status;
437 }
438
439 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
440 if (!NT_STATUS_IS_OK(status)) {
441 return status;
442 }
443
444 status = pvfs_can_rename(pvfs, req, name1, &lck);
445 /*
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
450 */
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);
455 }
456
457 if (!NT_STATUS_IS_OK(status)) {
458 return status;
459 }
460
461 status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
462 if (!NT_STATUS_IS_OK(status)) {
463 return status;
464 }
465
466 return NT_STATUS_OK;
467}
468
469
470/*
471 rename a stream
472*/
473static NTSTATUS pvfs_rename_stream(struct ntvfs_module_context *ntvfs,
474 struct ntvfs_request *req, union smb_rename *ren,
475 struct pvfs_filename *name1)
476{
477 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
478 struct pvfs_state);
479 NTSTATUS status;
480 struct odb_lock *lck = NULL;
481
482 if (name1->has_wildcard) {
483 DEBUG(3,(__location__ ": Invalid wildcard rename for %s\n",
484 name1->original_name));
485 return NT_STATUS_INVALID_PARAMETER;
486 }
487
488 if (ren->ntrename.in.new_name[0] != ':') {
489 DEBUG(3,(__location__ ": Invalid rename for %s\n",
490 ren->ntrename.in.new_name));
491 return NT_STATUS_INVALID_PARAMETER;
492 }
493
494 if (!name1->exists) {
495 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
496 }
497
498 if (ren->ntrename.in.flags != RENAME_FLAG_RENAME) {
499 DEBUG(3,(__location__ ": Invalid rename flags 0x%x for %s\n",
500 ren->ntrename.in.flags, ren->ntrename.in.new_name));
501 return NT_STATUS_INVALID_PARAMETER;
502 }
503
504 status = pvfs_can_rename(pvfs, req, name1, &lck);
505 /*
506 * on a sharing violation we need to retry when the file is closed by
507 * the other user, or after 1 second
508 * on a non granted oplock we need to retry when the file is closed by
509 * the other user, or after 30 seconds
510 */
511 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
512 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
513 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
514 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
515 }
516 if (!NT_STATUS_IS_OK(status)) {
517 return status;
518 }
519
520 status = pvfs_access_check_simple(pvfs, req, name1, SEC_FILE_WRITE_ATTRIBUTE);
521 NT_STATUS_NOT_OK_RETURN(status);
522
523 status = pvfs_stream_rename(pvfs, name1, -1,
524 ren->ntrename.in.new_name+1,
525 true);
526 NT_STATUS_NOT_OK_RETURN(status);
527
528 return NT_STATUS_OK;
529}
530
531/*
532 rename a set of files - ntrename interface
533*/
534static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
535 struct ntvfs_request *req, union smb_rename *ren)
536{
537 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
538 struct pvfs_state);
539 NTSTATUS status;
540 struct pvfs_filename *name1, *name2;
541 struct odb_lock *lck = NULL;
542
543 switch (ren->ntrename.in.flags) {
544 case RENAME_FLAG_RENAME:
545 case RENAME_FLAG_HARD_LINK:
546 case RENAME_FLAG_COPY:
547 case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
548 break;
549 default:
550 return NT_STATUS_ACCESS_DENIED;
551 }
552
553 /* resolve the cifs name to a posix name */
554 status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name,
555 PVFS_RESOLVE_WILDCARD | PVFS_RESOLVE_STREAMS, &name1);
556 if (!NT_STATUS_IS_OK(status)) {
557 return status;
558 }
559
560 if (name1->stream_name) {
561 /* stream renames need to be handled separately */
562 return pvfs_rename_stream(ntvfs, req, ren, name1);
563 }
564
565 status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name,
566 PVFS_RESOLVE_WILDCARD, &name2);
567 if (!NT_STATUS_IS_OK(status)) {
568 return status;
569 }
570
571 if (name1->has_wildcard || name2->has_wildcard) {
572 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
573 }
574
575 if (!name1->exists) {
576 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
577 }
578
579 if (strcmp(name1->full_name, name2->full_name) == 0) {
580 return NT_STATUS_OK;
581 }
582
583 if (name2->exists) {
584 return NT_STATUS_OBJECT_NAME_COLLISION;
585 }
586
587 status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
588 if (!NT_STATUS_IS_OK(status)) {
589 return status;
590 }
591
592 status = pvfs_can_rename(pvfs, req, name1, &lck);
593 /*
594 * on a sharing violation we need to retry when the file is closed by
595 * the other user, or after 1 second
596 * on a non granted oplock we need to retry when the file is closed by
597 * the other user, or after 30 seconds
598 */
599 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
600 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
601 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
602 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
603 }
604 if (!NT_STATUS_IS_OK(status)) {
605 return status;
606 }
607
608 switch (ren->ntrename.in.flags) {
609 case RENAME_FLAG_RENAME:
610 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
611 NT_STATUS_NOT_OK_RETURN(status);
612 status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
613 NT_STATUS_NOT_OK_RETURN(status);
614 break;
615
616 case RENAME_FLAG_HARD_LINK:
617 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
618 NT_STATUS_NOT_OK_RETURN(status);
619 if (link(name1->full_name, name2->full_name) == -1) {
620 return pvfs_map_errno(pvfs, errno);
621 }
622 break;
623
624 case RENAME_FLAG_COPY:
625 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
626 NT_STATUS_NOT_OK_RETURN(status);
627 return pvfs_copy_file(pvfs, name1, name2);
628
629 case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
630 DEBUG(3,(__location__ ": Invalid rename cluster for %s\n",
631 name1->original_name));
632 return NT_STATUS_INVALID_PARAMETER;
633
634 default:
635 return NT_STATUS_ACCESS_DENIED;
636 }
637
638
639 return NT_STATUS_OK;
640}
641
642/*
643 rename a set of files - ntrename interface
644*/
645NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
646 struct ntvfs_request *req, union smb_rename *ren)
647{
648 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
649 struct pvfs_state);
650 struct pvfs_file *f;
651
652 switch (ren->generic.level) {
653 case RAW_RENAME_RENAME:
654 return pvfs_rename_mv(ntvfs, req, ren);
655
656 case RAW_RENAME_NTRENAME:
657 return pvfs_rename_nt(ntvfs, req, ren);
658
659 case RAW_RENAME_NTTRANS:
660 f = pvfs_find_fd(pvfs, req, ren->nttrans.in.file.ntvfs);
661 if (!f) {
662 return NT_STATUS_INVALID_HANDLE;
663 }
664
665 /* wk23 ignores the request */
666 return NT_STATUS_OK;
667
668 default:
669 break;
670 }
671
672 return NT_STATUS_INVALID_LEVEL;
673}
674
Note: See TracBrowser for help on using the repository browser.