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,
33
const struct pvfs_filename *name1,
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;
50
mask = FILE_NOTIFY_CHANGE_FILE_NAME;
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,
65
notify_trigger(pvfs->notify_context,
70
notify_trigger(pvfs->notify_context,
71
NOTIFY_ACTION_OLD_NAME,
74
notify_trigger(pvfs->notify_context,
75
NOTIFY_ACTION_NEW_NAME,
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
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,
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,
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);
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);
121
d += push_codepoint(iconv_convenience, d, c1);
122
} else if (c2 == '*') {
123
memcpy(d, p1, strlen(p1));
127
d += push_codepoint(iconv_convenience, d, c2);
136
talloc_set_name_const(dest, 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,
149
const char *base1, *base2;
150
const char *ext1, *ext2;
153
/* break into base part plus extension */
154
p = strrchr_m(fname, '.');
159
ext1 = talloc_strdup(mem_ctx, p+1);
160
base1 = talloc_strndup(mem_ctx, fname, p-fname);
162
if (ext1 == NULL || base1 == NULL) {
166
p = strrchr_m(pattern, '.');
171
ext2 = talloc_strdup(mem_ctx, p+1);
172
base2 = talloc_strndup(mem_ctx, pattern, p-pattern);
174
if (ext2 == NULL || base2 == 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) {
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,
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;
207
case PVFS_WAIT_CANCEL:
209
status = NT_STATUS_CANCELLED;
211
case PVFS_WAIT_TIMEOUT:
212
/* if it timed out, then give the failure
215
status = NT_STATUS_SHARING_VIOLATION;
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
231
/* re-mark it async, just in case someone up the chain does
233
req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
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,
252
struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
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);
263
return NT_STATUS_INTERNAL_ERROR;
266
return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
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,
280
struct pvfs_filename *name1, *name2;
281
TALLOC_CTX *mem_ctx = talloc_new(req);
282
struct odb_lock *lck = NULL;
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,
294
PVFS_RESOLVE_NO_OPENDB,
296
if (!NT_STATUS_IS_OK(status)) {
300
/* make sure its matches the given attributes */
301
status = pvfs_match_attrib(pvfs, name1, attrib, 0);
302
if (!NT_STATUS_IS_OK(status)) {
306
status = pvfs_can_rename(pvfs, req, name1, &lck);
307
if (!NT_STATUS_IS_OK(status)) {
312
/* get a pvfs_filename dest object */
313
status = pvfs_resolve_partial(pvfs, mem_ctx,
315
PVFS_RESOLVE_NO_OPENDB,
317
if (NT_STATUS_IS_OK(status)) {
318
status = pvfs_can_delete(pvfs, req, name2, NULL);
319
if (!NT_STATUS_IS_OK(status)) {
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);
334
talloc_free(mem_ctx);
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;
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)) {
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,
380
fname, fname2, attrib);
381
if (NT_STATUS_IS_OK(status)) {
386
if (total_renamed == 0) {
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,
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)) {
412
status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2,
413
PVFS_RESOLVE_WILDCARD, &name2);
414
if (!NT_STATUS_IS_OK(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) {
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)) {
439
status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
440
if (!NT_STATUS_IS_OK(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)) {
461
status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
462
if (!NT_STATUS_IS_OK(status)) {
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,
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)) {
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
NT_STATUS_NOT_OK_RETURN(status);
525
rename a set of files - ntrename interface
527
static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
528
struct ntvfs_request *req, union smb_rename *ren)
530
struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
533
struct pvfs_filename *name1, *name2;
534
struct odb_lock *lck = NULL;
536
switch (ren->ntrename.in.flags) {
537
case RENAME_FLAG_RENAME:
538
case RENAME_FLAG_HARD_LINK:
539
case RENAME_FLAG_COPY:
540
case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
543
return NT_STATUS_ACCESS_DENIED;
546
/* resolve the cifs name to a posix name */
547
status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name,
548
PVFS_RESOLVE_WILDCARD | PVFS_RESOLVE_STREAMS, &name1);
549
if (!NT_STATUS_IS_OK(status)) {
553
if (name1->stream_name) {
554
/* stream renames need to be handled separately */
555
return pvfs_rename_stream(ntvfs, req, ren, name1);
558
status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name,
559
PVFS_RESOLVE_WILDCARD, &name2);
560
if (!NT_STATUS_IS_OK(status)) {
564
if (name1->has_wildcard || name2->has_wildcard) {
565
return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
568
if (!name1->exists) {
569
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
572
if (strcmp(name1->full_name, name2->full_name) == 0) {
577
return NT_STATUS_OBJECT_NAME_COLLISION;
580
status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
581
if (!NT_STATUS_IS_OK(status)) {
585
status = pvfs_can_rename(pvfs, req, name1, &lck);
587
* on a sharing violation we need to retry when the file is closed by
588
* the other user, or after 1 second
589
* on a non granted oplock we need to retry when the file is closed by
590
* the other user, or after 30 seconds
592
if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
593
NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
594
(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
595
return pvfs_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
597
if (!NT_STATUS_IS_OK(status)) {
601
switch (ren->ntrename.in.flags) {
602
case RENAME_FLAG_RENAME:
603
status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
604
NT_STATUS_NOT_OK_RETURN(status);
605
status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
606
NT_STATUS_NOT_OK_RETURN(status);
609
case RENAME_FLAG_HARD_LINK:
610
status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
611
NT_STATUS_NOT_OK_RETURN(status);
612
if (link(name1->full_name, name2->full_name) == -1) {
613
return pvfs_map_errno(pvfs, errno);
617
case RENAME_FLAG_COPY:
618
status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
619
NT_STATUS_NOT_OK_RETURN(status);
620
return pvfs_copy_file(pvfs, name1, name2);
622
case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
623
return NT_STATUS_INVALID_PARAMETER;
626
return NT_STATUS_ACCESS_DENIED;
634
rename a set of files - ntrename interface
636
NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
637
struct ntvfs_request *req, union smb_rename *ren)
639
struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
643
switch (ren->generic.level) {
644
case RAW_RENAME_RENAME:
645
return pvfs_rename_mv(ntvfs, req, ren);
647
case RAW_RENAME_NTRENAME:
648
return pvfs_rename_nt(ntvfs, req, ren);
650
case RAW_RENAME_NTTRANS:
651
f = pvfs_find_fd(pvfs, req, ren->nttrans.in.file.ntvfs);
653
return NT_STATUS_INVALID_HANDLE;
656
/* wk23 ignores the request */
663
return NT_STATUS_INVALID_LEVEL;