diff options
author | Andrew Tridgell <tridge@samba.org> | 2004-09-18 08:16:14 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 12:58:48 -0500 |
commit | 03cb4367d67ba9cdedf22df743d25d81093941d7 (patch) | |
tree | 0d6d97adec85cf9d4d67c34d8fc4e55b0b0eb019 /source4/ntvfs | |
parent | 046380c56c35177ccf49d0a40e3771242b134277 (diff) | |
download | samba-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')
-rw-r--r-- | source4/ntvfs/config.mk | 8 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_dirlist.c | 126 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_fileinfo.c | 128 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_resolve.c | 303 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_search.c | 250 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_shortname.c | 34 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_unlink.c | 111 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_util.c | 56 | ||||
-rw-r--r-- | source4/ntvfs/posix/vfs_posix.c | 81 | ||||
-rw-r--r-- | source4/ntvfs/posix/vfs_posix.h | 85 |
10 files changed, 1145 insertions, 37 deletions
diff --git a/source4/ntvfs/config.mk b/source4/ntvfs/config.mk index 25dd51b487..2298fcd554 100644 --- a/source4/ntvfs/config.mk +++ b/source4/ntvfs/config.mk @@ -44,6 +44,14 @@ INIT_OBJ_FILES = \ [MODULE::ntvfs_posix] INIT_OBJ_FILES = \ ntvfs/posix/vfs_posix.o +ADD_OBJ_FILES = \ + ntvfs/posix/pvfs_util.o \ + ntvfs/posix/pvfs_search.o \ + ntvfs/posix/pvfs_dirlist.o \ + ntvfs/posix/pvfs_fileinfo.o \ + ntvfs/posix/pvfs_unlink.o \ + ntvfs/posix/pvfs_resolve.o \ + ntvfs/posix/pvfs_shortname.o # End MODULE ntvfs_posix ################################################ diff --git a/source4/ntvfs/posix/pvfs_dirlist.c b/source4/ntvfs/posix/pvfs_dirlist.c new file mode 100644 index 0000000000..1b4d01c197 --- /dev/null +++ b/source4/ntvfs/posix/pvfs_dirlist.c @@ -0,0 +1,126 @@ +/* + Unix SMB/CIFS implementation. + + 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. +*/ +/* + directory listing functions for posix backend +*/ + +#include "includes.h" +#include "vfs_posix.h" + +/* + a special directory listing case where the pattern has no wildcard. We can just do a single stat() + thus avoiding the more expensive directory scan +*/ +static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filename *name, + const char *pattern, struct pvfs_dir *dir) +{ + if (!name->exists) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + dir->count = 0; + dir->unix_path = name->full_name; + + dir->names = talloc_array_p(dir, const char *, 1); + if (!dir->names) { + return NT_STATUS_NO_MEMORY; + } + + dir->names[0] = talloc_strdup(dir, pattern); + if (!dir->names[0]) { + return NT_STATUS_NO_MEMORY; + } + + dir->count = 1; + + return NT_STATUS_OK; +} + +/* + read a directory and find all matching file names, returning them in + the structure *dir. The returned names are relative to the directory + + if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0 +*/ +NTSTATUS pvfs_list(struct pvfs_state *pvfs, struct pvfs_filename *name, struct pvfs_dir *dir) +{ + DIR *odir; + struct dirent *dent; + uint_t allocated = 0; + char *pattern; + + /* split the unix path into a directory + pattern */ + pattern = strrchr(name->full_name, '/'); + if (!pattern) { + /* this should not happen, as pvfs_unix_path is supposed to + return an absolute path */ + return NT_STATUS_UNSUCCESSFUL; + } + + *pattern++ = 0; + + if (!name->has_wildcard) { + return pvfs_list_no_wildcard(pvfs, name, pattern, dir); + } + + dir->count = 0; + dir->unix_path = name->full_name; + dir->names = talloc(dir, 0); + if (!dir->names) { + return NT_STATUS_NO_MEMORY; + } + + odir = opendir(name->full_name); + if (!odir) { + return pvfs_map_errno(pvfs, errno); + } + + while ((dent = readdir(odir))) { + uint_t i = dir->count; + const char *dname = dent->d_name; + + /* check it matches the wildcard pattern */ + if (ms_fnmatch(pattern, dname, PROTOCOL_NT1) != 0) { + continue; + } + + if (dir->count >= allocated) { + allocated = (allocated + 100) * 1.2; + dir->names = talloc_realloc_p(dir->names, const char *, allocated); + if (!dir->names) { + closedir(odir); + return NT_STATUS_NO_MEMORY; + } + } + + dir->names[i] = talloc_strdup(dir, dname); + if (!dir->names[i]) { + closedir(odir); + return NT_STATUS_NO_MEMORY; + } + + dir->count++; + } + + closedir(odir); + + return NT_STATUS_OK; +} + diff --git a/source4/ntvfs/posix/pvfs_fileinfo.c b/source4/ntvfs/posix/pvfs_fileinfo.c new file mode 100644 index 0000000000..83a577d092 --- /dev/null +++ b/source4/ntvfs/posix/pvfs_fileinfo.c @@ -0,0 +1,128 @@ +/* + Unix SMB/CIFS implementation. + + POSIX NTVFS backend - + + 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. +*/ + +#include "include/includes.h" +#include "vfs_posix.h" + + +/* UNIX filetype mappings. */ +#define UNIX_TYPE_FILE 0 +#define UNIX_TYPE_DIR 1 +#define UNIX_TYPE_SYMLINK 2 +#define UNIX_TYPE_CHARDEV 3 +#define UNIX_TYPE_BLKDEV 4 +#define UNIX_TYPE_FIFO 5 +#define UNIX_TYPE_SOCKET 6 +#define UNIX_TYPE_UNKNOWN 0xFFFFFFFF + + +/* + Return the major devicenumber for UNIX extensions. +*/ +static uint32_t unix_dev_major(dev_t dev) +{ +#if defined(HAVE_DEVICE_MAJOR_FN) + return (uint32)major(dev); +#else + return (uint32)(dev >> 8); +#endif +} + +/* + Return the minor devicenumber for UNIX extensions. +*/ +static uint32_t unix_dev_minor(dev_t dev) +{ +#if defined(HAVE_DEVICE_MINOR_FN) + return (uint32)minor(dev); +#else + return (uint32)(dev & 0xff); +#endif +} + +/* + Return the filetype for UNIX extensions +*/ +static uint32_t unix_filetype(mode_t mode) +{ + if (S_ISREG(mode)) return UNIX_TYPE_FILE; + if (S_ISDIR(mode)) return UNIX_TYPE_DIR; +#ifdef S_ISLNK + if (S_ISLNK(mode)) return UNIX_TYPE_SYMLINK; +#endif +#ifdef S_ISCHR + if (S_ISCHR(mode)) return UNIX_TYPE_CHARDEV; +#endif +#ifdef S_ISBLK + if (S_ISBLK(mode)) return UNIX_TYPE_BLKDEV; +#endif +#ifdef S_ISFIFO + if (S_ISFIFO(mode)) return UNIX_TYPE_FIFO; +#endif +#ifdef S_ISSOCK + if (S_ISSOCK(mode)) return UNIX_TYPE_SOCKET; +#endif + + DEBUG(0,("unix_filetype: unknown filetype %u", (unsigned)mode)); + return UNIX_TYPE_UNKNOWN; +} + + +/* + return all basic information about a file. This call is case-sensitive (it assumes that the + pathnames given have already had case conversion) +*/ +NTSTATUS pvfs_relative_file_info_cs(struct pvfs_state *pvfs, const char *dir_path, + const char *name, struct pvfs_file_info *finfo) +{ + char *full_name = NULL; + struct stat st; + + asprintf(&full_name, "%s/%s", dir_path, name); + if (full_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (stat(full_name, &st) == -1) { + free(full_name); + return pvfs_map_errno(pvfs, errno); + } + + unix_to_nt_time(&finfo->create_time, st.st_ctime); + unix_to_nt_time(&finfo->access_time, st.st_atime); + unix_to_nt_time(&finfo->write_time, st.st_mtime); + unix_to_nt_time(&finfo->change_time, st.st_mtime); + finfo->attrib = 0; + finfo->alloc_size = st.st_size; + finfo->size = st.st_size; + finfo->nlink = st.st_nlink; + finfo->ea_size = 0; + finfo->file_id = st.st_ino; + finfo->unix_uid = st.st_uid; + finfo->unix_gid = st.st_gid; + finfo->unix_file_type = unix_filetype(st.st_mode); + finfo->unix_dev_major = unix_dev_major(st.st_rdev); + finfo->unix_dev_minor = unix_dev_minor(st.st_rdev); + finfo->unix_permissions = unix_perms_to_wire(st.st_mode); + + return NT_STATUS_OK; +} 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; +} diff --git a/source4/ntvfs/posix/pvfs_search.c b/source4/ntvfs/posix/pvfs_search.c new file mode 100644 index 0000000000..da47bba75a --- /dev/null +++ b/source4/ntvfs/posix/pvfs_search.c @@ -0,0 +1,250 @@ +/* + Unix SMB/CIFS implementation. + + POSIX NTVFS backend - directory search functions + + 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. +*/ + +#include "include/includes.h" +#include "vfs_posix.h" + +/* + fill in a single search result for a given info level +*/ +static NTSTATUS fill_search_info(struct pvfs_state *pvfs, + enum smb_search_level level, + const char *unix_path, + const char *name, + uint16_t search_attrib, + uint32_t dir_index, + union smb_search_data *file) +{ + struct pvfs_file_info *finfo; + NTSTATUS status; + + finfo = talloc_p((TALLOC_CTX *)file, struct pvfs_file_info); + if (!finfo) { + return NT_STATUS_NO_MEMORY; + } + + status = pvfs_relative_file_info_cs(pvfs, unix_path, name, finfo); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(finfo); + return status; + } + + switch (level) { + + case RAW_SEARCH_BOTH_DIRECTORY_INFO: + file->both_directory_info.file_index = dir_index; + file->both_directory_info.create_time = finfo->create_time; + file->both_directory_info.access_time = finfo->access_time; + file->both_directory_info.write_time = finfo->write_time; + file->both_directory_info.change_time = finfo->change_time; + file->both_directory_info.size = finfo->size; + file->both_directory_info.alloc_size = finfo->alloc_size; + file->both_directory_info.attrib = finfo->attrib; + file->both_directory_info.ea_size = finfo->ea_size; + file->both_directory_info.short_name.s = pvfs_short_name(pvfs, (TALLOC_CTX *)file, + unix_path, name); + file->both_directory_info.name.s = name; + break; + } + + talloc_free(finfo); + + return NT_STATUS_OK; +} + +/* + return the next available search handle +*/ +static NTSTATUS pvfs_next_search_handle(struct pvfs_state *pvfs, uint16_t *handle) +{ + struct pvfs_search_state *search; + + if (pvfs->search.num_active_searches >= 0x10000) { + return NT_STATUS_INSUFFICIENT_RESOURCES; + } + + (*handle) = pvfs->search.next_search_handle; + for (search=pvfs->search.open_searches;search;search=search->next) { + if (*handle == search->handle) { + *handle = ((*handle)+1) & 0xFFFF; + continue; + } + } + pvfs->search.next_search_handle = ((*handle)+1) & 0xFFFF; + + return NT_STATUS_OK; +} + +/* + list files in a directory matching a wildcard pattern +*/ +NTSTATUS pvfs_search_first(struct smbsrv_request *req, union smb_search_first *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct pvfs_dir *dir; + struct pvfs_state *pvfs = req->tcon->ntvfs_private; + struct pvfs_search_state *search; + union smb_search_data *file; + uint16_t max_count, reply_count; + uint16_t search_attrib; + const char *pattern; + int i; + NTSTATUS status; + struct pvfs_filename *name; + + switch (io->generic.level) { + case RAW_SEARCH_SEARCH: + max_count = io->search_first.in.max_count; + search_attrib = io->search_first.in.search_attrib; + pattern = io->search_first.in.pattern; + break; + + case RAW_SEARCH_STANDARD: + case RAW_SEARCH_EA_SIZE: + case RAW_SEARCH_DIRECTORY_INFO: + case RAW_SEARCH_FULL_DIRECTORY_INFO: + case RAW_SEARCH_NAME_INFO: + case RAW_SEARCH_BOTH_DIRECTORY_INFO: + case RAW_SEARCH_ID_FULL_DIRECTORY_INFO: + case RAW_SEARCH_ID_BOTH_DIRECTORY_INFO: + case RAW_SEARCH_UNIX_INFO: + max_count = io->t2ffirst.in.max_count; + search_attrib = io->t2ffirst.in.search_attrib; + pattern = io->t2ffirst.in.pattern; + break; + + case RAW_SEARCH_FCLOSE: + case RAW_SEARCH_GENERIC: + DEBUG(0,("WARNING: Invalid search class %d in pvfs_search_first\n", io->generic.level)); + return NT_STATUS_INVALID_INFO_CLASS; + } + + /* resolve the cifs name to a posix name */ + status = pvfs_resolve_name(pvfs, req, pattern, 0, &name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (!name->has_wildcard && !name->exists) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + /* we initially make search a child of the request, then if we + need to keep it long term we steal it for the private + structure */ + search = talloc_p(req, struct pvfs_search_state); + if (!search) { + return NT_STATUS_NO_MEMORY; + } + + dir = talloc_p(search, struct pvfs_dir); + if (!dir) { + return NT_STATUS_NO_MEMORY; + } + + /* do the actual directory listing */ + status = pvfs_list(pvfs, name, dir); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* we need to give a handle back to the client so it + can continue a search */ + status = pvfs_next_search_handle(pvfs, &search->handle); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + search->dir = dir; + search->current_index = 0; + search->search_attrib = search_attrib; + + if (dir->count < max_count) { + max_count = dir->count; + } + + file = talloc_p(req, union smb_search_data); + if (!file) { + return NT_STATUS_NO_MEMORY; + } + + /* note that fill_search_info() can fail, if for example a + file disappears during a search or we don't have sufficient + permissions to stat() it, or the search_attrib does not + match the files attribute. In that case the name is ignored + and the search continues. */ + for (i=reply_count=0; i < dir->count && reply_count < max_count;i++) { + status = fill_search_info(pvfs, io->generic.level, dir->unix_path, dir->names[i], + search_attrib, i, file); + if (NT_STATUS_IS_OK(status)) { + if (!callback(search_private, file)) { + break; + } + reply_count++; + } + } + + /* not matching any entries is an error */ + if (reply_count == 0) { + return NT_STATUS_NO_MORE_ENTRIES; + } + + search->current_index = i; + + if (io->generic.level == RAW_SEARCH_SEARCH) { + io->search_first.out.count = reply_count; + DEBUG(0,("TODO: handle RAW_SEARCH_SEARCH continue\n")); + } else { + io->t2ffirst.out.count = reply_count; + io->t2ffirst.out.handle = search->handle; + io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0; + /* work out if we are going to keep the search state + and allow for a search continue */ + if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) || + ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) { + talloc_free(search); + } else { + pvfs->search.num_active_searches++; + pvfs->search.next_search_handle++; + talloc_steal(pvfs, search); + DLIST_ADD(pvfs->search.open_searches, search); + } + } + + return NT_STATUS_OK; +} + +/* continue a search */ +NTSTATUS pvfs_search_next(struct smbsrv_request *req, union smb_search_next *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* close a search */ +NTSTATUS pvfs_search_close(struct smbsrv_request *req, union smb_search_close *io) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + diff --git a/source4/ntvfs/posix/pvfs_shortname.c b/source4/ntvfs/posix/pvfs_shortname.c new file mode 100644 index 0000000000..b68f60d5e7 --- /dev/null +++ b/source4/ntvfs/posix/pvfs_shortname.c @@ -0,0 +1,34 @@ +/* + Unix SMB/CIFS implementation. + + POSIX NTVFS backend - 8.3 name routines + + 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. +*/ + +#include "include/includes.h" +#include "vfs_posix.h" + + +/* + return the short name for a given entry in a directory +*/ +char *pvfs_short_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, + const char *unix_path, const char *name) +{ + return talloc_strndup(mem_ctx, name, 12); +} diff --git a/source4/ntvfs/posix/pvfs_unlink.c b/source4/ntvfs/posix/pvfs_unlink.c new file mode 100644 index 0000000000..d93c15f19e --- /dev/null +++ b/source4/ntvfs/posix/pvfs_unlink.c @@ -0,0 +1,111 @@ +/* + Unix SMB/CIFS implementation. + + POSIX NTVFS backend - unlink + + 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. +*/ + +#include "include/includes.h" +#include "vfs_posix.h" + + +/* + unlink one file +*/ +static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx, + const char *unix_path, + const char *fname, uint32_t attrib) +{ + struct pvfs_filename *name; + NTSTATUS status; + + /* get a pvfs_filename object */ + status = pvfs_resolve_partial(pvfs, mem_ctx, + unix_path, fname, &name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* make sure its matches the given attributes */ + if (!pvfs_match_attrib(pvfs, name, attrib)) { + talloc_free(name); + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + /* finally try the actual unlink */ + if (unlink(name->full_name) == -1) { + status = pvfs_map_errno(pvfs, errno); + } + + talloc_free(name); + + return status; +} + +/* + delete a file - the dirtype specifies the file types to include in the search. + The name can contain CIFS wildcards, but rarely does (except with OS/2 clients) +*/ +NTSTATUS pvfs_unlink(struct smbsrv_request *req, struct smb_unlink *unl) +{ + struct pvfs_dir *dir; + struct pvfs_state *pvfs = req->tcon->ntvfs_private; + NTSTATUS status; + uint32_t i, total_deleted=0; + struct pvfs_filename *name; + + /* resolve the cifs name to a posix name */ + status = pvfs_resolve_name(pvfs, req, unl->in.pattern, 0, &name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (!name->exists && !name->has_wildcard) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + dir = talloc_p(req, struct pvfs_dir); + if (dir == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* get list of matching files */ + status = pvfs_list(pvfs, name, dir); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (dir->count == 0) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + for (i=0;i<dir->count;i++) { + status = pvfs_unlink_one(pvfs, req, dir->unix_path, dir->names[i], unl->in.attrib); + if (NT_STATUS_IS_OK(status)) { + total_deleted++; + } + } + + if (total_deleted == 0) { + return status; + } + + return NT_STATUS_OK; +} + + diff --git a/source4/ntvfs/posix/pvfs_util.c b/source4/ntvfs/posix/pvfs_util.c new file mode 100644 index 0000000000..2524261245 --- /dev/null +++ b/source4/ntvfs/posix/pvfs_util.c @@ -0,0 +1,56 @@ +/* + Unix SMB/CIFS implementation. + + 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. +*/ +/* + utility functions for posix backend +*/ + +#include "includes.h" +#include "vfs_posix.h" + +/* + return True if a string contains one of the CIFS wildcard characters +*/ +BOOL pvfs_has_wildcard(const char *str) +{ + if (strpbrk(str, "*?<>\"")) { + return True; + } + return False; +} + +/* + map a unix errno to a NTSTATUS +*/ +NTSTATUS pvfs_map_errno(struct pvfs_state *pvfs, int unix_errno) +{ + return map_nt_error_from_unix(unix_errno); +} + + +/* + check if a filename has an attribute matching the given attribute search value + 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) +{ + /* TODO: add attribute conversion */ + return True; +} diff --git a/source4/ntvfs/posix/vfs_posix.c b/source4/ntvfs/posix/vfs_posix.c index ee70e79835..86179debc5 100644 --- a/source4/ntvfs/posix/vfs_posix.c +++ b/source4/ntvfs/posix/vfs_posix.c @@ -39,6 +39,7 @@ static NTSTATUS pvfs_connect(struct smbsrv_request *req, const char *sharename) struct smbsrv_tcon *tcon = req->tcon; struct pvfs_state *pvfs; struct stat st; + char *base_directory; DEBUG(0,("WARNING: the posix vfs handler is incomplete - you probably want \"ntvfs handler = simple\"\n")); @@ -46,8 +47,13 @@ static NTSTATUS pvfs_connect(struct smbsrv_request *req, const char *sharename) if (pvfs == NULL) { return NT_STATUS_NO_MEMORY; } + ZERO_STRUCTP(pvfs); - pvfs->base_directory = talloc_strdup(pvfs, lp_pathname(tcon->service)); + /* for simplicity of path construction, remove any trailing slash now */ + base_directory = talloc_strdup(pvfs, lp_pathname(tcon->service)); + trim_string(base_directory, NULL, "/"); + + pvfs->base_directory = base_directory; /* the directory must exist. Note that we deliberately don't check that it is readable */ @@ -59,6 +65,7 @@ static NTSTATUS pvfs_connect(struct smbsrv_request *req, const char *sharename) tcon->fs_type = talloc_strdup(tcon, "NTFS"); tcon->dev_type = talloc_strdup(tcon, "A:"); + tcon->ntvfs_private = pvfs; return NT_STATUS_OK; } @@ -72,20 +79,11 @@ static NTSTATUS pvfs_disconnect(struct smbsrv_tcon *tcon) } /* - delete a file - the dirtype specifies the file types to include in the search. - The name can contain CIFS wildcards, but rarely does (except with OS/2 clients) -*/ -static NTSTATUS pvfs_unlink(struct smbsrv_request *req, struct smb_unlink *unl) -{ - return NT_STATUS_NOT_IMPLEMENTED; -} - - -/* ioctl interface - we don't do any */ static NTSTATUS pvfs_ioctl(struct smbsrv_request *req, union smb_ioctl *io) { + DEBUG(0,("pvfs_ioctl not implemented\n")); return NT_STATUS_INVALID_PARAMETER; } @@ -94,7 +92,25 @@ static NTSTATUS pvfs_ioctl(struct smbsrv_request *req, union smb_ioctl *io) */ static NTSTATUS pvfs_chkpath(struct smbsrv_request *req, struct smb_chkpath *cp) { - return NT_STATUS_NOT_IMPLEMENTED; + struct pvfs_state *pvfs = req->tcon->ntvfs_private; + struct pvfs_filename *name; + NTSTATUS status; + + /* resolve the cifs name to a posix name */ + status = pvfs_resolve_name(pvfs, req, cp->in.path, 0, &name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (!name->exists) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + if (!S_ISDIR(name->st.st_mode)) { + return NT_STATUS_NOT_A_DIRECTORY; + } + + return NT_STATUS_OK; } /* @@ -102,6 +118,7 @@ static NTSTATUS pvfs_chkpath(struct smbsrv_request *req, struct smb_chkpath *cp) */ static NTSTATUS pvfs_qpathinfo(struct smbsrv_request *req, union smb_fileinfo *info) { + DEBUG(0,("pvfs_qpathinfo not implemented\n")); return NT_STATUS_NOT_IMPLEMENTED; } @@ -110,6 +127,7 @@ static NTSTATUS pvfs_qpathinfo(struct smbsrv_request *req, union smb_fileinfo *i */ static NTSTATUS pvfs_qfileinfo(struct smbsrv_request *req, union smb_fileinfo *info) { + DEBUG(0,("pvfs_qfileinfo not implemented\n")); return NT_STATUS_NOT_IMPLEMENTED; } @@ -119,6 +137,7 @@ static NTSTATUS pvfs_qfileinfo(struct smbsrv_request *req, union smb_fileinfo *i */ static NTSTATUS pvfs_open(struct smbsrv_request *req, union smb_open *io) { + DEBUG(0,("pvfs_open not implemented\n")); return NT_STATUS_NOT_IMPLEMENTED; } @@ -127,6 +146,7 @@ static NTSTATUS pvfs_open(struct smbsrv_request *req, union smb_open *io) */ static NTSTATUS pvfs_mkdir(struct smbsrv_request *req, union smb_mkdir *md) { + DEBUG(0,("pvfs_mkdir not implemented\n")); return NT_STATUS_NOT_IMPLEMENTED; } @@ -135,6 +155,7 @@ static NTSTATUS pvfs_mkdir(struct smbsrv_request *req, union smb_mkdir *md) */ static NTSTATUS pvfs_rmdir(struct smbsrv_request *req, struct smb_rmdir *rd) { + DEBUG(0,("pvfs_rmdir not implemented\n")); return NT_STATUS_NOT_IMPLEMENTED; } @@ -143,6 +164,7 @@ static NTSTATUS pvfs_rmdir(struct smbsrv_request *req, struct smb_rmdir *rd) */ static NTSTATUS pvfs_rename(struct smbsrv_request *req, union smb_rename *ren) { + DEBUG(0,("pvfs_rename not implemented\n")); return NT_STATUS_NOT_IMPLEMENTED; } @@ -151,6 +173,7 @@ static NTSTATUS pvfs_rename(struct smbsrv_request *req, union smb_rename *ren) */ static NTSTATUS pvfs_copy(struct smbsrv_request *req, struct smb_copy *cp) { + DEBUG(0,("pvfs_copy not implemented\n")); return NT_STATUS_NOT_SUPPORTED; } @@ -159,6 +182,7 @@ static NTSTATUS pvfs_copy(struct smbsrv_request *req, struct smb_copy *cp) */ static NTSTATUS pvfs_read(struct smbsrv_request *req, union smb_read *rd) { + DEBUG(0,("pvfs_read not implemented\n")); return NT_STATUS_NOT_IMPLEMENTED; } @@ -167,6 +191,7 @@ static NTSTATUS pvfs_read(struct smbsrv_request *req, union smb_read *rd) */ static NTSTATUS pvfs_write(struct smbsrv_request *req, union smb_write *wr) { + DEBUG(0,("pvfs_write not implemented\n")); return NT_STATUS_NOT_IMPLEMENTED; } @@ -175,6 +200,7 @@ static NTSTATUS pvfs_write(struct smbsrv_request *req, union smb_write *wr) */ static NTSTATUS pvfs_seek(struct smbsrv_request *req, struct smb_seek *io) { + DEBUG(0,("pvfs_seek not implemented\n")); return NT_STATUS_NOT_SUPPORTED; } @@ -183,6 +209,7 @@ static NTSTATUS pvfs_seek(struct smbsrv_request *req, struct smb_seek *io) */ static NTSTATUS pvfs_flush(struct smbsrv_request *req, struct smb_flush *io) { + DEBUG(0,("pvfs_flush not implemented\n")); return NT_STATUS_NOT_IMPLEMENTED; } @@ -191,6 +218,7 @@ static NTSTATUS pvfs_flush(struct smbsrv_request *req, struct smb_flush *io) */ static NTSTATUS pvfs_close(struct smbsrv_request *req, union smb_close *io) { + DEBUG(0,("pvfs_close not implemented\n")); return NT_STATUS_NOT_IMPLEMENTED; } @@ -199,6 +227,7 @@ static NTSTATUS pvfs_close(struct smbsrv_request *req, union smb_close *io) */ static NTSTATUS pvfs_exit(struct smbsrv_request *req) { + DEBUG(0,("pvfs_exit not implemented\n")); return NT_STATUS_NOT_SUPPORTED; } @@ -207,6 +236,7 @@ static NTSTATUS pvfs_exit(struct smbsrv_request *req) */ static NTSTATUS pvfs_lock(struct smbsrv_request *req, union smb_lock *lck) { + DEBUG(0,("pvfs_lock not implemented\n")); return NT_STATUS_NOT_IMPLEMENTED; } @@ -215,6 +245,7 @@ static NTSTATUS pvfs_lock(struct smbsrv_request *req, union smb_lock *lck) */ static NTSTATUS pvfs_setpathinfo(struct smbsrv_request *req, union smb_setfileinfo *st) { + DEBUG(0,("pvfs_setpathinfo not implemented\n")); return NT_STATUS_NOT_SUPPORTED; } @@ -224,6 +255,7 @@ static NTSTATUS pvfs_setpathinfo(struct smbsrv_request *req, union smb_setfilein static NTSTATUS pvfs_setfileinfo(struct smbsrv_request *req, union smb_setfileinfo *info) { + DEBUG(0,("pvfs_setfileinfo not implemented\n")); return NT_STATUS_NOT_IMPLEMENTED; } @@ -233,6 +265,7 @@ static NTSTATUS pvfs_setfileinfo(struct smbsrv_request *req, */ static NTSTATUS pvfs_fsinfo(struct smbsrv_request *req, union smb_fsinfo *fs) { + DEBUG(0,("pvfs_fsinfo not implemented\n")); return NT_STATUS_NOT_IMPLEMENTED; } @@ -244,30 +277,6 @@ static NTSTATUS pvfs_lpq(struct smbsrv_request *req, union smb_lpq *lpq) return NT_STATUS_NOT_SUPPORTED; } -/* - list files in a directory matching a wildcard pattern -*/ -static NTSTATUS pvfs_search_first(struct smbsrv_request *req, union smb_search_first *io, - void *search_private, - BOOL (*callback)(void *, union smb_search_data *)) -{ - return NT_STATUS_NOT_IMPLEMENTED; -} - -/* continue a search */ -static NTSTATUS pvfs_search_next(struct smbsrv_request *req, union smb_search_next *io, - void *search_private, - BOOL (*callback)(void *, union smb_search_data *)) -{ - return NT_STATUS_NOT_IMPLEMENTED; -} - -/* close a search */ -static NTSTATUS pvfs_search_close(struct smbsrv_request *req, union smb_search_close *io) -{ - return NT_STATUS_NOT_IMPLEMENTED; -} - /* SMBtrans - not used on file shares */ static NTSTATUS pvfs_trans(struct smbsrv_request *req, struct smb_trans2 *trans2) { diff --git a/source4/ntvfs/posix/vfs_posix.h b/source4/ntvfs/posix/vfs_posix.h index 6b05240f43..1e6d763fc1 100644 --- a/source4/ntvfs/posix/vfs_posix.h +++ b/source4/ntvfs/posix/vfs_posix.h @@ -1,7 +1,7 @@ /* Unix SMB/CIFS implementation. - POSIX NTVFS backend - header + POSIX NTVFS backend - structure definitions Copyright (C) Andrew Tridgell 2004 @@ -20,8 +20,91 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifndef _VFS_POSIX_H_ +#define _VFS_POSIX_H_ + /* this is the private structure for the posix vfs backend. It is used to hold per-connection (per tree connect) state information */ struct pvfs_state { const char *base_directory; + + uint_t flags; + + struct { + /* a linked list of open searches */ + struct pvfs_search_state *open_searches; + + /* search handles are returned to the clients so they + can continue searches */ + uint16_t next_search_handle; + + /* count of active searches */ + uint_t num_active_searches; + + /* during trans2 search continuations we need to use + the initial search attributes */ + uint16_t search_attrib; + } search; +}; + +/* + this is the structure returned by pvfs_resolve_name(). It holds the posix details of + a filename passed by the client to any function +*/ +struct pvfs_filename { + const char *original_name; + char *full_name; + const char *stream_name; + BOOL has_wildcard; + BOOL exists; + struct stat st; +}; + + +/* this holds a list of file names for a search. We deliberately do + not hold the file stat information here to minimise the memory + overhead of idle searches */ +struct pvfs_dir { + uint_t count; + const char *unix_path; + const char **names; +}; + +/* the state of a search started with pvfs_search_first() */ +struct pvfs_search_state { + struct pvfs_search_state *next, *prev; + uint16_t search_attrib; + uint16_t handle; + uint_t current_index; + struct pvfs_dir *dir; +}; + + +/* this is the basic information needed about a file from the filesystem */ +struct pvfs_file_info { + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint32_t attrib; + uint64_t alloc_size; + uint64_t size; + uint32_t nlink; + uint32_t ea_size; + uint64_t file_id; + uint64_t unix_uid; + uint64_t unix_gid; + uint32_t unix_file_type; + uint64_t unix_dev_major; + uint64_t unix_dev_minor; + uint64_t unix_permissions; }; + +/* flags to pvfs_resolve_name() */ +#define PVFS_RESOLVE_NO_WILDCARD (1<<0) +#define PVFS_RESOLVE_STREAMS (1<<1) + +/* flags in pvfs->flags */ +#define PVFS_FLAG_CI_FILESYSTEM (1<<0) /* the filesystem is case insensitive */ + +#endif /* _VFS_POSIX_H_ */ |