From 1c59d825afc3cdfe2ea42821123ece934ad7b5e0 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sat, 13 Nov 2004 05:47:27 +0000 Subject: r3717: - expanded the RAW-RENAME test a little - added support for wildcard rename in pvfs - made more consistent use of pvfs_map_errno() (This used to be commit e255d1c3a811c480a850452aaf636d9fa36f69fe) --- source4/ntvfs/posix/pvfs_open.c | 2 +- source4/ntvfs/posix/pvfs_rename.c | 225 ++++++++++++++++++++++++++++++++- source4/ntvfs/posix/pvfs_setfileinfo.c | 2 +- source4/ntvfs/posix/pvfs_write.c | 2 +- source4/ntvfs/posix/pvfs_xattr.c | 33 +++-- source4/torture/raw/rename.c | 49 ++++++- 6 files changed, 291 insertions(+), 22 deletions(-) diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c index cc4498eee1..af0f46ece3 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -937,7 +937,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, mode_t mode = pvfs_fileperms(pvfs, attrib); if (fchmod(fd, mode) == -1) { talloc_free(lck); - return map_nt_error_from_unix(errno); + return pvfs_map_errno(pvfs, errno); } name->dos.attrib = attrib; status = pvfs_dosattrib_save(pvfs, name, fd); diff --git a/source4/ntvfs/posix/pvfs_rename.c b/source4/ntvfs/posix/pvfs_rename.c index a621165ce4..d36af2b91d 100644 --- a/source4/ntvfs/posix/pvfs_rename.c +++ b/source4/ntvfs/posix/pvfs_rename.c @@ -23,6 +23,223 @@ #include "includes.h" #include "vfs_posix.h" + +/* + resolve a wildcard rename pattern. This works on one component of the name +*/ +static const char *pvfs_resolve_wildcard_component(TALLOC_CTX *mem_ctx, + const char *fname, + const char *pattern) +{ + const char *p1, *p2; + char *dest, *d; + + /* the length is bounded by the length of the two strings combined */ + dest = talloc(mem_ctx, strlen(fname) + strlen(pattern) + 1); + if (dest == NULL) { + return NULL; + } + + p1 = fname; + p2 = pattern; + d = dest; + + while (*p2) { + codepoint_t c1, c2; + size_t c_size1, c_size2; + c1 = next_codepoint(p1, &c_size1); + c2 = next_codepoint(p2, &c_size2); + if (c2 == '?') { + d += push_codepoint(d, c1); + } else if (c2 == '*') { + memcpy(d, p1, strlen(p1)); + d += strlen(p1); + break; + } else { + d += push_codepoint(d, c2); + } + + p1 += c_size1; + p2 += c_size2; + } + + *d = 0; + + return dest; +} + +/* + resolve a wildcard rename pattern. +*/ +static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx, + const char *fname, + const char *pattern) +{ + const char *base1, *base2; + const char *ext1, *ext2; + char *p; + + /* break into base part plus extension */ + p = strrchr_m(fname, '.'); + if (p == NULL) { + ext1 = ""; + base1 = fname; + } else { + ext1 = talloc_strdup(mem_ctx, p+1); + base1 = talloc_strndup(mem_ctx, fname, p-fname); + } + if (ext1 == NULL || base1 == NULL) { + return NULL; + } + + p = strrchr_m(pattern, '.'); + if (p == NULL) { + ext2 = ""; + base2 = fname; + } else { + ext2 = talloc_strdup(mem_ctx, p+1); + base2 = talloc_strndup(mem_ctx, pattern, p-pattern); + } + if (ext2 == NULL || base2 == NULL) { + return NULL; + } + + base1 = pvfs_resolve_wildcard_component(mem_ctx, base1, base2); + ext1 = pvfs_resolve_wildcard_component(mem_ctx, ext1, ext2); + if (base1 == NULL || ext1 == NULL) { + return NULL; + } + + if (*ext1 == 0) { + return base1; + } + + return talloc_asprintf(mem_ctx, "%s.%s", base1, ext1); +} + +/* + rename one file from a wildcard set +*/ +static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs, + struct smbsrv_request *req, + const char *dir_path, + const char *fname1, + const char *fname2, + uint16_t attrib) +{ + struct pvfs_filename *name1, *name2; + TALLOC_CTX *mem_ctx = talloc(req, 0); + NTSTATUS status; + + /* resolve the wildcard pattern for this name */ + fname2 = pvfs_resolve_wildcard(mem_ctx, fname1, fname2); + if (fname2 == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* get a pvfs_filename source object */ + status = pvfs_resolve_partial(pvfs, mem_ctx, + dir_path, fname1, &name1); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return status; + } + + /* make sure its matches the given attributes */ + status = pvfs_match_attrib(pvfs, name1, attrib, 0); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return status; + } + + status = pvfs_can_rename(pvfs, name1); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return status; + } + + /* get a pvfs_filename dest object */ + status = pvfs_resolve_partial(pvfs, mem_ctx, + dir_path, fname2, &name2); + if (NT_STATUS_IS_OK(status)) { + status = pvfs_can_delete(pvfs, name2); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return status; + } + } + + fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2); + if (fname2 == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* finally try the actual rename */ + if (rename(name1->full_name, fname2) == -1) { + talloc_free(mem_ctx); + return pvfs_map_errno(pvfs, errno); + } + + talloc_free(mem_ctx); + + return NT_STATUS_OK; +} + + +/* + rename a set of files with wildcards +*/ +static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs, + struct smbsrv_request *req, + union smb_rename *ren, + struct pvfs_filename *name1, + struct pvfs_filename *name2) +{ + struct pvfs_dir *dir; + NTSTATUS status; + uint_t ofs = 0; + const char *fname, *fname2, *dir_path; + uint16_t attrib = ren->rename.in.attrib; + int total_renamed = 0; + + /* get list of matching files */ + status = pvfs_list_start(pvfs, name1, req, &dir); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = NT_STATUS_NO_SUCH_FILE; + + dir_path = pvfs_list_unix_path(dir); + + /* only allow wildcard renames within a directory */ + if (strncmp(dir_path, name2->full_name, strlen(dir_path)) != 0 || + name2->full_name[strlen(dir_path)] != '/' || + strchr(name2->full_name + strlen(dir_path) + 1, '/')) { + return NT_STATUS_INVALID_PARAMETER; + } + + fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1); + if (fname2 == NULL) { + return NT_STATUS_NO_MEMORY; + } + + while ((fname = pvfs_list_next(dir, &ofs))) { + status = pvfs_rename_one(pvfs, req, + dir_path, + fname, fname2, attrib); + if (NT_STATUS_IS_OK(status)) { + total_renamed++; + } + } + + if (total_renamed == 0) { + return status; + } + + return NT_STATUS_OK; +} + /* rename a set of files */ @@ -49,15 +266,17 @@ NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs, } if (name1->has_wildcard || name2->has_wildcard) { - DEBUG(3,("Rejecting wildcard rename '%s' -> '%s'\n", - ren->rename.in.pattern1, ren->rename.in.pattern2)); - return NT_STATUS_NOT_SUPPORTED; + return pvfs_rename_wildcard(pvfs, req, ren, name1, name2); } if (!name1->exists) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } + if (strcmp(name1->full_name, name2->full_name) == 0) { + return NT_STATUS_OK; + } + if (name2->exists) { return NT_STATUS_OBJECT_NAME_COLLISION; } diff --git a/source4/ntvfs/posix/pvfs_setfileinfo.c b/source4/ntvfs/posix/pvfs_setfileinfo.c index 391a2e9d82..e54c37741c 100644 --- a/source4/ntvfs/posix/pvfs_setfileinfo.c +++ b/source4/ntvfs/posix/pvfs_setfileinfo.c @@ -96,7 +96,7 @@ static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs, } if (rename(name->full_name, name2->full_name) == -1) { - return map_nt_error_from_unix(errno); + return pvfs_map_errno(pvfs, errno); } name->full_name = talloc_steal(name, name2->full_name); diff --git a/source4/ntvfs/posix/pvfs_write.c b/source4/ntvfs/posix/pvfs_write.c index 989e8d7b1b..c24de08a13 100644 --- a/source4/ntvfs/posix/pvfs_write.c +++ b/source4/ntvfs/posix/pvfs_write.c @@ -68,7 +68,7 @@ NTSTATUS pvfs_write(struct ntvfs_module_context *ntvfs, if (errno == EFBIG) { return NT_STATUS_INVALID_PARAMETER; } - return map_nt_error_from_unix(errno); + return pvfs_map_errno(pvfs, errno); } f->handle->seek_offset = wr->writex.in.offset + ret; diff --git a/source4/ntvfs/posix/pvfs_xattr.c b/source4/ntvfs/posix/pvfs_xattr.c index f4063b48b5..d5a7d17a31 100644 --- a/source4/ntvfs/posix/pvfs_xattr.c +++ b/source4/ntvfs/posix/pvfs_xattr.c @@ -28,7 +28,8 @@ /* pull a xattr as a blob, from either a file or a file descriptor */ -static NTSTATUS pull_xattr_blob(TALLOC_CTX *mem_ctx, +static NTSTATUS pull_xattr_blob(struct pvfs_state *pvfs, + TALLOC_CTX *mem_ctx, const char *attr_name, const char *fname, int fd, @@ -61,7 +62,7 @@ again: if (ret == -1) { data_blob_free(blob); - return map_nt_error_from_unix(errno); + return pvfs_map_errno(pvfs, errno); } blob->length = ret; @@ -75,7 +76,8 @@ again: /* push a xattr as a blob, from either a file or a file descriptor */ -static NTSTATUS push_xattr_blob(const char *attr_name, +static NTSTATUS push_xattr_blob(struct pvfs_state *pvfs, + const char *attr_name, const char *fname, int fd, const DATA_BLOB *blob) @@ -89,7 +91,7 @@ static NTSTATUS push_xattr_blob(const char *attr_name, ret = setxattr(fname, attr_name, blob->data, blob->length, 0); } if (ret == -1) { - return map_nt_error_from_unix(errno); + return pvfs_map_errno(pvfs, errno); } return NT_STATUS_OK; @@ -101,14 +103,15 @@ static NTSTATUS push_xattr_blob(const char *attr_name, /* load a NDR structure from a xattr */ -static NTSTATUS pvfs_xattr_ndr_load(TALLOC_CTX *mem_ctx, +static NTSTATUS pvfs_xattr_ndr_load(struct pvfs_state *pvfs, + TALLOC_CTX *mem_ctx, const char *fname, int fd, const char *attr_name, void *p, ndr_pull_flags_fn_t pull_fn) { NTSTATUS status; DATA_BLOB blob; - status = pull_xattr_blob(mem_ctx, attr_name, fname, + status = pull_xattr_blob(pvfs, mem_ctx, attr_name, fname, fd, XATTR_DOSATTRIB_ESTIMATED_SIZE, &blob); if (!NT_STATUS_IS_OK(status)) { return status; @@ -125,7 +128,8 @@ static NTSTATUS pvfs_xattr_ndr_load(TALLOC_CTX *mem_ctx, /* save a NDR structure into a xattr */ -static NTSTATUS pvfs_xattr_ndr_save(const char *fname, int fd, const char *attr_name, +static NTSTATUS pvfs_xattr_ndr_save(struct pvfs_state *pvfs, + const char *fname, int fd, const char *attr_name, void *p, ndr_push_flags_fn_t push_fn) { TALLOC_CTX *mem_ctx = talloc(NULL, 0); @@ -138,7 +142,7 @@ static NTSTATUS pvfs_xattr_ndr_save(const char *fname, int fd, const char *attr_ return status; } - status = push_xattr_blob(attr_name, fname, fd, &blob); + status = push_xattr_blob(pvfs, attr_name, fname, fd, &blob); talloc_free(mem_ctx); return status; @@ -159,8 +163,10 @@ NTSTATUS pvfs_dosattrib_load(struct pvfs_state *pvfs, struct pvfs_filename *name return NT_STATUS_OK; } - status = pvfs_xattr_ndr_load(mem_ctx, name->full_name, fd, XATTR_DOSATTRIB_NAME, - &attrib, (ndr_pull_flags_fn_t)ndr_pull_xattr_DosAttrib); + status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name, + fd, XATTR_DOSATTRIB_NAME, + &attrib, + (ndr_pull_flags_fn_t)ndr_pull_xattr_DosAttrib); /* if the filesystem doesn't support them, then tell pvfs not to try again */ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { @@ -233,7 +239,8 @@ NTSTATUS pvfs_dosattrib_save(struct pvfs_state *pvfs, struct pvfs_filename *name info1->create_time = name->dos.create_time; info1->change_time = name->dos.change_time; - return pvfs_xattr_ndr_save(name->full_name, fd, XATTR_DOSATTRIB_NAME, &attrib, + return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, + XATTR_DOSATTRIB_NAME, &attrib, (ndr_push_flags_fn_t)ndr_push_xattr_DosAttrib); } @@ -249,7 +256,7 @@ NTSTATUS pvfs_doseas_load(struct pvfs_state *pvfs, struct pvfs_filename *name, i if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) { return NT_STATUS_OK; } - status = pvfs_xattr_ndr_load(eas, name->full_name, fd, XATTR_DOSEAS_NAME, + status = pvfs_xattr_ndr_load(pvfs, eas, name->full_name, fd, XATTR_DOSEAS_NAME, eas, (ndr_pull_flags_fn_t)ndr_pull_xattr_DosEAs); if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { return NT_STATUS_OK; @@ -266,6 +273,6 @@ NTSTATUS pvfs_doseas_save(struct pvfs_state *pvfs, struct pvfs_filename *name, i if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) { return NT_STATUS_OK; } - return pvfs_xattr_ndr_save(name->full_name, fd, XATTR_DOSEAS_NAME, eas, + return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, XATTR_DOSEAS_NAME, eas, (ndr_push_flags_fn_t)ndr_push_xattr_DosEAs); } diff --git a/source4/torture/raw/rename.c b/source4/torture/raw/rename.c index 68bd2eda2d..c3fc739d6a 100644 --- a/source4/torture/raw/rename.c +++ b/source4/torture/raw/rename.c @@ -48,6 +48,7 @@ static BOOL test_mv(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) int fnum; const char *fname1 = BASEDIR "\\test1.txt"; const char *fname2 = BASEDIR "\\test2.txt"; + union smb_open op; printf("Testing SMBmv\n"); @@ -57,16 +58,58 @@ static BOOL test_mv(struct smbcli_state *cli, TALLOC_CTX *mem_ctx) printf("Trying simple rename\n"); - fnum = create_complex_file(cli, mem_ctx, fname1); - + op.generic.level = RAW_OPEN_NTCREATEX; + op.ntcreatex.in.root_fid = 0; + op.ntcreatex.in.flags = 0; + op.ntcreatex.in.access_mask = SEC_RIGHT_MAXIMUM_ALLOWED; + op.ntcreatex.in.create_options = 0; + op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + op.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + op.ntcreatex.in.alloc_size = 0; + op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + op.ntcreatex.in.security_flags = 0; + op.ntcreatex.in.fname = fname1; + + status = smb_raw_open(cli->tree, mem_ctx, &op); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = op.ntcreatex.out.fnum; + io.generic.level = RAW_RENAME_RENAME; io.rename.in.pattern1 = fname1; io.rename.in.pattern2 = fname2; io.rename.in.attrib = 0; + printf("trying rename while first file open\n"); status = smb_raw_rename(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); - + + smbcli_close(cli->tree, fnum); + + op.ntcreatex.in.access_mask = GENERIC_RIGHTS_FILE_READ; + op.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE | + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + status = smb_raw_open(cli->tree, mem_ctx, &op); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = op.ntcreatex.out.fnum; + + printf("trying rename while first file open with SHARE_ACCESS_DELETE\n"); + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.rename.in.pattern1 = fname2; + io.rename.in.pattern2 = fname1; + status = smb_raw_rename(cli->tree, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + io.rename.in.pattern1 = fname1; + io.rename.in.pattern2 = fname2; + + printf("trying rename while not open\n"); smb_raw_exit(cli->session); status = smb_raw_rename(cli->tree, &io); CHECK_STATUS(status, NT_STATUS_OK); -- cgit