mirror of
https://github.com/samba-team/samba.git
synced 2025-10-25 19:33:18 +03:00
ntvfs handler = nbench posix and the nbench pass-thru module will be called before the posix module. The chaining logic is now much saner, and less racy, with each level in the chain getting its own private pointer rather than relying on save/restore logic in the pass-thru module. The only pass-thru module we have at the moment is the nbench one (which records all traffic in a nbench compatibe format), but I plan on soon writing a "unixuid" pass-thru module that will implement the setegid()/setgroups()/seteuid() logic for standard posix uid handling. This separation of the posix backend from the uid handling should simplify the code, and make development easier. I also modified the nbench module so it can do multiple chaining, so if you want to you can do: ntvfs module = nbench nbench posix and it will save 2 copies of the log file in /tmp. This is really only useful for testing at the moment until we have more than one pass-thru module.
344 lines
8.3 KiB
C
344 lines
8.3 KiB
C
/*
|
|
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) {
|
|
if (i < num_components-1) {
|
|
closedir(dir);
|
|
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
} else {
|
|
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;
|
|
|
|
if (name->exists) {
|
|
return pvfs_fill_dos_info(pvfs, 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 = talloc_strdup(name, cifs_name);
|
|
name->stream_name = NULL;
|
|
name->has_wildcard = False;
|
|
|
|
while (*cifs_name == '\\') {
|
|
cifs_name++;
|
|
}
|
|
|
|
if (*cifs_name == 0) {
|
|
name->full_name = talloc_asprintf(name, "%s/.", pvfs->base_directory);
|
|
if (name->full_name == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
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;
|
|
|
|
if (p[strlen(cifs_name)-1] == '\\') {
|
|
p[strlen(cifs_name)-1] = 0;
|
|
}
|
|
|
|
/* 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 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;
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
*name = talloc_p(mem_ctx, struct pvfs_filename);
|
|
if (*name == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
(*name)->full_name = talloc_asprintf(*name, "%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 = talloc_strdup(*name, fname);
|
|
(*name)->stream_name = NULL;
|
|
|
|
status = pvfs_fill_dos_info(pvfs, *name);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
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 by pvfs_open()
|
|
*/
|
|
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;
|
|
}
|
|
|
|
name->exists = True;
|
|
|
|
return pvfs_fill_dos_info(pvfs, name);
|
|
}
|