summaryrefslogtreecommitdiff
path: root/source4/ntvfs/posix/pvfs_resolve.c
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2004-09-18 08:16:14 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:58:48 -0500
commit03cb4367d67ba9cdedf22df743d25d81093941d7 (patch)
tree0d6d97adec85cf9d4d67c34d8fc4e55b0b0eb019 /source4/ntvfs/posix/pvfs_resolve.c
parent046380c56c35177ccf49d0a40e3771242b134277 (diff)
downloadsamba-03cb4367d67ba9cdedf22df743d25d81093941d7.tar.gz
samba-03cb4367d67ba9cdedf22df743d25d81093941d7.tar.bz2
samba-03cb4367d67ba9cdedf22df743d25d81093941d7.zip
r2404: the first large lump of posix vfs stuff.
this is still very much a skeleton (with many limbs missing too!). I am committing this early to get some feedback on the approach taken. (This used to be commit 40d5cae5ebbfe328e193eadb685df6a370730299)
Diffstat (limited to 'source4/ntvfs/posix/pvfs_resolve.c')
-rw-r--r--source4/ntvfs/posix/pvfs_resolve.c303
1 files changed, 303 insertions, 0 deletions
diff --git a/source4/ntvfs/posix/pvfs_resolve.c b/source4/ntvfs/posix/pvfs_resolve.c
new file mode 100644
index 0000000000..66e7a5d103
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_resolve.c
@@ -0,0 +1,303 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - filename resolution
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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.
+*/
+
+/*
+ this is the core code for converting a filename from the format as
+ given by a client to a posix filename, including any case-matching
+ required, and checks for legal characters
+*/
+
+
+#include "include/includes.h"
+#include "vfs_posix.h"
+
+/*
+ compare two filename components. This is where the name mangling hook will go
+*/
+static int component_compare(const char *c1, const char *c2)
+{
+ return StrCaseCmp(c1, c2);
+}
+
+/*
+ search for a filename in a case insensitive fashion
+
+ 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)
+{
+ /* break into a series of components */
+ int num_components;
+ char **components;
+ char *p, *partial_name;
+ int i;
+
+ /* break up the full name info pathname components */
+ num_components=2;
+ p = name->full_name + strlen(pvfs->base_directory) + 1;
+
+ for (;*p;p++) {
+ if (*p == '/') {
+ num_components++;
+ }
+ }
+
+ components = talloc_array_p(name, char *, num_components);
+ p = name->full_name + strlen(pvfs->base_directory);
+ *p++ = 0;
+
+ components[0] = name->full_name;
+
+ for (i=1;i<num_components;i++) {
+ components[i] = p;
+ p = strchr(p, '/');
+ if (p) *p++ = 0;
+ }
+
+ partial_name = talloc_strdup(name, components[0]);
+ if (!partial_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* for each component, check if it exists as-is, and if not then
+ do a directory scan */
+ for (i=1;i<num_components;i++) {
+ char *test_name;
+ DIR *dir;
+ struct dirent *de;
+
+ test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
+ if (!test_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* 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;
+ }
+ talloc_free(partial_name);
+ partial_name = test_name;
+ if (i == num_components - 1) {
+ name->exists = True;
+ }
+ continue;
+ }
+
+ dir = opendir(partial_name);
+ if (!dir) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ while ((de = readdir(dir))) {
+ if (component_compare(components[i], de->d_name) == 0) {
+ break;
+ }
+ }
+
+ if (!de) {
+ closedir(dir);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ components[i] = talloc_strdup(name, de->d_name);
+ test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
+ talloc_free(partial_name);
+ partial_name = test_name;
+
+ closedir(dir);
+ }
+
+ if (!name->exists) {
+ if (stat(partial_name, &name->st) == 0) {
+ name->exists = True;
+ }
+ }
+
+ talloc_free(name->full_name);
+ name->full_name = partial_name;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ convert a CIFS pathname to a unix pathname. Note that this does NOT
+ take into account case insensitivity, and in fact does not access
+ the filesystem at all. It is merely a reformatting and charset
+ checking routine.
+
+ 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)
+{
+ char *ret, *p;
+
+ name->original_name = cifs_name;
+ name->stream_name = NULL;
+ name->has_wildcard = False;
+
+ if (*cifs_name == '\\') {
+ cifs_name++;
+ }
+
+ ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
+ if (ret == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = ret + strlen(pvfs->base_directory) + 1;
+
+ /* now do an in-place conversion of '\' to '/', checking
+ for legal characters */
+ for (;*p;p++) {
+ switch (*p) {
+ case '\\':
+ if (name->has_wildcard) {
+ /* wildcards are only allowed in the last part
+ of a name */
+ return NT_STATUS_ILLEGAL_CHARACTER;
+ }
+ *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;
+ }
+ *p-- = 0;
+ break;
+ case '*':
+ case '>':
+ case '<':
+ case '?':
+ case '"':
+ if (flags & PVFS_RESOLVE_NO_WILDCARD) {
+ return NT_STATUS_ILLEGAL_CHARACTER;
+ }
+ name->has_wildcard = True;
+ break;
+ case '/':
+ case '|':
+ return NT_STATUS_ILLEGAL_CHARACTER;
+ }
+ }
+
+ name->full_name = 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'
+
+ flags include:
+ 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,
+ const char *cifs_name,
+ uint_t flags, struct pvfs_filename **name)
+{
+ NTSTATUS status;
+
+ *name = talloc_p(mem_ctx, struct pvfs_filename);
+ if (*name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*name)->exists = False;
+
+ /* 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_IS_OK(status)) {
+ return status;
+ }
+
+ /* if it has a wildcard then no point doing a stat() */
+ if ((*name)->has_wildcard) {
+ 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 NT_STATUS_OK;
+ }
+
+ /* the filesystem might be case insensitive, in which
+ case a search is pointless */
+ if (pvfs->flags & PVFS_FLAG_CI_FILESYSTEM) {
+ return NT_STATUS_OK;
+ }
+
+ /* search for a matching filename */
+ status = pvfs_case_search(pvfs, *name);
+
+ return status;
+}
+
+
+/*
+ do a partial resolve, returning a pvfs_filename structure given a
+ base path and a relative component. It is an error if the file does
+ not exist. No case-insensitive matching is done.
+
+ this is used in places like directory searching where we need a pvfs_filename
+ to pass to a function, but already know the unix base directory and component
+*/
+NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ const char *unix_dir, const char *fname,
+ struct pvfs_filename **name)
+{
+ *name = talloc_p(mem_ctx, struct pvfs_filename);
+ if (*name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*name)->full_name = talloc_asprintf(mem_ctx, "%s/%s", unix_dir, fname);
+ if ((*name)->full_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (stat((*name)->full_name, &(*name)->st) == -1) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ (*name)->exists = True;
+ (*name)->has_wildcard = False;
+ (*name)->original_name = fname;
+ (*name)->stream_name = NULL;
+
+ return NT_STATUS_OK;
+}