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"
28 do a file rename, and send any notify triggers
30 NTSTATUS pvfs_do_rename(struct pvfs_state *pvfs, const struct pvfs_filename *name1,
36 if (rename(name1->full_name, name2) == -1) {
37 return pvfs_map_errno(pvfs, errno);
40 if (name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
41 mask = FILE_NOTIFY_CHANGE_DIR_NAME;
43 mask = FILE_NOTIFY_CHANGE_FILE_NAME;
46 renames to the same directory cause a OLD_NAME->NEW_NAME notify.
47 renames to a different directory are considered a remove/add
49 r1 = strrchr_m(name1->full_name, '/');
50 r2 = strrchr_m(name2, '/');
52 if ((r1-name1->full_name) != (r2-name2) ||
53 strncmp(name1->full_name, name2, r1-name1->full_name) != 0) {
54 notify_trigger(pvfs->notify_context,
55 NOTIFY_ACTION_REMOVED,
58 notify_trigger(pvfs->notify_context,
63 notify_trigger(pvfs->notify_context,
64 NOTIFY_ACTION_OLD_NAME,
67 notify_trigger(pvfs->notify_context,
68 NOTIFY_ACTION_NEW_NAME,
73 /* this is a strange one. w2k3 gives an additional event for CHANGE_ATTRIBUTES
74 and CHANGE_CREATION on the new file when renaming files, but not
76 if ((name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) == 0) {
77 notify_trigger(pvfs->notify_context,
78 NOTIFY_ACTION_MODIFIED,
79 FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_CREATION,
88 resolve a wildcard rename pattern. This works on one component of the name
90 static const char *pvfs_resolve_wildcard_component(TALLOC_CTX *mem_ctx,
97 /* the length is bounded by the length of the two strings combined */
98 dest = talloc_size(mem_ctx, strlen(fname) + strlen(pattern) + 1);
109 size_t c_size1, c_size2;
110 c1 = next_codepoint(p1, &c_size1);
111 c2 = next_codepoint(p2, &c_size2);
113 d += push_codepoint(d, c1);
114 } else if (c2 == '*') {
115 memcpy(d, p1, strlen(p1));
119 d += push_codepoint(d, c2);
132 resolve a wildcard rename pattern.
134 static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx,
138 const char *base1, *base2;
139 const char *ext1, *ext2;
142 /* break into base part plus extension */
143 p = strrchr_m(fname, '.');
148 ext1 = talloc_strdup(mem_ctx, p+1);
149 base1 = talloc_strndup(mem_ctx, fname, p-fname);
151 if (ext1 == NULL || base1 == NULL) {
155 p = strrchr_m(pattern, '.');
160 ext2 = talloc_strdup(mem_ctx, p+1);
161 base2 = talloc_strndup(mem_ctx, pattern, p-pattern);
163 if (ext2 == NULL || base2 == NULL) {
167 base1 = pvfs_resolve_wildcard_component(mem_ctx, base1, base2);
168 ext1 = pvfs_resolve_wildcard_component(mem_ctx, ext1, ext2);
169 if (base1 == NULL || ext1 == NULL) {
177 return talloc_asprintf(mem_ctx, "%s.%s", base1, ext1);
181 rename one file from a wildcard set
183 static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs,
184 struct ntvfs_request *req,
185 const char *dir_path,
190 struct pvfs_filename *name1, *name2;
191 TALLOC_CTX *mem_ctx = talloc_new(req);
193 struct odb_lock *lck, *lck2;
195 /* resolve the wildcard pattern for this name */
196 fname2 = pvfs_resolve_wildcard(mem_ctx, fname1, fname2);
197 if (fname2 == NULL) {
198 return NT_STATUS_NO_MEMORY;
201 /* get a pvfs_filename source object */
202 status = pvfs_resolve_partial(pvfs, mem_ctx,
203 dir_path, fname1, &name1);
204 if (!NT_STATUS_IS_OK(status)) {
208 /* make sure its matches the given attributes */
209 status = pvfs_match_attrib(pvfs, name1, attrib, 0);
210 if (!NT_STATUS_IS_OK(status)) {
214 status = pvfs_can_rename(pvfs, req, name1, &lck);
215 if (!NT_STATUS_IS_OK(status)) {
219 /* get a pvfs_filename dest object */
220 status = pvfs_resolve_partial(pvfs, mem_ctx,
221 dir_path, fname2, &name2);
222 if (NT_STATUS_IS_OK(status)) {
223 status = pvfs_can_delete(pvfs, req, name2, &lck2);
224 if (!NT_STATUS_IS_OK(status)) {
229 status = NT_STATUS_OK;
231 fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2);
232 if (fname2 == NULL) {
233 return NT_STATUS_NO_MEMORY;
236 status = pvfs_do_rename(pvfs, name1, fname2);
238 if (NT_STATUS_IS_OK(status)) {
239 status = odb_rename(lck, fname2);
243 talloc_free(mem_ctx);
249 rename a set of files with wildcards
251 static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs,
252 struct ntvfs_request *req,
253 union smb_rename *ren,
254 struct pvfs_filename *name1,
255 struct pvfs_filename *name2)
257 struct pvfs_dir *dir;
260 const char *fname, *fname2, *dir_path;
261 uint16_t attrib = ren->rename.in.attrib;
262 int total_renamed = 0;
264 /* get list of matching files */
265 status = pvfs_list_start(pvfs, name1, req, &dir);
266 if (!NT_STATUS_IS_OK(status)) {
270 status = NT_STATUS_NO_SUCH_FILE;
272 dir_path = pvfs_list_unix_path(dir);
274 /* only allow wildcard renames within a directory */
275 if (strncmp(dir_path, name2->full_name, strlen(dir_path)) != 0 ||
276 name2->full_name[strlen(dir_path)] != '/' ||
277 strchr(name2->full_name + strlen(dir_path) + 1, '/')) {
278 return NT_STATUS_INVALID_PARAMETER;
281 fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1);
282 if (fname2 == NULL) {
283 return NT_STATUS_NO_MEMORY;
286 while ((fname = pvfs_list_next(dir, &ofs))) {
287 status = pvfs_rename_one(pvfs, req,
289 fname, fname2, attrib);
290 if (NT_STATUS_IS_OK(status)) {
295 if (total_renamed == 0) {
303 rename a set of files - SMBmv interface
305 static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs,
306 struct ntvfs_request *req, union smb_rename *ren)
308 struct pvfs_state *pvfs = ntvfs->private_data;
310 struct pvfs_filename *name1, *name2;
311 struct odb_lock *lck;
313 /* resolve the cifs name to a posix name */
314 status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern1,
315 PVFS_RESOLVE_WILDCARD, &name1);
316 if (!NT_STATUS_IS_OK(status)) {
320 status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2,
321 PVFS_RESOLVE_WILDCARD, &name2);
322 if (!NT_STATUS_IS_OK(status)) {
326 if (name1->has_wildcard || name2->has_wildcard) {
327 return pvfs_rename_wildcard(pvfs, req, ren, name1, name2);
330 if (!name1->exists) {
331 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
334 if (strcmp(name1->full_name, name2->full_name) == 0) {
339 return NT_STATUS_OBJECT_NAME_COLLISION;
342 status = pvfs_match_attrib(pvfs, name1, ren->rename.in.attrib, 0);
343 if (!NT_STATUS_IS_OK(status)) {
347 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
348 if (!NT_STATUS_IS_OK(status)) {
352 status = pvfs_can_rename(pvfs, req, name1, &lck);
353 if (!NT_STATUS_IS_OK(status)) {
357 status = pvfs_do_rename(pvfs, name1, name2->full_name);
358 if (NT_STATUS_IS_OK(status)) {
359 status = odb_rename(lck, name2->full_name);
367 rename a set of files - ntrename interface
369 static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
370 struct ntvfs_request *req, union smb_rename *ren)
372 struct pvfs_state *pvfs = ntvfs->private_data;
374 struct pvfs_filename *name1, *name2;
375 struct odb_lock *lck;
377 switch (ren->ntrename.in.flags) {
378 case RENAME_FLAG_RENAME:
379 case RENAME_FLAG_HARD_LINK:
380 case RENAME_FLAG_COPY:
381 case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
384 return NT_STATUS_ACCESS_DENIED;
387 /* resolve the cifs name to a posix name */
388 status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name,
389 PVFS_RESOLVE_WILDCARD, &name1);
390 if (!NT_STATUS_IS_OK(status)) {
394 status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name,
395 PVFS_RESOLVE_WILDCARD, &name2);
396 if (!NT_STATUS_IS_OK(status)) {
400 if (name1->has_wildcard || name2->has_wildcard) {
401 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
404 if (!name1->exists) {
405 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
408 if (strcmp(name1->full_name, name2->full_name) == 0) {
413 return NT_STATUS_OBJECT_NAME_COLLISION;
416 status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
417 if (!NT_STATUS_IS_OK(status)) {
421 status = pvfs_can_rename(pvfs, req, name1, &lck);
422 if (!NT_STATUS_IS_OK(status)) {
426 switch (ren->ntrename.in.flags) {
427 case RENAME_FLAG_RENAME:
428 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
429 NT_STATUS_NOT_OK_RETURN(status);
430 status = pvfs_do_rename(pvfs, name1, name2->full_name);
431 NT_STATUS_NOT_OK_RETURN(status);
434 case RENAME_FLAG_HARD_LINK:
435 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
436 NT_STATUS_NOT_OK_RETURN(status);
437 if (link(name1->full_name, name2->full_name) == -1) {
438 return pvfs_map_errno(pvfs, errno);
442 case RENAME_FLAG_COPY:
443 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
444 NT_STATUS_NOT_OK_RETURN(status);
445 return pvfs_copy_file(pvfs, name1, name2);
447 case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
448 return NT_STATUS_INVALID_PARAMETER;
451 return NT_STATUS_ACCESS_DENIED;
459 rename a set of files - ntrename interface
461 NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
462 struct ntvfs_request *req, union smb_rename *ren)
464 switch (ren->generic.level) {
465 case RAW_RENAME_RENAME:
466 return pvfs_rename_mv(ntvfs, req, ren);
468 case RAW_RENAME_NTRENAME:
469 return pvfs_rename_nt(ntvfs, req, ren);
475 return NT_STATUS_INVALID_LEVEL;