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

Last change on this file was 988, checked in by Silvan Scherrer, 8 years ago

Samba Server: update vendor to version 4.4.3

File size: 17.1 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,
41 name1->allow_override) == -1) {
42 return pvfs_map_errno(pvfs, errno);
43 }
44
45 status = odb_rename(lck, name2);
46 NT_STATUS_NOT_OK_RETURN(status);
47
48 if (name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
49 mask = FILE_NOTIFY_CHANGE_DIR_NAME;
50 } else {
51 mask = FILE_NOTIFY_CHANGE_FILE_NAME;
52 }
53 /*
54 renames to the same directory cause a OLD_NAME->NEW_NAME notify.
55 renames to a different directory are considered a remove/add
56 */
57 r1 = strrchr_m(name1->full_name, '/');
58 r2 = strrchr_m(name2, '/');
59
60 if ((r1-name1->full_name) != (r2-name2) ||
61 strncmp(name1->full_name, name2, r1-name1->full_name) != 0) {
62 notify_trigger(pvfs->notify_context,
63 NOTIFY_ACTION_REMOVED,
64 mask,
65 name1->full_name);
66 notify_trigger(pvfs->notify_context,
67 NOTIFY_ACTION_ADDED,
68 mask,
69 name2);
70 } else {
71 notify_trigger(pvfs->notify_context,
72 NOTIFY_ACTION_OLD_NAME,
73 mask,
74 name1->full_name);
75 notify_trigger(pvfs->notify_context,
76 NOTIFY_ACTION_NEW_NAME,
77 mask,
78 name2);
79 }
80
81 /* this is a strange one. w2k3 gives an additional event for CHANGE_ATTRIBUTES
82 and CHANGE_CREATION on the new file when renaming files, but not
83 directories */
84 if ((name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) == 0) {
85 notify_trigger(pvfs->notify_context,
86 NOTIFY_ACTION_MODIFIED,
87 FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_CREATION,
88 name2);
89 }
90
91 return NT_STATUS_OK;
92}
93
94
95/*
96 resolve a wildcard rename pattern. This works on one component of the name
97*/
98static const char *pvfs_resolve_wildcard_component(TALLOC_CTX *mem_ctx,
99 const char *fname,
100 const char *pattern)
101{
102 const char *p1, *p2;
103 char *dest, *d;
104
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;
109 }
110
111 p1 = fname;
112 p2 = pattern;
113 d = dest;
114
115 while (*p2) {
116 codepoint_t c1, c2;
117 size_t c_size1, c_size2;
118 c1 = next_codepoint(p1, &c_size1);
119 c2 = next_codepoint(p2, &c_size2);
120 if (c2 == '?') {
121 d += push_codepoint(d, c1);
122 } else if (c2 == '*') {
123 memcpy(d, p1, strlen(p1));
124 d += strlen(p1);
125 break;
126 } else {
127 d += push_codepoint(d, c2);
128 }
129
130 p1 += c_size1;
131 p2 += c_size2;
132 }
133
134 *d = 0;
135
136 talloc_set_name_const(dest, dest);
137
138 return dest;
139}
140
141/*
142 resolve a wildcard rename pattern.
143*/
144static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx,
145 const char *fname,
146 const char *pattern)
147{
148 const char *base1, *base2;
149 const char *ext1, *ext2;
150 char *p;
151
152 /* break into base part plus extension */
153 p = strrchr_m(fname, '.');
154 if (p == NULL) {
155 ext1 = "";
156 base1 = fname;
157 } else {
158 ext1 = talloc_strdup(mem_ctx, p+1);
159 base1 = talloc_strndup(mem_ctx, fname, p-fname);
160 }
161 if (ext1 == NULL || base1 == NULL) {
162 return NULL;
163 }
164
165 p = strrchr_m(pattern, '.');
166 if (p == NULL) {
167 ext2 = "";
168 base2 = fname;
169 } else {
170 ext2 = talloc_strdup(mem_ctx, p+1);
171 base2 = talloc_strndup(mem_ctx, pattern, p-pattern);
172 }
173 if (ext2 == NULL || base2 == NULL) {
174 return NULL;
175 }
176
177 base1 = pvfs_resolve_wildcard_component(mem_ctx, base1, base2);
178 ext1 = pvfs_resolve_wildcard_component(mem_ctx, ext1, ext2);
179 if (base1 == NULL || ext1 == NULL) {
180 return NULL;
181 }
182
183 if (*ext1 == 0) {
184 return base1;
185 }
186
187 return talloc_asprintf(mem_ctx, "%s.%s", base1, ext1);
188}
189
190/*
191 retry an rename after a sharing violation
192*/
193static void pvfs_retry_rename(struct pvfs_odb_retry *r,
194 struct ntvfs_module_context *ntvfs,
195 struct ntvfs_request *req,
196 void *_io,
197 void *private_data,
198 enum pvfs_wait_notice reason)
199{
200 union smb_rename *io = talloc_get_type(_io, union smb_rename);
201 NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
202
203 talloc_free(r);
204
205 switch (reason) {
206 case PVFS_WAIT_CANCEL:
207/*TODO*/
208 status = NT_STATUS_CANCELLED;
209 break;
210 case PVFS_WAIT_TIMEOUT:
211 /* if it timed out, then give the failure
212 immediately */
213/*TODO*/
214 status = NT_STATUS_SHARING_VIOLATION;
215 break;
216 case PVFS_WAIT_EVENT:
217
218 /* try the open again, which could trigger another retry setup
219 if it wants to, so we have to unmark the async flag so we
220 will know if it does a second async reply */
221 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
222
223 status = pvfs_rename(ntvfs, req, io);
224 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
225 /* the 2nd try also replied async, so we don't send
226 the reply yet */
227 return;
228 }
229
230 /* re-mark it async, just in case someone up the chain does
231 paranoid checking */
232 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
233 break;
234 }
235
236 /* send the reply up the chain */
237 req->async_states->status = status;
238 req->async_states->send_fn(req);
239}
240
241/*
242 setup for a rename retry after a sharing violation
243 or a non granted oplock
244*/
245static NTSTATUS pvfs_rename_setup_retry(struct ntvfs_module_context *ntvfs,
246 struct ntvfs_request *req,
247 union smb_rename *io,
248 struct odb_lock *lck,
249 NTSTATUS status)
250{
251 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
252 struct pvfs_state);
253 struct timeval end_time;
254
255 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
256 end_time = timeval_add(&req->statistics.request_time,
257 0, pvfs->sharing_violation_delay);
258 } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
259 end_time = timeval_add(&req->statistics.request_time,
260 pvfs->oplock_break_timeout, 0);
261 } else {
262 return NT_STATUS_INTERNAL_ERROR;
263 }
264
265 return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
266 pvfs_retry_rename);
267}
268
269/*
270 rename one file from a wildcard set
271*/
272static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs,
273 struct ntvfs_request *req,
274 const char *dir_path,
275 const char *fname1,
276 const char *fname2,
277 uint16_t attrib)
278{
279 struct pvfs_filename *name1, *name2;
280 TALLOC_CTX *mem_ctx = talloc_new(req);
281 struct odb_lock *lck = NULL;
282 NTSTATUS status;
283
284 /* resolve the wildcard pattern for this name */
285 fname2 = pvfs_resolve_wildcard(mem_ctx, fname1, fname2);
286 if (fname2 == NULL) {
287 return NT_STATUS_NO_MEMORY;
288 }
289
290 /* get a pvfs_filename source object */
291 status = pvfs_resolve_partial(pvfs, mem_ctx,
292 dir_path, fname1,
293 PVFS_RESOLVE_NO_OPENDB,
294 &name1);
295 if (!NT_STATUS_IS_OK(status)) {
296 goto failed;
297 }
298
299 /* make sure its matches the given attributes */
300 status = pvfs_match_attrib(pvfs, name1, attrib, 0);
301 if (!NT_STATUS_IS_OK(status)) {
302 goto failed;
303 }
304
305 status = pvfs_can_rename(pvfs, req, name1, &lck);
306 if (!NT_STATUS_IS_OK(status)) {
307 talloc_free(lck);
308 goto failed;
309 }
310
311 /* get a pvfs_filename dest object */
312 status = pvfs_resolve_partial(pvfs, mem_ctx,
313 dir_path, fname2,
314 PVFS_RESOLVE_NO_OPENDB,
315 &name2);
316 if (NT_STATUS_IS_OK(status)) {
317 status = pvfs_can_delete(pvfs, req, name2, NULL);
318 if (!NT_STATUS_IS_OK(status)) {
319 goto failed;
320 }
321 }
322
323 status = NT_STATUS_OK;
324
325 fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2);
326 if (fname2 == NULL) {
327 return NT_STATUS_NO_MEMORY;
328 }
329
330 status = pvfs_do_rename(pvfs, lck, name1, fname2);
331
332failed:
333 talloc_free(mem_ctx);
334 return status;
335}
336
337
338/*
339 rename a set of files with wildcards
340*/
341static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs,
342 struct ntvfs_request *req,
343 union smb_rename *ren,
344 struct pvfs_filename *name1,
345 struct pvfs_filename *name2)
346{
347 struct pvfs_dir *dir;
348 NTSTATUS status;
349 off_t ofs = 0;
350 const char *fname, *fname2, *dir_path;
351 uint16_t attrib = ren->rename.in.attrib;
352 int total_renamed = 0;
353
354 /* get list of matching files */
355 status = pvfs_list_start(pvfs, name1, req, &dir);
356 if (!NT_STATUS_IS_OK(status)) {
357 return status;
358 }
359
360 status = NT_STATUS_NO_SUCH_FILE;
361
362 dir_path = pvfs_list_unix_path(dir);
363
364 /* only allow wildcard renames within a directory */
365 if (strncmp(dir_path, name2->full_name, strlen(dir_path)) != 0 ||
366 name2->full_name[strlen(dir_path)] != '/' ||
367 strchr(name2->full_name + strlen(dir_path) + 1, '/')) {
368 DEBUG(3,(__location__ ": Invalid rename for %s -> %s\n",
369 name1->original_name, name2->original_name));
370 return NT_STATUS_INVALID_PARAMETER;
371 }
372
373 fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1);
374 if (fname2 == NULL) {
375 return NT_STATUS_NO_MEMORY;
376 }
377
378 while ((fname = pvfs_list_next(dir, &ofs))) {
379 status = pvfs_rename_one(pvfs, req,
380 dir_path,
381 fname, fname2, attrib);
382 if (NT_STATUS_IS_OK(status)) {
383 total_renamed++;
384 }
385 }
386
387 if (total_renamed == 0) {
388 return status;
389 }
390
391 return NT_STATUS_OK;
392}
393
394/*
395 rename a set of files - SMBmv interface
396*/
397static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs,
398 struct ntvfs_request *req, union smb_rename *ren)
399{
400 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
401 struct pvfs_state);
402 NTSTATUS status;
403 struct pvfs_filename *name1, *name2;
404 struct odb_lock *lck = NULL;
405
406 /* resolve the cifs name to a posix name */
407 status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern1,
408 PVFS_RESOLVE_WILDCARD, &name1);
409 if (!NT_STATUS_IS_OK(status)) {
410 return status;
411 }
412
413 status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2,
414 PVFS_RESOLVE_WILDCARD, &name2);
415 if (!NT_STATUS_IS_OK(status)) {
416 return status;
417 }
418
419 if (name1->has_wildcard || name2->has_wildcard) {
420 return pvfs_rename_wildcard(pvfs, req, ren, name1, name2);
421 }
422
423 if (!name1->exists) {
424 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
425 }
426
427 if (strcmp(name1->full_name, name2->full_name) == 0) {
428 return NT_STATUS_OK;
429 }
430
431 if (name2->exists) {
432 return NT_STATUS_OBJECT_NAME_COLLISION;
433 }
434
435 status = pvfs_match_attrib(pvfs, name1, ren->rename.in.attrib, 0);
436 if (!NT_STATUS_IS_OK(status)) {
437 return status;
438 }
439
440 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
441 if (!NT_STATUS_IS_OK(status)) {
442 return status;
443 }
444
445 status = pvfs_can_rename(pvfs, req, name1, &lck);
446 /*
447 * on a sharing violation we need to retry when the file is closed by
448 * the other user, or after 1 second
449 * on a non granted oplock we need to retry when the file is closed by
450 * the other user, or after 30 seconds
451 */
452 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
453 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
454 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
455 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
456 }
457
458 if (!NT_STATUS_IS_OK(status)) {
459 return status;
460 }
461
462 status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
463 if (!NT_STATUS_IS_OK(status)) {
464 return status;
465 }
466
467 return NT_STATUS_OK;
468}
469
470
471/*
472 rename a stream
473*/
474static NTSTATUS pvfs_rename_stream(struct ntvfs_module_context *ntvfs,
475 struct ntvfs_request *req, union smb_rename *ren,
476 struct pvfs_filename *name1)
477{
478 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
479 struct pvfs_state);
480 NTSTATUS status;
481 struct odb_lock *lck = NULL;
482
483 if (name1->has_wildcard) {
484 DEBUG(3,(__location__ ": Invalid wildcard rename for %s\n",
485 name1->original_name));
486 return NT_STATUS_INVALID_PARAMETER;
487 }
488
489 if (ren->ntrename.in.new_name[0] != ':') {
490 DEBUG(3,(__location__ ": Invalid rename for %s\n",
491 ren->ntrename.in.new_name));
492 return NT_STATUS_INVALID_PARAMETER;
493 }
494
495 if (!name1->exists) {
496 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
497 }
498
499 if (ren->ntrename.in.flags != RENAME_FLAG_RENAME) {
500 DEBUG(3,(__location__ ": Invalid rename flags 0x%x for %s\n",
501 ren->ntrename.in.flags, ren->ntrename.in.new_name));
502 return NT_STATUS_INVALID_PARAMETER;
503 }
504
505 status = pvfs_can_rename(pvfs, req, name1, &lck);
506 /*
507 * on a sharing violation we need to retry when the file is closed by
508 * the other user, or after 1 second
509 * on a non granted oplock we need to retry when the file is closed by
510 * the other user, or after 30 seconds
511 */
512 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
513 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
514 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
515 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
516 }
517 if (!NT_STATUS_IS_OK(status)) {
518 return status;
519 }
520
521 status = pvfs_access_check_simple(pvfs, req, name1, SEC_FILE_WRITE_ATTRIBUTE);
522 NT_STATUS_NOT_OK_RETURN(status);
523
524 status = pvfs_stream_rename(pvfs, name1, -1,
525 ren->ntrename.in.new_name+1,
526 true);
527 NT_STATUS_NOT_OK_RETURN(status);
528
529 return NT_STATUS_OK;
530}
531
532/*
533 rename a set of files - ntrename interface
534*/
535static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
536 struct ntvfs_request *req, union smb_rename *ren)
537{
538 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
539 struct pvfs_state);
540 NTSTATUS status;
541 struct pvfs_filename *name1, *name2;
542 struct odb_lock *lck = NULL;
543
544 switch (ren->ntrename.in.flags) {
545 case RENAME_FLAG_RENAME:
546 case RENAME_FLAG_HARD_LINK:
547 case RENAME_FLAG_COPY:
548 case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
549 break;
550 default:
551 return NT_STATUS_ACCESS_DENIED;
552 }
553
554 /* resolve the cifs name to a posix name */
555 status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name,
556 PVFS_RESOLVE_WILDCARD | PVFS_RESOLVE_STREAMS, &name1);
557 if (!NT_STATUS_IS_OK(status)) {
558 return status;
559 }
560
561 if (name1->stream_name) {
562 /* stream renames need to be handled separately */
563 return pvfs_rename_stream(ntvfs, req, ren, name1);
564 }
565
566 status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name,
567 PVFS_RESOLVE_WILDCARD, &name2);
568 if (!NT_STATUS_IS_OK(status)) {
569 return status;
570 }
571
572 if (name1->has_wildcard || name2->has_wildcard) {
573 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
574 }
575
576 if (!name1->exists) {
577 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
578 }
579
580 if (strcmp(name1->full_name, name2->full_name) == 0) {
581 return NT_STATUS_OK;
582 }
583
584 if (name2->exists) {
585 return NT_STATUS_OBJECT_NAME_COLLISION;
586 }
587
588 status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
589 if (!NT_STATUS_IS_OK(status)) {
590 return status;
591 }
592
593 status = pvfs_can_rename(pvfs, req, name1, &lck);
594 /*
595 * on a sharing violation we need to retry when the file is closed by
596 * the other user, or after 1 second
597 * on a non granted oplock we need to retry when the file is closed by
598 * the other user, or after 30 seconds
599 */
600 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
601 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
602 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
603 return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
604 }
605 if (!NT_STATUS_IS_OK(status)) {
606 return status;
607 }
608
609 switch (ren->ntrename.in.flags) {
610 case RENAME_FLAG_RENAME:
611 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
612 NT_STATUS_NOT_OK_RETURN(status);
613 status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
614 NT_STATUS_NOT_OK_RETURN(status);
615 break;
616
617 case RENAME_FLAG_HARD_LINK:
618 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
619 NT_STATUS_NOT_OK_RETURN(status);
620 if (link(name1->full_name, name2->full_name) == -1) {
621 return pvfs_map_errno(pvfs, errno);
622 }
623 break;
624
625 case RENAME_FLAG_COPY:
626 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
627 NT_STATUS_NOT_OK_RETURN(status);
628 return pvfs_copy_file(pvfs, name1, name2, name1->allow_override && name2->allow_override);
629
630 case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
631 DEBUG(3,(__location__ ": Invalid rename cluster for %s\n",
632 name1->original_name));
633 return NT_STATUS_INVALID_PARAMETER;
634
635 default:
636 return NT_STATUS_ACCESS_DENIED;
637 }
638
639
640 return NT_STATUS_OK;
641}
642
643/*
644 rename a set of files - ntrename interface
645*/
646NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
647 struct ntvfs_request *req, union smb_rename *ren)
648{
649 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
650 struct pvfs_state);
651 struct pvfs_file *f;
652
653 switch (ren->generic.level) {
654 case RAW_RENAME_RENAME:
655 return pvfs_rename_mv(ntvfs, req, ren);
656
657 case RAW_RENAME_NTRENAME:
658 return pvfs_rename_nt(ntvfs, req, ren);
659
660 case RAW_RENAME_NTTRANS:
661 f = pvfs_find_fd(pvfs, req, ren->nttrans.in.file.ntvfs);
662 if (!f) {
663 return NT_STATUS_INVALID_HANDLE;
664 }
665
666 /* wk23 ignores the request */
667 return NT_STATUS_OK;
668
669 default:
670 break;
671 }
672
673 return NT_STATUS_INVALID_LEVEL;
674}
675
Note: See TracBrowser for help on using the repository browser.