This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
#include "includes.h"
#include "vfs_posix.h"
#include "system/dir.h"
+#include "param/param.h"
-/*
+/**
compare two filename components. This is where the name mangling hook will go
*/
static int component_compare(struct pvfs_state *pvfs, const char *comp, const char *name)
TODO: add a cache for previously resolved case-insensitive names
TODO: add mangled name support
*/
-static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs, struct pvfs_filename *name)
+static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs,
+ struct pvfs_filename *name,
+ unsigned int flags)
{
/* break into a series of components */
int num_components;
talloc_free(partial_name);
partial_name = test_name;
if (i == num_components - 1) {
- name->exists = True;
+ name->exists = true;
}
continue;
}
if (!name->exists) {
if (stat(partial_name, &name->st) == 0) {
- name->exists = True;
+ name->exists = true;
}
}
name->full_name = partial_name;
if (name->exists) {
- return pvfs_fill_dos_info(pvfs, name, -1);
+ return pvfs_fill_dos_info(pvfs, name, flags, -1);
}
return NT_STATUS_OK;
/*
parse a alternate data stream name
*/
-static NTSTATUS parse_stream_name(struct pvfs_filename *name, const char *s)
+static NTSTATUS parse_stream_name(struct pvfs_filename *name,
+ const char *s)
{
- char *p;
- name->stream_name = talloc_strdup(name, s+1);
+ char *p, *stream_name;
+ if (s[1] == '\0') {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ name->stream_name = stream_name = talloc_strdup(name, s+1);
if (name->stream_name == NULL) {
return NT_STATUS_NO_MEMORY;
}
- p = strchr_m(name->stream_name, ':');
- if (p == NULL) {
- name->stream_id = pvfs_name_hash(name->stream_name,
- strlen(name->stream_name));
- return NT_STATUS_OK;
- }
- if (strcasecmp_m(p, ":$DATA") != 0) {
- return NT_STATUS_OBJECT_NAME_INVALID;
+
+ p = stream_name;
+
+ while (*p) {
+ size_t c_size;
+ codepoint_t c = next_codepoint(p, &c_size);
+
+ switch (c) {
+ case '/':
+ case '\\':
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ case ':':
+ *p= 0;
+ p++;
+ if (*p == '\0') {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ if (strcasecmp_m(p, "$DATA") != 0) {
+ if (strchr_m(p, ':')) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ c_size = 0;
+ p--;
+ break;
+ }
+
+ p += c_size;
}
- *p = 0;
+
if (strcmp(name->stream_name, "") == 0) {
- name->stream_name = NULL;
+ /*
+ * we don't set stream_name to NULL, here
+ * as this would be wrong for directories
+ *
+ * pvfs_fill_dos_info() will set it to NULL
+ * if it's not a directory.
+ */
name->stream_id = 0;
} else {
name->stream_id = pvfs_name_hash(name->stream_name,
errors are returned if the filename is illegal given the flags
*/
static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
- uint_t flags, struct pvfs_filename *name)
+ unsigned int flags, struct pvfs_filename *name)
{
char *ret, *p, *p_start;
NTSTATUS status;
name->original_name = talloc_strdup(name, cifs_name);
+
+ /* remove any :$DATA */
+ p = strrchr(name->original_name, ':');
+ if (p && strcasecmp_m(p, ":$DATA") == 0) {
+ if (p > name->original_name && p[-1] == ':') {
+ p--;
+ }
+ *p = 0;
+ }
+
name->stream_name = NULL;
name->stream_id = 0;
- name->has_wildcard = False;
+ name->has_wildcard = false;
while (*cifs_name == '\\') {
cifs_name++;
while (*p) {
size_t c_size;
codepoint_t c = next_codepoint(p, &c_size);
+
+ if (c <= 0x1F) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+
switch (c) {
case '\\':
if (name->has_wildcard) {
/* wildcards are only allowed in the last part
of a name */
- return NT_STATUS_ILLEGAL_CHARACTER;
+ return NT_STATUS_OBJECT_NAME_INVALID;
}
- if (p > p_start && p[1] == 0) {
- *p = 0;
+ if (p > p_start && (p[1] == '\\' || p[1] == '\0')) {
+ /* see if it is definately a "\\" or
+ * a trailing "\". 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
+ */
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
} else {
*p = '/';
}
break;
case ':':
if (!(flags & PVFS_RESOLVE_STREAMS)) {
- return NT_STATUS_ILLEGAL_CHARACTER;
+ return NT_STATUS_OBJECT_NAME_INVALID;
}
if (name->has_wildcard) {
- return NT_STATUS_ILLEGAL_CHARACTER;
+ return NT_STATUS_OBJECT_NAME_INVALID;
}
status = parse_stream_name(name, p);
if (!NT_STATUS_IS_OK(status)) {
if (!(flags & PVFS_RESOLVE_WILDCARD)) {
return NT_STATUS_OBJECT_NAME_INVALID;
}
- name->has_wildcard = True;
+ name->has_wildcard = true;
break;
case '/':
case '|':
- return NT_STATUS_ILLEGAL_CHARACTER;
+ return NT_STATUS_OBJECT_NAME_INVALID;
case '.':
/* see if it is definately a .. or
. component. If it is then fail here, and
reduce a name that contains .. components or repeated \ separators
return NULL if it can't be reduced
*/
-static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx, const char **fname, uint_t flags)
+static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx,
+ const char **fname, unsigned int flags)
{
codepoint_t c;
size_t c_size, len;
if (ISDOTDOT(components[i])) {
if (i < 1) return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
memmove(&components[i-1], &components[i+1],
- sizeof(char *)*(num_components-(i+1)));
+ sizeof(char *)*(num_components-i));
i -= 2;
continue;
}
}
/* rebuild the name */
- ret = talloc_size(mem_ctx, len+1);
+ ret = talloc_array(mem_ctx, char, len+1);
if (ret == NULL) {
talloc_free(s);
return NT_STATUS_NO_MEMORY;
}
ret[len] = 0;
+ talloc_set_name_const(ret, ret);
+
talloc_free(s);
*fname = ret;
TODO: ../ collapsing, and outside share checking
*/
-NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
const char *cifs_name,
- uint_t flags, struct pvfs_filename **name)
+ unsigned int flags, struct pvfs_filename **name)
{
NTSTATUS status;
- *name = talloc(mem_ctx, struct pvfs_filename);
+ *name = talloc(req, struct pvfs_filename);
if (*name == NULL) {
return NT_STATUS_NO_MEMORY;
}
- (*name)->exists = False;
- (*name)->stream_exists = False;
+ (*name)->exists = false;
+ (*name)->stream_exists = false;
if (!(pvfs->fs_attribs & FS_ATTR_NAMED_STREAMS)) {
flags &= ~PVFS_RESOLVE_STREAMS;
}
+ /* SMB2 doesn't allow a leading slash */
+ if (req->ctx->protocol == PROTOCOL_SMB2 &&
+ *cifs_name == '\\') {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
/* do the basic conversion to a unix formatted path,
also checking for allowable characters */
status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
return status;
}
- /* if it has a wildcard then no point doing a stat() */
+ /* if it has a wildcard then no point doing a stat() of the
+ full name. Instead We need check if the directory exists
+ */
if ((*name)->has_wildcard) {
+ const char *p;
+ char *dir_name, *saved_name;
+ p = strrchr((*name)->full_name, '/');
+ if (p == NULL) {
+ /* root directory wildcard is OK */
+ return NT_STATUS_OK;
+ }
+ dir_name = talloc_strndup(*name, (*name)->full_name, (p-(*name)->full_name));
+ if (stat(dir_name, &(*name)->st) == 0) {
+ talloc_free(dir_name);
+ return NT_STATUS_OK;
+ }
+ /* we need to search for a matching name */
+ saved_name = (*name)->full_name;
+ (*name)->full_name = dir_name;
+ status = pvfs_case_search(pvfs, *name, flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* the directory doesn't exist */
+ (*name)->full_name = saved_name;
+ return status;
+ }
+ /* it does exist, but might need a case change */
+ if (dir_name != (*name)->full_name) {
+ (*name)->full_name = talloc_asprintf(*name, "%s%s",
+ (*name)->full_name, p);
+ NT_STATUS_HAVE_NO_MEMORY((*name)->full_name);
+ } else {
+ (*name)->full_name = saved_name;
+ talloc_free(dir_name);
+ }
return NT_STATUS_OK;
}
/* if we can stat() the full name now then we are done */
if (stat((*name)->full_name, &(*name)->st) == 0) {
- (*name)->exists = True;
- return pvfs_fill_dos_info(pvfs, *name, -1);
+ (*name)->exists = true;
+ return pvfs_fill_dos_info(pvfs, *name, flags, -1);
}
/* search for a matching filename */
- status = pvfs_case_search(pvfs, *name);
+ status = pvfs_case_search(pvfs, *name, flags);
return status;
}
*/
NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
const char *unix_dir, const char *fname,
- struct pvfs_filename **name)
+ unsigned int flags, struct pvfs_filename **name)
{
NTSTATUS status;
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
- (*name)->exists = True;
- (*name)->stream_exists = True;
- (*name)->has_wildcard = False;
+ (*name)->exists = true;
+ (*name)->stream_exists = true;
+ (*name)->has_wildcard = false;
(*name)->original_name = talloc_strdup(*name, fname);
(*name)->stream_name = NULL;
(*name)->stream_id = 0;
- status = pvfs_fill_dos_info(pvfs, *name, -1);
+ status = pvfs_fill_dos_info(pvfs, *name, flags, -1);
return status;
}
to update the pvfs_filename stat information, and by pvfs_open()
*/
NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
- struct pvfs_filename *name)
+ struct pvfs_filename *name, unsigned int flags)
{
- dev_t device = 0;
+ dev_t device = (dev_t)0;
ino_t inode = 0;
if (name->exists) {
return NT_STATUS_UNEXPECTED_IO_ERROR;
}
- name->exists = True;
+ name->exists = true;
- return pvfs_fill_dos_info(pvfs, name, fd);
+ return pvfs_fill_dos_info(pvfs, name, flags, fd);
+}
+
+/*
+ fill in the pvfs_filename info for an open file, given the current
+ info for a (possibly) non-open file. This is used by places that need
+ to update the pvfs_filename stat information, and the path
+ after a possible rename on a different handle.
+*/
+NTSTATUS pvfs_resolve_name_handle(struct pvfs_state *pvfs,
+ struct pvfs_file_handle *h)
+{
+ NTSTATUS status;
+
+ if (h->have_opendb_entry) {
+ struct odb_lock *lck;
+ char *name = NULL;
+
+ lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("%s: failed to lock file '%s' in opendb\n",
+ __FUNCTION__, h->name->full_name));
+ /* we were supposed to do a blocking lock, so something
+ is badly wrong! */
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = odb_get_path(lck, (const char **) &name);
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * This relies an the fact that
+ * renames of open files are only
+ * allowed by setpathinfo() and setfileinfo()
+ * and there're only renames within the same
+ * directory supported
+ */
+ if (strcmp(h->name->full_name, name) != 0) {
+ const char *orig_dir;
+ const char *new_file;
+ char *new_orig;
+ char *delim;
+
+ delim = strrchr(name, '/');
+ if (!delim) {
+ talloc_free(lck);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ new_file = delim + 1;
+ delim = strrchr(h->name->original_name, '\\');
+ if (delim) {
+ delim[0] = '\0';
+ orig_dir = h->name->original_name;
+ new_orig = talloc_asprintf(h->name, "%s\\%s",
+ orig_dir, new_file);
+ if (!new_orig) {
+ talloc_free(lck);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ new_orig = talloc_strdup(h->name, new_file);
+ if (!new_orig) {
+ talloc_free(lck);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ talloc_free(h->name->original_name);
+ talloc_free(h->name->full_name);
+ h->name->full_name = talloc_steal(h->name, name);
+ h->name->original_name = new_orig;
+ }
+ }
+
+ talloc_free(lck);
+ }
+
+ /*
+ * TODO: pass PVFS_RESOLVE_NO_OPENDB and get
+ * the write time from odb_lock() above.
+ */
+ status = pvfs_resolve_name_fd(pvfs, h->fd, h->name, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (!null_nttime(h->write_time.close_time)) {
+ h->name->dos.write_time = h->write_time.close_time;
+ }
+
+ return NT_STATUS_OK;
}
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
- (*name)->exists = True;
- (*name)->stream_exists = True;
- (*name)->has_wildcard = False;
+ (*name)->exists = true;
+ (*name)->stream_exists = true;
+ (*name)->has_wildcard = false;
/* we can't get the correct 'original_name', but for the purposes
of this call this is close enough */
- (*name)->original_name = talloc_reference(*name, child->original_name);
+ (*name)->original_name = talloc_strdup(*name, child->original_name);
+ if ((*name)->original_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
(*name)->stream_name = NULL;
(*name)->stream_id = 0;
- status = pvfs_fill_dos_info(pvfs, *name, -1);
+ status = pvfs_fill_dos_info(pvfs, *name, PVFS_RESOLVE_NO_OPENDB, -1);
return status;
}