summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/lib/doscalls.c16
-rw-r--r--source3/lib/util.c4
-rw-r--r--source3/smbd/vfs-wrap.c295
-rw-r--r--source3/smbd/vfs.c450
4 files changed, 761 insertions, 4 deletions
diff --git a/source3/lib/doscalls.c b/source3/lib/doscalls.c
index 4ce84cbca2..2bd68d3097 100644
--- a/source3/lib/doscalls.c
+++ b/source3/lib/doscalls.c
@@ -52,18 +52,23 @@ int dos_open(char *fname,int flags,mode_t mode)
}
/*******************************************************************
- Opendir() wrapper that calls dos_to_unix.
+ Opendir() wrapper that calls dos_to_unix. Should use the
+ vfs_ops->opendir() function instead.
********************************************************************/
+#if 0
DIR *dos_opendir(char *dname)
{
return(opendir(dos_to_unix(dname,False)));
}
+#endif
/*******************************************************************
- Readdirname() wrapper that calls unix_to_dos.
+ Readdirname() wrapper that calls unix_to_dos. Should use the
+ vfs_readdirname() function instead.
********************************************************************/
+#if 0
char *dos_readdirname(DIR *p)
{
char *dname = readdirname(p);
@@ -74,6 +79,7 @@ char *dos_readdirname(DIR *p)
unix_to_dos(dname, True);
return(dname);
}
+#endif
/*******************************************************************
A chown() wrapper that calls dos_to_unix.
@@ -106,6 +112,8 @@ int dos_lstat(char *fname,SMB_STRUCT_STAT *sbuf)
Mkdir() that calls dos_to_unix.
Cope with UNIXes that don't allow high order mode bits on mkdir.
Patch from gcarter@lanier.com.
+ Don't use this call unless you really want to access a file on
+ disk. Use the vfs_ops.mkdir() function instead.
********************************************************************/
int dos_mkdir(char *dname,mode_t mode)
@@ -289,13 +297,15 @@ char *dos_getwd(char *unix_path)
}
/*******************************************************************
- Check if a DOS file exists.
+ Check if a DOS file exists. Use vfs_file_exist function instead.
********************************************************************/
+#if 0
BOOL dos_file_exist(char *fname,SMB_STRUCT_STAT *sbuf)
{
return file_exist(dos_to_unix(fname, False), sbuf);
}
+#endif
/*******************************************************************
Check if a DOS directory exists.
diff --git a/source3/lib/util.c b/source3/lib/util.c
index 0db12e92c6..71f440eae5 100644
--- a/source3/lib/util.c
+++ b/source3/lib/util.c
@@ -334,7 +334,7 @@ int name_mangle( char *In, char *Out, char name_type )
} /* name_mangle */
/*******************************************************************
- check if a file exists
+ check if a file exists - call vfs_file_exist for samba files
********************************************************************/
BOOL file_exist(char *fname,SMB_STRUCT_STAT *sbuf)
{
@@ -1562,6 +1562,8 @@ set the length of a file from a filedescriptor.
Returns 0 on success, -1 on failure.
****************************************************************************/
+/* tpot vfs need to recode this function */
+
int set_filelen(int fd, SMB_OFF_T len)
{
/* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot
diff --git a/source3/smbd/vfs-wrap.c b/source3/smbd/vfs-wrap.c
new file mode 100644
index 0000000000..24e45a6d24
--- /dev/null
+++ b/source3/smbd/vfs-wrap.c
@@ -0,0 +1,295 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+s Wrap disk only vfs functions to sidestep dodgy compilers.
+ Copyright (C) Tim Potter 1998
+
+ 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 "includes.h"
+
+/* Check for NULL pointer parameters in vfswrap_* functions */
+
+#define VFS_CHECK_NULL
+
+/* We don't want to have NULL function pointers lying around. Someone
+ is sure to try and execute them. These stubs are used to prevent
+ this possibility. */
+
+int vfswrap_dummy_connect(struct vfs_connection_struct *conn, char *service,
+ char *user)
+{
+ return 0; /* Return >= 0 for success */
+}
+
+void vfswrap_dummy_disconnect(void)
+{
+}
+
+/* Disk operations */
+
+SMB_BIG_UINT vfswrap_disk_free(char *path, BOOL small_query, SMB_BIG_UINT *bsize,
+ SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
+{
+ SMB_BIG_UINT result;
+
+#ifdef VFS_CHECK_NULL
+ if ((path == NULL) || (bsize == NULL) || (dfree == NULL) ||
+ (dsize == NULL)) {
+
+ smb_panic("NULL pointer passed to vfswrap_disk_free() function\n");
+ }
+#endif
+
+ result = sys_disk_free(path, small_query, bsize, dfree, dsize);
+ return result;
+}
+
+/* Directory operations */
+
+DIR *vfswrap_opendir(char *fname)
+{
+ DIR *result;
+
+#ifdef VFS_CHECK_NULL
+ if (fname == NULL) {
+ smb_panic("NULL pointer passed to vfswrap_opendir()\n");
+ }
+#endif
+
+ result = opendir(fname);
+ return result;
+}
+
+struct dirent *vfswrap_readdir(DIR *dirp)
+{
+ struct dirent *result;
+
+#ifdef VFS_CHECK_NULL
+ if (dirp == NULL) {
+ smb_panic("NULL pointer passed to vfswrap_readdir()\n");
+ }
+#endif
+
+ result = readdir(dirp);
+ return result;
+}
+
+int vfswrap_mkdir(char *path, mode_t mode)
+{
+ int result;
+
+#ifdef VFS_CHECK_NULL
+ if (path == NULL) {
+ smb_panic("NULL pointer passed to vfswrap_mkdir()\n");
+ }
+#endif
+
+ result = mkdir(path, mode);
+ return result;
+}
+
+int vfswrap_rmdir(char *path)
+{
+ int result;
+
+#ifdef VFS_CHECK_NULL
+ if (path == NULL) {
+ smb_panic("NULL pointer passed to vfswrap_rmdir()\n");
+ }
+#endif
+
+ result = rmdir(path);
+ return result;
+}
+
+int vfswrap_closedir(DIR *dirp)
+{
+ int result;
+
+#ifdef VFS_CHECK_NULL
+ if (dirp == NULL) {
+ smb_panic("NULL pointer passed to vfswrap_closedir()\n");
+ }
+#endif
+
+ result = closedir(dirp);
+ return result;
+}
+
+/* File operations */
+
+int vfswrap_open(char *fname, int flags, mode_t mode)
+{
+ int result;
+
+#ifdef VFS_CHECK_NULL
+ if (fname == NULL) {
+ smb_panic("NULL pointer passed to vfswrap_open()\n");
+ }
+#endif
+
+ result = sys_open(fname, flags, mode);
+ return result;
+}
+
+int vfswrap_close(int fd)
+{
+ int result;
+
+ result = close(fd);
+ return result;
+}
+
+ssize_t vfswrap_read(int fd, char *data, size_t n)
+{
+ ssize_t result;
+
+#ifdef VFS_CHECK_NULL
+ if (data == NULL) {
+ smb_panic("NULL pointer passed to vfswrap_read()\n");
+ }
+#endif
+
+ result = read(fd, data, n);
+ return result;
+}
+
+ssize_t vfswrap_write(int fd, char *data, size_t n)
+{
+ ssize_t result;
+
+#ifdef VFS_CHECK_NULL
+ if (data == NULL) {
+ smb_panic("NULL pointer passed to vfswrap_write()\n");
+ }
+#endif
+
+ result = write(fd, data, n);
+ return result;
+}
+
+SMB_OFF_T vfswrap_lseek(int filedes, SMB_OFF_T offset, int whence)
+{
+ SMB_OFF_T result;
+
+ result = sys_lseek(filedes, offset, whence);
+ return result;
+}
+
+int vfswrap_rename(char *old, char *new)
+{
+ int result;
+
+#ifdef VFS_CHECK_NULL
+ if ((old == NULL) || (new == NULL)) {
+ smb_panic("NULL pointer passed to vfswrap_rename()\n");
+ }
+#endif
+
+ result = rename(old, new);
+ return result;
+}
+
+void vfswrap_fsync(int fd)
+{
+ fsync(fd);
+}
+
+int vfswrap_stat(char *fname, SMB_STRUCT_STAT *sbuf)
+{
+ int result;
+
+#ifdef VFS_CHECK_NULL
+ if ((fname == NULL) || (sbuf == NULL)) {
+ smb_panic("NULL pointer passed to vfswrap_stat()\n");
+ }
+#endif
+
+ result = sys_stat(fname, sbuf);
+ return result;
+}
+
+int vfswrap_fstat(int fd, SMB_STRUCT_STAT *sbuf)
+{
+ int result;
+
+#ifdef VFS_CHECK_NULL
+ if (sbuf == NULL) {
+ smb_panic("NULL pointer passed to vfswrap_fstat()\n");
+ }
+#endif
+
+ result = sys_fstat(fd, sbuf);
+ return result;
+}
+
+int vfswrap_lstat(char *path,
+ SMB_STRUCT_STAT *sbuf)
+{
+ int result;
+
+#ifdef VFS_CHECK_NULL
+ if ((path == NULL) || (sbuf == NULL)) {
+ smb_panic("NULL pointer passed to vfswrap_lstat()\n");
+ }
+#endif
+
+ result = sys_lstat(path, sbuf);
+ return result;
+}
+
+int vfswrap_unlink(char *path)
+{
+ int result;
+
+#ifdef VFS_CHECK_NULL
+ if (path == NULL) {
+ smb_panic("NULL pointer passed to vfswrap_unlink()\n");
+ }
+#endif
+
+ result = unlink(path);
+ return result;
+}
+
+int vfswrap_chmod(char *path, mode_t mode)
+{
+ int result;
+
+#ifdef VFS_CHECK_NULL
+ if (path == NULL) {
+ smb_panic("NULL pointer passed to vfswrap_chmod()\n");
+ }
+#endif
+
+ result = chmod(path, mode);
+ return result;
+}
+
+int vfswrap_utime(char *path, struct utimbuf *times)
+{
+ int result;
+
+#ifdef VFS_CHECK_NULL
+ if ((path == NULL) || (times == NULL)) {
+ smb_panic("NULL pointer passed to vfswrap_utime()\n");
+ }
+#endif
+
+ result = utime(path, times);
+ return result;
+}
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
new file mode 100644
index 0000000000..2f9f847188
--- /dev/null
+++ b/source3/smbd/vfs.c
@@ -0,0 +1,450 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 1.9.
+ VFS initialisation and support functions
+ Copyright (C) Tim Potter 1999
+
+ 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 "includes.h"
+
+extern int DEBUGLEVEL;
+
+/* Some structures to help us initialise the vfs operations table */
+
+struct vfs_syminfo {
+ char *name;
+ void *fptr;
+};
+
+/* Default vfs hooks. WARNING: The order of these initialisers is
+ very important. They must be in the same order as defined in
+ vfs.h. Change at your own peril. */
+
+struct vfs_ops default_vfs_ops = {
+
+ /* Disk operations */
+
+ vfswrap_dummy_connect,
+ vfswrap_dummy_disconnect,
+ vfswrap_disk_free,
+
+ /* Directory operations */
+
+ vfswrap_opendir,
+ vfswrap_readdir,
+ vfswrap_mkdir,
+ vfswrap_rmdir,
+ vfswrap_closedir,
+
+ /* File operations */
+
+ vfswrap_open,
+ vfswrap_close,
+ vfswrap_read,
+ vfswrap_write,
+ vfswrap_lseek,
+ vfswrap_rename,
+ vfswrap_fsync,
+ vfswrap_stat,
+ vfswrap_fstat,
+ vfswrap_lstat,
+ vfswrap_unlink,
+ vfswrap_chmod,
+ vfswrap_utime
+};
+
+/****************************************************************************
+ initialise default vfs hooks
+****************************************************************************/
+int vfs_init_default(connection_struct *conn)
+{
+ DEBUG(3, ("Initialising default vfs hooks\n"));
+
+ memcpy(&conn->vfs_ops, &default_vfs_ops, sizeof(conn->vfs_ops));
+ return True;
+}
+
+/****************************************************************************
+ initialise custom vfs hooks
+****************************************************************************/
+#ifdef HAVE_LIBDL
+BOOL vfs_init_custom(connection_struct *conn)
+{
+ void *handle;
+ struct vfs_ops *ops, *(*fptr)(struct vfs_options *options);
+
+ DEBUG(3, ("Initialising custom vfs hooks from %s\n",
+ lp_vfsobj(SNUM(conn))));
+
+ /* Open object file */
+
+ handle = dlopen(lp_vfsobj(SNUM(conn)), RTLD_NOW | RTLD_GLOBAL);
+ conn->vfs_conn->dl_handle = handle;
+
+ if (!handle) {
+ DEBUG(0, ("Error opening %s: %s\n", lp_vfsobj(SNUM(conn)),
+ dlerror()));
+ return False;
+ }
+
+ /* Get handle on vfs_init() symbol */
+
+ fptr = dlsym(handle, "vfs_init");
+
+ if (fptr == NULL) {
+ DEBUG(0, ("No vfs_init() symbol found in %s\n",
+ lp_vfsobj(SNUM(conn))));
+ return False;
+ }
+
+ /* Initialise vfs_ops structure */
+
+ if ((ops = fptr(NULL)) == NULL) {
+ DEBUG(0, ("vfs_init function from %s failed\n", lp_vfsobj(SNUM(conn))));
+ return False;
+ }
+
+ /* Fill in unused operations with default (disk based) ones.
+ There's probably a neater way to do this then a whole bunch of
+ if statements. */
+
+ memcpy(&conn->vfs_ops, ops, sizeof(conn->vfs_ops));
+
+ if (conn->vfs_ops.connect == NULL) {
+ conn->vfs_ops.connect = default_vfs_ops.connect;
+ }
+
+ if (conn->vfs_ops.disconnect == NULL) {
+ conn->vfs_ops.disconnect = default_vfs_ops.disconnect;
+ }
+
+ if (conn->vfs_ops.disk_free == NULL) {
+ conn->vfs_ops.disk_free = default_vfs_ops.disk_free;
+ }
+
+ if (conn->vfs_ops.opendir == NULL) {
+ conn->vfs_ops.opendir = default_vfs_ops.opendir;
+ }
+
+ if (conn->vfs_ops.readdir == NULL) {
+ conn->vfs_ops.readdir = default_vfs_ops.readdir;
+ }
+
+ if (conn->vfs_ops.mkdir == NULL) {
+ conn->vfs_ops.mkdir = default_vfs_ops.mkdir;
+ }
+
+ if (conn->vfs_ops.rmdir == NULL) {
+ conn->vfs_ops.rmdir = default_vfs_ops.rmdir;
+ }
+
+ if (conn->vfs_ops.closedir == NULL) {
+ conn->vfs_ops.closedir = default_vfs_ops.closedir;
+ }
+
+ if (conn->vfs_ops.open == NULL) {
+ conn->vfs_ops.open = default_vfs_ops.open;
+ }
+
+ if (conn->vfs_ops.close == NULL) {
+ conn->vfs_ops.close = default_vfs_ops.close;
+ }
+
+ if (conn->vfs_ops.read == NULL) {
+ conn->vfs_ops.read = default_vfs_ops.read;
+ }
+
+ if (conn->vfs_ops.write == NULL) {
+ conn->vfs_ops.write = default_vfs_ops.write;
+ }
+
+ if (conn->vfs_ops.lseek == NULL) {
+ conn->vfs_ops.lseek = default_vfs_ops.lseek;
+ }
+
+ if (conn->vfs_ops.rename == NULL) {
+ conn->vfs_ops.rename = default_vfs_ops.rename;
+ }
+
+ if (conn->vfs_ops.fsync == NULL) {
+ conn->vfs_ops.fsync = default_vfs_ops.fsync;
+ }
+
+ if (conn->vfs_ops.stat == NULL) {
+ conn->vfs_ops.stat = default_vfs_ops.stat;
+ }
+
+ if (conn->vfs_ops.fstat == NULL) {
+ conn->vfs_ops.fstat = default_vfs_ops.fstat;
+ }
+
+ if (conn->vfs_ops.lstat == NULL) {
+ conn->vfs_ops.lstat = default_vfs_ops.lstat;
+ }
+
+ if (conn->vfs_ops.unlink == NULL) {
+ conn->vfs_ops.unlink = default_vfs_ops.unlink;
+ }
+
+ if (conn->vfs_ops.chmod == NULL) {
+ conn->vfs_ops.chmod = default_vfs_ops.chmod;
+ }
+
+ if (conn->vfs_ops.utime == NULL) {
+ conn->vfs_ops.utime = default_vfs_ops.utime;
+ }
+
+ return True;
+}
+#endif
+
+BOOL vfs_directory_exist(connection_struct *conn, char *dname,
+ SMB_STRUCT_STAT *st)
+{
+ SMB_STRUCT_STAT st2;
+ BOOL ret;
+
+ if (!st) st = &st2;
+
+ if (conn->vfs_ops.stat(dname,st) != 0)
+ return(False);
+
+ ret = S_ISDIR(st->st_mode);
+ if(!ret)
+ errno = ENOTDIR;
+
+ return ret;
+}
+
+/*******************************************************************
+ check if a vfs file exists
+********************************************************************/
+BOOL vfs_file_exist(connection_struct *conn,char *fname,SMB_STRUCT_STAT *sbuf)
+{
+ SMB_STRUCT_STAT st;
+ if (!sbuf) sbuf = &st;
+
+ if (conn->vfs_ops.stat(fname,sbuf) != 0)
+ return(False);
+
+ return(S_ISREG(sbuf->st_mode));
+}
+
+/****************************************************************************
+ write data to a fd on the vfs
+****************************************************************************/
+ssize_t vfs_write_data(files_struct *fsp,char *buffer,size_t N)
+{
+ size_t total=0;
+ ssize_t ret;
+ int fd = fsp->fd_ptr->fd;
+
+ while (total < N)
+ {
+ ret = fsp->conn->vfs_ops.write(fd,buffer + total,N - total);
+
+ if (ret == -1) return -1;
+ if (ret == 0) return total;
+
+ total += ret;
+ }
+ return (ssize_t)total;
+}
+
+/****************************************************************************
+transfer some data between two file_struct's
+****************************************************************************/
+SMB_OFF_T vfs_transfer_file(int in_fd, files_struct *in_fsp,
+ int out_fd, files_struct *out_fsp,
+ SMB_OFF_T n, char *header, int headlen, int align)
+{
+ static char *buf=NULL;
+ static int size=0;
+ char *buf1,*abuf;
+ SMB_OFF_T total = 0;
+
+ DEBUG(4,("vfs_transfer_file n=%.0f (head=%d) called\n",(double)n,headlen));
+
+ /* Check we have at least somewhere to read from */
+
+ SMB_ASSERT((in_fd != -1) || (in_fsp != NULL));
+
+ if (size == 0) {
+ size = lp_readsize();
+ size = MAX(size,1024);
+ }
+
+ while (!buf && size>0) {
+ buf = (char *)Realloc(buf,size+8);
+ if (!buf) size /= 2;
+ }
+
+ if (!buf) {
+ DEBUG(0,("Can't allocate transfer buffer!\n"));
+ exit(1);
+ }
+
+ abuf = buf + (align%8);
+
+ if (header)
+ n += headlen;
+
+ while (n > 0)
+ {
+ int s = (int)MIN(n,(SMB_OFF_T)size);
+ int ret,ret2=0;
+
+ ret = 0;
+
+ if (header && (headlen >= MIN(s,1024))) {
+ buf1 = header;
+ s = headlen;
+ ret = headlen;
+ headlen = 0;
+ header = NULL;
+ } else {
+ buf1 = abuf;
+ }
+
+ if (header && headlen > 0)
+ {
+ ret = MIN(headlen,size);
+ memcpy(buf1,header,ret);
+ headlen -= ret;
+ header += ret;
+ if (headlen <= 0) header = NULL;
+ }
+
+ if (s > ret) {
+ ret += in_fsp ?
+ in_fsp->conn->vfs_ops.read(in_fsp->fd_ptr->fd,buf1+ret,s-ret) : read(in_fd,buf1+ret,s-ret);
+ }
+
+ if (ret > 0)
+ {
+ if (out_fsp) {
+ ret2 = out_fsp->conn->vfs_ops.write(out_fsp->fd_ptr->fd,buf1,ret);
+ } else {
+ ret2= (out_fd != -1) ? write_data(out_fd,buf1,ret) : ret;
+ }
+ }
+
+ if (ret2 > 0) total += ret2;
+ /* if we can't write then dump excess data */
+ if (ret2 != ret)
+ vfs_transfer_file(in_fd, in_fsp, -1,NULL,n-(ret+headlen),NULL,0,0);
+
+ if (ret <= 0 || ret2 != ret)
+ return(total);
+ n -= ret;
+ }
+ return(total);
+}
+
+/*******************************************************************
+a vfs_readdir wrapper which just returns the file name
+********************************************************************/
+char *vfs_readdirname(connection_struct *conn, void *p)
+{
+ struct dirent *ptr;
+ char *dname;
+
+ if (!p) return(NULL);
+
+ ptr = (struct dirent *)conn->vfs_ops.readdir(p);
+ if (!ptr) return(NULL);
+
+ dname = ptr->d_name;
+
+#ifdef NEXT2
+ if (telldir(p) < 0) return(NULL);
+#endif
+
+#ifdef HAVE_BROKEN_READDIR
+ /* using /usr/ucb/cc is BAD */
+ dname = dname - 2;
+#endif
+
+ {
+ static pstring buf;
+ memcpy(buf, dname, NAMLEN(ptr)+1);
+ unix_to_dos(buf, True);
+ dname = buf;
+ }
+
+ unix_to_dos(dname, True);
+ return(dname);
+}
+
+/***************************************************************************
+ handle the interpretation of the vfs option parameter
+ *************************************************************************/
+static BOOL handle_vfs_option(char *pszParmValue, char **ptr)
+{
+ struct vfs_options *new_option, **options = (struct vfs_options **)ptr;
+ int i;
+
+ /* Create new vfs option */
+
+ new_option = (struct vfs_options *)malloc(sizeof(*new_option));
+ if (new_option == NULL) {
+ return False;
+ }
+
+ ZERO_STRUCTP(new_option);
+
+ /* Get name and value */
+
+ new_option->name = strtok(pszParmValue, "=");
+
+ if (new_option->name == NULL) {
+ return False;
+ }
+
+ while(isspace(*new_option->name)) {
+ new_option->name++;
+ }
+
+ for (i = strlen(new_option->name); i > 0; i--) {
+ if (!isspace(new_option->name[i - 1])) break;
+ }
+
+ new_option->name[i] = '\0';
+ new_option->name = strdup(new_option->name);
+
+ new_option->value = strtok(NULL, "=");
+
+ if (new_option->value != NULL) {
+
+ while(isspace(*new_option->value)) {
+ new_option->value++;
+ }
+
+ for (i = strlen(new_option->value); i > 0; i--) {
+ if (!isspace(new_option->value[i - 1])) break;
+ }
+
+ new_option->value[i] = '\0';
+ new_option->value = strdup(new_option->value);
+ }
+
+ /* Add to list */
+
+ DLIST_ADD(*options, new_option);
+
+ return True;
+}