From e9820e1b6e62240c5a18fa85e38a99685beed2df Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Tue, 26 Oct 2004 05:39:54 +0000 Subject: r3240: - update the rules for what error codes should be given on the different type of unlink an seach mismatches - wildcard directory listings that have attribute FILE_ATTRIBUTE_DIRECTORY and match "." or ".." should be failed. - don't set the write_time on SMBclose unless it is non-zero - added much better support for setfileinfo and setpathinfo in pvfs - better (and more efficient) handling of .. and . components in filenames (This used to be commit 9305b07af395a158cb9f0c1c9486f7122c79d357) --- source4/ntvfs/posix/pvfs_open.c | 8 +- source4/ntvfs/posix/pvfs_read.c | 9 +- source4/ntvfs/posix/pvfs_resolve.c | 116 ++++++++++++++++-- source4/ntvfs/posix/pvfs_search.c | 5 +- source4/ntvfs/posix/pvfs_setfileinfo.c | 213 ++++++++++++++++++++++++++------- source4/ntvfs/posix/pvfs_unlink.c | 26 +++- source4/ntvfs/posix/pvfs_util.c | 16 +-- 7 files changed, 327 insertions(+), 66 deletions(-) (limited to 'source4/ntvfs') diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c index 5e162ad147..ffd1520b07 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -643,9 +643,11 @@ NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs, return NT_STATUS_INVALID_HANDLE; } - unix_times.actime = 0; - unix_times.modtime = io->close.in.write_time; - utime(f->name->full_name, &unix_times); + if (!null_time(io->close.in.write_time)) { + unix_times.actime = 0; + unix_times.modtime = io->close.in.write_time; + utime(f->name->full_name, &unix_times); + } if (f->fd != -1 && close(f->fd) == -1) { diff --git a/source4/ntvfs/posix/pvfs_read.c b/source4/ntvfs/posix/pvfs_read.c index eb821d1f31..ee750a138f 100644 --- a/source4/ntvfs/posix/pvfs_read.c +++ b/source4/ntvfs/posix/pvfs_read.c @@ -51,6 +51,12 @@ NTSTATUS pvfs_read(struct ntvfs_module_context *ntvfs, return NT_STATUS_ACCESS_VIOLATION; } + /* this matches w2k3 behaviour for attempted large reads */ + if (rd->readx.in.maxcnt > UINT16_MAX) { + ret = 0; + goto done_read; + } + status = pvfs_check_lock(pvfs, f, req->smbpid, rd->readx.in.offset, rd->readx.in.maxcnt, @@ -67,10 +73,11 @@ NTSTATUS pvfs_read(struct ntvfs_module_context *ntvfs, return pvfs_map_errno(pvfs, errno); } +done_read: f->position = f->seek_offset = rd->readx.in.offset + ret; rd->readx.out.nread = ret; - rd->readx.out.remaining = 0; /* should fill this in? */ + rd->readx.out.remaining = 0xFFFF; rd->readx.out.compaction_mode = 0; return NT_STATUS_OK; diff --git a/source4/ntvfs/posix/pvfs_resolve.c b/source4/ntvfs/posix/pvfs_resolve.c index 271dbc2b9a..a7a693b217 100644 --- a/source4/ntvfs/posix/pvfs_resolve.c +++ b/source4/ntvfs/posix/pvfs_resolve.c @@ -194,7 +194,6 @@ static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name, { char *ret, *p, *p_start; size_t len; - int num_components=0; name->original_name = talloc_strdup(name, cifs_name); name->stream_name = NULL; @@ -242,7 +241,6 @@ static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name, return NT_STATUS_ILLEGAL_CHARACTER; } *p = '/'; - num_components++; break; case ':': if (!(flags & PVFS_RESOLVE_STREAMS)) { @@ -268,14 +266,19 @@ static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name, case '|': return NT_STATUS_ILLEGAL_CHARACTER; case '.': - if (p[1] != '.' || - (p[2] != '\\' && p[2] != 0) || - (p != p_start && p[-1] != '/')) { - break; + /* see if it is definately a .. or + . component. If it is then fail here, and + let the next layer up try again after + pvfs_reduce_name() if it wants to. This is + much more efficient on average than always + scanning for these separately */ + if (p[1] == '.' && + (p[2] == 0 || p[2] == '\\') && + (p == p_start || p[-1] == '/')) { + return NT_STATUS_OBJECT_PATH_SYNTAX_BAD; } - /* its definately a .. component */ - num_components--; - if (num_components <= 0) { + if ((p[1] == 0 || p[1] == '\\') && + (p == p_start || p[-1] == '/')) { return NT_STATUS_OBJECT_PATH_SYNTAX_BAD; } break; @@ -290,6 +293,92 @@ static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name, } +/* + reduce a name that contains .. components or repeated \ separators + return NULL if it can't be reduced +*/ +const char *pvfs_reduce_name(TALLOC_CTX *mem_ctx, const char *fname) +{ + codepoint_t c; + size_t c_size, len; + int i, num_components; + char **components; + char *p, *s, *ret; + + s = talloc_strdup(mem_ctx, fname); + if (s == NULL) return NULL; + + for (num_components=1, p=s; *p; p += c_size) { + c = next_codepoint(p, &c_size); + if (c == '\\') num_components++; + } + if (num_components < 2) { + talloc_free(s); + return NULL; + } + + components = talloc_array_p(s, char *, num_components+1); + if (components == NULL) { + talloc_free(s); + return NULL; + } + + components[0] = s; + for (i=0, p=s; *p; p += c_size) { + c = next_codepoint(p, &c_size); + if (c == '\\') { + *p = 0; + components[++i] = p+1; + } + } + components[i+1] = NULL; + + /* remove any null components */ + for (i=0;components[i];i++) { + if (strcmp(components[i], "") == 0 || + strcmp(components[i], ".") == 0) { + memmove(&components[i], &components[i+1], + sizeof(char *)*(num_components-i)); + i--; + } + if (strcmp(components[i], "..") == 0) { + if (i < 1) return NULL; + memmove(&components[i-1], &components[i+1], + sizeof(char *)*(num_components-(i+1))); + i -= 2; + } + } + + if (components[0] == NULL) { + talloc_free(s); + return talloc_strdup(mem_ctx, "\\"); + } + + for (len=i=0;components[i];i++) { + len += strlen(components[i]) + 1; + } + + /* rebuild the name */ + ret = talloc(mem_ctx, len+1); + if (ret == NULL) { + talloc_free(s); + return NULL; + } + + for (len=0,i=0;components[i];i++) { + size_t len1 = strlen(components[i]); + ret[len] = '\\'; + memcpy(ret+len+1, components[i], len1); + len += len1 + 1; + } + ret[len] = 0; + + talloc_free(s); + + return ret; +} + + /* resolve a name from relative client format to a struct pvfs_filename the memory for the filename is made as a talloc child of 'name' @@ -316,6 +405,15 @@ NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, /* do the basic conversion to a unix formatted path, also checking for allowable characters */ status = pvfs_unix_path(pvfs, cifs_name, flags, *name); + + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) { + /* it might contain .. components which need to be reduced */ + cifs_name = pvfs_reduce_name(*name, cifs_name); + if (cifs_name) { + status = pvfs_unix_path(pvfs, cifs_name, flags, *name); + } + } + if (!NT_STATUS_IS_OK(status)) { return status; } diff --git a/source4/ntvfs/posix/pvfs_search.c b/source4/ntvfs/posix/pvfs_search.c index ff9ad20b43..61e4651c5f 100644 --- a/source4/ntvfs/posix/pvfs_search.c +++ b/source4/ntvfs/posix/pvfs_search.c @@ -54,8 +54,9 @@ static NTSTATUS fill_search_info(struct pvfs_state *pvfs, return status; } - if (!pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib)) { - return NT_STATUS_OBJECT_NAME_NOT_FOUND; + status = pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib); + if (!NT_STATUS_IS_OK(status)) { + return status; } switch (level) { diff --git a/source4/ntvfs/posix/pvfs_setfileinfo.c b/source4/ntvfs/posix/pvfs_setfileinfo.c index 6a9e2003f4..71a9ce1266 100644 --- a/source4/ntvfs/posix/pvfs_setfileinfo.c +++ b/source4/ntvfs/posix/pvfs_setfileinfo.c @@ -34,37 +34,64 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs, struct utimbuf unix_times; struct pvfs_file *f; uint32_t create_options; + struct pvfs_filename newstats; + NTSTATUS status; f = pvfs_find_fd(pvfs, req, info->generic.file.fnum); if (!f) { return NT_STATUS_INVALID_HANDLE; } + /* update the file information */ + status = pvfs_resolve_name_fd(pvfs, f->fd, f->name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* we take a copy of the current file stats, then update + newstats in each of the elements below. At the end we + compare, and make any changes needed */ + newstats = *f->name; + switch (info->generic.level) { - case RAW_SFILEINFO_END_OF_FILE_INFO: - case RAW_SFILEINFO_END_OF_FILE_INFORMATION: - if (ftruncate(f->fd, - info->end_of_file_info.in.size) == -1) { - return pvfs_map_errno(pvfs, errno); + case RAW_SFILEINFO_SETATTR: + if (!null_time(info->setattr.in.write_time)) { + unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time); } - break; + if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) { + newstats.dos.attrib = info->setattr.in.attrib; + } + break; case RAW_SFILEINFO_SETATTRE: - unix_times.actime = info->setattre.in.access_time; - unix_times.modtime = info->setattre.in.write_time; - - if (unix_times.actime == 0 && unix_times.modtime == 0) { - break; - } - - /* set modify time = to access time if modify time was 0 */ - if (unix_times.actime != 0 && unix_times.modtime == 0) { - unix_times.modtime = unix_times.actime; + case RAW_SFILEINFO_STANDARD: + if (!null_time(info->setattre.in.create_time)) { + unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time); + } + if (!null_time(info->setattre.in.access_time)) { + unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time); } + if (!null_time(info->setattre.in.write_time)) { + unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time); + } + break; - /* Set the date on this file */ - if (utime(f->name->full_name, &unix_times) == -1) { - return pvfs_map_errno(pvfs, errno); + case RAW_SFILEINFO_BASIC_INFO: + case RAW_SFILEINFO_BASIC_INFORMATION: + if (info->basic_info.in.create_time) { + newstats.dos.create_time = info->basic_info.in.create_time; + } + if (info->basic_info.in.access_time) { + newstats.dos.access_time = info->basic_info.in.access_time; + } + if (info->basic_info.in.write_time) { + newstats.dos.write_time = info->basic_info.in.write_time; + } + if (info->basic_info.in.change_time) { + newstats.dos.change_time = info->basic_info.in.change_time; + } + if (info->basic_info.in.attrib != FILE_ATTRIBUTE_NORMAL) { + newstats.dos.attrib = info->basic_info.in.attrib; } break; @@ -81,11 +108,54 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs, } return pvfs_change_create_options(pvfs, req, f, create_options); + case RAW_SFILEINFO_ALLOCATION_INFO: + case RAW_SFILEINFO_ALLOCATION_INFORMATION: + newstats.dos.alloc_size = info->allocation_info.in.alloc_size; + break; + + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + newstats.st.st_size = info->end_of_file_info.in.size; + break; + case RAW_SFILEINFO_POSITION_INFORMATION: f->position = info->position_information.in.position; break; + + default: + return NT_STATUS_INVALID_LEVEL; + } + + /* possibly change the file size */ + if (newstats.st.st_size != f->name->st.st_size) { + if (ftruncate(f->fd, newstats.st.st_size) == -1) { + return pvfs_map_errno(pvfs, errno); + } + } + + /* possibly change the file timestamps */ + ZERO_STRUCT(unix_times); + if (newstats.dos.access_time != f->name->dos.access_time) { + unix_times.actime = nt_time_to_unix(newstats.dos.access_time); + } + if (newstats.dos.write_time != f->name->dos.write_time) { + unix_times.modtime = nt_time_to_unix(newstats.dos.write_time); + } + if (unix_times.actime != 0 || unix_times.modtime != 0) { + if (utime(f->name->full_name, &unix_times) == -1) { + return pvfs_map_errno(pvfs, errno); + } + } + + /* possibly change the attribute */ + if (newstats.dos.attrib != f->name->dos.attrib) { + mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib); + if (fchmod(f->fd, mode) == -1) { + return pvfs_map_errno(pvfs, errno); + } } + return NT_STATUS_OK; } @@ -98,6 +168,7 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs, { struct pvfs_state *pvfs = ntvfs->private_data; struct pvfs_filename *name; + struct pvfs_filename newstats; NTSTATUS status; struct utimbuf unix_times; @@ -112,42 +183,102 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs, return NT_STATUS_OBJECT_NAME_NOT_FOUND; } + + /* we take a copy of the current file stats, then update + newstats in each of the elements below. At the end we + compare, and make any changes needed */ + newstats = *name; + switch (info->generic.level) { - case RAW_SFILEINFO_END_OF_FILE_INFO: - case RAW_SFILEINFO_END_OF_FILE_INFORMATION: - if (truncate(name->full_name, - info->end_of_file_info.in.size) == -1) { - return pvfs_map_errno(pvfs, errno); + case RAW_SFILEINFO_SETATTR: + if (!null_time(info->setattr.in.write_time)) { + unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time); } - break; + if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) { + newstats.dos.attrib = info->setattr.in.attrib; + } + break; case RAW_SFILEINFO_SETATTRE: - unix_times.actime = info->setattre.in.access_time; - unix_times.modtime = info->setattre.in.write_time; - - if (unix_times.actime == 0 && unix_times.modtime == 0) { - break; - } - - /* set modify time = to access time if modify time was 0 */ - if (unix_times.actime != 0 && unix_times.modtime == 0) { - unix_times.modtime = unix_times.actime; + case RAW_SFILEINFO_STANDARD: + if (!null_time(info->setattre.in.create_time)) { + unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time); + } + if (!null_time(info->setattre.in.access_time)) { + unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time); } + if (!null_time(info->setattre.in.write_time)) { + unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time); + } + break; - /* Set the date on this file */ - if (utime(name->full_name, &unix_times) == -1) { - return NT_STATUS_ACCESS_DENIED; + case RAW_SFILEINFO_BASIC_INFO: + case RAW_SFILEINFO_BASIC_INFORMATION: + if (info->basic_info.in.create_time) { + newstats.dos.create_time = info->basic_info.in.create_time; + } + if (info->basic_info.in.access_time) { + newstats.dos.access_time = info->basic_info.in.access_time; + } + if (info->basic_info.in.write_time) { + newstats.dos.write_time = info->basic_info.in.write_time; + } + if (info->basic_info.in.change_time) { + newstats.dos.change_time = info->basic_info.in.change_time; + } + if (info->basic_info.in.attrib != FILE_ATTRIBUTE_NORMAL) { + newstats.dos.attrib = info->basic_info.in.attrib; } break; + case RAW_SFILEINFO_ALLOCATION_INFO: + case RAW_SFILEINFO_ALLOCATION_INFORMATION: + newstats.dos.alloc_size = info->allocation_info.in.alloc_size; + break; + + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + newstats.st.st_size = info->end_of_file_info.in.size; + break; + case RAW_SFILEINFO_DISPOSITION_INFO: case RAW_SFILEINFO_DISPOSITION_INFORMATION: - return NT_STATUS_INVALID_PARAMETER; - case RAW_SFILEINFO_POSITION_INFORMATION: return NT_STATUS_OK; + + default: + return NT_STATUS_INVALID_LEVEL; } - return NT_STATUS_INVALID_LEVEL; + /* possibly change the file size */ + if (newstats.st.st_size != name->st.st_size) { + if (truncate(name->full_name, newstats.st.st_size) == -1) { + return pvfs_map_errno(pvfs, errno); + } + } + + /* possibly change the file timestamps */ + ZERO_STRUCT(unix_times); + if (newstats.dos.access_time != name->dos.access_time) { + unix_times.actime = nt_time_to_unix(newstats.dos.access_time); + } + if (newstats.dos.write_time != name->dos.write_time) { + unix_times.modtime = nt_time_to_unix(newstats.dos.write_time); + } + if (unix_times.actime != 0 || unix_times.modtime != 0) { + if (utime(name->full_name, &unix_times) == -1) { + return pvfs_map_errno(pvfs, errno); + } + } + + /* possibly change the attribute */ + if (newstats.dos.attrib != name->dos.attrib) { + mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib); + if (chmod(name->full_name, mode) == -1) { + return pvfs_map_errno(pvfs, errno); + } + } + + return NT_STATUS_OK; } diff --git a/source4/ntvfs/posix/pvfs_unlink.c b/source4/ntvfs/posix/pvfs_unlink.c index 12ab583082..432481a88a 100644 --- a/source4/ntvfs/posix/pvfs_unlink.c +++ b/source4/ntvfs/posix/pvfs_unlink.c @@ -42,16 +42,23 @@ static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, } /* make sure its matches the given attributes */ - if (!pvfs_match_attrib(pvfs, name, attrib, 0)) { + status = pvfs_match_attrib(pvfs, name, attrib, 0); + if (!NT_STATUS_IS_OK(status)) { talloc_free(name); - return NT_STATUS_OBJECT_NAME_NOT_FOUND; + return status; } status = pvfs_can_delete(pvfs, name); if (!NT_STATUS_IS_OK(status)) { + talloc_free(name); return status; } + if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) { + talloc_free(name); + return NT_STATUS_FILE_IS_A_DIRECTORY; + } + /* finally try the actual unlink */ if (unlink(name->full_name) == -1) { status = pvfs_map_errno(pvfs, errno); @@ -85,6 +92,11 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs, return NT_STATUS_OBJECT_NAME_NOT_FOUND; } + if (name->exists && + (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) { + return NT_STATUS_FILE_IS_A_DIRECTORY; + } + dir = talloc_p(req, struct pvfs_dir); if (dir == NULL) { return NT_STATUS_NO_MEMORY; @@ -97,10 +109,18 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs, } if (dir->count == 0) { - return NT_STATUS_OBJECT_NAME_NOT_FOUND; + return NT_STATUS_NO_SUCH_FILE; } for (i=0;icount;i++) { + + /* this seems to be a special case */ + if ((unl->in.attrib & FILE_ATTRIBUTE_DIRECTORY) && + (strcmp(dir->names[i], ".") == 0 || + strcmp(dir->names[i], "..") == 0)) { + return NT_STATUS_OBJECT_NAME_INVALID; + } + status = pvfs_unlink_one(pvfs, req, dir->unix_path, dir->names[i], unl->in.attrib); if (NT_STATUS_IS_OK(status)) { diff --git a/source4/ntvfs/posix/pvfs_util.c b/source4/ntvfs/posix/pvfs_util.c index ae1dc6236d..50a78c2965 100644 --- a/source4/ntvfs/posix/pvfs_util.c +++ b/source4/ntvfs/posix/pvfs_util.c @@ -49,15 +49,17 @@ NTSTATUS pvfs_map_errno(struct pvfs_state *pvfs, int unix_errno) this is used by calls like unlink and search which take an attribute and only include special files if they match the given attribute */ -BOOL pvfs_match_attrib(struct pvfs_state *pvfs, struct pvfs_filename *name, - uint32_t attrib, uint32_t must_attrib) +NTSTATUS pvfs_match_attrib(struct pvfs_state *pvfs, struct pvfs_filename *name, + uint32_t attrib, uint32_t must_attrib) { - if ((name->dos.attrib & ~attrib) & - (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM)) { - return False; + if ((name->dos.attrib & ~attrib) & FILE_ATTRIBUTE_DIRECTORY) { + return NT_STATUS_FILE_IS_A_DIRECTORY; + } + if ((name->dos.attrib & ~attrib) & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)) { + return NT_STATUS_NO_SUCH_FILE; } if (must_attrib & ~name->dos.attrib) { - return False; + return NT_STATUS_OBJECT_NAME_NOT_FOUND; } - return True; + return NT_STATUS_OK; } -- cgit