*/
-#include "include/includes.h"
+#include "includes.h"
#include "vfs_posix.h"
+#include "system/dir.h"
/*
compare two filename components. This is where the name mangling hook will go
components[i] = p;
p = strchr(p, '/');
if (p) *p++ = 0;
+ if (pvfs_is_reserved_name(pvfs, components[i])) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
}
partial_name = talloc_strdup(name, components[0]);
/* check if this component exists as-is */
if (stat(test_name, &name->st) == 0) {
if (i<num_components-1 && !S_ISDIR(name->st.st_mode)) {
- return NT_STATUS_NOT_A_DIRECTORY;
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
}
talloc_free(partial_name);
partial_name = test_name;
}
continue;
}
+
+ /* the filesystem might be case insensitive, in which
+ case a search is pointless unless the name is
+ mangled */
+ if ((pvfs->flags & PVFS_FLAG_CI_FILESYSTEM) &&
+ !pvfs_is_mangled_component(pvfs, components[i])) {
+ if (i < num_components-1) {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ partial_name = test_name;
+ continue;
+ }
dir = opendir(partial_name);
if (!dir) {
if (!de) {
if (i < num_components-1) {
closedir(dir);
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
}
} else {
components[i] = talloc_strdup(name, de->d_name);
name->full_name = partial_name;
if (name->exists) {
- return pvfs_fill_dos_info(pvfs, name);
+ return pvfs_fill_dos_info(pvfs, name, -1);
}
return NT_STATUS_OK;
}
+/*
+ parse a alternate data stream name
+*/
+static NTSTATUS parse_stream_name(struct pvfs_filename *name, const char *s)
+{
+ char *p;
+ 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(p, ":$DATA") != 0) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ *p = 0;
+ if (strcmp(name->stream_name, "") == 0) {
+ name->stream_name = NULL;
+ name->stream_id = 0;
+ } else {
+ name->stream_id = pvfs_name_hash(name->stream_name,
+ strlen(name->stream_name));
+ }
+
+ return NT_STATUS_OK;
+}
+
/*
convert a CIFS pathname to a unix pathname. Note that this does NOT
static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
uint_t flags, struct pvfs_filename *name)
{
- char *ret, *p;
- size_t len;
+ char *ret, *p, *p_start;
+ NTSTATUS status;
name->original_name = talloc_strdup(name, cifs_name);
name->stream_name = NULL;
+ name->stream_id = 0;
name->has_wildcard = False;
while (*cifs_name == '\\') {
p = ret + strlen(pvfs->base_directory) + 1;
- len = strlen(cifs_name);
- if (len>0 && p[len-1] == '\\') {
- p[len-1] = 0;
- len--;
- }
- if (len>1 && p[len-1] == '.' && p[len-2] == '\\') {
- return NT_STATUS_OBJECT_NAME_INVALID;
- }
-
/* now do an in-place conversion of '\' to '/', checking
for legal characters */
- for (;*p;p++) {
- switch (*p) {
+ p_start = p;
+
+ while (*p) {
+ size_t c_size;
+ codepoint_t c = next_codepoint(p, &c_size);
+ switch (c) {
case '\\':
if (name->has_wildcard) {
/* wildcards are only allowed in the last part
of a name */
return NT_STATUS_ILLEGAL_CHARACTER;
}
- *p = '/';
+ if (p > p_start && p[1] == 0) {
+ *p = 0;
+ } else {
+ *p = '/';
+ }
break;
case ':':
if (!(flags & PVFS_RESOLVE_STREAMS)) {
return NT_STATUS_ILLEGAL_CHARACTER;
}
- name->stream_name = talloc_strdup(name, p+1);
- if (name->stream_name == NULL) {
- return NT_STATUS_NO_MEMORY;
+ if (name->has_wildcard) {
+ return NT_STATUS_ILLEGAL_CHARACTER;
+ }
+ status = parse_stream_name(name, p);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
*p-- = 0;
break;
case '<':
case '?':
case '"':
- if (flags & PVFS_RESOLVE_NO_WILDCARD) {
- return NT_STATUS_ILLEGAL_CHARACTER;
+ if (!(flags & PVFS_RESOLVE_WILDCARD)) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
}
name->has_wildcard = True;
break;
case '/':
case '|':
return NT_STATUS_ILLEGAL_CHARACTER;
+ case '.':
+ /* 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;
+ }
+ if ((p[1] == 0 || p[1] == '\\') &&
+ (p == p_start || p[-1] == '/')) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+ break;
}
+
+ p += c_size;
}
name->full_name = ret;
}
+/*
+ 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)
+{
+ codepoint_t c;
+ size_t c_size, len;
+ int i, num_components, err_count;
+ char **components;
+ char *p, *s, *ret;
+
+ s = talloc_strdup(mem_ctx, *fname);
+ if (s == NULL) return NT_STATUS_NO_MEMORY;
+
+ for (num_components=1, p=s; *p; p += c_size) {
+ c = next_codepoint(p, &c_size);
+ if (c == '\\') num_components++;
+ }
+
+ components = talloc_array_p(s, char *, num_components+1);
+ if (components == NULL) {
+ talloc_free(s);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ 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;
+
+ /*
+ rather bizarre!
+
+ '.' components are not allowed, but the rules for what error
+ code to give don't seem to make sense. This is a close
+ approximation.
+ */
+ for (err_count=i=0;components[i];i++) {
+ if (strcmp(components[i], "") == 0) {
+ continue;
+ }
+ if (strcmp(components[i], ".") == 0 || err_count) {
+ err_count++;
+ }
+ }
+ if (err_count) {
+ if (flags & PVFS_RESOLVE_WILDCARD) err_count--;
+
+ if (err_count==1) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ } else {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ }
+
+ /* remove any null components */
+ for (i=0;components[i];i++) {
+ if (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 NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ memmove(&components[i-1], &components[i+1],
+ sizeof(char *)*(num_components-(i+1)));
+ i -= 2;
+ }
+ }
+
+ if (components[0] == NULL) {
+ talloc_free(s);
+ *fname = talloc_strdup(mem_ctx, "\\");
+ return NT_STATUS_OK;
+ }
+
+ 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 NT_STATUS_NO_MEMORY;
+ }
+
+ 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);
+
+ *fname = ret;
+
+ return NT_STATUS_OK;
+}
+
+
/*
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'
PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
PVFS_RESOLVE_STREAMS = stream names are allowed
- TODO: add reserved name checking (for things like LPT1)
TODO: ../ collapsing, and outside share checking
*/
NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
}
(*name)->exists = False;
+ (*name)->stream_exists = False;
+
+ if (!(pvfs->fs_attribs & FS_ATTR_NAMED_STREAMS)) {
+ flags &= ~PVFS_RESOLVE_STREAMS;
+ }
/* 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 */
+ status = pvfs_reduce_name(*name, &cifs_name, flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
+ }
+
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/* 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);
- }
-
- /* the filesystem might be case insensitive, in which
- case a search is pointless */
- if (pvfs->flags & PVFS_FLAG_CI_FILESYSTEM) {
- return NT_STATUS_OK;
+ return pvfs_fill_dos_info(pvfs, *name, -1);
}
/* search for a matching filename */
}
(*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);
+ status = pvfs_fill_dos_info(pvfs, *name, -1);
return status;
}
NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
struct pvfs_filename *name)
{
- if (fstat(fd, &name->st) == -1) {
- return NT_STATUS_INVALID_HANDLE;
+ dev_t device = 0;
+ ino_t inode = 0;
+
+ if (name->exists) {
+ device = name->st.st_dev;
+ inode = name->st.st_ino;
+ }
+
+ if (fd == -1) {
+ if (stat(name->full_name, &name->st) == -1) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ } else {
+ if (fstat(fd, &name->st) == -1) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ }
+
+ if (name->exists &&
+ (device != name->st.st_dev || inode != name->st.st_ino)) {
+ /* the file we are looking at has changed! this could
+ be someone trying to exploit a race
+ condition. Certainly we don't want to continue
+ operating on this file */
+ DEBUG(0,("pvfs: WARNING: file '%s' changed during resolve - failing\n",
+ name->full_name));
+ return NT_STATUS_UNEXPECTED_IO_ERROR;
}
name->exists = True;
- return pvfs_fill_dos_info(pvfs, name);
+ return pvfs_fill_dos_info(pvfs, name, fd);
+}
+
+
+/*
+ resolve the parent of a given name
+*/
+NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ const struct pvfs_filename *child,
+ struct pvfs_filename **name)
+{
+ NTSTATUS status;
+ char *p;
+
+ *name = talloc_p(mem_ctx, struct pvfs_filename);
+ if (*name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*name)->full_name = talloc_strdup(*name, child->full_name);
+ if ((*name)->full_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = strrchr_m((*name)->full_name, '/');
+ if (p == NULL) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ /* this handles the root directory */
+ if (p == (*name)->full_name) {
+ p[1] = 0;
+ } else {
+ p[0] = 0;
+ }
+
+ if (stat((*name)->full_name, &(*name)->st) == -1) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ (*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)->stream_name = NULL;
+ (*name)->stream_id = 0;
+
+ status = pvfs_fill_dos_info(pvfs, *name, -1);
+
+ return status;
}