diff options
Diffstat (limited to 'source3/libsmb')
-rw-r--r-- | source3/libsmb/libsmb_cache.c | 74 | ||||
-rw-r--r-- | source3/libsmb/libsmb_compat.c | 329 | ||||
-rw-r--r-- | source3/libsmb/libsmb_context.c | 1283 | ||||
-rw-r--r-- | source3/libsmb/libsmb_dir.c | 1928 | ||||
-rw-r--r-- | source3/libsmb/libsmb_file.c | 859 | ||||
-rw-r--r-- | source3/libsmb/libsmb_misc.c | 73 | ||||
-rw-r--r-- | source3/libsmb/libsmb_path.c | 399 | ||||
-rw-r--r-- | source3/libsmb/libsmb_printjob.c | 334 | ||||
-rw-r--r-- | source3/libsmb/libsmb_server.c | 675 | ||||
-rw-r--r-- | source3/libsmb/libsmb_stat.c | 302 | ||||
-rw-r--r-- | source3/libsmb/libsmb_xattr.c | 2293 | ||||
-rw-r--r-- | source3/libsmb/libsmbclient.c | 7233 |
12 files changed, 8385 insertions, 7397 deletions
diff --git a/source3/libsmb/libsmb_cache.c b/source3/libsmb/libsmb_cache.c index b98df024fa..e0571fa9fe 100644 --- a/source3/libsmb/libsmb_cache.c +++ b/source3/libsmb/libsmb_cache.c @@ -22,11 +22,8 @@ */ #include "includes.h" - -#include "include/libsmbclient.h" -#include "../include/libsmb_internal.h" - -int smbc_default_cache_functions(SMBCCTX * context); +#include "libsmbclient.h" +#include "libsmb_internal.h" /* * Structure we use if internal caching mechanism is used @@ -48,9 +45,13 @@ struct smbc_server_cache { * Add a new connection to the server cache. * This function is only used if the external cache is not enabled */ -static int smbc_add_cached_server(SMBCCTX * context, SMBCSRV * newsrv, - const char * server, const char * share, - const char * workgroup, const char * username) +int +SMBC_add_cached_server(SMBCCTX * context, + SMBCSRV * newsrv, + const char * server, + const char * share, + const char * workgroup, + const char * username) { struct smbc_server_cache * srvcache = NULL; @@ -88,7 +89,7 @@ static int smbc_add_cached_server(SMBCCTX * context, SMBCSRV * newsrv, goto failed; } - DLIST_ADD((context->server_cache), srvcache); + DLIST_ADD((context->cache.server_cache_data), srvcache); return 0; failed: @@ -108,13 +109,17 @@ static int smbc_add_cached_server(SMBCCTX * context, SMBCSRV * newsrv, * returns server handle on success, NULL on error (not found) * This function is only used if the external cache is not enabled */ -static SMBCSRV * smbc_get_cached_server(SMBCCTX * context, const char * server, - const char * share, const char * workgroup, const char * user) +SMBCSRV * +SMBC_get_cached_server(SMBCCTX * context, + const char * server, + const char * share, + const char * workgroup, + const char * user) { struct smbc_server_cache * srv = NULL; /* Search the cache lines */ - for (srv=((struct smbc_server_cache *)context->server_cache);srv;srv=srv->next) { + for (srv = context->cache.server_cache_data; srv; srv = srv->next) { if (strcmp(server,srv->server_name) == 0 && strcmp(workgroup,srv->workgroup) == 0 && @@ -146,7 +151,7 @@ static SMBCSRV * smbc_get_cached_server(SMBCCTX * context, const char * server, * a connection to the server (other than the * attribute server connection) is cool. */ - if (context->options.one_share_per_server) { + if (context->one_share_per_server) { /* * The currently connected share name * doesn't match the requested share, so @@ -156,7 +161,7 @@ static SMBCSRV * smbc_get_cached_server(SMBCCTX * context, const char * server, /* Sigh. Couldn't disconnect. */ cli_shutdown(srv->server->cli); srv->server->cli = NULL; - context->callbacks.remove_cached_srv_fn(context, srv->server); + context->cache.remove_cached_server_fn(context, srv->server); continue; } @@ -171,7 +176,7 @@ static SMBCSRV * smbc_get_cached_server(SMBCCTX * context, const char * server, /* Out of memory. */ cli_shutdown(srv->server->cli); srv->server->cli = NULL; - context->callbacks.remove_cached_srv_fn(context, srv->server); + context->cache.remove_cached_server_fn(context, srv->server); continue; } @@ -190,15 +195,17 @@ static SMBCSRV * smbc_get_cached_server(SMBCCTX * context, const char * server, * returns 0 on success * This function is only used if the external cache is not enabled */ -static int smbc_remove_cached_server(SMBCCTX * context, SMBCSRV * server) +int +SMBC_remove_cached_server(SMBCCTX * context, + SMBCSRV * server) { struct smbc_server_cache * srv = NULL; - for (srv=((struct smbc_server_cache *)context->server_cache);srv;srv=srv->next) { + for (srv = context->cache.server_cache_data; srv; srv = srv->next) { if (server == srv->server) { /* remove this sucker */ - DLIST_REMOVE(context->server_cache, srv); + DLIST_REMOVE(context->cache.server_cache_data, srv); SAFE_FREE(srv->server_name); SAFE_FREE(srv->share_name); SAFE_FREE(srv->workgroup); @@ -216,40 +223,23 @@ static int smbc_remove_cached_server(SMBCCTX * context, SMBCSRV * server) * Try to remove all the servers in cache * returns 1 on failure and 0 if all servers could be removed. */ -static int smbc_purge_cached(SMBCCTX * context) +int +SMBC_purge_cached_servers(SMBCCTX * context) { struct smbc_server_cache * srv; struct smbc_server_cache * next; int could_not_purge_all = 0; - for (srv = ((struct smbc_server_cache *) context->server_cache), - next = (srv ? srv->next :NULL); + for (srv = context->cache.server_cache_data, + next = (srv ? srv->next :NULL); srv; - srv = next, next = (srv ? srv->next : NULL)) { + srv = next, + next = (srv ? srv->next : NULL)) { - if (smbc_remove_unused_server(context, srv->server)) { + if (SMBC_remove_unused_server(context, srv->server)) { /* could not be removed */ could_not_purge_all = 1; } } return could_not_purge_all; } - - - -/* - * This functions initializes all server-cache related functions - * to the default (internal) system. - * - * We use this to make the rest of the cache system static. - */ - -int smbc_default_cache_functions(SMBCCTX * context) -{ - context->callbacks.add_cached_srv_fn = smbc_add_cached_server; - context->callbacks.get_cached_srv_fn = smbc_get_cached_server; - context->callbacks.remove_cached_srv_fn = smbc_remove_cached_server; - context->callbacks.purge_cached_fn = smbc_purge_cached; - - return 0; -} diff --git a/source3/libsmb/libsmb_compat.c b/source3/libsmb/libsmb_compat.c index 6042464fd2..7a0536a92d 100644 --- a/source3/libsmb/libsmb_compat.c +++ b/source3/libsmb/libsmb_compat.c @@ -5,7 +5,7 @@ Copyright (C) Richard Sharpe 2000 Copyright (C) John Terpstra 2000 Copyright (C) Tom Jansen (Ninja ISD) 2002 - Copyright (C) Derrell Lipman 2003 + Copyright (C) Derrell Lipman 2003, 2008 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 @@ -23,8 +23,7 @@ #include "includes.h" - -#include "include/libsmb_internal.h" +#include "libsmb_internal.h" struct smbc_compat_fdlist { SMBCFILE * file; @@ -39,7 +38,8 @@ static struct smbc_compat_fdlist * smbc_compat_fd_in_use = NULL; static struct smbc_compat_fdlist * smbc_compat_fd_avail = NULL; /* Find an fd and return the SMBCFILE * or NULL on failure */ -static SMBCFILE * find_fd(int fd) +static SMBCFILE * +find_fd(int fd) { struct smbc_compat_fdlist * f = smbc_compat_fd_in_use; while (f) { @@ -51,7 +51,8 @@ static SMBCFILE * find_fd(int fd) } /* Add an fd, returns 0 on success, -1 on error with errno set */ -static int add_fd(SMBCFILE * file) +static int +add_fd(SMBCFILE * file) { struct smbc_compat_fdlist * f = smbc_compat_fd_avail; @@ -90,7 +91,8 @@ static int add_fd(SMBCFILE * file) /* Delete an fd, returns 0 on success */ -static int del_fd(int fd) +static int +del_fd(int fd) { struct smbc_compat_fdlist * f = smbc_compat_fd_in_use; @@ -112,15 +114,17 @@ static int del_fd(int fd) -int smbc_init(smbc_get_auth_data_fn fn, int debug) +int +smbc_init(smbc_get_auth_data_fn fn, + int debug) { if (!smbc_compat_initialized) { statcont = smbc_new_context(); if (!statcont) return -1; - statcont->debug = debug; - statcont->callbacks.auth_fn = fn; + smbc_setDebug(statcont, debug); + smbc_setFunctionAuthData(statcont, fn); if (!smbc_init_context(statcont)) { smbc_free_context(statcont, False); @@ -135,7 +139,8 @@ int smbc_init(smbc_get_auth_data_fn fn, int debug) } -SMBCCTX *smbc_set_context(SMBCCTX * context) +SMBCCTX * +smbc_set_context(SMBCCTX * context) { SMBCCTX *old_context = statcont; @@ -151,307 +156,387 @@ SMBCCTX *smbc_set_context(SMBCCTX * context) } -int smbc_open(const char *furl, int flags, mode_t mode) +int +smbc_open(const char *furl, + int flags, + mode_t mode) { SMBCFILE * file; int fd; - file = (statcont->open)(statcont, furl, flags, mode); + file = smbc_getFunctionOpen(statcont)(statcont, furl, flags, mode); if (!file) return -1; fd = add_fd(file); if (fd == -1) - (statcont->close_fn)(statcont, file); + smbc_getFunctionClose(statcont)(statcont, file); return fd; } -int smbc_creat(const char *furl, mode_t mode) +int +smbc_creat(const char *furl, + mode_t mode) { SMBCFILE * file; int fd; - file = (statcont->creat)(statcont, furl, mode); + file = smbc_getFunctionCreat(statcont)(statcont, furl, mode); if (!file) return -1; fd = add_fd(file); if (fd == -1) { /* Hmm... should we delete the file too ? I guess we could try */ - (statcont->close_fn)(statcont, file); - (statcont->unlink)(statcont, furl); + smbc_getFunctionClose(statcont)(statcont, file); + smbc_getFunctionUnlink(statcont)(statcont, furl); } return fd; } -ssize_t smbc_read(int fd, void *buf, size_t bufsize) +ssize_t +smbc_read(int fd, + void *buf, + size_t bufsize) { SMBCFILE * file = find_fd(fd); - return (statcont->read)(statcont, file, buf, bufsize); + return smbc_getFunctionRead(statcont)(statcont, file, buf, bufsize); } -ssize_t smbc_write(int fd, void *buf, size_t bufsize) +ssize_t +smbc_write(int fd, + void *buf, + size_t bufsize) { SMBCFILE * file = find_fd(fd); - return (statcont->write)(statcont, file, buf, bufsize); + return smbc_getFunctionWrite(statcont)(statcont, file, buf, bufsize); } -off_t smbc_lseek(int fd, off_t offset, int whence) +off_t +smbc_lseek(int fd, + off_t offset, + int whence) { SMBCFILE * file = find_fd(fd); - return (statcont->lseek)(statcont, file, offset, whence); + return smbc_getFunctionLseek(statcont)(statcont, file, offset, whence); } -int smbc_close(int fd) +int +smbc_close(int fd) { SMBCFILE * file = find_fd(fd); del_fd(fd); - return (statcont->close_fn)(statcont, file); + return smbc_getFunctionClose(statcont)(statcont, file); } -int smbc_unlink(const char *fname) +int +smbc_unlink(const char *fname) { - return (statcont->unlink)(statcont, fname); + return smbc_getFunctionUnlink(statcont)(statcont, fname); } -int smbc_rename(const char *ourl, const char *nurl) +int +smbc_rename(const char *ourl, + const char *nurl) { - return (statcont->rename)(statcont, ourl, statcont, nurl); + return smbc_getFunctionRename(statcont)(statcont, ourl, + statcont, nurl); } -int smbc_opendir(const char *durl) +int +smbc_opendir(const char *durl) { SMBCFILE * file; int fd; - file = (statcont->opendir)(statcont, durl); + file = smbc_getFunctionOpendir(statcont)(statcont, durl); if (!file) return -1; fd = add_fd(file); if (fd == -1) - (statcont->closedir)(statcont, file); + smbc_getFunctionClosedir(statcont)(statcont, file); return fd; } -int smbc_closedir(int dh) +int +smbc_closedir(int dh) { SMBCFILE * file = find_fd(dh); del_fd(dh); - return (statcont->closedir)(statcont, file); + return smbc_getFunctionClosedir(statcont)(statcont, file); } -int smbc_getdents(unsigned int dh, struct smbc_dirent *dirp, int count) +int +smbc_getdents(unsigned int dh, + struct smbc_dirent *dirp, + int count) { SMBCFILE * file = find_fd(dh); - return (statcont->getdents)(statcont, file,dirp, count); + return smbc_getFunctionGetdents(statcont)(statcont, file, dirp, count); } -struct smbc_dirent* smbc_readdir(unsigned int dh) +struct smbc_dirent * +smbc_readdir(unsigned int dh) { SMBCFILE * file = find_fd(dh); - return (statcont->readdir)(statcont, file); + return smbc_getFunctionReaddir(statcont)(statcont, file); } -off_t smbc_telldir(int dh) +off_t +smbc_telldir(int dh) { SMBCFILE * file = find_fd(dh); - return (statcont->telldir)(statcont, file); + return smbc_getFunctionTelldir(statcont)(statcont, file); } -int smbc_lseekdir(int fd, off_t offset) +int +smbc_lseekdir(int fd, + off_t offset) { SMBCFILE * file = find_fd(fd); - return (statcont->lseekdir)(statcont, file, offset); + return smbc_getFunctionLseekdir(statcont)(statcont, file, offset); } -int smbc_mkdir(const char *durl, mode_t mode) +int +smbc_mkdir(const char *durl, + mode_t mode) { - return (statcont->mkdir)(statcont, durl, mode); + return smbc_getFunctionMkdir(statcont)(statcont, durl, mode); } -int smbc_rmdir(const char *durl) +int +smbc_rmdir(const char *durl) { - return (statcont->rmdir)(statcont, durl); + return smbc_getFunctionRmdir(statcont)(statcont, durl); } -int smbc_stat(const char *url, struct stat *st) +int +smbc_stat(const char *url, + struct stat *st) { - return (statcont->stat)(statcont, url, st); + return smbc_getFunctionStat(statcont)(statcont, url, st); } -int smbc_fstat(int fd, struct stat *st) +int +smbc_fstat(int fd, + struct stat *st) { SMBCFILE * file = find_fd(fd); - return (statcont->fstat)(statcont, file, st); + return smbc_getFunctionFstat(statcont)(statcont, file, st); } -int smbc_ftruncate(int fd, off_t size) +int +smbc_ftruncate(int fd, + off_t size) { SMBCFILE * file = find_fd(fd); - return (statcont->ftruncate)(statcont, file, size); + return smbc_getFunctionFtruncate(statcont)(statcont, file, size); } -int smbc_chmod(const char *url, mode_t mode) +int +smbc_chmod(const char *url, + mode_t mode) { - return (statcont->chmod)(statcont, url, mode); + return smbc_getFunctionChmod(statcont)(statcont, url, mode); } -int smbc_utimes(const char *fname, struct timeval *tbuf) +int +smbc_utimes(const char *fname, + struct timeval *tbuf) { - return (statcont->utimes)(statcont, fname, tbuf); + return smbc_getFunctionUtimes(statcont)(statcont, fname, tbuf); } #ifdef HAVE_UTIME_H -int smbc_utime(const char *fname, struct utimbuf *utbuf) +int +smbc_utime(const char *fname, + struct utimbuf *utbuf) { struct timeval tv[2]; if (utbuf == NULL) - return (statcont->utimes)(statcont, fname, NULL); + return smbc_getFunctionUtimes(statcont)(statcont, fname, NULL); tv[0].tv_sec = utbuf->actime; tv[1].tv_sec = utbuf->modtime; tv[0].tv_usec = tv[1].tv_usec = 0; - return (statcont->utimes)(statcont, fname, tv); + return smbc_getFunctionUtimes(statcont)(statcont, fname, tv); } #endif -int smbc_setxattr(const char *fname, - const char *name, - const void *value, - size_t size, - int flags) +int +smbc_setxattr(const char *fname, + const char *name, + const void *value, + size_t size, + int flags) { - return (statcont->setxattr)(statcont, fname, name, value, size, flags); + return smbc_getFunctionSetxattr(statcont)(statcont, + fname, name, + value, size, flags); } -int smbc_lsetxattr(const char *fname, - const char *name, - const void *value, - size_t size, - int flags) +int +smbc_lsetxattr(const char *fname, + const char *name, + const void *value, + size_t size, + int flags) { - return (statcont->setxattr)(statcont, fname, name, value, size, flags); + return smbc_getFunctionSetxattr(statcont)(statcont, + fname, name, + value, size, flags); } -int smbc_fsetxattr(int fd, - const char *name, - const void *value, - size_t size, - int flags) +int +smbc_fsetxattr(int fd, + const char *name, + const void *value, + size_t size, + int flags) { SMBCFILE * file = find_fd(fd); if (file == NULL) { errno = EBADF; return -1; } - return (statcont->setxattr)(statcont, file->fname, - name, value, size, flags); + return smbc_getFunctionSetxattr(statcont)(statcont, + file->fname, name, + value, size, flags); } -int smbc_getxattr(const char *fname, - const char *name, - const void *value, - size_t size) +int +smbc_getxattr(const char *fname, + const char *name, + const void *value, + size_t size) { - return (statcont->getxattr)(statcont, fname, name, value, size); + return smbc_getFunctionGetxattr(statcont)(statcont, + fname, name, + value, size); } -int smbc_lgetxattr(const char *fname, - const char *name, - const void *value, - size_t size) +int +smbc_lgetxattr(const char *fname, + const char *name, + const void *value, + size_t size) { - return (statcont->getxattr)(statcont, fname, name, value, size); + return smbc_getFunctionGetxattr(statcont)(statcont, + fname, name, + value, size); } -int smbc_fgetxattr(int fd, - const char *name, - const void *value, - size_t size) +int +smbc_fgetxattr(int fd, + const char *name, + const void *value, + size_t size) { SMBCFILE * file = find_fd(fd); if (file == NULL) { errno = EBADF; return -1; } - return (statcont->getxattr)(statcont, file->fname, name, value, size); + return smbc_getFunctionGetxattr(statcont)(statcont, + file->fname, name, + value, size); } -int smbc_removexattr(const char *fname, - const char *name) +int +smbc_removexattr(const char *fname, + const char *name) { - return (statcont->removexattr)(statcont, fname, name); + return smbc_getFunctionRemovexattr(statcont)(statcont, fname, name); } -int smbc_lremovexattr(const char *fname, - const char *name) +int +smbc_lremovexattr(const char *fname, + const char *name) { - return (statcont->removexattr)(statcont, fname, name); + return smbc_getFunctionRemovexattr(statcont)(statcont, fname, name); } -int smbc_fremovexattr(int fd, - const char *name) +int +smbc_fremovexattr(int fd, + const char *name) { SMBCFILE * file = find_fd(fd); if (file == NULL) { errno = EBADF; return -1; } - return (statcont->removexattr)(statcont, file->fname, name); + return smbc_getFunctionRemovexattr(statcont)(statcont, + file->fname, name); } -int smbc_listxattr(const char *fname, - char *list, - size_t size) +int +smbc_listxattr(const char *fname, + char *list, + size_t size) { - return (statcont->listxattr)(statcont, fname, list, size); + return smbc_getFunctionListxattr(statcont)(statcont, + fname, list, size); } -int smbc_llistxattr(const char *fname, - char *list, - size_t size) +int +smbc_llistxattr(const char *fname, + char *list, + size_t size) { - return (statcont->listxattr)(statcont, fname, list, size); + return smbc_getFunctionListxattr(statcont)(statcont, + fname, list, size); } -int smbc_flistxattr(int fd, - char *list, - size_t size) +int +smbc_flistxattr(int fd, + char *list, + size_t size) { SMBCFILE * file = find_fd(fd); if (file == NULL) { errno = EBADF; return -1; } - return (statcont->listxattr)(statcont, file->fname, list, size); + return smbc_getFunctionListxattr(statcont)(statcont, + file->fname, list, size); } -int smbc_print_file(const char *fname, const char *printq) +int +smbc_print_file(const char *fname, + const char *printq) { - return (statcont->print_file)(statcont, fname, statcont, printq); + return smbc_getFunctionPrintFile(statcont)(statcont, fname, + statcont, printq); } -int smbc_open_print_job(const char *fname) +int +smbc_open_print_job(const char *fname) { - SMBCFILE * file = (statcont->open_print_job)(statcont, fname); + SMBCFILE * file; + + file = smbc_getFunctionOpenPrintJob(statcont)(statcont, fname); if (!file) return -1; return file->cli_fd; } -int smbc_list_print_jobs(const char *purl, smbc_list_print_job_fn fn) +int +smbc_list_print_jobs(const char *purl, + smbc_list_print_job_fn fn) { - return (statcont->list_print_jobs)(statcont, purl, fn); + return smbc_getFunctionListPrintJobs(statcont)(statcont, purl, fn); } -int smbc_unlink_print_job(const char *purl, int id) +int +smbc_unlink_print_job(const char *purl, + int id) { - return (statcont->unlink_print_job)(statcont, purl, id); + return smbc_getFunctionUnlinkPrintJob(statcont)(statcont, purl, id); } diff --git a/source3/libsmb/libsmb_context.c b/source3/libsmb/libsmb_context.c new file mode 100644 index 0000000000..1505d50a43 --- /dev/null +++ b/source3/libsmb/libsmb_context.c @@ -0,0 +1,1283 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/* + * Is the logging working / configfile read ? + */ +static int SMBC_initialized = 0; + + + +/* + * Get a new empty handle to fill in with your own info + */ +SMBCCTX * +smbc_new_context(void) +{ + SMBCCTX *context; + + context = SMB_MALLOC_P(SMBCCTX); + if (!context) { + errno = ENOMEM; + return NULL; + } + + + /* Initialize the context and establish reasonable defaults */ + ZERO_STRUCTP(context); + + context->debug = 0; + context->timeout = 20000; /* 20 seconds */ + + context->full_time_names = False; + context->share_mode = SMBC_SHAREMODE_DENY_NONE; + context->smb_encryption_level = 0; + context->browse_max_lmb_count = 3; /* # LMBs to query */ + context->urlencode_readdir_entries = False; + context->one_share_per_server = False; + context->use_kerberos = False; + context->fallback_after_kerberos = False; + context->no_auto_anonymous_login = False; + + context->server.get_auth_data_fn = SMBC_get_auth_data; + context->server.check_server_fn = SMBC_check_server; + context->server.remove_unused_server_fn = SMBC_remove_unused_server; + + context->cache.server_cache_data = NULL; + context->cache.add_cached_server_fn = SMBC_add_cached_server; + context->cache.get_cached_server_fn = SMBC_get_cached_server; + context->cache.remove_cached_server_fn = SMBC_remove_cached_server; + context->cache.purge_cached_server_fn = SMBC_purge_cached_servers; + + context->posix_emu.open_fn = SMBC_open_ctx; + context->posix_emu.creat_fn = SMBC_creat_ctx; + context->posix_emu.read_fn = SMBC_read_ctx; + context->posix_emu.write_fn = SMBC_write_ctx; + context->posix_emu.close_fn = SMBC_close_ctx; + context->posix_emu.unlink_fn = SMBC_unlink_ctx; + context->posix_emu.rename_fn = SMBC_rename_ctx; + context->posix_emu.lseek_fn = SMBC_lseek_ctx; + context->posix_emu.ftruncate_fn = SMBC_ftruncate_ctx; + context->posix_emu.stat_fn = SMBC_stat_ctx; + context->posix_emu.fstat_fn = SMBC_fstat_ctx; + context->posix_emu.opendir_fn = SMBC_opendir_ctx; + context->posix_emu.closedir_fn = SMBC_closedir_ctx; + context->posix_emu.readdir_fn = SMBC_readdir_ctx; + context->posix_emu.getdents_fn = SMBC_getdents_ctx; + context->posix_emu.mkdir_fn = SMBC_mkdir_ctx; + context->posix_emu.rmdir_fn = SMBC_rmdir_ctx; + context->posix_emu.telldir_fn = SMBC_telldir_ctx; + context->posix_emu.lseekdir_fn = SMBC_lseekdir_ctx; + context->posix_emu.fstatdir_fn = SMBC_fstatdir_ctx; + context->posix_emu.chmod_fn = SMBC_chmod_ctx; + context->posix_emu.utimes_fn = SMBC_utimes_ctx; + context->posix_emu.setxattr_fn = SMBC_setxattr_ctx; + context->posix_emu.getxattr_fn = SMBC_getxattr_ctx; + context->posix_emu.removexattr_fn = SMBC_removexattr_ctx; + context->posix_emu.listxattr_fn = SMBC_listxattr_ctx; + + context->printing.open_print_job_fn = SMBC_open_print_job_ctx; + context->printing.print_file_fn = SMBC_print_file_ctx; + context->printing.list_print_jobs_fn = SMBC_list_print_jobs_ctx; + context->printing.unlink_print_job_fn = SMBC_unlink_print_job_ctx; + + return context; +} + +/* + * Free a context + * + * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed + * and thus you'll be leaking memory if not handled properly. + * + */ +int +smbc_free_context(SMBCCTX *context, + int shutdown_ctx) +{ + if (!context) { + errno = EBADF; + return 1; + } + + if (shutdown_ctx) { + SMBCFILE * f; + DEBUG(1,("Performing aggressive shutdown.\n")); + + f = context->files; + while (f) { + (context->posix_emu.close_fn)(context, f); + f = f->next; + } + context->files = NULL; + + /* First try to remove the servers the nice way. */ + if (context->cache.purge_cached_server_fn(context)) { + SMBCSRV * s; + SMBCSRV * next; + DEBUG(1, ("Could not purge all servers, " + "Nice way shutdown failed.\n")); + s = context->servers; + while (s) { + DEBUG(1, ("Forced shutdown: %p (fd=%d)\n", + s, s->cli->fd)); + cli_shutdown(s->cli); + (context->cache.remove_cached_server_fn)(context, + s); + next = s->next; + DLIST_REMOVE(context->servers, s); + SAFE_FREE(s); + s = next; + } + context->servers = NULL; + } + } + else { + /* This is the polite way */ + if ((context->cache.purge_cached_server_fn)(context)) { + DEBUG(1, ("Could not purge all servers, " + "free_context failed.\n")); + errno = EBUSY; + return 1; + } + if (context->servers) { + DEBUG(1, ("Active servers in context, " + "free_context failed.\n")); + errno = EBUSY; + return 1; + } + if (context->files) { + DEBUG(1, ("Active files in context, " + "free_context failed.\n")); + errno = EBUSY; + return 1; + } + } + + /* Things we have to clean up */ + SAFE_FREE(context->workgroup); + SAFE_FREE(context->netbios_name); + SAFE_FREE(context->user); + + DEBUG(3, ("Context %p successfully freed\n", context)); + SAFE_FREE(context); + return 0; +} + + +/* + * Each time the context structure is changed, we have binary backward + * compatibility issues. Instead of modifying the public portions of the + * context structure to add new options, instead, we put them in the internal + * portion of the context structure and provide a set function for these new + * options. + */ +void +smbc_option_set(SMBCCTX *context, + char *option_name, + ... /* option_value */) +{ + va_list ap; + union { + int i; + bool b; + smbc_get_auth_data_with_context_fn auth_fn; + void *v; + const char *s; + } option_value; + + va_start(ap, option_name); + + if (strcmp(option_name, "debug_to_stderr") == 0) { + /* + * Log to standard error instead of standard output. + */ + option_value.b = (bool) va_arg(ap, int); + context->debug_stderr = option_value.b; + + } else if (strcmp(option_name, "full_time_names") == 0) { + /* + * Use new-style time attribute names, e.g. WRITE_TIME rather + * than the old-style names such as M_TIME. This allows also + * setting/getting CREATE_TIME which was previously + * unimplemented. (Note that the old C_TIME was supposed to + * be CHANGE_TIME but was confused and sometimes referred to + * CREATE_TIME.) + */ + option_value.b = (bool) va_arg(ap, int); + context->full_time_names = option_value.b; + + } else if (strcmp(option_name, "open_share_mode") == 0) { + /* + * The share mode to use for files opened with + * SMBC_open_ctx(). The default is SMBC_SHAREMODE_DENY_NONE. + */ + option_value.i = va_arg(ap, int); + context->share_mode = (smbc_share_mode) option_value.i; + + } else if (strcmp(option_name, "user_data") == 0) { + /* + * Save a user data handle which may be retrieved by the user + * with smbc_option_get() + */ + option_value.v = va_arg(ap, void *); + context->user_data = option_value.v; + } else if (strcmp(option_name, "smb_encrypt_level") == 0) { + /* + * Save an encoded value for encryption level. + * 0 = off, 1 = attempt, 2 = required. + */ + option_value.s = va_arg(ap, const char *); + if (strcmp(option_value.s, "none") == 0) { + context->smb_encryption_level = 0; + } else if (strcmp(option_value.s, "request") == 0) { + context->smb_encryption_level = 1; + } else if (strcmp(option_value.s, "require") == 0) { + context->smb_encryption_level = 2; + } + } else if (strcmp(option_name, "browse_max_lmb_count") == 0) { + /* + * From how many local master browsers should the list of + * workgroups be retrieved? It can take up to 12 minutes or + * longer after a server becomes a local master browser, for + * it to have the entire browse list (the list of + * workgroups/domains) from an entire network. Since a client + * never knows which local master browser will be found first, + * the one which is found first and used to retrieve a browse + * list may have an incomplete or empty browse list. By + * requesting the browse list from multiple local master + * browsers, a more complete list can be generated. For small + * networks (few workgroups), it is recommended that this + * value be set to 0, causing the browse lists from all found + * local master browsers to be retrieved and merged. For + * networks with many workgroups, a suitable value for this + * variable is probably somewhere around 3. (Default: 3). + */ + option_value.i = va_arg(ap, int); + context->browse_max_lmb_count = option_value.i; + + } else if (strcmp(option_name, "urlencode_readdir_entries") == 0) { + /* + * There is a difference in the desired return strings from + * smbc_readdir() depending upon whether the filenames are to + * be displayed to the user, or whether they are to be + * appended to the path name passed to smbc_opendir() to call + * a further smbc_ function (e.g. open the file with + * smbc_open()). In the former case, the filename should be + * in "human readable" form. In the latter case, the smbc_ + * functions expect a URL which must be url-encoded. Those + * functions decode the URL. If, for example, smbc_readdir() + * returned a file name of "abc%20def.txt", passing a path + * with this file name attached to smbc_open() would cause + * smbc_open to attempt to open the file "abc def.txt" since + * the %20 is decoded into a space. + * + * Set this option to True if the names returned by + * smbc_readdir() should be url-encoded such that they can be + * passed back to another smbc_ call. Set it to False if the + * names returned by smbc_readdir() are to be presented to the + * user. + * + * For backwards compatibility, this option defaults to False. + */ + option_value.b = (bool) va_arg(ap, int); + context->urlencode_readdir_entries = option_value.b; + + } else if (strcmp(option_name, "one_share_per_server") == 0) { + /* + * Some Windows versions appear to have a limit to the number + * of concurrent SESSIONs and/or TREE CONNECTions. In + * one-shot programs (i.e. the program runs and then quickly + * ends, thereby shutting down all connections), it is + * probably reasonable to establish a new connection for each + * share. In long-running applications, the limitation can be + * avoided by using only a single connection to each server, + * and issuing a new TREE CONNECT when the share is accessed. + */ + option_value.b = (bool) va_arg(ap, int); + context->one_share_per_server = option_value.b; + + } else if (strcmp(option_name, "use_kerberos") == 0) { + option_value.b = (bool) va_arg(ap, int); + context->use_kerberos = option_value.b; + + } else if (strcmp(option_name, "fallback_after_kerberos") == 0) { + option_value.b = (bool) va_arg(ap, int); + context->fallback_after_kerberos = option_value.b; + + } else if (strcmp(option_name, "no_auto_anonymous_login") == 0) { + option_value.b = (bool) va_arg(ap, int); + context->no_auto_anonymous_login = option_value.b; + } + + va_end(ap); +} + + +/* + * Retrieve the current value of an option + */ +void * +smbc_option_get(SMBCCTX *context, + char *option_name) +{ + if (strcmp(option_name, "debug_stderr") == 0) { + /* + * Log to standard error instead of standard output. + */ +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) context->debug_stderr; +#else + return (void *) context->debug_stderr; +#endif + + } else if (strcmp(option_name, "full_time_names") == 0) { + /* + * Use new-style time attribute names, e.g. WRITE_TIME rather + * than the old-style names such as M_TIME. This allows also + * setting/getting CREATE_TIME which was previously + * unimplemented. (Note that the old C_TIME was supposed to + * be CHANGE_TIME but was confused and sometimes referred to + * CREATE_TIME.) + */ +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) context->full_time_names; +#else + return (void *) context->full_time_names; +#endif + + } else if (strcmp(option_name, "user_data") == 0) { + /* + * Return the user data handle which was saved by the user + * with smbc_option_set() + */ + return context->user_data; + + } else if (strcmp(option_name, "smb_encrypt_level") == 0) { + /* + * Return the current smb encrypt negotiate option as a string. + */ + switch (context->smb_encryption_level) { + case 0: + return (void *) "none"; + case 1: + return (void *) "request"; + case 2: + return (void *) "require"; + } + + } else if (strcmp(option_name, "smb_encrypt_on") == 0) { + /* + * Return the current smb encrypt status option as a bool. + * false = off, true = on. We don't know what server is + * being requested, so we only return true if all servers + * are using an encrypted connection. + */ + SMBCSRV *s; + unsigned int num_servers = 0; + + for (s = context->servers; s; s = s->next) { + num_servers++; + if (s->cli->trans_enc_state == NULL) { + return (void *)false; + } + } +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) (bool) (num_servers > 0); +#else + return (void *) (bool) (num_servers > 0); +#endif + + } else if (strcmp(option_name, "browse_max_lmb_count") == 0) { + /* + * From how many local master browsers should the list of + * workgroups be retrieved? It can take up to 12 minutes or + * longer after a server becomes a local master browser, for + * it to have the entire browse list (the list of + * workgroups/domains) from an entire network. Since a client + * never knows which local master browser will be found first, + * the one which is found first and used to retrieve a browse + * list may have an incomplete or empty browse list. By + * requesting the browse list from multiple local master + * browsers, a more complete list can be generated. For small + * networks (few workgroups), it is recommended that this + * value be set to 0, causing the browse lists from all found + * local master browsers to be retrieved and merged. For + * networks with many workgroups, a suitable value for this + * variable is probably somewhere around 3. (Default: 3). + */ +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) context->browse_max_lmb_count; +#else + return (void *) context->browse_max_lmb_count; +#endif + + } else if (strcmp(option_name, "urlencode_readdir_entries") == 0) { + /* + * There is a difference in the desired return strings from + * smbc_readdir() depending upon whether the filenames are to + * be displayed to the user, or whether they are to be + * appended to the path name passed to smbc_opendir() to call + * a further smbc_ function (e.g. open the file with + * smbc_open()). In the former case, the filename should be + * in "human readable" form. In the latter case, the smbc_ + * functions expect a URL which must be url-encoded. Those + * functions decode the URL. If, for example, smbc_readdir() + * returned a file name of "abc%20def.txt", passing a path + * with this file name attached to smbc_open() would cause + * smbc_open to attempt to open the file "abc def.txt" since + * the %20 is decoded into a space. + * + * Set this option to True if the names returned by + * smbc_readdir() should be url-encoded such that they can be + * passed back to another smbc_ call. Set it to False if the + * names returned by smbc_readdir() are to be presented to the + * user. + * + * For backwards compatibility, this option defaults to False. + */ +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) context->urlencode_readdir_entries; +#else + return (void *) (bool) context->urlencode_readdir_entries; +#endif + + } else if (strcmp(option_name, "one_share_per_server") == 0) { + /* + * Some Windows versions appear to have a limit to the number + * of concurrent SESSIONs and/or TREE CONNECTions. In + * one-shot programs (i.e. the program runs and then quickly + * ends, thereby shutting down all connections), it is + * probably reasonable to establish a new connection for each + * share. In long-running applications, the limitation can be + * avoided by using only a single connection to each server, + * and issuing a new TREE CONNECT when the share is accessed. + */ +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) context->one_share_per_server; +#else + return (void *) (bool) context->one_share_per_server; +#endif + + } else if (strcmp(option_name, "use_kerberos") == 0) { +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) context->use_kerberos; +#else + return (void *) (bool) context->use_kerberos; +#endif + + } else if (strcmp(option_name, "fallback_after_kerberos") == 0) { +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) context->fallback_after_kerberos; +#else + return (void *) (bool) context->fallback_after_kerberos; +#endif + + } else if (strcmp(option_name, "no_auto_anonymous_login") == 0) { +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) context->no_auto_anonymous_login; +#else + return (void *) (bool) context->no_auto_anonymous_login; +#endif + } + + return NULL; +} + + +/* + * Initialize the library, etc. + * + * We accept a struct containing handle information. + * valid values for info->debug from 0 to 100, + * and insist that info->fn must be non-null. + */ +SMBCCTX * +smbc_init_context(SMBCCTX *context) +{ + int pid; + char *user = NULL; + char *home = NULL; + extern bool in_client; + + if (!context) { + errno = EBADF; + return NULL; + } + + /* Do not initialise the same client twice */ + if (context->initialized) { + return 0; + } + + if (!context->server.get_auth_data_fn || + context->debug < 0 || + context->debug > 100) { + + errno = EINVAL; + return NULL; + + } + + if (!SMBC_initialized) { + /* + * Do some library-wide intializations the first time we get + * called + */ + bool conf_loaded = False; + TALLOC_CTX *frame = talloc_stackframe(); + + /* Set this to what the user wants */ + DEBUGLEVEL = context->debug; + + load_case_tables(); + + setup_logging("libsmbclient", True); + if (context->debug_stderr) { + dbf = x_stderr; + x_setbuf(x_stderr, NULL); + } + + /* Here we would open the smb.conf file if needed ... */ + + in_client = True; /* FIXME, make a param */ + + home = getenv("HOME"); + if (home) { + char *conf = NULL; + if (asprintf(&conf, "%s/.smb/smb.conf", home) > 0) { + if (lp_load(conf, True, False, False, True)) { + conf_loaded = True; + } else { + DEBUG(5, ("Could not load config file: %s\n", + conf)); + } + SAFE_FREE(conf); + } + } + + if (!conf_loaded) { + /* + * Well, if that failed, try the get_dyn_CONFIGFILE + * Which points to the standard locn, and if that + * fails, silently ignore it and use the internal + * defaults ... + */ + + if (!lp_load(get_dyn_CONFIGFILE(), True, False, False, False)) { + DEBUG(5, ("Could not load config file: %s\n", + get_dyn_CONFIGFILE())); + } else if (home) { + char *conf; + /* + * We loaded the global config file. Now lets + * load user-specific modifications to the + * global config. + */ + if (asprintf(&conf, + "%s/.smb/smb.conf.append", + home) > 0) { + if (!lp_load(conf, True, False, False, False)) { + DEBUG(10, + ("Could not append config file: " + "%s\n", + conf)); + } + SAFE_FREE(conf); + } + } + } + + load_interfaces(); /* Load the list of interfaces ... */ + + reopen_logs(); /* Get logging working ... */ + + /* + * Block SIGPIPE (from lib/util_sock.c: write()) + * It is not needed and should not stop execution + */ + BlockSignals(True, SIGPIPE); + + /* Done with one-time initialisation */ + SMBC_initialized = 1; + + TALLOC_FREE(frame); + } + + if (!context->user) { + /* + * FIXME: Is this the best way to get the user info? + */ + user = getenv("USER"); + /* walk around as "guest" if no username can be found */ + if (!user) context->user = SMB_STRDUP("guest"); + else context->user = SMB_STRDUP(user); + } + + if (!context->netbios_name) { + /* + * We try to get our netbios name from the config. If that + * fails we fall back on constructing our netbios name from + * our hostname etc + */ + if (global_myname()) { + context->netbios_name = SMB_STRDUP(global_myname()); + } + else { + /* + * Hmmm, I want to get hostname as well, but I am too + * lazy for the moment + */ + pid = sys_getpid(); + context->netbios_name = (char *)SMB_MALLOC(17); + if (!context->netbios_name) { + errno = ENOMEM; + return NULL; + } + slprintf(context->netbios_name, 16, + "smbc%s%d", context->user, pid); + } + } + + DEBUG(1, ("Using netbios name %s.\n", context->netbios_name)); + + if (!context->workgroup) { + if (lp_workgroup()) { + context->workgroup = SMB_STRDUP(lp_workgroup()); + } + else { + /* TODO: Think about a decent default workgroup */ + context->workgroup = SMB_STRDUP("samba"); + } + } + + DEBUG(1, ("Using workgroup %s.\n", context->workgroup)); + + /* shortest timeout is 1 second */ + if (context->timeout > 0 && context->timeout < 1000) + context->timeout = 1000; + + /* + * FIXME: Should we check the function pointers here? + */ + + context->initialized = True; + + return context; +} + + +/* Return the verion of samba, and thus libsmbclient */ +const char * +smbc_version(void) +{ + return samba_version_string(); +} + + +/** Get the netbios name used for making connections */ +char * +smbc_getNetbiosName(SMBCCTX *c) +{ + return c->netbios_name; +} + +/** Set the netbios name used for making connections */ +void +smbc_setNetbiosName(SMBCCTX *c, char * netbios_name) +{ + c->netbios_name = netbios_name; +} + +/** Get the workgroup used for making connections */ +char * +smbc_getWorkgroup(SMBCCTX *c) +{ + return c->workgroup; +} + +/** Set the workgroup used for making connections */ +void +smbc_setWorkgroup(SMBCCTX *c, char * workgroup) +{ + c->workgroup = workgroup; +} + +/** Get the username used for making connections */ +char * +smbc_getUser(SMBCCTX *c) +{ + return c->user; +} + +/** Set the username used for making connections */ +void +smbc_setUser(SMBCCTX *c, char * user) +{ + c->user = user; +} + +/** Get the debug level */ +int +smbc_getDebug(SMBCCTX *c) +{ + return c->debug; +} + +/** Set the debug level */ +void +smbc_setDebug(SMBCCTX *c, int debug) +{ + c->debug = debug; +} + +/** + * Get the timeout used for waiting on connections and response data + * (in milliseconds) + */ +int +smbc_getTimeout(SMBCCTX *c) +{ + return c->timeout; +} + +/** + * Set the timeout used for waiting on connections and response data + * (in milliseconds) + */ +void +smbc_setTimeout(SMBCCTX *c, int timeout) +{ + c->timeout = timeout; +} + +/** Get the function for obtaining authentication data */ + +smbc_get_auth_data_fn +smbc_getFunctionAuthData(SMBCCTX *c) +{ + return c->server.get_auth_data_fn; +} + +/** Set the function for obtaining authentication data */ +void +smbc_setFunctionAuthData(SMBCCTX *c, smbc_get_auth_data_fn fn) +{ + c->server.get_auth_data_fn = fn; +} + +/** Get the function for checking if a server is still good */ +smbc_check_server_fn +smbc_getFunctionCheckServer(SMBCCTX *c) +{ + return c->server.check_server_fn; +} + +/** Set the function for checking if a server is still good */ +void +smbc_setFunctionCheckServer(SMBCCTX *c, smbc_check_server_fn fn) +{ + c->server.check_server_fn = fn; +} + +/** Get the function for removing a server if unused */ +smbc_remove_unused_server_fn +smbc_getFunctionRemoveUnusedServer(SMBCCTX *c) +{ + return c->server.remove_unused_server_fn; +} + +/** Set the function for removing a server if unused */ +void +smbc_setFunctionRemoveUnusedServer(SMBCCTX *c, + smbc_remove_unused_server_fn fn) +{ + c->server.remove_unused_server_fn = fn; +} + +/** Get the function to store private data of the server cache */ +struct +smbc_server_cache * smbc_getServerCacheData(SMBCCTX *c) +{ + return c->cache.server_cache_data; +} + +/** Set the function to store private data of the server cache */ +void +smbc_setServerCacheData(SMBCCTX *c, struct smbc_server_cache * cache) +{ + c->cache.server_cache_data = cache; +} + + +/** Get the function for adding a cached server */ +smbc_add_cached_srv_fn +smbc_getFunctionAddCachedServer(SMBCCTX *c) +{ + return c->cache.add_cached_server_fn; +} + +/** Set the function for adding a cached server */ +void +smbc_setFunctionAddCachedServer(SMBCCTX *c, smbc_add_cached_srv_fn fn) +{ + c->cache.add_cached_server_fn = fn; +} + +/** Get the function for server cache lookup */ +smbc_get_cached_srv_fn +smbc_getFunctionGetCachedServer(SMBCCTX *c) +{ + return c->cache.get_cached_server_fn; +} + +/** Set the function for server cache lookup */ +void +smbc_setFunctionGetCachedServer(SMBCCTX *c, smbc_get_cached_srv_fn fn) +{ + c->cache.get_cached_server_fn = fn; +} + +/** Get the function for server cache removal */ +smbc_remove_cached_srv_fn +smbc_getFunctionRemoveCachedServer(SMBCCTX *c) +{ + return c->cache.remove_cached_server_fn; +} + +/** Set the function for server cache removal */ +void +smbc_setFunctionRemoveCachedServer(SMBCCTX *c, + smbc_remove_cached_srv_fn fn) +{ + c->cache.remove_cached_server_fn = fn; +} + +/** + * Get the function for server cache purging. This function tries to + * remove all cached servers (e.g. on disconnect) + */ +smbc_purge_cached_srv_fn +smbc_getFunctionPurgeCachedServers(SMBCCTX *c) +{ + return c->cache.purge_cached_server_fn; +} + +/** + * Set the function for server cache purging. This function tries to + * remove all cached servers (e.g. on disconnect) + */ +void +smbc_setFunctionPurgeCachedServers(SMBCCTX *c, smbc_purge_cached_srv_fn fn) +{ + c->cache.purge_cached_server_fn = fn; +} + +/** + * Callable functions for files. + */ + +smbc_open_fn +smbc_getFunctionOpen(SMBCCTX *c) +{ + return c->posix_emu.open_fn; +} + +void +smbc_setFunctionOpen(SMBCCTX *c, smbc_open_fn fn) +{ + c->posix_emu.open_fn = fn; +} + +smbc_creat_fn +smbc_getFunctionCreat(SMBCCTX *c) +{ + return c->posix_emu.creat_fn; +} + +void +smbc_setFunctionCreat(SMBCCTX *c, smbc_creat_fn fn) +{ + c->posix_emu.creat_fn = fn; +} + +smbc_read_fn +smbc_getFunctionRead(SMBCCTX *c) +{ + return c->posix_emu.read_fn; +} + +void +smbc_setFunctionRead(SMBCCTX *c, smbc_read_fn fn) +{ + c->posix_emu.read_fn = fn; +} + +smbc_write_fn +smbc_getFunctionWrite(SMBCCTX *c) +{ + return c->posix_emu.write_fn; +} + +void +smbc_setFunctionWrite(SMBCCTX *c, smbc_write_fn fn) +{ + c->posix_emu.write_fn = fn; +} + +smbc_unlink_fn +smbc_getFunctionUnlink(SMBCCTX *c) +{ + return c->posix_emu.unlink_fn; +} + +void +smbc_setFunctionUnlink(SMBCCTX *c, smbc_unlink_fn fn) +{ + c->posix_emu.unlink_fn = fn; +} + +smbc_rename_fn +smbc_getFunctionRename(SMBCCTX *c) +{ + return c->posix_emu.rename_fn; +} + +void +smbc_setFunctionRename(SMBCCTX *c, smbc_rename_fn fn) +{ + c->posix_emu.rename_fn = fn; +} + +smbc_lseek_fn +smbc_getFunctionLseek(SMBCCTX *c) +{ + return c->posix_emu.lseek_fn; +} + +void +smbc_setFunctionLseek(SMBCCTX *c, smbc_lseek_fn fn) +{ + c->posix_emu.lseek_fn = fn; +} + +smbc_stat_fn +smbc_getFunctionStat(SMBCCTX *c) +{ + return c->posix_emu.stat_fn; +} + +void +smbc_setFunctionStat(SMBCCTX *c, smbc_stat_fn fn) +{ + c->posix_emu.stat_fn = fn; +} + +smbc_fstat_fn +smbc_getFunctionFstat(SMBCCTX *c) +{ + return c->posix_emu.fstat_fn; +} + +void +smbc_setFunctionFstat(SMBCCTX *c, smbc_fstat_fn fn) +{ + c->posix_emu.fstat_fn = fn; +} + +smbc_ftruncate_fn +smbc_getFunctionFtruncate(SMBCCTX *c) +{ + return c->posix_emu.ftruncate_fn; +} + +void +smbc_setFunctionFtruncate(SMBCCTX *c, smbc_ftruncate_fn fn) +{ + c->posix_emu.ftruncate_fn = fn; +} + +smbc_close_fn +smbc_getFunctionClose(SMBCCTX *c) +{ + return c->posix_emu.close_fn; +} + +void +smbc_setFunctionClose(SMBCCTX *c, smbc_close_fn fn) +{ + c->posix_emu.close_fn = fn; +} + + +/** + * Callable functions for directories. + */ + +smbc_opendir_fn +smbc_getFunctionOpendir(SMBCCTX *c) +{ + return c->posix_emu.opendir_fn; +} + +void +smbc_setFunctionOpendir(SMBCCTX *c, smbc_opendir_fn fn) +{ + c->posix_emu.opendir_fn = fn; +} + +smbc_closedir_fn +smbc_getFunctionClosedir(SMBCCTX *c) +{ + return c->posix_emu.closedir_fn; +} + +void +smbc_setFunctionClosedir(SMBCCTX *c, smbc_closedir_fn fn) +{ + c->posix_emu.closedir_fn = fn; +} + +smbc_readdir_fn +smbc_getFunctionReaddir(SMBCCTX *c) +{ + return c->posix_emu.readdir_fn; +} + +void +smbc_setFunctionReaddir(SMBCCTX *c, smbc_readdir_fn fn) +{ + c->posix_emu.readdir_fn = fn; +} + +smbc_getdents_fn +smbc_getFunctionGetdents(SMBCCTX *c) +{ + return c->posix_emu.getdents_fn; +} + +void +smbc_setFunctionGetdents(SMBCCTX *c, smbc_getdents_fn fn) +{ + c->posix_emu.getdents_fn = fn; +} + +smbc_mkdir_fn +smbc_getFunctionMkdir(SMBCCTX *c) +{ + return c->posix_emu.mkdir_fn; +} + +void +smbc_setFunctionMkdir(SMBCCTX *c, smbc_mkdir_fn fn) +{ + c->posix_emu.mkdir_fn = fn; +} + +smbc_rmdir_fn +smbc_getFunctionRmdir(SMBCCTX *c) +{ + return c->posix_emu.rmdir_fn; +} + +void +smbc_setFunctionRmdir(SMBCCTX *c, smbc_rmdir_fn fn) +{ + c->posix_emu.rmdir_fn = fn; +} + +smbc_telldir_fn +smbc_getFunctionTelldir(SMBCCTX *c) +{ + return c->posix_emu.telldir_fn; +} + +void +smbc_setFunctionTelldir(SMBCCTX *c, smbc_telldir_fn fn) +{ + c->posix_emu.telldir_fn = fn; +} + +smbc_lseekdir_fn +smbc_getFunctionLseekdir(SMBCCTX *c) +{ + return c->posix_emu.lseekdir_fn; +} + +void +smbc_setFunctionLseekdir(SMBCCTX *c, smbc_lseekdir_fn fn) +{ + c->posix_emu.lseekdir_fn = fn; +} + +smbc_fstatdir_fn +smbc_getFunctionFstatdir(SMBCCTX *c) +{ + return c->posix_emu.fstatdir_fn; +} + +void +smbc_setFunctionFstatdir(SMBCCTX *c, smbc_fstatdir_fn fn) +{ + c->posix_emu.fstatdir_fn = fn; +} + + +/** + * Callable functions applicable to both files and directories. + */ + +smbc_chmod_fn +smbc_getFunctionChmod(SMBCCTX *c) +{ + return c->posix_emu.chmod_fn; +} + +void +smbc_setFunctionChmod(SMBCCTX *c, smbc_chmod_fn fn) +{ + c->posix_emu.chmod_fn = fn; +} + +smbc_utimes_fn +smbc_getFunctionUtimes(SMBCCTX *c) +{ + return c->posix_emu.utimes_fn; +} + +void +smbc_setFunctionUtimes(SMBCCTX *c, smbc_utimes_fn fn) +{ + c->posix_emu.utimes_fn = fn; +} + +smbc_setxattr_fn +smbc_getFunctionSetxattr(SMBCCTX *c) +{ + return c->posix_emu.setxattr_fn; +} + +void +smbc_setFunctionSetxattr(SMBCCTX *c, smbc_setxattr_fn fn) +{ + c->posix_emu.setxattr_fn = fn; +} + +smbc_getxattr_fn +smbc_getFunctionGetxattr(SMBCCTX *c) +{ + return c->posix_emu.getxattr_fn; +} + +void +smbc_setFunctionGetxattr(SMBCCTX *c, smbc_getxattr_fn fn) +{ + c->posix_emu.getxattr_fn = fn; +} + +smbc_removexattr_fn +smbc_getFunctionRemovexattr(SMBCCTX *c) +{ + return c->posix_emu.removexattr_fn; +} + +void +smbc_setFunctionRemovexattr(SMBCCTX *c, smbc_removexattr_fn fn) +{ + c->posix_emu.removexattr_fn = fn; +} + +smbc_listxattr_fn +smbc_getFunctionListxattr(SMBCCTX *c) +{ + return c->posix_emu.listxattr_fn; +} + +void +smbc_setFunctionListxattr(SMBCCTX *c, smbc_listxattr_fn fn) +{ + c->posix_emu.listxattr_fn = fn; +} + + +/** + * Callable functions related to printing + */ + +smbc_print_file_fn +smbc_getFunctionPrintFile(SMBCCTX *c) +{ + return c->printing.print_file_fn; +} + +void +smbc_setFunctionPrintFile(SMBCCTX *c, smbc_print_file_fn fn) +{ + c->printing.print_file_fn = fn; +} + +smbc_open_print_job_fn +smbc_getFunctionOpenPrintJob(SMBCCTX *c) +{ + return c->printing.open_print_job_fn; +} + +void +smbc_setFunctionOpenPrintJob(SMBCCTX *c, + smbc_open_print_job_fn fn) +{ + c->printing.open_print_job_fn = fn; +} + +smbc_list_print_jobs_fn +smbc_getFunctionListPrintJobs(SMBCCTX *c) +{ + return c->printing.list_print_jobs_fn; +} + +void +smbc_setFunctionListPrintJobs(SMBCCTX *c, + smbc_list_print_jobs_fn fn) +{ + c->printing.list_print_jobs_fn = fn; +} + +smbc_unlink_print_job_fn +smbc_getFunctionUnlinkPrintJob(SMBCCTX *c) +{ + return c->printing.unlink_print_job_fn; +} + +void +smbc_setFunctionUnlinkPrintJob(SMBCCTX *c, + smbc_unlink_print_job_fn fn) +{ + c->printing.unlink_print_job_fn = fn; +} + diff --git a/source3/libsmb/libsmb_dir.c b/source3/libsmb/libsmb_dir.c new file mode 100644 index 0000000000..9cb3351433 --- /dev/null +++ b/source3/libsmb/libsmb_dir.c @@ -0,0 +1,1928 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/* + * Routine to open a directory + * We accept the URL syntax explained in SMBC_parse_path(), above. + */ + +static void +remove_dir(SMBCFILE *dir) +{ + struct smbc_dir_list *d,*f; + + d = dir->dir_list; + while (d) { + + f = d; d = d->next; + + SAFE_FREE(f->dirent); + SAFE_FREE(f); + + } + + dir->dir_list = dir->dir_end = dir->dir_next = NULL; + +} + +static int +add_dirent(SMBCFILE *dir, + const char *name, + const char *comment, + uint32 type) +{ + struct smbc_dirent *dirent; + int size; + int name_length = (name == NULL ? 0 : strlen(name)); + int comment_len = (comment == NULL ? 0 : strlen(comment)); + + /* + * Allocate space for the dirent, which must be increased by the + * size of the name and the comment and 1 each for the null terminator. + */ + + size = sizeof(struct smbc_dirent) + name_length + comment_len + 2; + + dirent = (struct smbc_dirent *)SMB_MALLOC(size); + + if (!dirent) { + + dir->dir_error = ENOMEM; + return -1; + + } + + ZERO_STRUCTP(dirent); + + if (dir->dir_list == NULL) { + + dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list); + if (!dir->dir_list) { + + SAFE_FREE(dirent); + dir->dir_error = ENOMEM; + return -1; + + } + ZERO_STRUCTP(dir->dir_list); + + dir->dir_end = dir->dir_next = dir->dir_list; + } + else { + + dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list); + + if (!dir->dir_end->next) { + + SAFE_FREE(dirent); + dir->dir_error = ENOMEM; + return -1; + + } + ZERO_STRUCTP(dir->dir_end->next); + + dir->dir_end = dir->dir_end->next; + } + + dir->dir_end->next = NULL; + dir->dir_end->dirent = dirent; + + dirent->smbc_type = type; + dirent->namelen = name_length; + dirent->commentlen = comment_len; + dirent->dirlen = size; + + /* + * dirent->namelen + 1 includes the null (no null termination needed) + * Ditto for dirent->commentlen. + * The space for the two null bytes was allocated. + */ + strncpy(dirent->name, (name?name:""), dirent->namelen + 1); + dirent->comment = (char *)(&dirent->name + dirent->namelen + 1); + strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1); + + return 0; + +} + +static void +list_unique_wg_fn(const char *name, + uint32 type, + const char *comment, + void *state) +{ + SMBCFILE *dir = (SMBCFILE *)state; + struct smbc_dir_list *dir_list; + struct smbc_dirent *dirent; + int dirent_type; + int do_remove = 0; + + dirent_type = dir->dir_type; + + if (add_dirent(dir, name, comment, dirent_type) < 0) { + + /* An error occurred, what do we do? */ + /* FIXME: Add some code here */ + } + + /* Point to the one just added */ + dirent = dir->dir_end->dirent; + + /* See if this was a duplicate */ + for (dir_list = dir->dir_list; + dir_list != dir->dir_end; + dir_list = dir_list->next) { + if (! do_remove && + strcmp(dir_list->dirent->name, dirent->name) == 0) { + /* Duplicate. End end of list need to be removed. */ + do_remove = 1; + } + + if (do_remove && dir_list->next == dir->dir_end) { + /* Found the end of the list. Remove it. */ + dir->dir_end = dir_list; + free(dir_list->next); + free(dirent); + dir_list->next = NULL; + break; + } + } +} + +static void +list_fn(const char *name, + uint32 type, + const char *comment, + void *state) +{ + SMBCFILE *dir = (SMBCFILE *)state; + int dirent_type; + + /* + * We need to process the type a little ... + * + * Disk share = 0x00000000 + * Print share = 0x00000001 + * Comms share = 0x00000002 (obsolete?) + * IPC$ share = 0x00000003 + * + * administrative shares: + * ADMIN$, IPC$, C$, D$, E$ ... are type |= 0x80000000 + */ + + if (dir->dir_type == SMBC_FILE_SHARE) { + switch (type) { + case 0 | 0x80000000: + case 0: + dirent_type = SMBC_FILE_SHARE; + break; + + case 1: + dirent_type = SMBC_PRINTER_SHARE; + break; + + case 2: + dirent_type = SMBC_COMMS_SHARE; + break; + + case 3 | 0x80000000: + case 3: + dirent_type = SMBC_IPC_SHARE; + break; + + default: + dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */ + break; + } + } + else { + dirent_type = dir->dir_type; + } + + if (add_dirent(dir, name, comment, dirent_type) < 0) { + + /* An error occurred, what do we do? */ + /* FIXME: Add some code here */ + + } +} + +static void +dir_list_fn(const char *mnt, + file_info *finfo, + const char *mask, + void *state) +{ + + if (add_dirent((SMBCFILE *)state, finfo->name, "", + (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) { + + /* Handle an error ... */ + + /* FIXME: Add some code ... */ + + } + +} + +static int +net_share_enum_rpc(struct cli_state *cli, + void (*fn)(const char *name, + uint32 type, + const char *comment, + void *state), + void *state) +{ + int i; + WERROR result; + ENUM_HND enum_hnd; + uint32 info_level = 1; + uint32 preferred_len = 0xffffffff; + uint32 type; + SRV_SHARE_INFO_CTR ctr; + fstring name = ""; + fstring comment = ""; + struct rpc_pipe_client *pipe_hnd; + NTSTATUS nt_status; + + /* Open the server service pipe */ + pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, &nt_status); + if (!pipe_hnd) { + DEBUG(1, ("net_share_enum_rpc pipe open fail!\n")); + return -1; + } + + /* Issue the NetShareEnum RPC call and retrieve the response */ + init_enum_hnd(&enum_hnd, 0); + result = rpccli_srvsvc_net_share_enum(pipe_hnd, + talloc_tos(), + info_level, + &ctr, + preferred_len, + &enum_hnd); + + /* Was it successful? */ + if (!W_ERROR_IS_OK(result) || ctr.num_entries == 0) { + /* Nope. Go clean up. */ + goto done; + } + + /* For each returned entry... */ + for (i = 0; i < ctr.num_entries; i++) { + + /* pull out the share name */ + rpcstr_pull_unistr2_fstring( + name, &ctr.share.info1[i].info_1_str.uni_netname); + + /* pull out the share's comment */ + rpcstr_pull_unistr2_fstring( + comment, &ctr.share.info1[i].info_1_str.uni_remark); + + /* Get the type value */ + type = ctr.share.info1[i].info_1.type; + + /* Add this share to the list */ + (*fn)(name, type, comment, state); + } + +done: + /* Close the server service pipe */ + cli_rpc_pipe_close(pipe_hnd); + + /* Tell 'em if it worked */ + return W_ERROR_IS_OK(result) ? 0 : -1; +} + + +/* + * Verify that the options specified in a URL are valid + */ +int +SMBC_check_options(char *server, + char *share, + char *path, + char *options) +{ + DEBUG(4, ("SMBC_check_options(): server='%s' share='%s' " + "path='%s' options='%s'\n", + server, share, path, options)); + + /* No options at all is always ok */ + if (! *options) return 0; + + /* Currently, we don't support any options. */ + return -1; +} + + +SMBCFILE * +SMBC_opendir_ctx(SMBCCTX *context, + const char *fname) +{ + int saved_errno; + char *server = NULL, *share = NULL, *user = NULL, *password = NULL, *options = NULL; + char *workgroup = NULL; + char *path = NULL; + uint16 mode; + char *p = NULL; + SMBCSRV *srv = NULL; + SMBCFILE *dir = NULL; + struct sockaddr_storage rem_ss; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + DEBUG(4, ("no valid context\n")); + errno = EINVAL + 8192; + TALLOC_FREE(frame); + return NULL; + + } + + if (!fname) { + DEBUG(4, ("no valid fname\n")); + errno = EINVAL + 8193; + TALLOC_FREE(frame); + return NULL; + } + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &share, + &path, + &user, + &password, + &options)) { + DEBUG(4, ("no valid path\n")); + errno = EINVAL + 8194; + TALLOC_FREE(frame); + return NULL; + } + + DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' " + "path='%s' options='%s'\n", + fname, server, share, path, options)); + + /* Ensure the options are valid */ + if (SMBC_check_options(server, share, path, options)) { + DEBUG(4, ("unacceptable options (%s)\n", options)); + errno = EINVAL + 8195; + TALLOC_FREE(frame); + return NULL; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, context->user); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return NULL; + } + } + + dir = SMB_MALLOC_P(SMBCFILE); + + if (!dir) { + errno = ENOMEM; + TALLOC_FREE(frame); + return NULL; + } + + ZERO_STRUCTP(dir); + + dir->cli_fd = 0; + dir->fname = SMB_STRDUP(fname); + dir->srv = NULL; + dir->offset = 0; + dir->file = False; + dir->dir_list = dir->dir_next = dir->dir_end = NULL; + + if (server[0] == (char)0) { + + int i; + int count; + int max_lmb_count; + struct ip_service *ip_list; + struct ip_service server_addr; + struct user_auth_info u_info; + + if (share[0] != (char)0 || path[0] != (char)0) { + + errno = EINVAL + 8196; + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + } + + /* Determine how many local master browsers to query */ + max_lmb_count = (context->browse_max_lmb_count == 0 + ? INT_MAX + : context->browse_max_lmb_count); + + memset(&u_info, '\0', sizeof(u_info)); + u_info.username = talloc_strdup(frame,user); + u_info.password = talloc_strdup(frame,password); + if (!u_info.username || !u_info.password) { + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + } + + /* + * We have server and share and path empty but options + * requesting that we scan all master browsers for their list + * of workgroups/domains. This implies that we must first try + * broadcast queries to find all master browsers, and if that + * doesn't work, then try our other methods which return only + * a single master browser. + */ + + ip_list = NULL; + if (!NT_STATUS_IS_OK(name_resolve_bcast(MSBROWSE, 1, &ip_list, + &count))) + { + + SAFE_FREE(ip_list); + + if (!find_master_ip(workgroup, &server_addr.ss)) { + + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + errno = ENOENT; + TALLOC_FREE(frame); + return NULL; + } + + ip_list = (struct ip_service *)memdup( + &server_addr, sizeof(server_addr)); + if (ip_list == NULL) { + errno = ENOMEM; + TALLOC_FREE(frame); + return NULL; + } + count = 1; + } + + for (i = 0; i < count && i < max_lmb_count; i++) { + char addr[INET6_ADDRSTRLEN]; + char *wg_ptr = NULL; + struct cli_state *cli = NULL; + + print_sockaddr(addr, sizeof(addr), &ip_list[i].ss); + DEBUG(99, ("Found master browser %d of %d: %s\n", + i+1, MAX(count, max_lmb_count), + addr)); + + cli = get_ipc_connect_master_ip(talloc_tos(), + &ip_list[i], + &u_info, + &wg_ptr); + /* cli == NULL is the master browser refused to talk or + could not be found */ + if (!cli) { + continue; + } + + workgroup = talloc_strdup(frame, wg_ptr); + server = talloc_strdup(frame, cli->desthost); + + cli_shutdown(cli); + + if (!workgroup || !server) { + errno = ENOMEM; + TALLOC_FREE(frame); + return NULL; + } + + DEBUG(4, ("using workgroup %s %s\n", + workgroup, server)); + + /* + * For each returned master browser IP address, get a + * connection to IPC$ on the server if we do not + * already have one, and determine the + * workgroups/domains that it knows about. + */ + + srv = SMBC_server(frame, context, True, server, "IPC$", + &workgroup, &user, &password); + if (!srv) { + continue; + } + + dir->srv = srv; + dir->dir_type = SMBC_WORKGROUP; + + /* Now, list the stuff ... */ + + if (!cli_NetServerEnum(srv->cli, + workgroup, + SV_TYPE_DOMAIN_ENUM, + list_unique_wg_fn, + (void *)dir)) { + continue; + } + } + + SAFE_FREE(ip_list); + } else { + /* + * Server not an empty string ... Check the rest and see what + * gives + */ + if (*share == '\0') { + if (*path != '\0') { + + /* Should not have empty share with path */ + errno = EINVAL + 8197; + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + + } + + /* + * We don't know if <server> is really a server name + * or is a workgroup/domain name. If we already have + * a server structure for it, we'll use it. + * Otherwise, check to see if <server><1D>, + * <server><1B>, or <server><20> translates. We check + * to see if <server> is an IP address first. + */ + + /* + * See if we have an existing server. Do not + * establish a connection if one does not already + * exist. + */ + srv = SMBC_server(frame, context, False, server, "IPC$", + &workgroup, &user, &password); + + /* + * If no existing server and not an IP addr, look for + * LMB or DMB + */ + if (!srv && + !is_ipaddress(server) && + (resolve_name(server, &rem_ss, 0x1d) || /* LMB */ + resolve_name(server, &rem_ss, 0x1b) )) { /* DMB */ + + fstring buserver; + + dir->dir_type = SMBC_SERVER; + + /* + * Get the backup list ... + */ + if (!name_status_find(server, 0, 0, + &rem_ss, buserver)) { + + DEBUG(0, ("Could not get name of " + "local/domain master browser " + "for server %s\n", server)); + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + errno = EPERM; + TALLOC_FREE(frame); + return NULL; + + } + + /* + * Get a connection to IPC$ on the server if + * we do not already have one + */ + srv = SMBC_server(frame, context, True, + buserver, "IPC$", + &workgroup, &user, &password); + if (!srv) { + DEBUG(0, ("got no contact to IPC$\n")); + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + + } + + dir->srv = srv; + + /* Now, list the servers ... */ + if (!cli_NetServerEnum(srv->cli, server, + 0x0000FFFE, list_fn, + (void *)dir)) { + + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + } + } else if (srv || + (resolve_name(server, &rem_ss, 0x20))) { + + /* If we hadn't found the server, get one now */ + if (!srv) { + srv = SMBC_server(frame, context, True, + server, "IPC$", + &workgroup, + &user, &password); + } + + if (!srv) { + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + + } + + dir->dir_type = SMBC_FILE_SHARE; + dir->srv = srv; + + /* List the shares ... */ + + if (net_share_enum_rpc( + srv->cli, + list_fn, + (void *) dir) < 0 && + cli_RNetShareEnum( + srv->cli, + list_fn, + (void *)dir) < 0) { + + errno = cli_errno(srv->cli); + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + + } + } else { + /* Neither the workgroup nor server exists */ + errno = ECONNREFUSED; + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + } + + } + else { + /* + * The server and share are specified ... work from + * there ... + */ + char *targetpath; + struct cli_state *targetcli; + + /* We connect to the server and list the directory */ + dir->dir_type = SMBC_FILE_SHARE; + + srv = SMBC_server(frame, context, True, server, share, + &workgroup, &user, &password); + + if (!srv) { + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + } + + dir->srv = srv; + + /* Now, list the files ... */ + + p = path + strlen(path); + path = talloc_asprintf_append(path, "\\*"); + if (!path) { + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + } + + if (!cli_resolve_path(frame, "", srv->cli, path, + &targetcli, &targetpath)) { + d_printf("Could not resolve %s\n", path); + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + } + + if (cli_list(targetcli, targetpath, + aDIR | aSYSTEM | aHIDDEN, + dir_list_fn, (void *)dir) < 0) { + + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + saved_errno = SMBC_errno(context, targetcli); + + if (saved_errno == EINVAL) { + /* + * See if they asked to opendir something + * other than a directory. If so, the + * converted error value we got would have + * been EINVAL rather than ENOTDIR. + */ + *p = '\0'; /* restore original path */ + + if (SMBC_getatr(context, srv, path, + &mode, NULL, + NULL, NULL, NULL, NULL, + NULL) && + ! IS_DOS_DIR(mode)) { + + /* It is. Correct the error value */ + saved_errno = ENOTDIR; + } + } + + /* + * If there was an error and the server is no + * good any more... + */ + if (cli_is_error(targetcli) && + (context->server.check_server_fn)(context, srv)) { + + /* ... then remove it. */ + if ((context->server.remove_unused_server_fn)(context, + srv)) { + /* + * We could not remove the + * server completely, remove + * it from the cache so we + * will not get it again. It + * will be removed when the + * last file/dir is closed. + */ + (context->cache.remove_cached_server_fn)(context, srv); + } + } + + errno = saved_errno; + TALLOC_FREE(frame); + return NULL; + } + } + + } + + DLIST_ADD(context->files, dir); + TALLOC_FREE(frame); + return dir; + +} + +/* + * Routine to close a directory + */ + +int +SMBC_closedir_ctx(SMBCCTX *context, + SMBCFILE *dir) +{ + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!dir || !SMBC_dlist_contains(context->files, dir)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + } + + remove_dir(dir); /* Clean it up */ + + DLIST_REMOVE(context->files, dir); + + if (dir) { + + SAFE_FREE(dir->fname); + SAFE_FREE(dir); /* Free the space too */ + } + + TALLOC_FREE(frame); + return 0; + +} + +static void +smbc_readdir_internal(SMBCCTX * context, + struct smbc_dirent *dest, + struct smbc_dirent *src, + int max_namebuf_len) +{ + if (context->urlencode_readdir_entries) { + + /* url-encode the name. get back remaining buffer space */ + max_namebuf_len = + SMBC_urlencode(dest->name, src->name, max_namebuf_len); + + /* We now know the name length */ + dest->namelen = strlen(dest->name); + + /* Save the pointer to the beginning of the comment */ + dest->comment = dest->name + dest->namelen + 1; + + /* Copy the comment */ + strncpy(dest->comment, src->comment, max_namebuf_len - 1); + dest->comment[max_namebuf_len - 1] = '\0'; + + /* Save other fields */ + dest->smbc_type = src->smbc_type; + dest->commentlen = strlen(dest->comment); + dest->dirlen = ((dest->comment + dest->commentlen + 1) - + (char *) dest); + } else { + + /* No encoding. Just copy the entry as is. */ + memcpy(dest, src, src->dirlen); + dest->comment = (char *)(&dest->name + src->namelen + 1); + } + +} + +/* + * Routine to get a directory entry + */ + +struct smbc_dirent * +SMBC_readdir_ctx(SMBCCTX *context, + SMBCFILE *dir) +{ + int maxlen; + struct smbc_dirent *dirp, *dirent; + TALLOC_CTX *frame = talloc_stackframe(); + + /* Check that all is ok first ... */ + + if (!context || !context->initialized) { + + errno = EINVAL; + DEBUG(0, ("Invalid context in SMBC_readdir_ctx()\n")); + TALLOC_FREE(frame); + return NULL; + + } + + if (!dir || !SMBC_dlist_contains(context->files, dir)) { + + errno = EBADF; + DEBUG(0, ("Invalid dir in SMBC_readdir_ctx()\n")); + TALLOC_FREE(frame); + return NULL; + + } + + if (dir->file != False) { /* FIXME, should be dir, perhaps */ + + errno = ENOTDIR; + DEBUG(0, ("Found file vs directory in SMBC_readdir_ctx()\n")); + TALLOC_FREE(frame); + return NULL; + + } + + if (!dir->dir_next) { + TALLOC_FREE(frame); + return NULL; + } + + dirent = dir->dir_next->dirent; + if (!dirent) { + + errno = ENOENT; + TALLOC_FREE(frame); + return NULL; + + } + + dirp = (struct smbc_dirent *)context->dirent; + maxlen = (sizeof(context->dirent) - + sizeof(struct smbc_dirent)); + + smbc_readdir_internal(context, dirp, dirent, maxlen); + + dir->dir_next = dir->dir_next->next; + + TALLOC_FREE(frame); + return dirp; +} + +/* + * Routine to get directory entries + */ + +int +SMBC_getdents_ctx(SMBCCTX *context, + SMBCFILE *dir, + struct smbc_dirent *dirp, + int count) +{ + int rem = count; + int reqd; + int maxlen; + char *ndir = (char *)dirp; + struct smbc_dir_list *dirlist; + TALLOC_CTX *frame = talloc_stackframe(); + + /* Check that all is ok first ... */ + + if (!context || !context->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + if (!dir || !SMBC_dlist_contains(context->files, dir)) { + + errno = EBADF; + TALLOC_FREE(frame); + return -1; + + } + + if (dir->file != False) { /* FIXME, should be dir, perhaps */ + + errno = ENOTDIR; + TALLOC_FREE(frame); + return -1; + + } + + /* + * Now, retrieve the number of entries that will fit in what was passed + * We have to figure out if the info is in the list, or we need to + * send a request to the server to get the info. + */ + + while ((dirlist = dir->dir_next)) { + struct smbc_dirent *dirent; + + if (!dirlist->dirent) { + + errno = ENOENT; /* Bad error */ + TALLOC_FREE(frame); + return -1; + + } + + /* Do urlencoding of next entry, if so selected */ + dirent = (struct smbc_dirent *)context->dirent; + maxlen = (sizeof(context->dirent) - + sizeof(struct smbc_dirent)); + smbc_readdir_internal(context, dirent, dirlist->dirent, maxlen); + + reqd = dirent->dirlen; + + if (rem < reqd) { + + if (rem < count) { /* We managed to copy something */ + + errno = 0; + TALLOC_FREE(frame); + return count - rem; + + } + else { /* Nothing copied ... */ + + errno = EINVAL; /* Not enough space ... */ + TALLOC_FREE(frame); + return -1; + + } + + } + + memcpy(ndir, dirent, reqd); /* Copy the data in ... */ + + ((struct smbc_dirent *)ndir)->comment = + (char *)(&((struct smbc_dirent *)ndir)->name + + dirent->namelen + + 1); + + ndir += reqd; + + rem -= reqd; + + dir->dir_next = dirlist = dirlist -> next; + } + + TALLOC_FREE(frame); + + if (rem == count) + return 0; + else + return count - rem; + +} + +/* + * Routine to create a directory ... + */ + +int +SMBC_mkdir_ctx(SMBCCTX *context, + const char *fname, + mode_t mode) +{ + SMBCSRV *srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_mkdir(%s)\n", fname)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, context->user); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, share, &workgroup, &user, &password); + + if (!srv) { + + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + + } + + /*d_printf(">>>mkdir: resolving %s\n", path);*/ + if (!cli_resolve_path(frame, "", srv->cli, path, + &targetcli, &targetpath)) { + d_printf("Could not resolve %s\n", path); + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/ + + if (!cli_mkdir(targetcli, targetpath)) { + + errno = SMBC_errno(context, targetcli); + TALLOC_FREE(frame); + return -1; + + } + + TALLOC_FREE(frame); + return 0; + +} + +/* + * Our list function simply checks to see if a directory is not empty + */ + +static int smbc_rmdir_dirempty = True; + +static void +rmdir_list_fn(const char *mnt, + file_info *finfo, + const char *mask, + void *state) +{ + if (strncmp(finfo->name, ".", 1) != 0 && + strncmp(finfo->name, "..", 2) != 0) { + smbc_rmdir_dirempty = False; + } +} + +/* + * Routine to remove a directory + */ + +int +SMBC_rmdir_ctx(SMBCCTX *context, + const char *fname) +{ + SMBCSRV *srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_rmdir(%s)\n", fname)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, context->user); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, share, &workgroup, &user, &password); + + if (!srv) { + + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + + } + + /*d_printf(">>>rmdir: resolving %s\n", path);*/ + if (!cli_resolve_path(frame, "", srv->cli, path, + &targetcli, &targetpath)) { + d_printf("Could not resolve %s\n", path); + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/ + + + if (!cli_rmdir(targetcli, targetpath)) { + + errno = SMBC_errno(context, targetcli); + + if (errno == EACCES) { /* Check if the dir empty or not */ + + /* Local storage to avoid buffer overflows */ + char *lpath; + + smbc_rmdir_dirempty = True; /* Make this so ... */ + + lpath = talloc_asprintf(frame, "%s\\*", + targetpath); + if (!lpath) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + + if (cli_list(targetcli, lpath, + aDIR | aSYSTEM | aHIDDEN, + rmdir_list_fn, NULL) < 0) { + + /* Fix errno to ignore latest error ... */ + DEBUG(5, ("smbc_rmdir: " + "cli_list returned an error: %d\n", + SMBC_errno(context, targetcli))); + errno = EACCES; + + } + + if (smbc_rmdir_dirempty) + errno = EACCES; + else + errno = ENOTEMPTY; + + } + + TALLOC_FREE(frame); + return -1; + + } + + TALLOC_FREE(frame); + return 0; + +} + +/* + * Routine to return the current directory position + */ + +off_t +SMBC_telldir_ctx(SMBCCTX *context, + SMBCFILE *dir) +{ + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + if (!dir || !SMBC_dlist_contains(context->files, dir)) { + + errno = EBADF; + TALLOC_FREE(frame); + return -1; + + } + + if (dir->file != False) { /* FIXME, should be dir, perhaps */ + + errno = ENOTDIR; + TALLOC_FREE(frame); + return -1; + + } + + /* See if we're already at the end. */ + if (dir->dir_next == NULL) { + /* We are. */ + TALLOC_FREE(frame); + return -1; + } + + /* + * We return the pointer here as the offset + */ + TALLOC_FREE(frame); + return (off_t)(long)dir->dir_next->dirent; +} + +/* + * A routine to run down the list and see if the entry is OK + */ + +static struct smbc_dir_list * +check_dir_ent(struct smbc_dir_list *list, + struct smbc_dirent *dirent) +{ + + /* Run down the list looking for what we want */ + + if (dirent) { + + struct smbc_dir_list *tmp = list; + + while (tmp) { + + if (tmp->dirent == dirent) + return tmp; + + tmp = tmp->next; + + } + + } + + return NULL; /* Not found, or an error */ + +} + + +/* + * Routine to seek on a directory + */ + +int +SMBC_lseekdir_ctx(SMBCCTX *context, + SMBCFILE *dir, + off_t offset) +{ + long int l_offset = offset; /* Handle problems of size */ + struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset; + struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + if (dir->file != False) { /* FIXME, should be dir, perhaps */ + + errno = ENOTDIR; + TALLOC_FREE(frame); + return -1; + + } + + /* Now, check what we were passed and see if it is OK ... */ + + if (dirent == NULL) { /* Seek to the begining of the list */ + + dir->dir_next = dir->dir_list; + TALLOC_FREE(frame); + return 0; + + } + + if (offset == -1) { /* Seek to the end of the list */ + dir->dir_next = NULL; + TALLOC_FREE(frame); + return 0; + } + + /* Now, run down the list and make sure that the entry is OK */ + /* This may need to be changed if we change the format of the list */ + + if ((list_ent = check_dir_ent(dir->dir_list, dirent)) == NULL) { + errno = EINVAL; /* Bad entry */ + TALLOC_FREE(frame); + return -1; + } + + dir->dir_next = list_ent; + + TALLOC_FREE(frame); + return 0; +} + +/* + * Routine to fstat a dir + */ + +int +SMBC_fstatdir_ctx(SMBCCTX *context, + SMBCFILE *dir, + struct stat *st) +{ + + if (!context || !context->initialized) { + + errno = EINVAL; + return -1; + } + + /* No code yet ... */ + return 0; +} + +int +SMBC_chmod_ctx(SMBCCTX *context, + const char *fname, + mode_t newmode) +{ + SMBCSRV *srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + uint16 mode; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, context->user); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, share, &workgroup, &user, &password); + + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + mode = 0; + + if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY; + if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH; + if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM; + if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN; + + if (!cli_setatr(srv->cli, path, mode, 0)) { + errno = SMBC_errno(context, srv->cli); + TALLOC_FREE(frame); + return -1; + } + + TALLOC_FREE(frame); + return 0; +} + +int +SMBC_utimes_ctx(SMBCCTX *context, + const char *fname, + struct timeval *tbuf) +{ + SMBCSRV *srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + time_t access_time; + time_t write_time; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (tbuf == NULL) { + access_time = write_time = time(NULL); + } else { + access_time = tbuf[0].tv_sec; + write_time = tbuf[1].tv_sec; + } + + if (DEBUGLVL(4)) { + char *p; + char atimebuf[32]; + char mtimebuf[32]; + + strncpy(atimebuf, ctime(&access_time), sizeof(atimebuf) - 1); + atimebuf[sizeof(atimebuf) - 1] = '\0'; + if ((p = strchr(atimebuf, '\n')) != NULL) { + *p = '\0'; + } + + strncpy(mtimebuf, ctime(&write_time), sizeof(mtimebuf) - 1); + mtimebuf[sizeof(mtimebuf) - 1] = '\0'; + if ((p = strchr(mtimebuf, '\n')) != NULL) { + *p = '\0'; + } + + dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n", + fname, atimebuf, mtimebuf); + } + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, context->user); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, share, &workgroup, &user, &password); + + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + if (!SMBC_setatr(context, srv, path, + 0, access_time, write_time, 0, 0)) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_setatr */ + } + + TALLOC_FREE(frame); + return 0; +} + +/* + * Routine to unlink() a file + */ + +int +SMBC_unlink_ctx(SMBCCTX *context, + const char *fname) +{ + char *server = NULL, *share = NULL, *user = NULL, *password = NULL, *workgroup = NULL; + char *path = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + SMBCSRV *srv = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, context->user); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, share, &workgroup, &user, &password); + + if (!srv) { + TALLOC_FREE(frame); + return -1; /* SMBC_server sets errno */ + + } + + /*d_printf(">>>unlink: resolving %s\n", path);*/ + if (!cli_resolve_path(frame, "", srv->cli, path, + &targetcli, &targetpath)) { + d_printf("Could not resolve %s\n", path); + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/ + + if (!cli_unlink(targetcli, targetpath)) { + + errno = SMBC_errno(context, targetcli); + + if (errno == EACCES) { /* Check if the file is a directory */ + + int saverr = errno; + SMB_OFF_T size = 0; + uint16 mode = 0; + struct timespec write_time_ts; + struct timespec access_time_ts; + struct timespec change_time_ts; + SMB_INO_T ino = 0; + + if (!SMBC_getatr(context, srv, path, &mode, &size, + NULL, + &access_time_ts, + &write_time_ts, + &change_time_ts, + &ino)) { + + /* Hmmm, bad error ... What? */ + + errno = SMBC_errno(context, targetcli); + TALLOC_FREE(frame); + return -1; + + } + else { + + if (IS_DOS_DIR(mode)) + errno = EISDIR; + else + errno = saverr; /* Restore this */ + + } + } + + TALLOC_FREE(frame); + return -1; + + } + + TALLOC_FREE(frame); + return 0; /* Success ... */ + +} + +/* + * Routine to rename() a file + */ + +int +SMBC_rename_ctx(SMBCCTX *ocontext, + const char *oname, + SMBCCTX *ncontext, + const char *nname) +{ + char *server1 = NULL; + char *share1 = NULL; + char *server2 = NULL; + char *share2 = NULL; + char *user1 = NULL; + char *user2 = NULL; + char *password1 = NULL; + char *password2 = NULL; + char *workgroup = NULL; + char *path1 = NULL; + char *path2 = NULL; + char *targetpath1 = NULL; + char *targetpath2 = NULL; + struct cli_state *targetcli1 = NULL; + struct cli_state *targetcli2 = NULL; + SMBCSRV *srv = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!ocontext || !ncontext || + !ocontext->initialized || + !ncontext->initialized) { + + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + } + + if (!oname || !nname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname)); + + if (SMBC_parse_path(frame, + ocontext, + oname, + &workgroup, + &server1, + &share1, + &path1, + &user1, + &password1, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user1 || user1[0] == (char)0) { + user1 = talloc_strdup(frame, ocontext->user); + if (!user1) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + if (SMBC_parse_path(frame, + ncontext, + nname, + NULL, + &server2, + &share2, + &path2, + &user2, + &password2, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user2 || user2[0] == (char)0) { + user2 = talloc_strdup(frame, ncontext->user); + if (!user2) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + if (strcmp(server1, server2) || strcmp(share1, share2) || + strcmp(user1, user2)) { + /* Can't rename across file systems, or users?? */ + errno = EXDEV; + TALLOC_FREE(frame); + return -1; + } + + srv = SMBC_server(frame, ocontext, True, + server1, share1, &workgroup, &user1, &password1); + if (!srv) { + TALLOC_FREE(frame); + return -1; + + } + + /*d_printf(">>>rename: resolving %s\n", path1);*/ + if (!cli_resolve_path(frame, "", srv->cli, path1, + &targetcli1, &targetpath1)) { + d_printf("Could not resolve %s\n", path1); + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/ + /*d_printf(">>>rename: resolving %s\n", path2);*/ + if (!cli_resolve_path(frame, "", srv->cli, path2, + &targetcli2, &targetpath2)) { + d_printf("Could not resolve %s\n", path2); + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/ + + if (strcmp(targetcli1->desthost, targetcli2->desthost) || + strcmp(targetcli1->share, targetcli2->share)) + { + /* can't rename across file systems */ + errno = EXDEV; + TALLOC_FREE(frame); + return -1; + } + + if (!cli_rename(targetcli1, targetpath1, targetpath2)) { + int eno = SMBC_errno(ocontext, targetcli1); + + if (eno != EEXIST || + !cli_unlink(targetcli1, targetpath2) || + !cli_rename(targetcli1, targetpath1, targetpath2)) { + + errno = eno; + TALLOC_FREE(frame); + return -1; + + } + } + + TALLOC_FREE(frame); + return 0; /* Success */ +} + diff --git a/source3/libsmb/libsmb_file.c b/source3/libsmb/libsmb_file.c new file mode 100644 index 0000000000..619176697b --- /dev/null +++ b/source3/libsmb/libsmb_file.c @@ -0,0 +1,859 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/* + * Routine to open() a file ... + */ + +SMBCFILE * +SMBC_open_ctx(SMBCCTX *context, + const char *fname, + int flags, + mode_t mode) +{ + char *server = NULL, *share = NULL, *user = NULL, *password = NULL, *workgroup = NULL; + char *path = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + SMBCSRV *srv = NULL; + SMBCFILE *file = NULL; + int fd; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return NULL; + + } + + if (!fname) { + + errno = EINVAL; + TALLOC_FREE(frame); + return NULL; + + } + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return NULL; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, context->user); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return NULL; + } + } + + srv = SMBC_server(frame, context, True, + server, share, &workgroup, &user, &password); + + if (!srv) { + if (errno == EPERM) errno = EACCES; + TALLOC_FREE(frame); + return NULL; /* SMBC_server sets errno */ + } + + /* Hmmm, the test for a directory is suspect here ... FIXME */ + + if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') { + fd = -1; + } else { + file = SMB_MALLOC_P(SMBCFILE); + + if (!file) { + errno = ENOMEM; + TALLOC_FREE(frame); + return NULL; + } + + ZERO_STRUCTP(file); + + /*d_printf(">>>open: resolving %s\n", path);*/ + if (!cli_resolve_path(frame, "", srv->cli, path, &targetcli, &targetpath)) { + d_printf("Could not resolve %s\n", path); + SAFE_FREE(file); + TALLOC_FREE(frame); + return NULL; + } + /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/ + + if ((fd = cli_open(targetcli, targetpath, flags, + context->share_mode)) < 0) { + + /* Handle the error ... */ + + SAFE_FREE(file); + errno = SMBC_errno(context, targetcli); + TALLOC_FREE(frame); + return NULL; + + } + + /* Fill in file struct */ + + file->cli_fd = fd; + file->fname = SMB_STRDUP(fname); + file->srv = srv; + file->offset = 0; + file->file = True; + + DLIST_ADD(context->files, file); + + /* + * If the file was opened in O_APPEND mode, all write + * operations should be appended to the file. To do that, + * though, using this protocol, would require a getattrE() + * call for each and every write, to determine where the end + * of the file is. (There does not appear to be an append flag + * in the protocol.) Rather than add all of that overhead of + * retrieving the current end-of-file offset prior to each + * write operation, we'll assume that most append operations + * will continuously write, so we'll just set the offset to + * the end of the file now and hope that's adequate. + * + * Note to self: If this proves inadequate, and O_APPEND + * should, in some cases, be forced for each write, add a + * field in the context options structure, for + * "strict_append_mode" which would select between the current + * behavior (if FALSE) or issuing a getattrE() prior to each + * write and forcing the write to the end of the file (if + * TRUE). Adding that capability will likely require adding + * an "append" flag into the _SMBCFILE structure to track + * whether a file was opened in O_APPEND mode. -- djl + */ + if (flags & O_APPEND) { + if (SMBC_lseek_ctx(context, file, 0, SEEK_END) < 0) { + (void) SMBC_close_ctx(context, file); + errno = ENXIO; + TALLOC_FREE(frame); + return NULL; + } + } + + TALLOC_FREE(frame); + return file; + + } + + /* Check if opendir needed ... */ + + if (fd == -1) { + int eno = 0; + + eno = SMBC_errno(context, srv->cli); + file = (context->posix_emu.opendir_fn)(context, fname); + if (!file) errno = eno; + TALLOC_FREE(frame); + return file; + + } + + errno = EINVAL; /* FIXME, correct errno ? */ + TALLOC_FREE(frame); + return NULL; + +} + +/* + * Routine to create a file + */ + +static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */ + +SMBCFILE * +SMBC_creat_ctx(SMBCCTX *context, + const char *path, + mode_t mode) +{ + + if (!context || !context->initialized) { + + errno = EINVAL; + return NULL; + + } + + return SMBC_open_ctx(context, path, creat_bits, mode); +} + +/* + * Routine to read() a file ... + */ + +ssize_t +SMBC_read_ctx(SMBCCTX *context, + SMBCFILE *file, + void *buf, + size_t count) +{ + int ret; + char *server = NULL, *share = NULL, *user = NULL, *password = NULL; + char *path = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + /* + * offset: + * + * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) -- + * appears to pass file->offset (which is type off_t) differently than + * a local variable of type off_t. Using local variable "offset" in + * the call to cli_read() instead of file->offset fixes a problem + * retrieving data at an offset greater than 4GB. + */ + off_t offset; + + if (!context || !context->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count)); + + if (!file || !SMBC_dlist_contains(context->files, file)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + + } + + offset = file->offset; + + /* Check that the buffer exists ... */ + + if (buf == NULL) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + /*d_printf(">>>read: parsing %s\n", file->fname);*/ + if (SMBC_parse_path(frame, + context, + file->fname, + NULL, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + /*d_printf(">>>read: resolving %s\n", path);*/ + if (!cli_resolve_path(frame, "", file->srv->cli, path, + &targetcli, &targetpath)) { + d_printf("Could not resolve %s\n", path); + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/ + + ret = cli_read(targetcli, file->cli_fd, (char *)buf, offset, count); + + if (ret < 0) { + + errno = SMBC_errno(context, targetcli); + TALLOC_FREE(frame); + return -1; + + } + + file->offset += ret; + + DEBUG(4, (" --> %d\n", ret)); + + TALLOC_FREE(frame); + return ret; /* Success, ret bytes of data ... */ + +} + +/* + * Routine to write() a file ... + */ + +ssize_t +SMBC_write_ctx(SMBCCTX *context, + SMBCFILE *file, + void *buf, + size_t count) +{ + int ret; + off_t offset; + char *server = NULL, *share = NULL, *user = NULL, *password = NULL; + char *path = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + /* First check all pointers before dereferencing them */ + + if (!context || !context->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + if (!file || !SMBC_dlist_contains(context->files, file)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + } + + /* Check that the buffer exists ... */ + + if (buf == NULL) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + offset = file->offset; /* See "offset" comment in SMBC_read_ctx() */ + + /*d_printf(">>>write: parsing %s\n", file->fname);*/ + if (SMBC_parse_path(frame, + context, + file->fname, + NULL, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + /*d_printf(">>>write: resolving %s\n", path);*/ + if (!cli_resolve_path(frame, "", file->srv->cli, path, + &targetcli, &targetpath)) { + d_printf("Could not resolve %s\n", path); + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>write: resolved path as %s\n", targetpath);*/ + + ret = cli_write(targetcli, file->cli_fd, 0, (char *)buf, offset, count); + + if (ret <= 0) { + errno = SMBC_errno(context, targetcli); + TALLOC_FREE(frame); + return -1; + + } + + file->offset += ret; + + TALLOC_FREE(frame); + return ret; /* Success, 0 bytes of data ... */ +} + +/* + * Routine to close() a file ... + */ + +int +SMBC_close_ctx(SMBCCTX *context, + SMBCFILE *file) +{ + SMBCSRV *srv; + char *server = NULL, *share = NULL, *user = NULL, *password = NULL; + char *path = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!file || !SMBC_dlist_contains(context->files, file)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + } + + /* IS a dir ... */ + if (!file->file) { + TALLOC_FREE(frame); + return (context->posix_emu.closedir_fn)(context, file); + } + + /*d_printf(">>>close: parsing %s\n", file->fname);*/ + if (SMBC_parse_path(frame, + context, + file->fname, + NULL, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + /*d_printf(">>>close: resolving %s\n", path);*/ + if (!cli_resolve_path(frame, "", file->srv->cli, path, + &targetcli, &targetpath)) { + d_printf("Could not resolve %s\n", path); + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>close: resolved path as %s\n", targetpath);*/ + + if (!cli_close(targetcli, file->cli_fd)) { + + DEBUG(3, ("cli_close failed on %s. purging server.\n", + file->fname)); + /* Deallocate slot and remove the server + * from the server cache if unused */ + errno = SMBC_errno(context, targetcli); + srv = file->srv; + DLIST_REMOVE(context->files, file); + SAFE_FREE(file->fname); + SAFE_FREE(file); + (context->server.remove_unused_server_fn)(context, srv); + TALLOC_FREE(frame); + return -1; + + } + + DLIST_REMOVE(context->files, file); + SAFE_FREE(file->fname); + SAFE_FREE(file); + TALLOC_FREE(frame); + + return 0; +} + +/* + * Get info from an SMB server on a file. Use a qpathinfo call first + * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo + */ +bool +SMBC_getatr(SMBCCTX * context, + SMBCSRV *srv, + char *path, + uint16 *mode, + SMB_OFF_T *size, + struct timespec *create_time_ts, + struct timespec *access_time_ts, + struct timespec *write_time_ts, + struct timespec *change_time_ts, + SMB_INO_T *ino) +{ + char *fixedpath = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + time_t write_time; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + /* path fixup for . and .. */ + if (strequal(path, ".") || strequal(path, "..")) { + fixedpath = talloc_strdup(frame, "\\"); + if (!fixedpath) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } else { + fixedpath = talloc_strdup(frame, path); + if (!fixedpath) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + trim_string(fixedpath, NULL, "\\.."); + trim_string(fixedpath, NULL, "\\."); + } + DEBUG(4,("SMBC_getatr: sending qpathinfo\n")); + + if (!cli_resolve_path(frame, "", srv->cli, fixedpath, + &targetcli, &targetpath)) { + d_printf("Couldn't resolve %s\n", path); + TALLOC_FREE(frame); + return False; + } + + if (!srv->no_pathinfo2 && + cli_qpathinfo2(targetcli, targetpath, + create_time_ts, + access_time_ts, + write_time_ts, + change_time_ts, + size, mode, ino)) { + TALLOC_FREE(frame); + return True; + } + + /* if this is NT then don't bother with the getatr */ + if (targetcli->capabilities & CAP_NT_SMBS) { + errno = EPERM; + TALLOC_FREE(frame); + return False; + } + + if (cli_getatr(targetcli, targetpath, mode, size, &write_time)) { + + struct timespec w_time_ts; + + w_time_ts = convert_time_t_to_timespec(write_time); + + if (write_time_ts != NULL) { + *write_time_ts = w_time_ts; + } + + if (create_time_ts != NULL) { + *create_time_ts = w_time_ts; + } + + if (access_time_ts != NULL) { + *access_time_ts = w_time_ts; + } + + if (change_time_ts != NULL) { + *change_time_ts = w_time_ts; + } + + srv->no_pathinfo2 = True; + TALLOC_FREE(frame); + return True; + } + + errno = EPERM; + TALLOC_FREE(frame); + return False; + +} + +/* + * Set file info on an SMB server. Use setpathinfo call first. If that + * fails, use setattrE.. + * + * Access and modification time parameters are always used and must be + * provided. Create time, if zero, will be determined from the actual create + * time of the file. If non-zero, the create time will be set as well. + * + * "mode" (attributes) parameter may be set to -1 if it is not to be set. + */ +bool +SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path, + time_t create_time, + time_t access_time, + time_t write_time, + time_t change_time, + uint16 mode) +{ + int fd; + int ret; + TALLOC_CTX *frame = talloc_stackframe(); + + /* + * First, try setpathinfo (if qpathinfo succeeded), for it is the + * modern function for "new code" to be using, and it works given a + * filename rather than requiring that the file be opened to have its + * attributes manipulated. + */ + if (srv->no_pathinfo || + ! cli_setpathinfo(srv->cli, path, + create_time, + access_time, + write_time, + change_time, + mode)) { + + /* + * setpathinfo is not supported; go to plan B. + * + * cli_setatr() does not work on win98, and it also doesn't + * support setting the access time (only the modification + * time), so in all cases, we open the specified file and use + * cli_setattrE() which should work on all OS versions, and + * supports both times. + */ + + /* Don't try {q,set}pathinfo() again, with this server */ + srv->no_pathinfo = True; + + /* Open the file */ + if ((fd = cli_open(srv->cli, path, O_RDWR, DENY_NONE)) < 0) { + + errno = SMBC_errno(context, srv->cli); + TALLOC_FREE(frame); + return -1; + } + + /* Set the new attributes */ + ret = cli_setattrE(srv->cli, fd, + change_time, + access_time, + write_time); + + /* Close the file */ + cli_close(srv->cli, fd); + + /* + * Unfortunately, setattrE() doesn't have a provision for + * setting the access mode (attributes). We'll have to try + * cli_setatr() for that, and with only this parameter, it + * seems to work on win98. + */ + if (ret && mode != (uint16) -1) { + ret = cli_setatr(srv->cli, path, mode, 0); + } + + if (! ret) { + errno = SMBC_errno(context, srv->cli); + TALLOC_FREE(frame); + return False; + } + } + + TALLOC_FREE(frame); + return True; +} + +/* + * A routine to lseek() a file + */ + +off_t +SMBC_lseek_ctx(SMBCCTX *context, + SMBCFILE *file, + off_t offset, + int whence) +{ + SMB_OFF_T size; + char *server = NULL, *share = NULL, *user = NULL, *password = NULL; + char *path = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!file || !SMBC_dlist_contains(context->files, file)) { + + errno = EBADF; + TALLOC_FREE(frame); + return -1; + + } + + if (!file->file) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; /* Can't lseek a dir ... */ + + } + + switch (whence) { + case SEEK_SET: + file->offset = offset; + break; + + case SEEK_CUR: + file->offset += offset; + break; + + case SEEK_END: + /*d_printf(">>>lseek: parsing %s\n", file->fname);*/ + if (SMBC_parse_path(frame, + context, + file->fname, + NULL, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + /*d_printf(">>>lseek: resolving %s\n", path);*/ + if (!cli_resolve_path(frame, "", file->srv->cli, path, + &targetcli, &targetpath)) { + d_printf("Could not resolve %s\n", path); + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/ + + if (!cli_qfileinfo(targetcli, file->cli_fd, NULL, + &size, NULL, NULL, NULL, NULL, NULL)) + { + SMB_OFF_T b_size = size; + if (!cli_getattrE(targetcli, file->cli_fd, + NULL, &b_size, NULL, NULL, NULL)) + { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } else + size = b_size; + } + file->offset = size + offset; + break; + + default: + errno = EINVAL; + break; + + } + + TALLOC_FREE(frame); + return file->offset; + +} + + +/* + * Routine to truncate a file given by its file descriptor, to a specified size + */ + +int +SMBC_ftruncate_ctx(SMBCCTX *context, + SMBCFILE *file, + off_t length) +{ + SMB_OFF_T size = length; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *path = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!file || !SMBC_dlist_contains(context->files, file)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + } + + if (!file->file) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + /*d_printf(">>>fstat: parsing %s\n", file->fname);*/ + if (SMBC_parse_path(frame, + context, + file->fname, + NULL, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + /*d_printf(">>>fstat: resolving %s\n", path);*/ + if (!cli_resolve_path(frame, "", file->srv->cli, path, + &targetcli, &targetpath)) { + d_printf("Could not resolve %s\n", path); + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/ + + if (!cli_ftruncate(targetcli, file->cli_fd, size)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + TALLOC_FREE(frame); + return 0; + +} diff --git a/source3/libsmb/libsmb_misc.c b/source3/libsmb/libsmb_misc.c new file mode 100644 index 0000000000..f2fd919ef6 --- /dev/null +++ b/source3/libsmb/libsmb_misc.c @@ -0,0 +1,73 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/* + * check if an element is part of the list. + */ +int +SMBC_dlist_contains(SMBCFILE * list, SMBCFILE *p) +{ + if (!p || !list) return False; + do { + if (p == list) return True; + list = list->next; + } while (list); + return False; +} + + +/* + * Convert an SMB error into a UNIX error ... + */ +int +SMBC_errno(SMBCCTX *context, + struct cli_state *c) +{ + int ret = cli_errno(c); + + if (cli_is_dos_error(c)) { + uint8 eclass; + uint32 ecode; + + cli_dos_error(c, &eclass, &ecode); + + DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n", + (int)eclass, (int)ecode, (int)ecode, ret)); + } else { + NTSTATUS status; + + status = cli_nt_error(c); + + DEBUG(3,("smbc errno %s -> %d\n", + nt_errstr(status), ret)); + } + + return ret; +} + diff --git a/source3/libsmb/libsmb_path.c b/source3/libsmb/libsmb_path.c new file mode 100644 index 0000000000..2533f536c3 --- /dev/null +++ b/source3/libsmb/libsmb_path.c @@ -0,0 +1,399 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/* Used by urldecode_talloc() */ +static int +hex2int( unsigned int _char ) +{ + if ( _char >= 'A' && _char <='F') + return _char - 'A' + 10; + if ( _char >= 'a' && _char <='f') + return _char - 'a' + 10; + if ( _char >= '0' && _char <='9') + return _char - '0'; + return -1; +} + +/* + * SMBC_urldecode() + * and urldecode_talloc() (internal fn.) + * + * Convert strings of %xx to their single character equivalent. Each 'x' must + * be a valid hexadecimal digit, or that % sequence is left undecoded. + * + * dest may, but need not be, the same pointer as src. + * + * Returns the number of % sequences which could not be converted due to lack + * of two following hexadecimal digits. + */ +static int +urldecode_talloc(TALLOC_CTX *ctx, char **pp_dest, const char *src) +{ + int old_length = strlen(src); + int i = 0; + int err_count = 0; + size_t newlen = 1; + char *p, *dest; + + if (old_length == 0) { + return 0; + } + + *pp_dest = NULL; + for (i = 0; i < old_length; ) { + unsigned char character = src[i++]; + + if (character == '%') { + int a = i+1 < old_length ? hex2int(src[i]) : -1; + int b = i+1 < old_length ? hex2int(src[i+1]) : -1; + + /* Replace valid sequence */ + if (a != -1 && b != -1) { + /* Replace valid %xx sequence with %dd */ + character = (a * 16) + b; + if (character == '\0') { + break; /* Stop at %00 */ + } + i += 2; + } else { + err_count++; + } + } + newlen++; + } + + dest = TALLOC_ARRAY(ctx, char, newlen); + if (!dest) { + return err_count; + } + + err_count = 0; + for (p = dest, i = 0; i < old_length; ) { + unsigned char character = src[i++]; + + if (character == '%') { + int a = i+1 < old_length ? hex2int(src[i]) : -1; + int b = i+1 < old_length ? hex2int(src[i+1]) : -1; + + /* Replace valid sequence */ + if (a != -1 && b != -1) { + /* Replace valid %xx sequence with %dd */ + character = (a * 16) + b; + if (character == '\0') { + break; /* Stop at %00 */ + } + i += 2; + } else { + err_count++; + } + } + *p++ = character; + } + + *p = '\0'; + *pp_dest = dest; + return err_count; +} + +int +SMBC_urldecode(char *dest, + char *src, + size_t max_dest_len) +{ + TALLOC_CTX *frame = talloc_stackframe(); + char *pdest; + int ret = urldecode_talloc(frame, &pdest, src); + + if (pdest) { + strlcpy(dest, pdest, max_dest_len); + } + TALLOC_FREE(frame); + return ret; +} + +/* + * SMBC_urlencode() + * + * Convert any characters not specifically allowed in a URL into their %xx + * equivalent. + * + * Returns the remaining buffer length. + */ +int +SMBC_urlencode(char *dest, + char *src, + int max_dest_len) +{ + char hex[] = "0123456789ABCDEF"; + + for (; *src != '\0' && max_dest_len >= 3; src++) { + + if ((*src < '0' && + *src != '-' && + *src != '.') || + (*src > '9' && + *src < 'A') || + (*src > 'Z' && + *src < 'a' && + *src != '_') || + (*src > 'z')) { + *dest++ = '%'; + *dest++ = hex[(*src >> 4) & 0x0f]; + *dest++ = hex[*src & 0x0f]; + max_dest_len -= 3; + } else { + *dest++ = *src; + max_dest_len--; + } + } + + *dest++ = '\0'; + max_dest_len--; + + return max_dest_len; +} + +/* + * Function to parse a path and turn it into components + * + * The general format of an SMB URI is explain in Christopher Hertel's CIFS + * book, at http://ubiqx.org/cifs/Appendix-D.html. We accept a subset of the + * general format ("smb:" only; we do not look for "cifs:"). + * + * + * We accept: + * smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options] + * + * Meaning of URLs: + * + * smb:// Show all workgroups. + * + * The method of locating the list of workgroups varies + * depending upon the setting of the context variable + * context->browse_max_lmb_count. This value determines + * the maximum number of local master browsers to query + * for the list of workgroups. In order to ensure that + * a complete list of workgroups is obtained, all master + * browsers must be queried, but if there are many + * workgroups, the time spent querying can begin to add up. + * For small networks (not many workgroups), it is suggested + * that this variable be set to 0, indicating query all local + * master browsers. When the network has many workgroups, a + * reasonable setting for this variable might be around 3. + * + * smb://name/ if name<1D> or name<1B> exists, list servers in + * workgroup, else, if name<20> exists, list all shares + * for server ... + * + * If "options" are provided, this function returns the entire option list as a + * string, for later parsing by the caller. Note that currently, no options + * are supported. + */ + +static const char *smbc_prefix = "smb:"; + +int +SMBC_parse_path(TALLOC_CTX *ctx, + SMBCCTX *context, + const char *fname, + char **pp_workgroup, + char **pp_server, + char **pp_share, + char **pp_path, + char **pp_user, + char **pp_password, + char **pp_options) +{ + char *s; + const char *p; + char *q, *r; + int len; + + /* Ensure these returns are at least valid pointers. */ + *pp_server = talloc_strdup(ctx, ""); + *pp_share = talloc_strdup(ctx, ""); + *pp_path = talloc_strdup(ctx, ""); + *pp_user = talloc_strdup(ctx, ""); + *pp_password = talloc_strdup(ctx, ""); + + if (!*pp_server || !*pp_share || !*pp_path || + !*pp_user || !*pp_password) { + return -1; + } + + /* + * Assume we wont find an authentication domain to parse, so default + * to the workgroup in the provided context. + */ + if (pp_workgroup != NULL) { + *pp_workgroup = talloc_strdup(ctx, context->workgroup); + } + + if (pp_options) { + *pp_options = talloc_strdup(ctx, ""); + } + s = talloc_strdup(ctx, fname); + + /* see if it has the right prefix */ + len = strlen(smbc_prefix); + if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) { + return -1; /* What about no smb: ? */ + } + + p = s + len; + + /* Watch the test below, we are testing to see if we should exit */ + + if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) { + DEBUG(1, ("Invalid path (does not begin with smb://")); + return -1; + } + + p += 2; /* Skip the double slash */ + + /* See if any options were specified */ + if ((q = strrchr(p, '?')) != NULL ) { + /* There are options. Null terminate here and point to them */ + *q++ = '\0'; + + DEBUG(4, ("Found options '%s'", q)); + + /* Copy the options */ + if (*pp_options != NULL) { + TALLOC_FREE(*pp_options); + *pp_options = talloc_strdup(ctx, q); + } + } + + if (*p == '\0') { + goto decoding; + } + + if (*p == '/') { + int wl = strlen(context->workgroup); + + if (wl > 16) { + wl = 16; + } + + *pp_server = talloc_strdup(ctx, context->workgroup); + if (!*pp_server) { + return -1; + } + *pp_server[wl] = '\0'; + return 0; + } + + /* + * ok, its for us. Now parse out the server, share etc. + * + * However, we want to parse out [[domain;]user[:password]@] if it + * exists ... + */ + + /* check that '@' occurs before '/', if '/' exists at all */ + q = strchr_m(p, '@'); + r = strchr_m(p, '/'); + if (q && (!r || q < r)) { + char *userinfo = NULL; + const char *u; + + next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@"); + if (!userinfo) { + return -1; + } + u = userinfo; + + if (strchr_m(u, ';')) { + char *workgroup; + next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";"); + if (!workgroup) { + return -1; + } + if (pp_workgroup) { + *pp_workgroup = workgroup; + } + } + + if (strchr_m(u, ':')) { + next_token_no_ltrim_talloc(ctx, &u, pp_user, ":"); + if (!*pp_user) { + return -1; + } + *pp_password = talloc_strdup(ctx, u); + if (!*pp_password) { + return -1; + } + } else { + *pp_user = talloc_strdup(ctx, u); + if (!*pp_user) { + return -1; + } + } + } + + if (!next_token_talloc(ctx, &p, pp_server, "/")) { + return -1; + } + + if (*p == (char)0) { + goto decoding; /* That's it ... */ + } + + if (!next_token_talloc(ctx, &p, pp_share, "/")) { + return -1; + } + + /* + * Prepend a leading slash if there's a file path, as required by + * NetApp filers. + */ + if (*p != '\0') { + *pp_path = talloc_asprintf(ctx, + "\\%s", + p); + } else { + *pp_path = talloc_strdup(ctx, ""); + } + if (!*pp_path) { + return -1; + } + string_replace(*pp_path, '/', '\\'); + + decoding: + + (void) urldecode_talloc(ctx, pp_path, *pp_path); + (void) urldecode_talloc(ctx, pp_server, *pp_server); + (void) urldecode_talloc(ctx, pp_share, *pp_share); + (void) urldecode_talloc(ctx, pp_user, *pp_user); + (void) urldecode_talloc(ctx, pp_password, *pp_password); + + return 0; +} + diff --git a/source3/libsmb/libsmb_printjob.c b/source3/libsmb/libsmb_printjob.c new file mode 100644 index 0000000000..f106080b6f --- /dev/null +++ b/source3/libsmb/libsmb_printjob.c @@ -0,0 +1,334 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/* + * Open a print file to be written to by other calls + */ + +SMBCFILE * +SMBC_open_print_job_ctx(SMBCCTX *context, + const char *fname) +{ + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *path = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return NULL; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return NULL; + } + + DEBUG(4, ("SMBC_open_print_job_ctx(%s)\n", fname)); + + if (SMBC_parse_path(frame, + context, + fname, + NULL, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return NULL; + } + + /* What if the path is empty, or the file exists? */ + + TALLOC_FREE(frame); + return (context->posix_emu.open_fn)(context, fname, O_WRONLY, 666); +} + +/* + * Routine to print a file on a remote server ... + * + * We open the file, which we assume to be on a remote server, and then + * copy it to a print file on the share specified by printq. + */ + +int +SMBC_print_file_ctx(SMBCCTX *c_file, + const char *fname, + SMBCCTX *c_print, + const char *printq) +{ + SMBCFILE *fid1; + SMBCFILE *fid2; + int bytes; + int saverr; + int tot_bytes = 0; + char buf[4096]; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!c_file || !c_file->initialized || + !c_print || !c_print->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + if (!fname && !printq) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + /* Try to open the file for reading ... */ + + if ((long)(fid1 = smbc_getFunctionOpen(c_file)(c_file, fname, O_RDONLY, 0666)) < 0) { + DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno)); + TALLOC_FREE(frame); + return -1; /* smbc_open sets errno */ + } + + /* Now, try to open the printer file for writing */ + + if ((long)(fid2 = smbc_getFunctionOpenPrintJob(c_print)(c_print, printq)) < 0) { + + saverr = errno; /* Save errno */ + smbc_getFunctionClose(c_file)(c_file, fid1); + errno = saverr; + TALLOC_FREE(frame); + return -1; + + } + + while ((bytes = smbc_getFunctionRead(c_file)(c_file, fid1, + buf, sizeof(buf))) > 0) { + + tot_bytes += bytes; + + if ((smbc_getFunctionWrite(c_print)(c_print, fid2, + buf, bytes)) < 0) { + + saverr = errno; + smbc_getFunctionClose(c_file)(c_file, fid1); + smbc_getFunctionClose(c_print)(c_print, fid2); + errno = saverr; + + } + + } + + saverr = errno; + + smbc_getFunctionClose(c_file)(c_file, fid1); /* We have to close these anyway */ + smbc_getFunctionClose(c_print)(c_print, fid2); + + if (bytes < 0) { + + errno = saverr; + TALLOC_FREE(frame); + return -1; + + } + + TALLOC_FREE(frame); + return tot_bytes; + +} + +/* + * Routine to list print jobs on a printer share ... + */ + +int +SMBC_list_print_jobs_ctx(SMBCCTX *context, + const char *fname, + smbc_list_print_job_fn fn) +{ + SMBCSRV *srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, context->user); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, share, &workgroup, &user, &password); + + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + if (cli_print_queue(srv->cli, + (void (*)(struct print_job_info *))fn) < 0) { + errno = SMBC_errno(context, srv->cli); + TALLOC_FREE(frame); + return -1; + } + + TALLOC_FREE(frame); + return 0; + +} + +/* + * Delete a print job from a remote printer share + */ + +int +SMBC_unlink_print_job_ctx(SMBCCTX *context, + const char *fname, + int id) +{ + SMBCSRV *srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + int err; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, context->user); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, share, &workgroup, &user, &password); + + if (!srv) { + + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + + } + + if ((err = cli_printjob_del(srv->cli, id)) != 0) { + + if (err < 0) + errno = SMBC_errno(context, srv->cli); + else if (err == ERRnosuchprintjob) + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + TALLOC_FREE(frame); + return 0; + +} + diff --git a/source3/libsmb/libsmb_server.c b/source3/libsmb/libsmb_server.c new file mode 100644 index 0000000000..978a843d30 --- /dev/null +++ b/source3/libsmb/libsmb_server.c @@ -0,0 +1,675 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/* + * Check a server for being alive and well. + * returns 0 if the server is in shape. Returns 1 on error + * + * Also useable outside libsmbclient to enable external cache + * to do some checks too. + */ +int +SMBC_check_server(SMBCCTX * context, + SMBCSRV * server) +{ + socklen_t size; + struct sockaddr addr; + + size = sizeof(addr); + return (getpeername(server->cli->fd, &addr, &size) == -1); +} + +/* + * Remove a server from the cached server list it's unused. + * On success, 0 is returned. 1 is returned if the server could not be removed. + * + * Also useable outside libsmbclient + */ +int +SMBC_remove_unused_server(SMBCCTX * context, + SMBCSRV * srv) +{ + SMBCFILE * file; + + /* are we being fooled ? */ + if (!context || !context->initialized || !srv) { + return 1; + } + + /* Check all open files/directories for a relation with this server */ + for (file = context->files; file; file = file->next) { + if (file->srv == srv) { + /* Still used */ + DEBUG(3, ("smbc_remove_usused_server: " + "%p still used by %p.\n", + srv, file)); + return 1; + } + } + + DLIST_REMOVE(context->servers, srv); + + cli_shutdown(srv->cli); + srv->cli = NULL; + + DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv)); + + (context->cache.remove_cached_server_fn)(context, srv); + + SAFE_FREE(srv); + return 0; +} + +/**************************************************************** + * Call the auth_fn with fixed size (fstring) buffers. + ***************************************************************/ +void +SMBC_call_auth_fn(TALLOC_CTX *ctx, + SMBCCTX *context, + const char *server, + const char *share, + char **pp_workgroup, + char **pp_username, + char **pp_password) +{ + fstring workgroup; + fstring username; + fstring password; + + strlcpy(workgroup, *pp_workgroup, sizeof(workgroup)); + strlcpy(username, *pp_username, sizeof(username)); + strlcpy(password, *pp_password, sizeof(password)); + + (context->server.get_auth_data_fn)( + server, share, + workgroup, sizeof(workgroup), + username, sizeof(username), + password, sizeof(password)); + + TALLOC_FREE(*pp_workgroup); + TALLOC_FREE(*pp_username); + TALLOC_FREE(*pp_password); + + *pp_workgroup = talloc_strdup(ctx, workgroup); + *pp_username = talloc_strdup(ctx, username); + *pp_password = talloc_strdup(ctx, password); +} + + +void +SMBC_get_auth_data(const char *server, const char *share, + char *workgroup_buf, int workgroup_buf_len, + char *username_buf, int username_buf_len, + char *password_buf, int password_buf_len) +{ + /* Default function just uses provided data. Nothing to do. */ +} + + + +SMBCSRV * +SMBC_find_server(TALLOC_CTX *ctx, + SMBCCTX *context, + const char *server, + const char *share, + char **pp_workgroup, + char **pp_username, + char **pp_password) +{ + SMBCSRV *srv; + int auth_called = 0; + + check_server_cache: + + srv = (context->cache.get_cached_server_fn)(context, + server, share, + *pp_workgroup, + *pp_username); + + if (!auth_called && !srv && (!*pp_username || !(*pp_username)[0] || + !*pp_password || !(*pp_password)[0])) { + SMBC_call_auth_fn(ctx, context, server, share, + pp_workgroup, pp_username, pp_password); + + if (!pp_workgroup || !pp_username || !pp_password) { + return NULL; + } + + /* + * However, smbc_auth_fn may have picked up info relating to + * an existing connection, so try for an existing connection + * again ... + */ + auth_called = 1; + goto check_server_cache; + + } + + if (srv) { + if ((context->server.check_server_fn)(context, srv)) { + /* + * This server is no good anymore + * Try to remove it and check for more possible + * servers in the cache + */ + if ((context->server.remove_unused_server_fn)(context, + srv)) { + /* + * We could not remove the server completely, + * remove it from the cache so we will not get + * it again. It will be removed when the last + * file/dir is closed. + */ + (context->cache.remove_cached_server_fn)(context, + srv); + } + + /* + * Maybe there are more cached connections to this + * server + */ + goto check_server_cache; + } + + return srv; + } + + return NULL; +} + +/* + * Connect to a server, possibly on an existing connection + * + * Here, what we want to do is: If the server and username + * match an existing connection, reuse that, otherwise, establish a + * new connection. + * + * If we have to create a new connection, call the auth_fn to get the + * info we need, unless the username and password were passed in. + */ + +SMBCSRV * +SMBC_server(TALLOC_CTX *ctx, + SMBCCTX *context, + bool connect_if_not_found, + const char *server, + const char *share, + char **pp_workgroup, + char **pp_username, + char **pp_password) +{ + SMBCSRV *srv=NULL; + struct cli_state *c; + struct nmb_name called, calling; + const char *server_n = server; + struct sockaddr_storage ss; + int tried_reverse = 0; + int port_try_first; + int port_try_next; + const char *username_used; + NTSTATUS status; + + zero_addr(&ss); + ZERO_STRUCT(c); + + if (server[0] == 0) { + errno = EPERM; + return NULL; + } + + /* Look for a cached connection */ + srv = SMBC_find_server(ctx, context, server, share, + pp_workgroup, pp_username, pp_password); + + /* + * If we found a connection and we're only allowed one share per + * server... + */ + if (srv && *share != '\0' && context->one_share_per_server) { + + /* + * ... then if there's no current connection to the share, + * connect to it. SMBC_find_server(), or rather the function + * pointed to by context->cache.get_cached_srv_fn which + * was called by SMBC_find_server(), will have issued a tree + * disconnect if the requested share is not the same as the + * one that was already connected. + */ + if (srv->cli->cnum == (uint16) -1) { + /* Ensure we have accurate auth info */ + SMBC_call_auth_fn(ctx, context, server, share, + pp_workgroup, pp_username, pp_password); + + if (!*pp_workgroup || !*pp_username || !*pp_password) { + errno = ENOMEM; + cli_shutdown(srv->cli); + srv->cli = NULL; + (context->cache.remove_cached_server_fn)(context, + srv); + return NULL; + } + + /* + * We don't need to renegotiate encryption + * here as the encryption context is not per + * tid. + */ + + if (!cli_send_tconX(srv->cli, share, "?????", + *pp_password, + strlen(*pp_password)+1)) { + + errno = SMBC_errno(context, srv->cli); + cli_shutdown(srv->cli); + srv->cli = NULL; + (context->cache.remove_cached_server_fn)(context, + srv); + srv = NULL; + } + + /* + * Regenerate the dev value since it's based on both + * server and share + */ + if (srv) { + srv->dev = (dev_t)(str_checksum(server) ^ + str_checksum(share)); + } + } + } + + /* If we have a connection... */ + if (srv) { + + /* ... then we're done here. Give 'em what they came for. */ + return srv; + } + + /* If we're not asked to connect when a connection doesn't exist... */ + if (! connect_if_not_found) { + /* ... then we're done here. */ + return NULL; + } + + if (!*pp_workgroup || !*pp_username || !*pp_password) { + errno = ENOMEM; + return NULL; + } + + make_nmb_name(&calling, context->netbios_name, 0x0); + make_nmb_name(&called , server, 0x20); + + DEBUG(4,("SMBC_server: server_n=[%s] server=[%s]\n", server_n, server)); + + DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server)); + + again: + + zero_addr(&ss); + + /* have to open a new connection */ + if ((c = cli_initialise()) == NULL) { + errno = ENOMEM; + return NULL; + } + + if (context->use_kerberos) { + c->use_kerberos = True; + } + if (context->fallback_after_kerberos) { + c->fallback_after_kerberos = True; + } + + c->timeout = context->timeout; + + /* + * Force use of port 139 for first try if share is $IPC, empty, or + * null, so browse lists can work + */ + if (share == NULL || *share == '\0' || strcmp(share, "IPC$") == 0) { + port_try_first = 139; + port_try_next = 445; + } else { + port_try_first = 445; + port_try_next = 139; + } + + c->port = port_try_first; + + status = cli_connect(c, server_n, &ss); + if (!NT_STATUS_IS_OK(status)) { + + /* First connection attempt failed. Try alternate port. */ + c->port = port_try_next; + + status = cli_connect(c, server_n, &ss); + if (!NT_STATUS_IS_OK(status)) { + cli_shutdown(c); + errno = ETIMEDOUT; + return NULL; + } + } + + if (!cli_session_request(c, &calling, &called)) { + cli_shutdown(c); + if (strcmp(called.name, "*SMBSERVER")) { + make_nmb_name(&called , "*SMBSERVER", 0x20); + goto again; + } else { /* Try one more time, but ensure we don't loop */ + + /* Only try this if server is an IP address ... */ + + if (is_ipaddress(server) && !tried_reverse) { + fstring remote_name; + struct sockaddr_storage rem_ss; + + if (!interpret_string_addr(&rem_ss, server, + NI_NUMERICHOST)) { + DEBUG(4, ("Could not convert IP address " + "%s to struct sockaddr_storage\n", + server)); + errno = ETIMEDOUT; + return NULL; + } + + tried_reverse++; /* Yuck */ + + if (name_status_find("*", 0, 0, &rem_ss, remote_name)) { + make_nmb_name(&called, remote_name, 0x20); + goto again; + } + } + } + errno = ETIMEDOUT; + return NULL; + } + + DEBUG(4,(" session request ok\n")); + + if (!cli_negprot(c)) { + cli_shutdown(c); + errno = ETIMEDOUT; + return NULL; + } + + username_used = *pp_username; + + if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used, + *pp_password, strlen(*pp_password), + *pp_password, strlen(*pp_password), + *pp_workgroup))) { + + /* Failed. Try an anonymous login, if allowed by flags. */ + username_used = ""; + + if (context->no_auto_anonymous_login || + !NT_STATUS_IS_OK(cli_session_setup(c, username_used, + *pp_password, 1, + *pp_password, 0, + *pp_workgroup))) { + + cli_shutdown(c); + errno = EPERM; + return NULL; + } + } + + DEBUG(4,(" session setup ok\n")); + + if (!cli_send_tconX(c, share, "?????", + *pp_password, strlen(*pp_password)+1)) { + errno = SMBC_errno(context, c); + cli_shutdown(c); + return NULL; + } + + DEBUG(4,(" tconx ok\n")); + + if (context->smb_encryption_level) { + /* Attempt UNIX smb encryption. */ + if (!NT_STATUS_IS_OK(cli_force_encryption(c, + username_used, + *pp_password, + *pp_workgroup))) { + + /* + * context->smb_encryption_level == 1 + * means don't fail if encryption can't be negotiated, + * == 2 means fail if encryption can't be negotiated. + */ + + DEBUG(4,(" SMB encrypt failed\n")); + + if (context->smb_encryption_level == 2) { + cli_shutdown(c); + errno = EPERM; + return NULL; + } + } + DEBUG(4,(" SMB encrypt ok\n")); + } + + /* + * Ok, we have got a nice connection + * Let's allocate a server structure. + */ + + srv = SMB_MALLOC_P(SMBCSRV); + if (!srv) { + errno = ENOMEM; + goto failed; + } + + ZERO_STRUCTP(srv); + srv->cli = c; + srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share)); + srv->no_pathinfo = False; + srv->no_pathinfo2 = False; + srv->no_nt_session = False; + + /* now add it to the cache (internal or external) */ + /* Let the cache function set errno if it wants to */ + errno = 0; + if ((context->cache.add_cached_server_fn)(context, srv, + server, share, + *pp_workgroup, + *pp_username)) { + int saved_errno = errno; + DEBUG(3, (" Failed to add server to cache\n")); + errno = saved_errno; + if (errno == 0) { + errno = ENOMEM; + } + goto failed; + } + + DEBUG(2, ("Server connect ok: //%s/%s: %p\n", + server, share, srv)); + + DLIST_ADD(context->servers, srv); + return srv; + + failed: + cli_shutdown(c); + if (!srv) { + return NULL; + } + + SAFE_FREE(srv); + return NULL; +} + +/* + * Connect to a server for getting/setting attributes, possibly on an existing + * connection. This works similarly to SMBC_server(). + */ +SMBCSRV * +SMBC_attr_server(TALLOC_CTX *ctx, + SMBCCTX *context, + const char *server, + const char *share, + char **pp_workgroup, + char **pp_username, + char **pp_password) +{ + int flags; + struct sockaddr_storage ss; + struct cli_state *ipc_cli; + struct rpc_pipe_client *pipe_hnd; + NTSTATUS nt_status; + SMBCSRV *ipc_srv=NULL; + + /* + * See if we've already created this special connection. Reference + * our "special" share name '*IPC$', which is an impossible real share + * name due to the leading asterisk. + */ + ipc_srv = SMBC_find_server(ctx, context, server, "*IPC$", + pp_workgroup, pp_username, pp_password); + if (!ipc_srv) { + + /* We didn't find a cached connection. Get the password */ + if (!*pp_password || (*pp_password)[0] == '\0') { + /* ... then retrieve it now. */ + SMBC_call_auth_fn(ctx, context, server, share, + pp_workgroup, pp_username, pp_password); + if (!*pp_workgroup || !*pp_username || !*pp_password) { + errno = ENOMEM; + return NULL; + } + } + + flags = 0; + if (context->use_kerberos) { + flags |= CLI_FULL_CONNECTION_USE_KERBEROS; + } + + zero_addr(&ss); + nt_status = cli_full_connection(&ipc_cli, + global_myname(), server, + &ss, 0, "IPC$", "?????", + *pp_username, + *pp_workgroup, + *pp_password, + flags, + Undefined, NULL); + if (! NT_STATUS_IS_OK(nt_status)) { + DEBUG(1,("cli_full_connection failed! (%s)\n", + nt_errstr(nt_status))); + errno = ENOTSUP; + return NULL; + } + + if (context->smb_encryption_level) { + /* Attempt UNIX smb encryption. */ + if (!NT_STATUS_IS_OK(cli_force_encryption(ipc_cli, + *pp_username, + *pp_password, + *pp_workgroup))) { + + /* + * context->smb_encryption_level == + * 1 means don't fail if encryption can't be + * negotiated, == 2 means fail if encryption + * can't be negotiated. + */ + + DEBUG(4,(" SMB encrypt failed on IPC$\n")); + + if (context->smb_encryption_level == 2) { + cli_shutdown(ipc_cli); + errno = EPERM; + return NULL; + } + } + DEBUG(4,(" SMB encrypt ok on IPC$\n")); + } + + ipc_srv = SMB_MALLOC_P(SMBCSRV); + if (!ipc_srv) { + errno = ENOMEM; + cli_shutdown(ipc_cli); + return NULL; + } + + ZERO_STRUCTP(ipc_srv); + ipc_srv->cli = ipc_cli; + + pipe_hnd = cli_rpc_pipe_open_noauth(ipc_srv->cli, + PI_LSARPC, + &nt_status); + if (!pipe_hnd) { + DEBUG(1, ("cli_nt_session_open fail!\n")); + errno = ENOTSUP; + cli_shutdown(ipc_srv->cli); + free(ipc_srv); + return NULL; + } + + /* + * Some systems don't support + * SEC_RIGHTS_MAXIMUM_ALLOWED, but NT sends 0x2000000 + * so we might as well do it too. + */ + + nt_status = rpccli_lsa_open_policy( + pipe_hnd, + talloc_tos(), + True, + GENERIC_EXECUTE_ACCESS, + &ipc_srv->pol); + + if (!NT_STATUS_IS_OK(nt_status)) { + errno = SMBC_errno(context, ipc_srv->cli); + cli_shutdown(ipc_srv->cli); + return NULL; + } + + /* now add it to the cache (internal or external) */ + + errno = 0; /* let cache function set errno if it likes */ + if ((context->cache.add_cached_server_fn)(context, ipc_srv, + server, + "*IPC$", + *pp_workgroup, + *pp_username)) { + DEBUG(3, (" Failed to add server to cache\n")); + if (errno == 0) { + errno = ENOMEM; + } + cli_shutdown(ipc_srv->cli); + free(ipc_srv); + return NULL; + } + + DLIST_ADD(context->servers, ipc_srv); + } + + return ipc_srv; +} diff --git a/source3/libsmb/libsmb_stat.c b/source3/libsmb/libsmb_stat.c new file mode 100644 index 0000000000..06238863b7 --- /dev/null +++ b/source3/libsmb/libsmb_stat.c @@ -0,0 +1,302 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/* + * Generate an inode number from file name for those things that need it + */ + +static ino_t +generate_inode(SMBCCTX *context, + const char *name) +{ + if (!context || !context->initialized) { + + errno = EINVAL; + return -1; + + } + + if (!*name) return 2; /* FIXME, why 2 ??? */ + return (ino_t)str_checksum(name); + +} + +/* + * Routine to put basic stat info into a stat structure ... Used by stat and + * fstat below. + */ + +static int +setup_stat(SMBCCTX *context, + struct stat *st, + char *fname, + SMB_OFF_T size, + int mode) +{ + TALLOC_CTX *frame = talloc_stackframe(); + + st->st_mode = 0; + + if (IS_DOS_DIR(mode)) { + st->st_mode = SMBC_DIR_MODE; + } else { + st->st_mode = SMBC_FILE_MODE; + } + + if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR; + if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP; + if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH; + if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR; + + st->st_size = size; +#ifdef HAVE_STAT_ST_BLKSIZE + st->st_blksize = 512; +#endif +#ifdef HAVE_STAT_ST_BLOCKS + st->st_blocks = (size+511)/512; +#endif +#ifdef HAVE_STRUCT_STAT_ST_RDEV + st->st_rdev = 0; +#endif + st->st_uid = getuid(); + st->st_gid = getgid(); + + if (IS_DOS_DIR(mode)) { + st->st_nlink = 2; + } else { + st->st_nlink = 1; + } + + if (st->st_ino == 0) { + st->st_ino = generate_inode(context, fname); + } + + TALLOC_FREE(frame); + return True; /* FIXME: Is this needed ? */ + +} + +/* + * Routine to stat a file given a name + */ + +int +SMBC_stat_ctx(SMBCCTX *context, + const char *fname, + struct stat *st) +{ + SMBCSRV *srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + struct timespec write_time_ts; + struct timespec access_time_ts; + struct timespec change_time_ts; + SMB_OFF_T size = 0; + uint16 mode = 0; + SMB_INO_T ino = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_stat(%s)\n", fname)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame,context->user); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, share, &workgroup, &user, &password); + + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + if (!SMBC_getatr(context, srv, path, &mode, &size, + NULL, + &access_time_ts, + &write_time_ts, + &change_time_ts, + &ino)) { + errno = SMBC_errno(context, srv->cli); + TALLOC_FREE(frame); + return -1; + } + + st->st_ino = ino; + + setup_stat(context, st, (char *) fname, size, mode); + + set_atimespec(st, access_time_ts); + set_ctimespec(st, change_time_ts); + set_mtimespec(st, write_time_ts); + st->st_dev = srv->dev; + + TALLOC_FREE(frame); + return 0; + +} + +/* + * Routine to stat a file given an fd + */ + +int +SMBC_fstat_ctx(SMBCCTX *context, + SMBCFILE *file, + struct stat *st) +{ + struct timespec change_time_ts; + struct timespec access_time_ts; + struct timespec write_time_ts; + SMB_OFF_T size; + uint16 mode; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *path = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + SMB_INO_T ino = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!file || !SMBC_dlist_contains(context->files, file)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + } + + if (!file->file) { + TALLOC_FREE(frame); + return (context->posix_emu.fstatdir_fn)(context, file, st); + } + + /*d_printf(">>>fstat: parsing %s\n", file->fname);*/ + if (SMBC_parse_path(frame, + context, + file->fname, + NULL, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + /*d_printf(">>>fstat: resolving %s\n", path);*/ + if (!cli_resolve_path(frame, "", file->srv->cli, path, + &targetcli, &targetpath)) { + d_printf("Could not resolve %s\n", path); + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/ + + if (!cli_qfileinfo(targetcli, file->cli_fd, &mode, &size, + NULL, + &access_time_ts, + &write_time_ts, + &change_time_ts, + &ino)) { + + time_t change_time, access_time, write_time; + + if (!cli_getattrE(targetcli, file->cli_fd, &mode, &size, + &change_time, &access_time, &write_time)) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + change_time_ts = convert_time_t_to_timespec(change_time); + access_time_ts = convert_time_t_to_timespec(access_time); + write_time_ts = convert_time_t_to_timespec(write_time); + } + + st->st_ino = ino; + + setup_stat(context, st, file->fname, size, mode); + + set_atimespec(st, access_time_ts); + set_ctimespec(st, change_time_ts); + set_mtimespec(st, write_time_ts); + st->st_dev = file->srv->dev; + + TALLOC_FREE(frame); + return 0; + +} diff --git a/source3/libsmb/libsmb_xattr.c b/source3/libsmb/libsmb_xattr.c new file mode 100644 index 0000000000..93ca0706b2 --- /dev/null +++ b/source3/libsmb/libsmb_xattr.c @@ -0,0 +1,2293 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/* + * Find an lsa pipe handle associated with a cli struct. + */ +static struct rpc_pipe_client * +find_lsa_pipe_hnd(struct cli_state *ipc_cli) +{ + struct rpc_pipe_client *pipe_hnd; + + for (pipe_hnd = ipc_cli->pipe_list; + pipe_hnd; + pipe_hnd = pipe_hnd->next) { + + if (pipe_hnd->pipe_idx == PI_LSARPC) { + return pipe_hnd; + } + } + + return NULL; +} + +/* + * Sort ACEs according to the documentation at + * http://support.microsoft.com/kb/269175, at least as far as it defines the + * order. + */ + +static int +ace_compare(SEC_ACE *ace1, + SEC_ACE *ace2) +{ + bool b1; + bool b2; + + /* If the ACEs are equal, we have nothing more to do. */ + if (sec_ace_equal(ace1, ace2)) { + return 0; + } + + /* Inherited follow non-inherited */ + b1 = ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0); + b2 = ((ace2->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0); + if (b1 != b2) { + return (b1 ? 1 : -1); + } + + /* + * What shall we do with AUDITs and ALARMs? It's undefined. We'll + * sort them after DENY and ALLOW. + */ + b1 = (ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED && + ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT && + ace1->type != SEC_ACE_TYPE_ACCESS_DENIED && + ace1->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT); + b2 = (ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED && + ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT && + ace2->type != SEC_ACE_TYPE_ACCESS_DENIED && + ace2->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT); + if (b1 != b2) { + return (b1 ? 1 : -1); + } + + /* Allowed ACEs follow denied ACEs */ + b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED || + ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT); + b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED || + ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT); + if (b1 != b2) { + return (b1 ? 1 : -1); + } + + /* + * ACEs applying to an entity's object follow those applying to the + * entity itself + */ + b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT || + ace1->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT); + b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT || + ace2->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT); + if (b1 != b2) { + return (b1 ? 1 : -1); + } + + /* + * If we get this far, the ACEs are similar as far as the + * characteristics we typically care about (those defined by the + * referenced MS document). We'll now sort by characteristics that + * just seems reasonable. + */ + + if (ace1->type != ace2->type) { + return ace2->type - ace1->type; + } + + if (sid_compare(&ace1->trustee, &ace2->trustee)) { + return sid_compare(&ace1->trustee, &ace2->trustee); + } + + if (ace1->flags != ace2->flags) { + return ace1->flags - ace2->flags; + } + + if (ace1->access_mask != ace2->access_mask) { + return ace1->access_mask - ace2->access_mask; + } + + if (ace1->size != ace2->size) { + return ace1->size - ace2->size; + } + + return memcmp(ace1, ace2, sizeof(SEC_ACE)); +} + + +static void +sort_acl(SEC_ACL *the_acl) +{ + uint32 i; + if (!the_acl) return; + + qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]), + QSORT_CAST ace_compare); + + for (i=1;i<the_acl->num_aces;) { + if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) { + int j; + for (j=i; j<the_acl->num_aces-1; j++) { + the_acl->aces[j] = the_acl->aces[j+1]; + } + the_acl->num_aces--; + } else { + i++; + } + } +} + +/* convert a SID to a string, either numeric or username/group */ +static void +convert_sid_to_string(struct cli_state *ipc_cli, + POLICY_HND *pol, + fstring str, + bool numeric, + DOM_SID *sid) +{ + char **domains = NULL; + char **names = NULL; + enum lsa_SidType *types = NULL; + struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli); + TALLOC_CTX *ctx; + + sid_to_fstring(str, sid); + + if (numeric) { + return; /* no lookup desired */ + } + + if (!pipe_hnd) { + return; + } + + /* Ask LSA to convert the sid to a name */ + + ctx = talloc_stackframe(); + + if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ctx, + pol, 1, sid, &domains, + &names, &types)) || + !domains || !domains[0] || !names || !names[0]) { + TALLOC_FREE(ctx); + return; + } + + TALLOC_FREE(ctx); + /* Converted OK */ + + slprintf(str, sizeof(fstring) - 1, "%s%s%s", + domains[0], lp_winbind_separator(), + names[0]); +} + +/* convert a string to a SID, either numeric or username/group */ +static bool +convert_string_to_sid(struct cli_state *ipc_cli, + POLICY_HND *pol, + bool numeric, + DOM_SID *sid, + const char *str) +{ + enum lsa_SidType *types = NULL; + DOM_SID *sids = NULL; + bool result = True; + TALLOC_CTX *ctx = NULL; + struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli); + + if (!pipe_hnd) { + return False; + } + + if (numeric) { + if (strncmp(str, "S-", 2) == 0) { + return string_to_sid(sid, str); + } + + result = False; + goto done; + } + + ctx = talloc_stackframe(); + if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ctx, + pol, 1, &str, NULL, 1, &sids, + &types))) { + result = False; + goto done; + } + + sid_copy(sid, &sids[0]); + done: + + TALLOC_FREE(ctx); + return result; +} + + +/* parse an ACE in the same format as print_ace() */ +static bool +parse_ace(struct cli_state *ipc_cli, + POLICY_HND *pol, + SEC_ACE *ace, + bool numeric, + char *str) +{ + char *p; + const char *cp; + char *tok; + unsigned int atype; + unsigned int aflags; + unsigned int amask; + DOM_SID sid; + SEC_ACCESS mask; + const struct perm_value *v; + struct perm_value { + const char *perm; + uint32 mask; + }; + TALLOC_CTX *frame = talloc_stackframe(); + + /* These values discovered by inspection */ + static const struct perm_value special_values[] = { + { "R", 0x00120089 }, + { "W", 0x00120116 }, + { "X", 0x001200a0 }, + { "D", 0x00010000 }, + { "P", 0x00040000 }, + { "O", 0x00080000 }, + { NULL, 0 }, + }; + + static const struct perm_value standard_values[] = { + { "READ", 0x001200a9 }, + { "CHANGE", 0x001301bf }, + { "FULL", 0x001f01ff }, + { NULL, 0 }, + }; + + + ZERO_STRUCTP(ace); + p = strchr_m(str,':'); + if (!p) { + TALLOC_FREE(frame); + return False; + } + *p = '\0'; + p++; + /* Try to parse numeric form */ + + if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 && + convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) { + goto done; + } + + /* Try to parse text form */ + + if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) { + TALLOC_FREE(frame); + return false; + } + + cp = p; + if (!next_token_talloc(frame, &cp, &tok, "/")) { + TALLOC_FREE(frame); + return false; + } + + if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) { + atype = SEC_ACE_TYPE_ACCESS_ALLOWED; + } else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) { + atype = SEC_ACE_TYPE_ACCESS_DENIED; + } else { + TALLOC_FREE(frame); + return false; + } + + /* Only numeric form accepted for flags at present */ + + if (!(next_token_talloc(frame, &cp, &tok, "/") && + sscanf(tok, "%i", &aflags))) { + TALLOC_FREE(frame); + return false; + } + + if (!next_token_talloc(frame, &cp, &tok, "/")) { + TALLOC_FREE(frame); + return false; + } + + if (strncmp(tok, "0x", 2) == 0) { + if (sscanf(tok, "%i", &amask) != 1) { + TALLOC_FREE(frame); + return false; + } + goto done; + } + + for (v = standard_values; v->perm; v++) { + if (strcmp(tok, v->perm) == 0) { + amask = v->mask; + goto done; + } + } + + p = tok; + + while(*p) { + bool found = False; + + for (v = special_values; v->perm; v++) { + if (v->perm[0] == *p) { + amask |= v->mask; + found = True; + } + } + + if (!found) { + TALLOC_FREE(frame); + return false; + } + p++; + } + + if (*p) { + TALLOC_FREE(frame); + return false; + } + + done: + mask = amask; + init_sec_ace(ace, &sid, atype, mask, aflags); + TALLOC_FREE(frame); + return true; +} + +/* add an ACE to a list of ACEs in a SEC_ACL */ +static bool +add_ace(SEC_ACL **the_acl, + SEC_ACE *ace, + TALLOC_CTX *ctx) +{ + SEC_ACL *newacl; + SEC_ACE *aces; + + if (! *the_acl) { + (*the_acl) = make_sec_acl(ctx, 3, 1, ace); + return True; + } + + if ((aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces)) == NULL) { + return False; + } + memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE)); + memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE)); + newacl = make_sec_acl(ctx, (*the_acl)->revision, + 1+(*the_acl)->num_aces, aces); + SAFE_FREE(aces); + (*the_acl) = newacl; + return True; +} + + +/* parse a ascii version of a security descriptor */ +static SEC_DESC * +sec_desc_parse(TALLOC_CTX *ctx, + struct cli_state *ipc_cli, + POLICY_HND *pol, + bool numeric, + char *str) +{ + const char *p = str; + char *tok; + SEC_DESC *ret = NULL; + size_t sd_size; + DOM_SID *group_sid=NULL; + DOM_SID *owner_sid=NULL; + SEC_ACL *dacl=NULL; + int revision=1; + + while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) { + + if (StrnCaseCmp(tok,"REVISION:", 9) == 0) { + revision = strtol(tok+9, NULL, 16); + continue; + } + + if (StrnCaseCmp(tok,"OWNER:", 6) == 0) { + if (owner_sid) { + DEBUG(5, ("OWNER specified more than once!\n")); + goto done; + } + owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1); + if (!owner_sid || + !convert_string_to_sid(ipc_cli, pol, + numeric, + owner_sid, tok+6)) { + DEBUG(5, ("Failed to parse owner sid\n")); + goto done; + } + continue; + } + + if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) { + if (owner_sid) { + DEBUG(5, ("OWNER specified more than once!\n")); + goto done; + } + owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1); + if (!owner_sid || + !convert_string_to_sid(ipc_cli, pol, + False, + owner_sid, tok+7)) { + DEBUG(5, ("Failed to parse owner sid\n")); + goto done; + } + continue; + } + + if (StrnCaseCmp(tok,"GROUP:", 6) == 0) { + if (group_sid) { + DEBUG(5, ("GROUP specified more than once!\n")); + goto done; + } + group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1); + if (!group_sid || + !convert_string_to_sid(ipc_cli, pol, + numeric, + group_sid, tok+6)) { + DEBUG(5, ("Failed to parse group sid\n")); + goto done; + } + continue; + } + + if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) { + if (group_sid) { + DEBUG(5, ("GROUP specified more than once!\n")); + goto done; + } + group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1); + if (!group_sid || + !convert_string_to_sid(ipc_cli, pol, + False, + group_sid, tok+6)) { + DEBUG(5, ("Failed to parse group sid\n")); + goto done; + } + continue; + } + + if (StrnCaseCmp(tok,"ACL:", 4) == 0) { + SEC_ACE ace; + if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) { + DEBUG(5, ("Failed to parse ACL %s\n", tok)); + goto done; + } + if(!add_ace(&dacl, &ace, ctx)) { + DEBUG(5, ("Failed to add ACL %s\n", tok)); + goto done; + } + continue; + } + + if (StrnCaseCmp(tok,"ACL+:", 5) == 0) { + SEC_ACE ace; + if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) { + DEBUG(5, ("Failed to parse ACL %s\n", tok)); + goto done; + } + if(!add_ace(&dacl, &ace, ctx)) { + DEBUG(5, ("Failed to add ACL %s\n", tok)); + goto done; + } + continue; + } + + DEBUG(5, ("Failed to parse security descriptor\n")); + goto done; + } + + ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE, + owner_sid, group_sid, NULL, dacl, &sd_size); + + done: + SAFE_FREE(group_sid); + SAFE_FREE(owner_sid); + + return ret; +} + + +/* Obtain the current dos attributes */ +static DOS_ATTR_DESC * +dos_attr_query(SMBCCTX *context, + TALLOC_CTX *ctx, + const char *filename, + SMBCSRV *srv) +{ + struct timespec create_time_ts; + struct timespec write_time_ts; + struct timespec access_time_ts; + struct timespec change_time_ts; + SMB_OFF_T size = 0; + uint16 mode = 0; + SMB_INO_T inode = 0; + DOS_ATTR_DESC *ret; + + ret = TALLOC_P(ctx, DOS_ATTR_DESC); + if (!ret) { + errno = ENOMEM; + return NULL; + } + + /* Obtain the DOS attributes */ + if (!SMBC_getatr(context, srv, CONST_DISCARD(char *, filename), + &mode, &size, + &create_time_ts, + &access_time_ts, + &write_time_ts, + &change_time_ts, + &inode)) { + errno = SMBC_errno(context, srv->cli); + DEBUG(5, ("dos_attr_query Failed to query old attributes\n")); + return NULL; + } + + ret->mode = mode; + ret->size = size; + ret->create_time = convert_timespec_to_time_t(create_time_ts); + ret->access_time = convert_timespec_to_time_t(access_time_ts); + ret->write_time = convert_timespec_to_time_t(write_time_ts); + ret->change_time = convert_timespec_to_time_t(change_time_ts); + ret->inode = inode; + + return ret; +} + + +/* parse a ascii version of a security descriptor */ +static void +dos_attr_parse(SMBCCTX *context, + DOS_ATTR_DESC *dad, + SMBCSRV *srv, + char *str) +{ + int n; + const char *p = str; + char *tok = NULL; + TALLOC_CTX *frame = NULL; + struct { + const char * create_time_attr; + const char * access_time_attr; + const char * write_time_attr; + const char * change_time_attr; + } attr_strings; + + /* Determine whether to use old-style or new-style attribute names */ + if (context->full_time_names) { + /* new-style names */ + attr_strings.create_time_attr = "CREATE_TIME"; + attr_strings.access_time_attr = "ACCESS_TIME"; + attr_strings.write_time_attr = "WRITE_TIME"; + attr_strings.change_time_attr = "CHANGE_TIME"; + } else { + /* old-style names */ + attr_strings.create_time_attr = NULL; + attr_strings.access_time_attr = "A_TIME"; + attr_strings.write_time_attr = "M_TIME"; + attr_strings.change_time_attr = "C_TIME"; + } + + /* if this is to set the entire ACL... */ + if (*str == '*') { + /* ... then increment past the first colon if there is one */ + if ((p = strchr(str, ':')) != NULL) { + ++p; + } else { + p = str; + } + } + + frame = talloc_stackframe(); + while (next_token_talloc(frame, &p, &tok, "\t,\r\n")) { + if (StrnCaseCmp(tok, "MODE:", 5) == 0) { + long request = strtol(tok+5, NULL, 16); + if (request == 0) { + dad->mode = (request | + (IS_DOS_DIR(dad->mode) + ? FILE_ATTRIBUTE_DIRECTORY + : FILE_ATTRIBUTE_NORMAL)); + } else { + dad->mode = request; + } + continue; + } + + if (StrnCaseCmp(tok, "SIZE:", 5) == 0) { + dad->size = (SMB_OFF_T)atof(tok+5); + continue; + } + + n = strlen(attr_strings.access_time_attr); + if (StrnCaseCmp(tok, attr_strings.access_time_attr, n) == 0) { + dad->access_time = (time_t)strtol(tok+n+1, NULL, 10); + continue; + } + + n = strlen(attr_strings.change_time_attr); + if (StrnCaseCmp(tok, attr_strings.change_time_attr, n) == 0) { + dad->change_time = (time_t)strtol(tok+n+1, NULL, 10); + continue; + } + + n = strlen(attr_strings.write_time_attr); + if (StrnCaseCmp(tok, attr_strings.write_time_attr, n) == 0) { + dad->write_time = (time_t)strtol(tok+n+1, NULL, 10); + continue; + } + + if (attr_strings.create_time_attr != NULL) { + n = strlen(attr_strings.create_time_attr); + if (StrnCaseCmp(tok, attr_strings.create_time_attr, + n) == 0) { + dad->create_time = (time_t)strtol(tok+n+1, + NULL, 10); + continue; + } + } + + if (StrnCaseCmp(tok, "INODE:", 6) == 0) { + dad->inode = (SMB_INO_T)atof(tok+6); + continue; + } + } + TALLOC_FREE(frame); +} + +/***************************************************** + Retrieve the acls for a file. +*******************************************************/ + +static int +cacl_get(SMBCCTX *context, + TALLOC_CTX *ctx, + SMBCSRV *srv, + struct cli_state *ipc_cli, + POLICY_HND *pol, + char *filename, + char *attr_name, + char *buf, + int bufsize) +{ + uint32 i; + int n = 0; + int n_used; + bool all; + bool all_nt; + bool all_nt_acls; + bool all_dos; + bool some_nt; + bool some_dos; + bool exclude_nt_revision = False; + bool exclude_nt_owner = False; + bool exclude_nt_group = False; + bool exclude_nt_acl = False; + bool exclude_dos_mode = False; + bool exclude_dos_size = False; + bool exclude_dos_create_time = False; + bool exclude_dos_access_time = False; + bool exclude_dos_write_time = False; + bool exclude_dos_change_time = False; + bool exclude_dos_inode = False; + bool numeric = True; + bool determine_size = (bufsize == 0); + int fnum = -1; + SEC_DESC *sd; + fstring sidstr; + fstring name_sandbox; + char *name; + char *pExclude; + char *p; + struct timespec create_time_ts; + struct timespec write_time_ts; + struct timespec access_time_ts; + struct timespec change_time_ts; + time_t create_time = (time_t)0; + time_t write_time = (time_t)0; + time_t access_time = (time_t)0; + time_t change_time = (time_t)0; + SMB_OFF_T size = 0; + uint16 mode = 0; + SMB_INO_T ino = 0; + struct cli_state *cli = srv->cli; + struct { + const char * create_time_attr; + const char * access_time_attr; + const char * write_time_attr; + const char * change_time_attr; + } attr_strings; + struct { + const char * create_time_attr; + const char * access_time_attr; + const char * write_time_attr; + const char * change_time_attr; + } excl_attr_strings; + + /* Determine whether to use old-style or new-style attribute names */ + if (context->full_time_names) { + /* new-style names */ + attr_strings.create_time_attr = "CREATE_TIME"; + attr_strings.access_time_attr = "ACCESS_TIME"; + attr_strings.write_time_attr = "WRITE_TIME"; + attr_strings.change_time_attr = "CHANGE_TIME"; + + excl_attr_strings.create_time_attr = "CREATE_TIME"; + excl_attr_strings.access_time_attr = "ACCESS_TIME"; + excl_attr_strings.write_time_attr = "WRITE_TIME"; + excl_attr_strings.change_time_attr = "CHANGE_TIME"; + } else { + /* old-style names */ + attr_strings.create_time_attr = NULL; + attr_strings.access_time_attr = "A_TIME"; + attr_strings.write_time_attr = "M_TIME"; + attr_strings.change_time_attr = "C_TIME"; + + excl_attr_strings.create_time_attr = NULL; + excl_attr_strings.access_time_attr = "dos_attr.A_TIME"; + excl_attr_strings.write_time_attr = "dos_attr.M_TIME"; + excl_attr_strings.change_time_attr = "dos_attr.C_TIME"; + } + + /* Copy name so we can strip off exclusions (if any are specified) */ + strncpy(name_sandbox, attr_name, sizeof(name_sandbox) - 1); + + /* Ensure name is null terminated */ + name_sandbox[sizeof(name_sandbox) - 1] = '\0'; + + /* Play in the sandbox */ + name = name_sandbox; + + /* If there are any exclusions, point to them and mask them from name */ + if ((pExclude = strchr(name, '!')) != NULL) + { + *pExclude++ = '\0'; + } + + all = (StrnCaseCmp(name, "system.*", 8) == 0); + all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0); + all_nt_acls = (StrnCaseCmp(name, "system.nt_sec_desc.acl.*", 24) == 0); + all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0); + some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0); + some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0); + numeric = (* (name + strlen(name) - 1) != '+'); + + /* Look for exclusions from "all" requests */ + if (all || all_nt || all_dos) { + + /* Exclusions are delimited by '!' */ + for (; + pExclude != NULL; + pExclude = (p == NULL ? NULL : p + 1)) { + + /* Find end of this exclusion name */ + if ((p = strchr(pExclude, '!')) != NULL) + { + *p = '\0'; + } + + /* Which exclusion name is this? */ + if (StrCaseCmp(pExclude, "nt_sec_desc.revision") == 0) { + exclude_nt_revision = True; + } + else if (StrCaseCmp(pExclude, "nt_sec_desc.owner") == 0) { + exclude_nt_owner = True; + } + else if (StrCaseCmp(pExclude, "nt_sec_desc.group") == 0) { + exclude_nt_group = True; + } + else if (StrCaseCmp(pExclude, "nt_sec_desc.acl") == 0) { + exclude_nt_acl = True; + } + else if (StrCaseCmp(pExclude, "dos_attr.mode") == 0) { + exclude_dos_mode = True; + } + else if (StrCaseCmp(pExclude, "dos_attr.size") == 0) { + exclude_dos_size = True; + } + else if (excl_attr_strings.create_time_attr != NULL && + StrCaseCmp(pExclude, + excl_attr_strings.change_time_attr) == 0) { + exclude_dos_create_time = True; + } + else if (StrCaseCmp(pExclude, + excl_attr_strings.access_time_attr) == 0) { + exclude_dos_access_time = True; + } + else if (StrCaseCmp(pExclude, + excl_attr_strings.write_time_attr) == 0) { + exclude_dos_write_time = True; + } + else if (StrCaseCmp(pExclude, + excl_attr_strings.change_time_attr) == 0) { + exclude_dos_change_time = True; + } + else if (StrCaseCmp(pExclude, "dos_attr.inode") == 0) { + exclude_dos_inode = True; + } + else { + DEBUG(5, ("cacl_get received unknown exclusion: %s\n", + pExclude)); + errno = ENOATTR; + return -1; + } + } + } + + n_used = 0; + + /* + * If we are (possibly) talking to an NT or new system and some NT + * attributes have been requested... + */ + if (ipc_cli && (all || some_nt || all_nt_acls)) { + /* Point to the portion after "system.nt_sec_desc." */ + name += 19; /* if (all) this will be invalid but unused */ + + /* ... then obtain any NT attributes which were requested */ + fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ); + + if (fnum == -1) { + DEBUG(5, ("cacl_get failed to open %s: %s\n", + filename, cli_errstr(cli))); + errno = 0; + return -1; + } + + sd = cli_query_secdesc(cli, fnum, ctx); + + if (!sd) { + DEBUG(5, + ("cacl_get Failed to query old descriptor\n")); + errno = 0; + return -1; + } + + cli_close(cli, fnum); + + if (! exclude_nt_revision) { + if (all || all_nt) { + if (determine_size) { + p = talloc_asprintf(ctx, + "REVISION:%d", + sd->revision); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "REVISION:%d", + sd->revision); + } + } else if (StrCaseCmp(name, "revision") == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%d", + sd->revision); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, "%d", + sd->revision); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_nt_owner) { + /* Get owner and group sid */ + if (sd->owner_sid) { + convert_sid_to_string(ipc_cli, pol, + sidstr, + numeric, + sd->owner_sid); + } else { + fstrcpy(sidstr, ""); + } + + if (all || all_nt) { + if (determine_size) { + p = talloc_asprintf(ctx, ",OWNER:%s", + sidstr); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else if (sidstr[0] != '\0') { + n = snprintf(buf, bufsize, + ",OWNER:%s", sidstr); + } + } else if (StrnCaseCmp(name, "owner", 5) == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%s", sidstr); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, "%s", + sidstr); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_nt_group) { + if (sd->group_sid) { + convert_sid_to_string(ipc_cli, pol, + sidstr, numeric, + sd->group_sid); + } else { + fstrcpy(sidstr, ""); + } + + if (all || all_nt) { + if (determine_size) { + p = talloc_asprintf(ctx, ",GROUP:%s", + sidstr); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else if (sidstr[0] != '\0') { + n = snprintf(buf, bufsize, + ",GROUP:%s", sidstr); + } + } else if (StrnCaseCmp(name, "group", 5) == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%s", sidstr); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%s", sidstr); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_nt_acl) { + /* Add aces to value buffer */ + for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) { + + SEC_ACE *ace = &sd->dacl->aces[i]; + convert_sid_to_string(ipc_cli, pol, + sidstr, numeric, + &ace->trustee); + + if (all || all_nt) { + if (determine_size) { + p = talloc_asprintf( + ctx, + ",ACL:" + "%s:%d/%d/0x%08x", + sidstr, + ace->type, + ace->flags, + ace->access_mask); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf( + buf, bufsize, + ",ACL:%s:%d/%d/0x%08x", + sidstr, + ace->type, + ace->flags, + ace->access_mask); + } + } else if ((StrnCaseCmp(name, "acl", 3) == 0 && + StrCaseCmp(name+3, sidstr) == 0) || + (StrnCaseCmp(name, "acl+", 4) == 0 && + StrCaseCmp(name+4, sidstr) == 0)) { + if (determine_size) { + p = talloc_asprintf( + ctx, + "%d/%d/0x%08x", + ace->type, + ace->flags, + ace->access_mask); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%d/%d/0x%08x", + ace->type, + ace->flags, + ace->access_mask); + } + } else if (all_nt_acls) { + if (determine_size) { + p = talloc_asprintf( + ctx, + "%s%s:%d/%d/0x%08x", + i ? "," : "", + sidstr, + ace->type, + ace->flags, + ace->access_mask); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%s%s:%d/%d/0x%08x", + i ? "," : "", + sidstr, + ace->type, + ace->flags, + ace->access_mask); + } + } + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + } + + /* Restore name pointer to its original value */ + name -= 19; + } + + if (all || some_dos) { + /* Point to the portion after "system.dos_attr." */ + name += 16; /* if (all) this will be invalid but unused */ + + /* Obtain the DOS attributes */ + if (!SMBC_getatr(context, srv, filename, &mode, &size, + &create_time_ts, + &access_time_ts, + &write_time_ts, + &change_time_ts, + &ino)) { + + errno = SMBC_errno(context, srv->cli); + return -1; + + } + + create_time = convert_timespec_to_time_t(create_time_ts); + access_time = convert_timespec_to_time_t(access_time_ts); + write_time = convert_timespec_to_time_t(write_time_ts); + change_time = convert_timespec_to_time_t(change_time_ts); + + if (! exclude_dos_mode) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + "%sMODE:0x%x", + (ipc_cli && + (all || some_nt) + ? "," + : ""), + mode); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%sMODE:0x%x", + (ipc_cli && + (all || some_nt) + ? "," + : ""), + mode); + } + } else if (StrCaseCmp(name, "mode") == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "0x%x", mode); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "0x%x", mode); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_dos_size) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf( + ctx, + ",SIZE:%.0f", + (double)size); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",SIZE:%.0f", + (double)size); + } + } else if (StrCaseCmp(name, "size") == 0) { + if (determine_size) { + p = talloc_asprintf( + ctx, + "%.0f", + (double)size); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%.0f", + (double)size); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_dos_create_time && + attr_strings.create_time_attr != NULL) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + ",%s:%lu", + attr_strings.create_time_attr, + create_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",%s:%lu", + attr_strings.create_time_attr, + create_time); + } + } else if (StrCaseCmp(name, attr_strings.create_time_attr) == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%lu", create_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%lu", create_time); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_dos_access_time) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + ",%s:%lu", + attr_strings.access_time_attr, + access_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",%s:%lu", + attr_strings.access_time_attr, + access_time); + } + } else if (StrCaseCmp(name, attr_strings.access_time_attr) == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%lu", access_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%lu", access_time); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_dos_write_time) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + ",%s:%lu", + attr_strings.write_time_attr, + write_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",%s:%lu", + attr_strings.write_time_attr, + write_time); + } + } else if (StrCaseCmp(name, attr_strings.write_time_attr) == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%lu", write_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%lu", write_time); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_dos_change_time) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + ",%s:%lu", + attr_strings.change_time_attr, + change_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",%s:%lu", + attr_strings.change_time_attr, + change_time); + } + } else if (StrCaseCmp(name, attr_strings.change_time_attr) == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%lu", change_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%lu", change_time); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_dos_inode) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf( + ctx, + ",INODE:%.0f", + (double)ino); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",INODE:%.0f", + (double) ino); + } + } else if (StrCaseCmp(name, "inode") == 0) { + if (determine_size) { + p = talloc_asprintf( + ctx, + "%.0f", + (double) ino); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%.0f", + (double) ino); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + /* Restore name pointer to its original value */ + name -= 16; + } + + if (n_used == 0) { + errno = ENOATTR; + return -1; + } + + return n_used; +} + +/***************************************************** +set the ACLs on a file given an ascii description +*******************************************************/ +static int +cacl_set(TALLOC_CTX *ctx, + struct cli_state *cli, + struct cli_state *ipc_cli, + POLICY_HND *pol, + const char *filename, + const char *the_acl, + int mode, + int flags) +{ + int fnum; + int err = 0; + SEC_DESC *sd = NULL, *old; + SEC_ACL *dacl = NULL; + DOM_SID *owner_sid = NULL; + DOM_SID *group_sid = NULL; + uint32 i, j; + size_t sd_size; + int ret = 0; + char *p; + bool numeric = True; + + /* the_acl will be null for REMOVE_ALL operations */ + if (the_acl) { + numeric = ((p = strchr(the_acl, ':')) != NULL && + p > the_acl && + p[-1] != '+'); + + /* if this is to set the entire ACL... */ + if (*the_acl == '*') { + /* ... then increment past the first colon */ + the_acl = p + 1; + } + + sd = sec_desc_parse(ctx, ipc_cli, pol, numeric, + CONST_DISCARD(char *, the_acl)); + + if (!sd) { + errno = EINVAL; + return -1; + } + } + + /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller + that doesn't deref sd */ + + if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) { + errno = EINVAL; + return -1; + } + + /* The desired access below is the only one I could find that works + with NT4, W2KP and Samba */ + + fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ); + + if (fnum == -1) { + DEBUG(5, ("cacl_set failed to open %s: %s\n", + filename, cli_errstr(cli))); + errno = 0; + return -1; + } + + old = cli_query_secdesc(cli, fnum, ctx); + + if (!old) { + DEBUG(5, ("cacl_set Failed to query old descriptor\n")); + errno = 0; + return -1; + } + + cli_close(cli, fnum); + + switch (mode) { + case SMBC_XATTR_MODE_REMOVE_ALL: + old->dacl->num_aces = 0; + dacl = old->dacl; + break; + + case SMBC_XATTR_MODE_REMOVE: + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + bool found = False; + + for (j=0;old->dacl && j<old->dacl->num_aces;j++) { + if (sec_ace_equal(&sd->dacl->aces[i], + &old->dacl->aces[j])) { + uint32 k; + for (k=j; k<old->dacl->num_aces-1;k++) { + old->dacl->aces[k] = + old->dacl->aces[k+1]; + } + old->dacl->num_aces--; + found = True; + dacl = old->dacl; + break; + } + } + + if (!found) { + err = ENOATTR; + ret = -1; + goto failed; + } + } + break; + + case SMBC_XATTR_MODE_ADD: + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + bool found = False; + + for (j=0;old->dacl && j<old->dacl->num_aces;j++) { + if (sid_equal(&sd->dacl->aces[i].trustee, + &old->dacl->aces[j].trustee)) { + if (!(flags & SMBC_XATTR_FLAG_CREATE)) { + err = EEXIST; + ret = -1; + goto failed; + } + old->dacl->aces[j] = sd->dacl->aces[i]; + ret = -1; + found = True; + } + } + + if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) { + err = ENOATTR; + ret = -1; + goto failed; + } + + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + add_ace(&old->dacl, &sd->dacl->aces[i], ctx); + } + } + dacl = old->dacl; + break; + + case SMBC_XATTR_MODE_SET: + old = sd; + owner_sid = old->owner_sid; + group_sid = old->group_sid; + dacl = old->dacl; + break; + + case SMBC_XATTR_MODE_CHOWN: + owner_sid = sd->owner_sid; + break; + + case SMBC_XATTR_MODE_CHGRP: + group_sid = sd->group_sid; + break; + } + + /* Denied ACE entries must come before allowed ones */ + sort_acl(old->dacl); + + /* Create new security descriptor and set it */ + sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE, + owner_sid, group_sid, NULL, dacl, &sd_size); + + fnum = cli_nt_create(cli, filename, + WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS); + + if (fnum == -1) { + DEBUG(5, ("cacl_set failed to open %s: %s\n", + filename, cli_errstr(cli))); + errno = 0; + return -1; + } + + if (!cli_set_secdesc(cli, fnum, sd)) { + DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli))); + ret = -1; + } + + /* Clean up */ + + failed: + cli_close(cli, fnum); + + if (err != 0) { + errno = err; + } + + return ret; +} + + +int +SMBC_setxattr_ctx(SMBCCTX *context, + const char *fname, + const char *name, + const void *value, + size_t size, + int flags) +{ + int ret; + int ret2; + SMBCSRV *srv = NULL; + SMBCSRV *ipc_srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + DOS_ATTR_DESC *dad = NULL; + struct { + const char * create_time_attr; + const char * access_time_attr; + const char * write_time_attr; + const char * change_time_attr; + } attr_strings; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n", + fname, name, (int) size, (const char*)value)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, context->user); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, share, &workgroup, &user, &password); + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + if (! srv->no_nt_session) { + ipc_srv = SMBC_attr_server(frame, context, server, share, + &workgroup, &user, &password); + if (! ipc_srv) { + srv->no_nt_session = True; + } + } else { + ipc_srv = NULL; + } + + /* + * Are they asking to set the entire set of known attributes? + */ + if (StrCaseCmp(name, "system.*") == 0 || + StrCaseCmp(name, "system.*+") == 0) { + /* Yup. */ + char *namevalue = + talloc_asprintf(talloc_tos(), "%s:%s", + name+7, (const char *) value); + if (! namevalue) { + errno = ENOMEM; + ret = -1; + TALLOC_FREE(frame); + return -1; + } + + if (ipc_srv) { + ret = cacl_set(talloc_tos(), srv->cli, + ipc_srv->cli, &ipc_srv->pol, path, + namevalue, + (*namevalue == '*' + ? SMBC_XATTR_MODE_SET + : SMBC_XATTR_MODE_ADD), + flags); + } else { + ret = 0; + } + + /* get a DOS Attribute Descriptor with current attributes */ + dad = dos_attr_query(context, talloc_tos(), path, srv); + if (dad) { + /* Overwrite old with new, using what was provided */ + dos_attr_parse(context, dad, srv, namevalue); + + /* Set the new DOS attributes */ + if (! SMBC_setatr(context, srv, path, + dad->create_time, + dad->access_time, + dad->write_time, + dad->change_time, + dad->mode)) { + + /* cause failure if NT failed too */ + dad = NULL; + } + } + + /* we only fail if both NT and DOS sets failed */ + if (ret < 0 && ! dad) { + ret = -1; /* in case dad was null */ + } + else { + ret = 0; + } + + TALLOC_FREE(frame); + return ret; + } + + /* + * Are they asking to set an access control element or to set + * the entire access control list? + */ + if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 || + StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 || + StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 || + StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 || + StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) { + + /* Yup. */ + char *namevalue = + talloc_asprintf(talloc_tos(), "%s:%s", + name+19, (const char *) value); + + if (! ipc_srv) { + ret = -1; /* errno set by SMBC_server() */ + } + else if (! namevalue) { + errno = ENOMEM; + ret = -1; + } else { + ret = cacl_set(talloc_tos(), srv->cli, + ipc_srv->cli, &ipc_srv->pol, path, + namevalue, + (*namevalue == '*' + ? SMBC_XATTR_MODE_SET + : SMBC_XATTR_MODE_ADD), + flags); + } + TALLOC_FREE(frame); + return ret; + } + + /* + * Are they asking to set the owner? + */ + if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 || + StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) { + + /* Yup. */ + char *namevalue = + talloc_asprintf(talloc_tos(), "%s:%s", + name+19, (const char *) value); + + if (! ipc_srv) { + ret = -1; /* errno set by SMBC_server() */ + } + else if (! namevalue) { + errno = ENOMEM; + ret = -1; + } else { + ret = cacl_set(talloc_tos(), srv->cli, + ipc_srv->cli, &ipc_srv->pol, path, + namevalue, SMBC_XATTR_MODE_CHOWN, 0); + } + TALLOC_FREE(frame); + return ret; + } + + /* + * Are they asking to set the group? + */ + if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 || + StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) { + + /* Yup. */ + char *namevalue = + talloc_asprintf(talloc_tos(), "%s:%s", + name+19, (const char *) value); + + if (! ipc_srv) { + /* errno set by SMBC_server() */ + ret = -1; + } + else if (! namevalue) { + errno = ENOMEM; + ret = -1; + } else { + ret = cacl_set(talloc_tos(), srv->cli, + ipc_srv->cli, &ipc_srv->pol, path, + namevalue, SMBC_XATTR_MODE_CHGRP, 0); + } + TALLOC_FREE(frame); + return ret; + } + + /* Determine whether to use old-style or new-style attribute names */ + if (context->full_time_names) { + /* new-style names */ + attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME"; + attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME"; + attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME"; + attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME"; + } else { + /* old-style names */ + attr_strings.create_time_attr = NULL; + attr_strings.access_time_attr = "system.dos_attr.A_TIME"; + attr_strings.write_time_attr = "system.dos_attr.M_TIME"; + attr_strings.change_time_attr = "system.dos_attr.C_TIME"; + } + + /* + * Are they asking to set a DOS attribute? + */ + if (StrCaseCmp(name, "system.dos_attr.*") == 0 || + StrCaseCmp(name, "system.dos_attr.mode") == 0 || + (attr_strings.create_time_attr != NULL && + StrCaseCmp(name, attr_strings.create_time_attr) == 0) || + StrCaseCmp(name, attr_strings.access_time_attr) == 0 || + StrCaseCmp(name, attr_strings.write_time_attr) == 0 || + StrCaseCmp(name, attr_strings.change_time_attr) == 0) { + + /* get a DOS Attribute Descriptor with current attributes */ + dad = dos_attr_query(context, talloc_tos(), path, srv); + if (dad) { + char *namevalue = + talloc_asprintf(talloc_tos(), "%s:%s", + name+16, (const char *) value); + if (! namevalue) { + errno = ENOMEM; + ret = -1; + } else { + /* Overwrite old with provided new params */ + dos_attr_parse(context, dad, srv, namevalue); + + /* Set the new DOS attributes */ + ret2 = SMBC_setatr(context, srv, path, + dad->create_time, + dad->access_time, + dad->write_time, + dad->change_time, + dad->mode); + + /* ret2 has True (success) / False (failure) */ + if (ret2) { + ret = 0; + } else { + ret = -1; + } + } + } else { + ret = -1; + } + + TALLOC_FREE(frame); + return ret; + } + + /* Unsupported attribute name */ + errno = EINVAL; + TALLOC_FREE(frame); + return -1; +} + +int +SMBC_getxattr_ctx(SMBCCTX *context, + const char *fname, + const char *name, + const void *value, + size_t size) +{ + int ret; + SMBCSRV *srv = NULL; + SMBCSRV *ipc_srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + struct { + const char * create_time_attr; + const char * access_time_attr; + const char * write_time_attr; + const char * change_time_attr; + } attr_strings; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, context->user); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, share, &workgroup, &user, &password); + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + if (! srv->no_nt_session) { + ipc_srv = SMBC_attr_server(frame, context, server, share, + &workgroup, &user, &password); + if (! ipc_srv) { + srv->no_nt_session = True; + } + } else { + ipc_srv = NULL; + } + + /* Determine whether to use old-style or new-style attribute names */ + if (context->full_time_names) { + /* new-style names */ + attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME"; + attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME"; + attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME"; + attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME"; + } else { + /* old-style names */ + attr_strings.create_time_attr = NULL; + attr_strings.access_time_attr = "system.dos_attr.A_TIME"; + attr_strings.write_time_attr = "system.dos_attr.M_TIME"; + attr_strings.change_time_attr = "system.dos_attr.C_TIME"; + } + + /* Are they requesting a supported attribute? */ + if (StrCaseCmp(name, "system.*") == 0 || + StrnCaseCmp(name, "system.*!", 9) == 0 || + StrCaseCmp(name, "system.*+") == 0 || + StrnCaseCmp(name, "system.*+!", 10) == 0 || + StrCaseCmp(name, "system.nt_sec_desc.*") == 0 || + StrnCaseCmp(name, "system.nt_sec_desc.*!", 21) == 0 || + StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 || + StrnCaseCmp(name, "system.nt_sec_desc.*+!", 22) == 0 || + StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 || + StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 || + StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 || + StrCaseCmp(name, "system.nt_sec_desc.group") == 0 || + StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 || + StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 || + StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 || + StrCaseCmp(name, "system.dos_attr.*") == 0 || + StrnCaseCmp(name, "system.dos_attr.*!", 18) == 0 || + StrCaseCmp(name, "system.dos_attr.mode") == 0 || + StrCaseCmp(name, "system.dos_attr.size") == 0 || + (attr_strings.create_time_attr != NULL && + StrCaseCmp(name, attr_strings.create_time_attr) == 0) || + StrCaseCmp(name, attr_strings.access_time_attr) == 0 || + StrCaseCmp(name, attr_strings.write_time_attr) == 0 || + StrCaseCmp(name, attr_strings.change_time_attr) == 0 || + StrCaseCmp(name, "system.dos_attr.inode") == 0) { + + /* Yup. */ + ret = cacl_get(context, talloc_tos(), srv, + ipc_srv == NULL ? NULL : ipc_srv->cli, + &ipc_srv->pol, path, + CONST_DISCARD(char *, name), + CONST_DISCARD(char *, value), size); + if (ret < 0 && errno == 0) { + errno = SMBC_errno(context, srv->cli); + } + TALLOC_FREE(frame); + return ret; + } + + /* Unsupported attribute name */ + errno = EINVAL; + TALLOC_FREE(frame); + return -1; +} + + +int +SMBC_removexattr_ctx(SMBCCTX *context, + const char *fname, + const char *name) +{ + int ret; + SMBCSRV *srv = NULL; + SMBCSRV *ipc_srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->initialized) { + + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, context->user); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, share, &workgroup, &user, &password); + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + if (! srv->no_nt_session) { + ipc_srv = SMBC_attr_server(frame, context, server, share, + &workgroup, &user, &password); + if (! ipc_srv) { + srv->no_nt_session = True; + } + } else { + ipc_srv = NULL; + } + + if (! ipc_srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_attr_server */ + } + + /* Are they asking to set the entire ACL? */ + if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 || + StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) { + + /* Yup. */ + ret = cacl_set(talloc_tos(), srv->cli, + ipc_srv->cli, &ipc_srv->pol, path, + NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0); + TALLOC_FREE(frame); + return ret; + } + + /* + * Are they asking to remove one or more spceific security descriptor + * attributes? + */ + if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 || + StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 || + StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 || + StrCaseCmp(name, "system.nt_sec_desc.group") == 0 || + StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 || + StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 || + StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) { + + /* Yup. */ + ret = cacl_set(talloc_tos(), srv->cli, + ipc_srv->cli, &ipc_srv->pol, path, + name + 19, SMBC_XATTR_MODE_REMOVE, 0); + TALLOC_FREE(frame); + return ret; + } + + /* Unsupported attribute name */ + errno = EINVAL; + TALLOC_FREE(frame); + return -1; +} + +int +SMBC_listxattr_ctx(SMBCCTX *context, + const char *fname, + char *list, + size_t size) +{ + /* + * This isn't quite what listxattr() is supposed to do. This returns + * the complete set of attribute names, always, rather than only those + * attribute names which actually exist for a file. Hmmm... + */ + size_t retsize; + const char supported_old[] = + "system.*\0" + "system.*+\0" + "system.nt_sec_desc.revision\0" + "system.nt_sec_desc.owner\0" + "system.nt_sec_desc.owner+\0" + "system.nt_sec_desc.group\0" + "system.nt_sec_desc.group+\0" + "system.nt_sec_desc.acl.*\0" + "system.nt_sec_desc.acl\0" + "system.nt_sec_desc.acl+\0" + "system.nt_sec_desc.*\0" + "system.nt_sec_desc.*+\0" + "system.dos_attr.*\0" + "system.dos_attr.mode\0" + "system.dos_attr.c_time\0" + "system.dos_attr.a_time\0" + "system.dos_attr.m_time\0" + ; + const char supported_new[] = + "system.*\0" + "system.*+\0" + "system.nt_sec_desc.revision\0" + "system.nt_sec_desc.owner\0" + "system.nt_sec_desc.owner+\0" + "system.nt_sec_desc.group\0" + "system.nt_sec_desc.group+\0" + "system.nt_sec_desc.acl.*\0" + "system.nt_sec_desc.acl\0" + "system.nt_sec_desc.acl+\0" + "system.nt_sec_desc.*\0" + "system.nt_sec_desc.*+\0" + "system.dos_attr.*\0" + "system.dos_attr.mode\0" + "system.dos_attr.create_time\0" + "system.dos_attr.access_time\0" + "system.dos_attr.write_time\0" + "system.dos_attr.change_time\0" + ; + const char * supported; + + if (context->full_time_names) { + supported = supported_new; + retsize = sizeof(supported_new); + } else { + supported = supported_old; + retsize = sizeof(supported_old); + } + + if (size == 0) { + return retsize; + } + + if (retsize > size) { + errno = ERANGE; + return -1; + } + + /* this can't be strcpy() because there are embedded null characters */ + memcpy(list, supported, retsize); + return retsize; +} diff --git a/source3/libsmb/libsmbclient.c b/source3/libsmb/libsmbclient.c deleted file mode 100644 index fe008ed6b6..0000000000 --- a/source3/libsmb/libsmbclient.c +++ /dev/null @@ -1,7233 +0,0 @@ -/* - Unix SMB/Netbios implementation. - SMB client library implementation - Copyright (C) Andrew Tridgell 1998 - Copyright (C) Richard Sharpe 2000, 2002 - Copyright (C) John Terpstra 2000 - Copyright (C) Tom Jansen (Ninja ISD) 2002 - Copyright (C) Derrell Lipman 2003, 2004 - Copyright (C) Jeremy Allison 2007, 2008 - - 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 3 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, see <http://www.gnu.org/licenses/>. -*/ - -#include "includes.h" - -#include "include/libsmb_internal.h" - -struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir); -struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list, - struct smbc_dirent *dirent); - -/* - * DOS Attribute values (used internally) - */ -typedef struct DOS_ATTR_DESC { - int mode; - SMB_OFF_T size; - time_t create_time; - time_t access_time; - time_t write_time; - time_t change_time; - SMB_INO_T inode; -} DOS_ATTR_DESC; - - -/* - * Internal flags for extended attributes - */ - -/* internal mode values */ -#define SMBC_XATTR_MODE_ADD 1 -#define SMBC_XATTR_MODE_REMOVE 2 -#define SMBC_XATTR_MODE_REMOVE_ALL 3 -#define SMBC_XATTR_MODE_SET 4 -#define SMBC_XATTR_MODE_CHOWN 5 -#define SMBC_XATTR_MODE_CHGRP 6 - -#define CREATE_ACCESS_READ READ_CONTROL_ACCESS - -/*We should test for this in configure ... */ -#ifndef ENOTSUP -#define ENOTSUP EOPNOTSUPP -#endif - -/* - * Functions exported by libsmb_cache.c that we need here - */ -int smbc_default_cache_functions(SMBCCTX *context); - -/* - * check if an element is part of the list. - * FIXME: Does not belong here ! - * Can anyone put this in a macro in dlinklist.h ? - * -- Tom - */ -static int DLIST_CONTAINS(SMBCFILE * list, SMBCFILE *p) { - if (!p || !list) return False; - do { - if (p == list) return True; - list = list->next; - } while (list); - return False; -} - -/* - * Find an lsa pipe handle associated with a cli struct. - */ -static struct rpc_pipe_client * -find_lsa_pipe_hnd(struct cli_state *ipc_cli) -{ - struct rpc_pipe_client *pipe_hnd; - - for (pipe_hnd = ipc_cli->pipe_list; - pipe_hnd; - pipe_hnd = pipe_hnd->next) { - - if (pipe_hnd->pipe_idx == PI_LSARPC) { - return pipe_hnd; - } - } - - return NULL; -} - -static int -smbc_close_ctx(SMBCCTX *context, - SMBCFILE *file); -static off_t -smbc_lseek_ctx(SMBCCTX *context, - SMBCFILE *file, - off_t offset, - int whence); - -extern bool in_client; - -/* - * Is the logging working / configfile read ? - */ -static int smbc_initialized = 0; - -static int -hex2int( unsigned int _char ) -{ - if ( _char >= 'A' && _char <='F') - return _char - 'A' + 10; - if ( _char >= 'a' && _char <='f') - return _char - 'a' + 10; - if ( _char >= '0' && _char <='9') - return _char - '0'; - return -1; -} - -/* - * smbc_urldecode() - * and smbc_urldecode_talloc() (internal fn.) - * - * Convert strings of %xx to their single character equivalent. Each 'x' must - * be a valid hexadecimal digit, or that % sequence is left undecoded. - * - * dest may, but need not be, the same pointer as src. - * - * Returns the number of % sequences which could not be converted due to lack - * of two following hexadecimal digits. - */ -static int -smbc_urldecode_talloc(TALLOC_CTX *ctx, char **pp_dest, const char *src) -{ - int old_length = strlen(src); - int i = 0; - int err_count = 0; - size_t newlen = 1; - char *p, *dest; - - if (old_length == 0) { - return 0; - } - - *pp_dest = NULL; - for (i = 0; i < old_length; ) { - unsigned char character = src[i++]; - - if (character == '%') { - int a = i+1 < old_length ? hex2int(src[i]) : -1; - int b = i+1 < old_length ? hex2int(src[i+1]) : -1; - - /* Replace valid sequence */ - if (a != -1 && b != -1) { - /* Replace valid %xx sequence with %dd */ - character = (a * 16) + b; - if (character == '\0') { - break; /* Stop at %00 */ - } - i += 2; - } else { - err_count++; - } - } - newlen++; - } - - dest = TALLOC_ARRAY(ctx, char, newlen); - if (!dest) { - return err_count; - } - - err_count = 0; - for (p = dest, i = 0; i < old_length; ) { - unsigned char character = src[i++]; - - if (character == '%') { - int a = i+1 < old_length ? hex2int(src[i]) : -1; - int b = i+1 < old_length ? hex2int(src[i+1]) : -1; - - /* Replace valid sequence */ - if (a != -1 && b != -1) { - /* Replace valid %xx sequence with %dd */ - character = (a * 16) + b; - if (character == '\0') { - break; /* Stop at %00 */ - } - i += 2; - } else { - err_count++; - } - } - *p++ = character; - } - - *p = '\0'; - *pp_dest = dest; - return err_count; -} - -int -smbc_urldecode(char *dest, char *src, size_t max_dest_len) -{ - TALLOC_CTX *frame = talloc_stackframe(); - char *pdest; - int ret = smbc_urldecode_talloc(frame, &pdest, src); - - if (pdest) { - strlcpy(dest, pdest, max_dest_len); - } - TALLOC_FREE(frame); - return ret; -} - -/* - * smbc_urlencode() - * - * Convert any characters not specifically allowed in a URL into their %xx - * equivalent. - * - * Returns the remaining buffer length. - */ -int -smbc_urlencode(char *dest, char *src, int max_dest_len) -{ - char hex[] = "0123456789ABCDEF"; - - for (; *src != '\0' && max_dest_len >= 3; src++) { - - if ((*src < '0' && - *src != '-' && - *src != '.') || - (*src > '9' && - *src < 'A') || - (*src > 'Z' && - *src < 'a' && - *src != '_') || - (*src > 'z')) { - *dest++ = '%'; - *dest++ = hex[(*src >> 4) & 0x0f]; - *dest++ = hex[*src & 0x0f]; - max_dest_len -= 3; - } else { - *dest++ = *src; - max_dest_len--; - } - } - - *dest++ = '\0'; - max_dest_len--; - - return max_dest_len; -} - -/* - * Function to parse a path and turn it into components - * - * The general format of an SMB URI is explain in Christopher Hertel's CIFS - * book, at http://ubiqx.org/cifs/Appendix-D.html. We accept a subset of the - * general format ("smb:" only; we do not look for "cifs:"). - * - * - * We accept: - * smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options] - * - * Meaning of URLs: - * - * smb:// Show all workgroups. - * - * The method of locating the list of workgroups varies - * depending upon the setting of the context variable - * context->options.browse_max_lmb_count. This value - * determine the maximum number of local master browsers to - * query for the list of workgroups. In order to ensure that - * a complete list of workgroups is obtained, all master - * browsers must be queried, but if there are many - * workgroups, the time spent querying can begin to add up. - * For small networks (not many workgroups), it is suggested - * that this variable be set to 0, indicating query all local - * master browsers. When the network has many workgroups, a - * reasonable setting for this variable might be around 3. - * - * smb://name/ if name<1D> or name<1B> exists, list servers in - * workgroup, else, if name<20> exists, list all shares - * for server ... - * - * If "options" are provided, this function returns the entire option list as a - * string, for later parsing by the caller. Note that currently, no options - * are supported. - */ - -static const char *smbc_prefix = "smb:"; - -static int -smbc_parse_path(TALLOC_CTX *ctx, - SMBCCTX *context, - const char *fname, - char **pp_workgroup, - char **pp_server, - char **pp_share, - char **pp_path, - char **pp_user, - char **pp_password, - char **pp_options) -{ - char *s; - const char *p; - char *q, *r; - int len; - - /* Ensure these returns are at least valid pointers. */ - *pp_server = talloc_strdup(ctx, ""); - *pp_share = talloc_strdup(ctx, ""); - *pp_path = talloc_strdup(ctx, ""); - *pp_user = talloc_strdup(ctx, ""); - *pp_password = talloc_strdup(ctx, ""); - - if (!*pp_server || !*pp_share || !*pp_path || - !*pp_user || !*pp_password) { - return -1; - } - - /* - * Assume we wont find an authentication domain to parse, so default - * to the workgroup in the provided context. - */ - if (pp_workgroup != NULL) { - *pp_workgroup = talloc_strdup(ctx, context->workgroup); - } - - if (pp_options) { - *pp_options = talloc_strdup(ctx, ""); - } - s = talloc_strdup(ctx, fname); - - /* see if it has the right prefix */ - len = strlen(smbc_prefix); - if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) { - return -1; /* What about no smb: ? */ - } - - p = s + len; - - /* Watch the test below, we are testing to see if we should exit */ - - if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) { - DEBUG(1, ("Invalid path (does not begin with smb://")); - return -1; - } - - p += 2; /* Skip the double slash */ - - /* See if any options were specified */ - if ((q = strrchr(p, '?')) != NULL ) { - /* There are options. Null terminate here and point to them */ - *q++ = '\0'; - - DEBUG(4, ("Found options '%s'", q)); - - /* Copy the options */ - if (*pp_options != NULL) { - TALLOC_FREE(*pp_options); - *pp_options = talloc_strdup(ctx, q); - } - } - - if (*p == '\0') { - goto decoding; - } - - if (*p == '/') { - int wl = strlen(context->workgroup); - - if (wl > 16) { - wl = 16; - } - - *pp_server = talloc_strdup(ctx, context->workgroup); - if (!*pp_server) { - return -1; - } - *pp_server[wl] = '\0'; - return 0; - } - - /* - * ok, its for us. Now parse out the server, share etc. - * - * However, we want to parse out [[domain;]user[:password]@] if it - * exists ... - */ - - /* check that '@' occurs before '/', if '/' exists at all */ - q = strchr_m(p, '@'); - r = strchr_m(p, '/'); - if (q && (!r || q < r)) { - char *userinfo = NULL; - const char *u; - - next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@"); - if (!userinfo) { - return -1; - } - u = userinfo; - - if (strchr_m(u, ';')) { - char *workgroup; - next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";"); - if (!workgroup) { - return -1; - } - if (pp_workgroup) { - *pp_workgroup = workgroup; - } - } - - if (strchr_m(u, ':')) { - next_token_no_ltrim_talloc(ctx, &u, pp_user, ":"); - if (!*pp_user) { - return -1; - } - *pp_password = talloc_strdup(ctx, u); - if (!*pp_password) { - return -1; - } - } else { - *pp_user = talloc_strdup(ctx, u); - if (!*pp_user) { - return -1; - } - } - } - - if (!next_token_talloc(ctx, &p, pp_server, "/")) { - return -1; - } - - if (*p == (char)0) { - goto decoding; /* That's it ... */ - } - - if (!next_token_talloc(ctx, &p, pp_share, "/")) { - return -1; - } - - /* - * Prepend a leading slash if there's a file path, as required by - * NetApp filers. - */ - if (*p != '\0') { - *pp_path = talloc_asprintf(ctx, - "\\%s", - p); - } else { - *pp_path = talloc_strdup(ctx, ""); - } - if (!*pp_path) { - return -1; - } - string_replace(*pp_path, '/', '\\'); - - decoding: - - (void) smbc_urldecode_talloc(ctx, pp_path, *pp_path); - (void) smbc_urldecode_talloc(ctx, pp_server, *pp_server); - (void) smbc_urldecode_talloc(ctx, pp_share, *pp_share); - (void) smbc_urldecode_talloc(ctx, pp_user, *pp_user); - (void) smbc_urldecode_talloc(ctx, pp_password, *pp_password); - - return 0; -} - -/* - * Verify that the options specified in a URL are valid - */ -static int -smbc_check_options(char *server, - char *share, - char *path, - char *options) -{ - DEBUG(4, ("smbc_check_options(): server='%s' share='%s' " - "path='%s' options='%s'\n", - server, share, path, options)); - - /* No options at all is always ok */ - if (! *options) return 0; - - /* Currently, we don't support any options. */ - return -1; -} - -/* - * Convert an SMB error into a UNIX error ... - */ -static int -smbc_errno(SMBCCTX *context, - struct cli_state *c) -{ - int ret = cli_errno(c); - - if (cli_is_dos_error(c)) { - uint8 eclass; - uint32 ecode; - - cli_dos_error(c, &eclass, &ecode); - - DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n", - (int)eclass, (int)ecode, (int)ecode, ret)); - } else { - NTSTATUS status; - - status = cli_nt_error(c); - - DEBUG(3,("smbc errno %s -> %d\n", - nt_errstr(status), ret)); - } - - return ret; -} - -/* - * Check a server for being alive and well. - * returns 0 if the server is in shape. Returns 1 on error - * - * Also useable outside libsmbclient to enable external cache - * to do some checks too. - */ -static int -smbc_check_server(SMBCCTX * context, - SMBCSRV * server) -{ - socklen_t size; - struct sockaddr addr; - - size = sizeof(addr); - return (getpeername(server->cli->fd, &addr, &size) == -1); -} - -/* - * Remove a server from the cached server list it's unused. - * On success, 0 is returned. 1 is returned if the server could not be removed. - * - * Also useable outside libsmbclient - */ -int -smbc_remove_unused_server(SMBCCTX * context, - SMBCSRV * srv) -{ - SMBCFILE * file; - - /* are we being fooled ? */ - if (!context || !context->internal || - !context->internal->_initialized || !srv) return 1; - - - /* Check all open files/directories for a relation with this server */ - for (file = context->internal->_files; file; file=file->next) { - if (file->srv == srv) { - /* Still used */ - DEBUG(3, ("smbc_remove_usused_server: " - "%p still used by %p.\n", - srv, file)); - return 1; - } - } - - DLIST_REMOVE(context->internal->_servers, srv); - - cli_shutdown(srv->cli); - srv->cli = NULL; - - DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv)); - - (context->callbacks.remove_cached_srv_fn)(context, srv); - - SAFE_FREE(srv); - return 0; -} - -/**************************************************************** - * Call the auth_fn with fixed size (fstring) buffers. - ***************************************************************/ - -static void call_auth_fn(TALLOC_CTX *ctx, - SMBCCTX *context, - const char *server, - const char *share, - char **pp_workgroup, - char **pp_username, - char **pp_password) -{ - fstring workgroup; - fstring username; - fstring password; - - strlcpy(workgroup, *pp_workgroup, sizeof(workgroup)); - strlcpy(username, *pp_username, sizeof(username)); - strlcpy(password, *pp_password, sizeof(password)); - - if (context->internal->_auth_fn_with_context != NULL) { - (context->internal->_auth_fn_with_context)( - context, - server, share, - workgroup, sizeof(workgroup), - username, sizeof(username), - password, sizeof(password)); - } else { - (context->callbacks.auth_fn)( - server, share, - workgroup, sizeof(workgroup), - username, sizeof(username), - password, sizeof(password)); - } - - TALLOC_FREE(*pp_workgroup); - TALLOC_FREE(*pp_username); - TALLOC_FREE(*pp_password); - - *pp_workgroup = talloc_strdup(ctx, workgroup); - *pp_username = talloc_strdup(ctx, username); - *pp_password = talloc_strdup(ctx, password); -} - -static SMBCSRV * -find_server(TALLOC_CTX *ctx, - SMBCCTX *context, - const char *server, - const char *share, - char **pp_workgroup, - char **pp_username, - char **pp_password) -{ - SMBCSRV *srv; - int auth_called = 0; - - check_server_cache: - - srv = (context->callbacks.get_cached_srv_fn)(context, server, share, - *pp_workgroup, *pp_username); - - if (!auth_called && !srv && (!*pp_username || !(*pp_username)[0] || - !*pp_password || !(*pp_password)[0])) { - call_auth_fn(ctx, context, server, share, - pp_workgroup, pp_username, pp_password); - - if (!pp_workgroup || !pp_username || !pp_password) { - return NULL; - } - - /* - * However, smbc_auth_fn may have picked up info relating to - * an existing connection, so try for an existing connection - * again ... - */ - auth_called = 1; - goto check_server_cache; - - } - - if (srv) { - if ((context->callbacks.check_server_fn)(context, srv)) { - /* - * This server is no good anymore - * Try to remove it and check for more possible - * servers in the cache - */ - if ((context->callbacks.remove_unused_server_fn)(context, - srv)) { - /* - * We could not remove the server completely, - * remove it from the cache so we will not get - * it again. It will be removed when the last - * file/dir is closed. - */ - (context->callbacks.remove_cached_srv_fn)(context, - srv); - } - - /* - * Maybe there are more cached connections to this - * server - */ - goto check_server_cache; - } - - return srv; - } - - return NULL; -} - -/* - * Connect to a server, possibly on an existing connection - * - * Here, what we want to do is: If the server and username - * match an existing connection, reuse that, otherwise, establish a - * new connection. - * - * If we have to create a new connection, call the auth_fn to get the - * info we need, unless the username and password were passed in. - */ - -static SMBCSRV * -smbc_server(TALLOC_CTX *ctx, - SMBCCTX *context, - bool connect_if_not_found, - const char *server, - const char *share, - char **pp_workgroup, - char **pp_username, - char **pp_password) -{ - SMBCSRV *srv=NULL; - struct cli_state *c; - struct nmb_name called, calling; - const char *server_n = server; - struct sockaddr_storage ss; - int tried_reverse = 0; - int port_try_first; - int port_try_next; - const char *username_used; - NTSTATUS status; - - zero_addr(&ss); - ZERO_STRUCT(c); - - if (server[0] == 0) { - errno = EPERM; - return NULL; - } - - /* Look for a cached connection */ - srv = find_server(ctx, context, server, share, - pp_workgroup, pp_username, pp_password); - - /* - * If we found a connection and we're only allowed one share per - * server... - */ - if (srv && *share != '\0' && context->options.one_share_per_server) { - - /* - * ... then if there's no current connection to the share, - * connect to it. find_server(), or rather the function - * pointed to by context->callbacks.get_cached_srv_fn which - * was called by find_server(), will have issued a tree - * disconnect if the requested share is not the same as the - * one that was already connected. - */ - if (srv->cli->cnum == (uint16) -1) { - /* Ensure we have accurate auth info */ - call_auth_fn(ctx, context, server, share, - pp_workgroup, pp_username, pp_password); - - if (!*pp_workgroup || !*pp_username || !*pp_password) { - errno = ENOMEM; - cli_shutdown(srv->cli); - srv->cli = NULL; - (context->callbacks.remove_cached_srv_fn)(context, - srv); - return NULL; - } - - /* - * We don't need to renegotiate encryption - * here as the encryption context is not per - * tid. - */ - - if (!cli_send_tconX(srv->cli, share, "?????", - *pp_password, - strlen(*pp_password)+1)) { - - errno = smbc_errno(context, srv->cli); - cli_shutdown(srv->cli); - srv->cli = NULL; - (context->callbacks.remove_cached_srv_fn)(context, - srv); - srv = NULL; - } - - /* - * Regenerate the dev value since it's based on both - * server and share - */ - if (srv) { - srv->dev = (dev_t)(str_checksum(server) ^ - str_checksum(share)); - } - } - } - - /* If we have a connection... */ - if (srv) { - - /* ... then we're done here. Give 'em what they came for. */ - return srv; - } - - /* If we're not asked to connect when a connection doesn't exist... */ - if (! connect_if_not_found) { - /* ... then we're done here. */ - return NULL; - } - - if (!*pp_workgroup || !*pp_username || !*pp_password) { - errno = ENOMEM; - return NULL; - } - - make_nmb_name(&calling, context->netbios_name, 0x0); - make_nmb_name(&called , server, 0x20); - - DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server)); - - DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server)); - - again: - - zero_addr(&ss); - - /* have to open a new connection */ - if ((c = cli_initialise()) == NULL) { - errno = ENOMEM; - return NULL; - } - - if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) { - c->use_kerberos = True; - } - if (context->flags & SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS) { - c->fallback_after_kerberos = True; - } - - c->timeout = context->timeout; - - /* - * Force use of port 139 for first try if share is $IPC, empty, or - * null, so browse lists can work - */ - if (share == NULL || *share == '\0' || strcmp(share, "IPC$") == 0) { - port_try_first = 139; - port_try_next = 445; - } else { - port_try_first = 445; - port_try_next = 139; - } - - c->port = port_try_first; - - status = cli_connect(c, server_n, &ss); - if (!NT_STATUS_IS_OK(status)) { - - /* First connection attempt failed. Try alternate port. */ - c->port = port_try_next; - - status = cli_connect(c, server_n, &ss); - if (!NT_STATUS_IS_OK(status)) { - cli_shutdown(c); - errno = ETIMEDOUT; - return NULL; - } - } - - if (!cli_session_request(c, &calling, &called)) { - cli_shutdown(c); - if (strcmp(called.name, "*SMBSERVER")) { - make_nmb_name(&called , "*SMBSERVER", 0x20); - goto again; - } else { /* Try one more time, but ensure we don't loop */ - - /* Only try this if server is an IP address ... */ - - if (is_ipaddress(server) && !tried_reverse) { - fstring remote_name; - struct sockaddr_storage rem_ss; - - if (!interpret_string_addr(&rem_ss, server, - NI_NUMERICHOST)) { - DEBUG(4, ("Could not convert IP address " - "%s to struct sockaddr_storage\n", - server)); - errno = ETIMEDOUT; - return NULL; - } - - tried_reverse++; /* Yuck */ - - if (name_status_find("*", 0, 0, &rem_ss, remote_name)) { - make_nmb_name(&called, remote_name, 0x20); - goto again; - } - } - } - errno = ETIMEDOUT; - return NULL; - } - - DEBUG(4,(" session request ok\n")); - - if (!cli_negprot(c)) { - cli_shutdown(c); - errno = ETIMEDOUT; - return NULL; - } - - username_used = *pp_username; - - if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used, - *pp_password, strlen(*pp_password), - *pp_password, strlen(*pp_password), - *pp_workgroup))) { - - /* Failed. Try an anonymous login, if allowed by flags. */ - username_used = ""; - - if ((context->flags & SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON) || - !NT_STATUS_IS_OK(cli_session_setup(c, username_used, - *pp_password, 1, - *pp_password, 0, - *pp_workgroup))) { - - cli_shutdown(c); - errno = EPERM; - return NULL; - } - } - - DEBUG(4,(" session setup ok\n")); - - if (!cli_send_tconX(c, share, "?????", - *pp_password, strlen(*pp_password)+1)) { - errno = smbc_errno(context, c); - cli_shutdown(c); - return NULL; - } - - DEBUG(4,(" tconx ok\n")); - - if (context->internal->_smb_encryption_level) { - /* Attempt UNIX smb encryption. */ - if (!NT_STATUS_IS_OK(cli_force_encryption(c, - username_used, - *pp_password, - *pp_workgroup))) { - - /* - * context->internal->_smb_encryption_level == 1 - * means don't fail if encryption can't be negotiated, - * == 2 means fail if encryption can't be negotiated. - */ - - DEBUG(4,(" SMB encrypt failed\n")); - - if (context->internal->_smb_encryption_level == 2) { - cli_shutdown(c); - errno = EPERM; - return NULL; - } - } - DEBUG(4,(" SMB encrypt ok\n")); - } - - /* - * Ok, we have got a nice connection - * Let's allocate a server structure. - */ - - srv = SMB_MALLOC_P(SMBCSRV); - if (!srv) { - errno = ENOMEM; - goto failed; - } - - ZERO_STRUCTP(srv); - srv->cli = c; - srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share)); - srv->no_pathinfo = False; - srv->no_pathinfo2 = False; - srv->no_nt_session = False; - - /* now add it to the cache (internal or external) */ - /* Let the cache function set errno if it wants to */ - errno = 0; - if ((context->callbacks.add_cached_srv_fn)(context, srv, - server, share, - *pp_workgroup, - *pp_username)) { - int saved_errno = errno; - DEBUG(3, (" Failed to add server to cache\n")); - errno = saved_errno; - if (errno == 0) { - errno = ENOMEM; - } - goto failed; - } - - DEBUG(2, ("Server connect ok: //%s/%s: %p\n", - server, share, srv)); - - DLIST_ADD(context->internal->_servers, srv); - return srv; - - failed: - cli_shutdown(c); - if (!srv) { - return NULL; - } - - SAFE_FREE(srv); - return NULL; -} - -/* - * Connect to a server for getting/setting attributes, possibly on an existing - * connection. This works similarly to smbc_server(). - */ -static SMBCSRV * -smbc_attr_server(TALLOC_CTX *ctx, - SMBCCTX *context, - const char *server, - const char *share, - char **pp_workgroup, - char **pp_username, - char **pp_password) -{ - int flags; - struct sockaddr_storage ss; - struct cli_state *ipc_cli; - struct rpc_pipe_client *pipe_hnd; - NTSTATUS nt_status; - SMBCSRV *ipc_srv=NULL; - - /* - * See if we've already created this special connection. Reference - * our "special" share name '*IPC$', which is an impossible real share - * name due to the leading asterisk. - */ - ipc_srv = find_server(ctx, context, server, "*IPC$", - pp_workgroup, pp_username, pp_password); - if (!ipc_srv) { - - /* We didn't find a cached connection. Get the password */ - if (!*pp_password || (*pp_password)[0] == '\0') { - /* ... then retrieve it now. */ - call_auth_fn(ctx, context, server, share, - pp_workgroup, pp_username, pp_password); - if (!*pp_workgroup || !*pp_username || !*pp_password) { - errno = ENOMEM; - return NULL; - } - } - - flags = 0; - if (context->flags & SMB_CTX_FLAG_USE_KERBEROS) { - flags |= CLI_FULL_CONNECTION_USE_KERBEROS; - } - - zero_addr(&ss); - nt_status = cli_full_connection(&ipc_cli, - global_myname(), server, - &ss, 0, "IPC$", "?????", - *pp_username, - *pp_workgroup, - *pp_password, - flags, - Undefined, NULL); - if (! NT_STATUS_IS_OK(nt_status)) { - DEBUG(1,("cli_full_connection failed! (%s)\n", - nt_errstr(nt_status))); - errno = ENOTSUP; - return NULL; - } - - if (context->internal->_smb_encryption_level) { - /* Attempt UNIX smb encryption. */ - if (!NT_STATUS_IS_OK(cli_force_encryption(ipc_cli, - *pp_username, - *pp_password, - *pp_workgroup))) { - - /* - * context->internal->_smb_encryption_level == 1 - * means don't fail if encryption can't be negotiated, - * == 2 means fail if encryption can't be negotiated. - */ - - DEBUG(4,(" SMB encrypt failed on IPC$\n")); - - if (context->internal->_smb_encryption_level == 2) { - cli_shutdown(ipc_cli); - errno = EPERM; - return NULL; - } - } - DEBUG(4,(" SMB encrypt ok on IPC$\n")); - } - - ipc_srv = SMB_MALLOC_P(SMBCSRV); - if (!ipc_srv) { - errno = ENOMEM; - cli_shutdown(ipc_cli); - return NULL; - } - - ZERO_STRUCTP(ipc_srv); - ipc_srv->cli = ipc_cli; - - pipe_hnd = cli_rpc_pipe_open_noauth(ipc_srv->cli, - PI_LSARPC, - &nt_status); - if (!pipe_hnd) { - DEBUG(1, ("cli_nt_session_open fail!\n")); - errno = ENOTSUP; - cli_shutdown(ipc_srv->cli); - free(ipc_srv); - return NULL; - } - - /* - * Some systems don't support - * SEC_RIGHTS_MAXIMUM_ALLOWED, but NT sends 0x2000000 - * so we might as well do it too. - */ - - nt_status = rpccli_lsa_open_policy( - pipe_hnd, - talloc_tos(), - True, - GENERIC_EXECUTE_ACCESS, - &ipc_srv->pol); - - if (!NT_STATUS_IS_OK(nt_status)) { - errno = smbc_errno(context, ipc_srv->cli); - cli_shutdown(ipc_srv->cli); - return NULL; - } - - /* now add it to the cache (internal or external) */ - - errno = 0; /* let cache function set errno if it likes */ - if ((context->callbacks.add_cached_srv_fn)(context, ipc_srv, - server, - "*IPC$", - *pp_workgroup, - *pp_username)) { - DEBUG(3, (" Failed to add server to cache\n")); - if (errno == 0) { - errno = ENOMEM; - } - cli_shutdown(ipc_srv->cli); - free(ipc_srv); - return NULL; - } - - DLIST_ADD(context->internal->_servers, ipc_srv); - } - - return ipc_srv; -} - -/* - * Routine to open() a file ... - */ - -static SMBCFILE * -smbc_open_ctx(SMBCCTX *context, - const char *fname, - int flags, - mode_t mode) -{ - char *server = NULL, *share = NULL, *user = NULL, *password = NULL, *workgroup = NULL; - char *path = NULL; - char *targetpath = NULL; - struct cli_state *targetcli = NULL; - SMBCSRV *srv = NULL; - SMBCFILE *file = NULL; - int fd; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - - errno = EINVAL; /* Best I can think of ... */ - TALLOC_FREE(frame); - return NULL; - - } - - if (!fname) { - - errno = EINVAL; - TALLOC_FREE(frame); - return NULL; - - } - - if (smbc_parse_path(frame, - context, - fname, - &workgroup, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return NULL; - } - - if (!user || user[0] == (char)0) { - user = talloc_strdup(frame, context->user); - if (!user) { - errno = ENOMEM; - TALLOC_FREE(frame); - return NULL; - } - } - - srv = smbc_server(frame, context, True, - server, share, &workgroup, &user, &password); - - if (!srv) { - if (errno == EPERM) errno = EACCES; - TALLOC_FREE(frame); - return NULL; /* smbc_server sets errno */ - } - - /* Hmmm, the test for a directory is suspect here ... FIXME */ - - if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') { - fd = -1; - } else { - file = SMB_MALLOC_P(SMBCFILE); - - if (!file) { - errno = ENOMEM; - TALLOC_FREE(frame); - return NULL; - } - - ZERO_STRUCTP(file); - - /*d_printf(">>>open: resolving %s\n", path);*/ - if (!cli_resolve_path(frame, "", srv->cli, path, &targetcli, &targetpath)) { - d_printf("Could not resolve %s\n", path); - SAFE_FREE(file); - TALLOC_FREE(frame); - return NULL; - } - /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/ - - if ((fd = cli_open(targetcli, targetpath, flags, - context->internal->_share_mode)) < 0) { - - /* Handle the error ... */ - - SAFE_FREE(file); - errno = smbc_errno(context, targetcli); - TALLOC_FREE(frame); - return NULL; - - } - - /* Fill in file struct */ - - file->cli_fd = fd; - file->fname = SMB_STRDUP(fname); - file->srv = srv; - file->offset = 0; - file->file = True; - - DLIST_ADD(context->internal->_files, file); - - /* - * If the file was opened in O_APPEND mode, all write - * operations should be appended to the file. To do that, - * though, using this protocol, would require a getattrE() - * call for each and every write, to determine where the end - * of the file is. (There does not appear to be an append flag - * in the protocol.) Rather than add all of that overhead of - * retrieving the current end-of-file offset prior to each - * write operation, we'll assume that most append operations - * will continuously write, so we'll just set the offset to - * the end of the file now and hope that's adequate. - * - * Note to self: If this proves inadequate, and O_APPEND - * should, in some cases, be forced for each write, add a - * field in the context options structure, for - * "strict_append_mode" which would select between the current - * behavior (if FALSE) or issuing a getattrE() prior to each - * write and forcing the write to the end of the file (if - * TRUE). Adding that capability will likely require adding - * an "append" flag into the _SMBCFILE structure to track - * whether a file was opened in O_APPEND mode. -- djl - */ - if (flags & O_APPEND) { - if (smbc_lseek_ctx(context, file, 0, SEEK_END) < 0) { - (void) smbc_close_ctx(context, file); - errno = ENXIO; - TALLOC_FREE(frame); - return NULL; - } - } - - TALLOC_FREE(frame); - return file; - - } - - /* Check if opendir needed ... */ - - if (fd == -1) { - int eno = 0; - - eno = smbc_errno(context, srv->cli); - file = (context->opendir)(context, fname); - if (!file) errno = eno; - TALLOC_FREE(frame); - return file; - - } - - errno = EINVAL; /* FIXME, correct errno ? */ - TALLOC_FREE(frame); - return NULL; - -} - -/* - * Routine to create a file - */ - -static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */ - -static SMBCFILE * -smbc_creat_ctx(SMBCCTX *context, - const char *path, - mode_t mode) -{ - - if (!context || !context->internal || - !context->internal->_initialized) { - - errno = EINVAL; - return NULL; - - } - - return smbc_open_ctx(context, path, creat_bits, mode); -} - -/* - * Routine to read() a file ... - */ - -static ssize_t -smbc_read_ctx(SMBCCTX *context, - SMBCFILE *file, - void *buf, - size_t count) -{ - int ret; - char *server = NULL, *share = NULL, *user = NULL, *password = NULL; - char *path = NULL; - char *targetpath = NULL; - struct cli_state *targetcli = NULL; - TALLOC_CTX *frame = talloc_stackframe(); - - /* - * offset: - * - * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) -- - * appears to pass file->offset (which is type off_t) differently than - * a local variable of type off_t. Using local variable "offset" in - * the call to cli_read() instead of file->offset fixes a problem - * retrieving data at an offset greater than 4GB. - */ - off_t offset; - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - - } - - DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count)); - - if (!file || !DLIST_CONTAINS(context->internal->_files, file)) { - errno = EBADF; - TALLOC_FREE(frame); - return -1; - - } - - offset = file->offset; - - /* Check that the buffer exists ... */ - - if (buf == NULL) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - - } - - /*d_printf(">>>read: parsing %s\n", file->fname);*/ - if (smbc_parse_path(frame, - context, - file->fname, - NULL, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - /*d_printf(">>>read: resolving %s\n", path);*/ - if (!cli_resolve_path(frame, "", file->srv->cli, path, - &targetcli, &targetpath)) { - d_printf("Could not resolve %s\n", path); - TALLOC_FREE(frame); - return -1; - } - /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/ - - ret = cli_read(targetcli, file->cli_fd, (char *)buf, offset, count); - - if (ret < 0) { - - errno = smbc_errno(context, targetcli); - TALLOC_FREE(frame); - return -1; - - } - - file->offset += ret; - - DEBUG(4, (" --> %d\n", ret)); - - TALLOC_FREE(frame); - return ret; /* Success, ret bytes of data ... */ - -} - -/* - * Routine to write() a file ... - */ - -static ssize_t -smbc_write_ctx(SMBCCTX *context, - SMBCFILE *file, - void *buf, - size_t count) -{ - int ret; - off_t offset; - char *server = NULL, *share = NULL, *user = NULL, *password = NULL; - char *path = NULL; - char *targetpath = NULL; - struct cli_state *targetcli = NULL; - TALLOC_CTX *frame = talloc_stackframe(); - - /* First check all pointers before dereferencing them */ - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - - } - - if (!file || !DLIST_CONTAINS(context->internal->_files, file)) { - errno = EBADF; - TALLOC_FREE(frame); - return -1; - } - - /* Check that the buffer exists ... */ - - if (buf == NULL) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - - } - - offset = file->offset; /* See "offset" comment in smbc_read_ctx() */ - - /*d_printf(">>>write: parsing %s\n", file->fname);*/ - if (smbc_parse_path(frame, - context, - file->fname, - NULL, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - /*d_printf(">>>write: resolving %s\n", path);*/ - if (!cli_resolve_path(frame, "", file->srv->cli, path, - &targetcli, &targetpath)) { - d_printf("Could not resolve %s\n", path); - TALLOC_FREE(frame); - return -1; - } - /*d_printf(">>>write: resolved path as %s\n", targetpath);*/ - - ret = cli_write(targetcli, file->cli_fd, 0, (char *)buf, offset, count); - - if (ret <= 0) { - errno = smbc_errno(context, targetcli); - TALLOC_FREE(frame); - return -1; - - } - - file->offset += ret; - - TALLOC_FREE(frame); - return ret; /* Success, 0 bytes of data ... */ -} - -/* - * Routine to close() a file ... - */ - -static int -smbc_close_ctx(SMBCCTX *context, - SMBCFILE *file) -{ - SMBCSRV *srv; - char *server = NULL, *share = NULL, *user = NULL, *password = NULL; - char *path = NULL; - char *targetpath = NULL; - struct cli_state *targetcli = NULL; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!file || !DLIST_CONTAINS(context->internal->_files, file)) { - errno = EBADF; - TALLOC_FREE(frame); - return -1; - } - - /* IS a dir ... */ - if (!file->file) { - TALLOC_FREE(frame); - return (context->closedir)(context, file); - } - - /*d_printf(">>>close: parsing %s\n", file->fname);*/ - if (smbc_parse_path(frame, - context, - file->fname, - NULL, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - /*d_printf(">>>close: resolving %s\n", path);*/ - if (!cli_resolve_path(frame, "", file->srv->cli, path, - &targetcli, &targetpath)) { - d_printf("Could not resolve %s\n", path); - TALLOC_FREE(frame); - return -1; - } - /*d_printf(">>>close: resolved path as %s\n", targetpath);*/ - - if (!cli_close(targetcli, file->cli_fd)) { - - DEBUG(3, ("cli_close failed on %s. purging server.\n", - file->fname)); - /* Deallocate slot and remove the server - * from the server cache if unused */ - errno = smbc_errno(context, targetcli); - srv = file->srv; - DLIST_REMOVE(context->internal->_files, file); - SAFE_FREE(file->fname); - SAFE_FREE(file); - (context->callbacks.remove_unused_server_fn)(context, srv); - TALLOC_FREE(frame); - return -1; - - } - - DLIST_REMOVE(context->internal->_files, file); - SAFE_FREE(file->fname); - SAFE_FREE(file); - TALLOC_FREE(frame); - - return 0; -} - -/* - * Get info from an SMB server on a file. Use a qpathinfo call first - * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo - */ -static bool -smbc_getatr(SMBCCTX * context, - SMBCSRV *srv, - char *path, - uint16 *mode, - SMB_OFF_T *size, - struct timespec *create_time_ts, - struct timespec *access_time_ts, - struct timespec *write_time_ts, - struct timespec *change_time_ts, - SMB_INO_T *ino) -{ - char *fixedpath = NULL; - char *targetpath = NULL; - struct cli_state *targetcli = NULL; - time_t write_time; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - /* path fixup for . and .. */ - if (strequal(path, ".") || strequal(path, "..")) { - fixedpath = talloc_strdup(frame, "\\"); - if (!fixedpath) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - } else { - fixedpath = talloc_strdup(frame, path); - if (!fixedpath) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - trim_string(fixedpath, NULL, "\\.."); - trim_string(fixedpath, NULL, "\\."); - } - DEBUG(4,("smbc_getatr: sending qpathinfo\n")); - - if (!cli_resolve_path(frame, "", srv->cli, fixedpath, - &targetcli, &targetpath)) { - d_printf("Couldn't resolve %s\n", path); - TALLOC_FREE(frame); - return False; - } - - if (!srv->no_pathinfo2 && - cli_qpathinfo2(targetcli, targetpath, - create_time_ts, - access_time_ts, - write_time_ts, - change_time_ts, - size, mode, ino)) { - TALLOC_FREE(frame); - return True; - } - - /* if this is NT then don't bother with the getatr */ - if (targetcli->capabilities & CAP_NT_SMBS) { - errno = EPERM; - TALLOC_FREE(frame); - return False; - } - - if (cli_getatr(targetcli, targetpath, mode, size, &write_time)) { - - struct timespec w_time_ts; - - w_time_ts = convert_time_t_to_timespec(write_time); - - if (write_time_ts != NULL) { - *write_time_ts = w_time_ts; - } - - if (create_time_ts != NULL) { - *create_time_ts = w_time_ts; - } - - if (access_time_ts != NULL) { - *access_time_ts = w_time_ts; - } - - if (change_time_ts != NULL) { - *change_time_ts = w_time_ts; - } - - srv->no_pathinfo2 = True; - TALLOC_FREE(frame); - return True; - } - - errno = EPERM; - TALLOC_FREE(frame); - return False; - -} - -/* - * Set file info on an SMB server. Use setpathinfo call first. If that - * fails, use setattrE.. - * - * Access and modification time parameters are always used and must be - * provided. Create time, if zero, will be determined from the actual create - * time of the file. If non-zero, the create time will be set as well. - * - * "mode" (attributes) parameter may be set to -1 if it is not to be set. - */ -static bool -smbc_setatr(SMBCCTX * context, SMBCSRV *srv, char *path, - time_t create_time, - time_t access_time, - time_t write_time, - time_t change_time, - uint16 mode) -{ - int fd; - int ret; - TALLOC_CTX *frame = talloc_stackframe(); - - /* - * First, try setpathinfo (if qpathinfo succeeded), for it is the - * modern function for "new code" to be using, and it works given a - * filename rather than requiring that the file be opened to have its - * attributes manipulated. - */ - if (srv->no_pathinfo || - ! cli_setpathinfo(srv->cli, path, - create_time, - access_time, - write_time, - change_time, - mode)) { - - /* - * setpathinfo is not supported; go to plan B. - * - * cli_setatr() does not work on win98, and it also doesn't - * support setting the access time (only the modification - * time), so in all cases, we open the specified file and use - * cli_setattrE() which should work on all OS versions, and - * supports both times. - */ - - /* Don't try {q,set}pathinfo() again, with this server */ - srv->no_pathinfo = True; - - /* Open the file */ - if ((fd = cli_open(srv->cli, path, O_RDWR, DENY_NONE)) < 0) { - - errno = smbc_errno(context, srv->cli); - TALLOC_FREE(frame); - return -1; - } - - /* Set the new attributes */ - ret = cli_setattrE(srv->cli, fd, - change_time, - access_time, - write_time); - - /* Close the file */ - cli_close(srv->cli, fd); - - /* - * Unfortunately, setattrE() doesn't have a provision for - * setting the access mode (attributes). We'll have to try - * cli_setatr() for that, and with only this parameter, it - * seems to work on win98. - */ - if (ret && mode != (uint16) -1) { - ret = cli_setatr(srv->cli, path, mode, 0); - } - - if (! ret) { - errno = smbc_errno(context, srv->cli); - TALLOC_FREE(frame); - return False; - } - } - - TALLOC_FREE(frame); - return True; -} - - /* - * Routine to unlink() a file - */ - -static int -smbc_unlink_ctx(SMBCCTX *context, - const char *fname) -{ - char *server = NULL, *share = NULL, *user = NULL, *password = NULL, *workgroup = NULL; - char *path = NULL; - char *targetpath = NULL; - struct cli_state *targetcli = NULL; - SMBCSRV *srv = NULL; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; /* Best I can think of ... */ - TALLOC_FREE(frame); - return -1; - - } - - if (!fname) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - - } - - if (smbc_parse_path(frame, - context, - fname, - &workgroup, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!user || user[0] == (char)0) { - user = talloc_strdup(frame, context->user); - if (!user) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - } - - srv = smbc_server(frame, context, True, - server, share, &workgroup, &user, &password); - - if (!srv) { - TALLOC_FREE(frame); - return -1; /* smbc_server sets errno */ - - } - - /*d_printf(">>>unlink: resolving %s\n", path);*/ - if (!cli_resolve_path(frame, "", srv->cli, path, - &targetcli, &targetpath)) { - d_printf("Could not resolve %s\n", path); - TALLOC_FREE(frame); - return -1; - } - /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/ - - if (!cli_unlink(targetcli, targetpath)) { - - errno = smbc_errno(context, targetcli); - - if (errno == EACCES) { /* Check if the file is a directory */ - - int saverr = errno; - SMB_OFF_T size = 0; - uint16 mode = 0; - struct timespec write_time_ts; - struct timespec access_time_ts; - struct timespec change_time_ts; - SMB_INO_T ino = 0; - - if (!smbc_getatr(context, srv, path, &mode, &size, - NULL, - &access_time_ts, - &write_time_ts, - &change_time_ts, - &ino)) { - - /* Hmmm, bad error ... What? */ - - errno = smbc_errno(context, targetcli); - TALLOC_FREE(frame); - return -1; - - } - else { - - if (IS_DOS_DIR(mode)) - errno = EISDIR; - else - errno = saverr; /* Restore this */ - - } - } - - TALLOC_FREE(frame); - return -1; - - } - - TALLOC_FREE(frame); - return 0; /* Success ... */ - -} - -/* - * Routine to rename() a file - */ - -static int -smbc_rename_ctx(SMBCCTX *ocontext, - const char *oname, - SMBCCTX *ncontext, - const char *nname) -{ - char *server1 = NULL; - char *share1 = NULL; - char *server2 = NULL; - char *share2 = NULL; - char *user1 = NULL; - char *user2 = NULL; - char *password1 = NULL; - char *password2 = NULL; - char *workgroup = NULL; - char *path1 = NULL; - char *path2 = NULL; - char *targetpath1 = NULL; - char *targetpath2 = NULL; - struct cli_state *targetcli1 = NULL; - struct cli_state *targetcli2 = NULL; - SMBCSRV *srv = NULL; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!ocontext || !ncontext || - !ocontext->internal || !ncontext->internal || - !ocontext->internal->_initialized || - !ncontext->internal->_initialized) { - errno = EINVAL; /* Best I can think of ... */ - TALLOC_FREE(frame); - return -1; - } - - if (!oname || !nname) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname)); - - if (smbc_parse_path(frame, - ocontext, - oname, - &workgroup, - &server1, - &share1, - &path1, - &user1, - &password1, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!user1 || user1[0] == (char)0) { - user1 = talloc_strdup(frame, ocontext->user); - if (!user1) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - } - - if (smbc_parse_path(frame, - ncontext, - nname, - NULL, - &server2, - &share2, - &path2, - &user2, - &password2, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!user2 || user2[0] == (char)0) { - user2 = talloc_strdup(frame, ncontext->user); - if (!user2) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - } - - if (strcmp(server1, server2) || strcmp(share1, share2) || - strcmp(user1, user2)) { - /* Can't rename across file systems, or users?? */ - errno = EXDEV; - TALLOC_FREE(frame); - return -1; - } - - srv = smbc_server(frame, ocontext, True, - server1, share1, &workgroup, &user1, &password1); - if (!srv) { - TALLOC_FREE(frame); - return -1; - - } - - /*d_printf(">>>rename: resolving %s\n", path1);*/ - if (!cli_resolve_path(frame, "", srv->cli, path1, - &targetcli1, &targetpath1)) { - d_printf("Could not resolve %s\n", path1); - TALLOC_FREE(frame); - return -1; - } - /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/ - /*d_printf(">>>rename: resolving %s\n", path2);*/ - if (!cli_resolve_path(frame, "", srv->cli, path2, - &targetcli2, &targetpath2)) { - d_printf("Could not resolve %s\n", path2); - TALLOC_FREE(frame); - return -1; - } - /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/ - - if (strcmp(targetcli1->desthost, targetcli2->desthost) || - strcmp(targetcli1->share, targetcli2->share)) - { - /* can't rename across file systems */ - errno = EXDEV; - TALLOC_FREE(frame); - return -1; - } - - if (!cli_rename(targetcli1, targetpath1, targetpath2)) { - int eno = smbc_errno(ocontext, targetcli1); - - if (eno != EEXIST || - !cli_unlink(targetcli1, targetpath2) || - !cli_rename(targetcli1, targetpath1, targetpath2)) { - - errno = eno; - TALLOC_FREE(frame); - return -1; - - } - } - - TALLOC_FREE(frame); - return 0; /* Success */ -} - -/* - * A routine to lseek() a file - */ - -static off_t -smbc_lseek_ctx(SMBCCTX *context, - SMBCFILE *file, - off_t offset, - int whence) -{ - SMB_OFF_T size; - char *server = NULL, *share = NULL, *user = NULL, *password = NULL; - char *path = NULL; - char *targetpath = NULL; - struct cli_state *targetcli = NULL; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!file || !DLIST_CONTAINS(context->internal->_files, file)) { - - errno = EBADF; - TALLOC_FREE(frame); - return -1; - - } - - if (!file->file) { - - errno = EINVAL; - TALLOC_FREE(frame); - return -1; /* Can't lseek a dir ... */ - - } - - switch (whence) { - case SEEK_SET: - file->offset = offset; - break; - - case SEEK_CUR: - file->offset += offset; - break; - - case SEEK_END: - /*d_printf(">>>lseek: parsing %s\n", file->fname);*/ - if (smbc_parse_path(frame, - context, - file->fname, - NULL, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - /*d_printf(">>>lseek: resolving %s\n", path);*/ - if (!cli_resolve_path(frame, "", file->srv->cli, path, - &targetcli, &targetpath)) { - d_printf("Could not resolve %s\n", path); - TALLOC_FREE(frame); - return -1; - } - /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/ - - if (!cli_qfileinfo(targetcli, file->cli_fd, NULL, - &size, NULL, NULL, NULL, NULL, NULL)) - { - SMB_OFF_T b_size = size; - if (!cli_getattrE(targetcli, file->cli_fd, - NULL, &b_size, NULL, NULL, NULL)) - { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } else - size = b_size; - } - file->offset = size + offset; - break; - - default: - errno = EINVAL; - break; - - } - - TALLOC_FREE(frame); - return file->offset; - -} - -/* - * Generate an inode number from file name for those things that need it - */ - -static ino_t -smbc_inode(SMBCCTX *context, - const char *name) -{ - if (!context || !context->internal || - !context->internal->_initialized) { - - errno = EINVAL; - return -1; - - } - - if (!*name) return 2; /* FIXME, why 2 ??? */ - return (ino_t)str_checksum(name); - -} - -/* - * Routine to put basic stat info into a stat structure ... Used by stat and - * fstat below. - */ - -static int -smbc_setup_stat(SMBCCTX *context, - struct stat *st, - char *fname, - SMB_OFF_T size, - int mode) -{ - TALLOC_CTX *frame = talloc_stackframe(); - - st->st_mode = 0; - - if (IS_DOS_DIR(mode)) { - st->st_mode = SMBC_DIR_MODE; - } else { - st->st_mode = SMBC_FILE_MODE; - } - - if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR; - if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP; - if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH; - if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR; - - st->st_size = size; -#ifdef HAVE_STAT_ST_BLKSIZE - st->st_blksize = 512; -#endif -#ifdef HAVE_STAT_ST_BLOCKS - st->st_blocks = (size+511)/512; -#endif -#ifdef HAVE_STRUCT_STAT_ST_RDEV - st->st_rdev = 0; -#endif - st->st_uid = getuid(); - st->st_gid = getgid(); - - if (IS_DOS_DIR(mode)) { - st->st_nlink = 2; - } else { - st->st_nlink = 1; - } - - if (st->st_ino == 0) { - st->st_ino = smbc_inode(context, fname); - } - - TALLOC_FREE(frame); - return True; /* FIXME: Is this needed ? */ - -} - -/* - * Routine to stat a file given a name - */ - -static int -smbc_stat_ctx(SMBCCTX *context, - const char *fname, - struct stat *st) -{ - SMBCSRV *srv = NULL; - char *server = NULL; - char *share = NULL; - char *user = NULL; - char *password = NULL; - char *workgroup = NULL; - char *path = NULL; - struct timespec write_time_ts; - struct timespec access_time_ts; - struct timespec change_time_ts; - SMB_OFF_T size = 0; - uint16 mode = 0; - SMB_INO_T ino = 0; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - - errno = EINVAL; /* Best I can think of ... */ - TALLOC_FREE(frame); - return -1; - } - - if (!fname) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - DEBUG(4, ("smbc_stat(%s)\n", fname)); - - if (smbc_parse_path(frame, - context, - fname, - &workgroup, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!user || user[0] == (char)0) { - user = talloc_strdup(frame,context->user); - if (!user) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - } - - srv = smbc_server(frame, context, True, - server, share, &workgroup, &user, &password); - - if (!srv) { - TALLOC_FREE(frame); - return -1; /* errno set by smbc_server */ - } - - if (!smbc_getatr(context, srv, path, &mode, &size, - NULL, - &access_time_ts, - &write_time_ts, - &change_time_ts, - &ino)) { - errno = smbc_errno(context, srv->cli); - TALLOC_FREE(frame); - return -1; - } - - st->st_ino = ino; - - smbc_setup_stat(context, st, (char *) fname, size, mode); - - set_atimespec(st, access_time_ts); - set_ctimespec(st, change_time_ts); - set_mtimespec(st, write_time_ts); - st->st_dev = srv->dev; - - TALLOC_FREE(frame); - return 0; - -} - -/* - * Routine to stat a file given an fd - */ - -static int -smbc_fstat_ctx(SMBCCTX *context, - SMBCFILE *file, - struct stat *st) -{ - struct timespec change_time_ts; - struct timespec access_time_ts; - struct timespec write_time_ts; - SMB_OFF_T size; - uint16 mode; - char *server = NULL; - char *share = NULL; - char *user = NULL; - char *password = NULL; - char *path = NULL; - char *targetpath = NULL; - struct cli_state *targetcli = NULL; - SMB_INO_T ino = 0; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!file || !DLIST_CONTAINS(context->internal->_files, file)) { - errno = EBADF; - TALLOC_FREE(frame); - return -1; - } - - if (!file->file) { - TALLOC_FREE(frame); - return (context->fstatdir)(context, file, st); - } - - /*d_printf(">>>fstat: parsing %s\n", file->fname);*/ - if (smbc_parse_path(frame, - context, - file->fname, - NULL, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - /*d_printf(">>>fstat: resolving %s\n", path);*/ - if (!cli_resolve_path(frame, "", file->srv->cli, path, - &targetcli, &targetpath)) { - d_printf("Could not resolve %s\n", path); - TALLOC_FREE(frame); - return -1; - } - /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/ - - if (!cli_qfileinfo(targetcli, file->cli_fd, &mode, &size, - NULL, - &access_time_ts, - &write_time_ts, - &change_time_ts, - &ino)) { - - time_t change_time, access_time, write_time; - - if (!cli_getattrE(targetcli, file->cli_fd, &mode, &size, - &change_time, &access_time, &write_time)) { - - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - change_time_ts = convert_time_t_to_timespec(change_time); - access_time_ts = convert_time_t_to_timespec(access_time); - write_time_ts = convert_time_t_to_timespec(write_time); - } - - st->st_ino = ino; - - smbc_setup_stat(context, st, file->fname, size, mode); - - set_atimespec(st, access_time_ts); - set_ctimespec(st, change_time_ts); - set_mtimespec(st, write_time_ts); - st->st_dev = file->srv->dev; - - TALLOC_FREE(frame); - return 0; - -} - -/* - * Routine to truncate a file given by its file descriptor, to a specified size - */ - -static int -smbc_ftruncate_ctx(SMBCCTX *context, - SMBCFILE *file, - off_t length) -{ - SMB_OFF_T size = length; - char *server = NULL; - char *share = NULL; - char *user = NULL; - char *password = NULL; - char *path = NULL; - char *targetpath = NULL; - struct cli_state *targetcli = NULL; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!file || !DLIST_CONTAINS(context->internal->_files, file)) { - errno = EBADF; - TALLOC_FREE(frame); - return -1; - } - - if (!file->file) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - /*d_printf(">>>fstat: parsing %s\n", file->fname);*/ - if (smbc_parse_path(frame, - context, - file->fname, - NULL, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - /*d_printf(">>>fstat: resolving %s\n", path);*/ - if (!cli_resolve_path(frame, "", file->srv->cli, path, - &targetcli, &targetpath)) { - d_printf("Could not resolve %s\n", path); - TALLOC_FREE(frame); - return -1; - } - /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/ - - if (!cli_ftruncate(targetcli, file->cli_fd, size)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - TALLOC_FREE(frame); - return 0; - -} - -/* - * Routine to open a directory - * We accept the URL syntax explained in smbc_parse_path(), above. - */ - -static void -smbc_remove_dir(SMBCFILE *dir) -{ - struct smbc_dir_list *d,*f; - - d = dir->dir_list; - while (d) { - - f = d; d = d->next; - - SAFE_FREE(f->dirent); - SAFE_FREE(f); - - } - - dir->dir_list = dir->dir_end = dir->dir_next = NULL; - -} - -static int -add_dirent(SMBCFILE *dir, - const char *name, - const char *comment, - uint32 type) -{ - struct smbc_dirent *dirent; - int size; - int name_length = (name == NULL ? 0 : strlen(name)); - int comment_len = (comment == NULL ? 0 : strlen(comment)); - - /* - * Allocate space for the dirent, which must be increased by the - * size of the name and the comment and 1 each for the null terminator. - */ - - size = sizeof(struct smbc_dirent) + name_length + comment_len + 2; - - dirent = (struct smbc_dirent *)SMB_MALLOC(size); - - if (!dirent) { - - dir->dir_error = ENOMEM; - return -1; - - } - - ZERO_STRUCTP(dirent); - - if (dir->dir_list == NULL) { - - dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list); - if (!dir->dir_list) { - - SAFE_FREE(dirent); - dir->dir_error = ENOMEM; - return -1; - - } - ZERO_STRUCTP(dir->dir_list); - - dir->dir_end = dir->dir_next = dir->dir_list; - } - else { - - dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list); - - if (!dir->dir_end->next) { - - SAFE_FREE(dirent); - dir->dir_error = ENOMEM; - return -1; - - } - ZERO_STRUCTP(dir->dir_end->next); - - dir->dir_end = dir->dir_end->next; - } - - dir->dir_end->next = NULL; - dir->dir_end->dirent = dirent; - - dirent->smbc_type = type; - dirent->namelen = name_length; - dirent->commentlen = comment_len; - dirent->dirlen = size; - - /* - * dirent->namelen + 1 includes the null (no null termination needed) - * Ditto for dirent->commentlen. - * The space for the two null bytes was allocated. - */ - strncpy(dirent->name, (name?name:""), dirent->namelen + 1); - dirent->comment = (char *)(&dirent->name + dirent->namelen + 1); - strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1); - - return 0; - -} - -static void -list_unique_wg_fn(const char *name, - uint32 type, - const char *comment, - void *state) -{ - SMBCFILE *dir = (SMBCFILE *)state; - struct smbc_dir_list *dir_list; - struct smbc_dirent *dirent; - int dirent_type; - int do_remove = 0; - - dirent_type = dir->dir_type; - - if (add_dirent(dir, name, comment, dirent_type) < 0) { - - /* An error occurred, what do we do? */ - /* FIXME: Add some code here */ - } - - /* Point to the one just added */ - dirent = dir->dir_end->dirent; - - /* See if this was a duplicate */ - for (dir_list = dir->dir_list; - dir_list != dir->dir_end; - dir_list = dir_list->next) { - if (! do_remove && - strcmp(dir_list->dirent->name, dirent->name) == 0) { - /* Duplicate. End end of list need to be removed. */ - do_remove = 1; - } - - if (do_remove && dir_list->next == dir->dir_end) { - /* Found the end of the list. Remove it. */ - dir->dir_end = dir_list; - free(dir_list->next); - free(dirent); - dir_list->next = NULL; - break; - } - } -} - -static void -list_fn(const char *name, - uint32 type, - const char *comment, - void *state) -{ - SMBCFILE *dir = (SMBCFILE *)state; - int dirent_type; - - /* - * We need to process the type a little ... - * - * Disk share = 0x00000000 - * Print share = 0x00000001 - * Comms share = 0x00000002 (obsolete?) - * IPC$ share = 0x00000003 - * - * administrative shares: - * ADMIN$, IPC$, C$, D$, E$ ... are type |= 0x80000000 - */ - - if (dir->dir_type == SMBC_FILE_SHARE) { - switch (type) { - case 0 | 0x80000000: - case 0: - dirent_type = SMBC_FILE_SHARE; - break; - - case 1: - dirent_type = SMBC_PRINTER_SHARE; - break; - - case 2: - dirent_type = SMBC_COMMS_SHARE; - break; - - case 3 | 0x80000000: - case 3: - dirent_type = SMBC_IPC_SHARE; - break; - - default: - dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */ - break; - } - } - else { - dirent_type = dir->dir_type; - } - - if (add_dirent(dir, name, comment, dirent_type) < 0) { - - /* An error occurred, what do we do? */ - /* FIXME: Add some code here */ - - } -} - -static void -dir_list_fn(const char *mnt, - file_info *finfo, - const char *mask, - void *state) -{ - - if (add_dirent((SMBCFILE *)state, finfo->name, "", - (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) { - - /* Handle an error ... */ - - /* FIXME: Add some code ... */ - - } - -} - -static int -net_share_enum_rpc(struct cli_state *cli, - void (*fn)(const char *name, - uint32 type, - const char *comment, - void *state), - void *state) -{ - int i; - WERROR result; - ENUM_HND enum_hnd; - uint32 info_level = 1; - uint32 preferred_len = 0xffffffff; - uint32 type; - SRV_SHARE_INFO_CTR ctr; - fstring name = ""; - fstring comment = ""; - struct rpc_pipe_client *pipe_hnd; - NTSTATUS nt_status; - - /* Open the server service pipe */ - pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, &nt_status); - if (!pipe_hnd) { - DEBUG(1, ("net_share_enum_rpc pipe open fail!\n")); - return -1; - } - - /* Issue the NetShareEnum RPC call and retrieve the response */ - init_enum_hnd(&enum_hnd, 0); - result = rpccli_srvsvc_net_share_enum(pipe_hnd, - talloc_tos(), - info_level, - &ctr, - preferred_len, - &enum_hnd); - - /* Was it successful? */ - if (!W_ERROR_IS_OK(result) || ctr.num_entries == 0) { - /* Nope. Go clean up. */ - goto done; - } - - /* For each returned entry... */ - for (i = 0; i < ctr.num_entries; i++) { - - /* pull out the share name */ - rpcstr_pull_unistr2_fstring( - name, &ctr.share.info1[i].info_1_str.uni_netname); - - /* pull out the share's comment */ - rpcstr_pull_unistr2_fstring( - comment, &ctr.share.info1[i].info_1_str.uni_remark); - - /* Get the type value */ - type = ctr.share.info1[i].info_1.type; - - /* Add this share to the list */ - (*fn)(name, type, comment, state); - } - -done: - /* Close the server service pipe */ - cli_rpc_pipe_close(pipe_hnd); - - /* Tell 'em if it worked */ - return W_ERROR_IS_OK(result) ? 0 : -1; -} - - - -static SMBCFILE * -smbc_opendir_ctx(SMBCCTX *context, - const char *fname) -{ - int saved_errno; - char *server = NULL, *share = NULL, *user = NULL, *password = NULL, *options = NULL; - char *workgroup = NULL; - char *path = NULL; - uint16 mode; - char *p = NULL; - SMBCSRV *srv = NULL; - SMBCFILE *dir = NULL; - struct _smbc_callbacks *cb = NULL; - struct sockaddr_storage rem_ss; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - DEBUG(4, ("no valid context\n")); - errno = EINVAL + 8192; - TALLOC_FREE(frame); - return NULL; - - } - - if (!fname) { - DEBUG(4, ("no valid fname\n")); - errno = EINVAL + 8193; - TALLOC_FREE(frame); - return NULL; - } - - if (smbc_parse_path(frame, - context, - fname, - &workgroup, - &server, - &share, - &path, - &user, - &password, - &options)) { - DEBUG(4, ("no valid path\n")); - errno = EINVAL + 8194; - TALLOC_FREE(frame); - return NULL; - } - - DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' " - "path='%s' options='%s'\n", - fname, server, share, path, options)); - - /* Ensure the options are valid */ - if (smbc_check_options(server, share, path, options)) { - DEBUG(4, ("unacceptable options (%s)\n", options)); - errno = EINVAL + 8195; - TALLOC_FREE(frame); - return NULL; - } - - if (!user || user[0] == (char)0) { - user = talloc_strdup(frame, context->user); - if (!user) { - errno = ENOMEM; - TALLOC_FREE(frame); - return NULL; - } - } - - dir = SMB_MALLOC_P(SMBCFILE); - - if (!dir) { - errno = ENOMEM; - TALLOC_FREE(frame); - return NULL; - } - - ZERO_STRUCTP(dir); - - dir->cli_fd = 0; - dir->fname = SMB_STRDUP(fname); - dir->srv = NULL; - dir->offset = 0; - dir->file = False; - dir->dir_list = dir->dir_next = dir->dir_end = NULL; - - if (server[0] == (char)0) { - - int i; - int count; - int max_lmb_count; - struct ip_service *ip_list; - struct ip_service server_addr; - struct user_auth_info u_info; - - if (share[0] != (char)0 || path[0] != (char)0) { - - errno = EINVAL + 8196; - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - TALLOC_FREE(frame); - return NULL; - } - - /* Determine how many local master browsers to query */ - max_lmb_count = (context->options.browse_max_lmb_count == 0 - ? INT_MAX - : context->options.browse_max_lmb_count); - - memset(&u_info, '\0', sizeof(u_info)); - u_info.username = talloc_strdup(frame,user); - u_info.password = talloc_strdup(frame,password); - if (!u_info.username || !u_info.password) { - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - TALLOC_FREE(frame); - return NULL; - } - - /* - * We have server and share and path empty but options - * requesting that we scan all master browsers for their list - * of workgroups/domains. This implies that we must first try - * broadcast queries to find all master browsers, and if that - * doesn't work, then try our other methods which return only - * a single master browser. - */ - - ip_list = NULL; - if (!NT_STATUS_IS_OK(name_resolve_bcast(MSBROWSE, 1, &ip_list, - &count))) - { - - SAFE_FREE(ip_list); - - if (!find_master_ip(workgroup, &server_addr.ss)) { - - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - errno = ENOENT; - TALLOC_FREE(frame); - return NULL; - } - - ip_list = (struct ip_service *)memdup( - &server_addr, sizeof(server_addr)); - if (ip_list == NULL) { - errno = ENOMEM; - TALLOC_FREE(frame); - return NULL; - } - count = 1; - } - - for (i = 0; i < count && i < max_lmb_count; i++) { - char addr[INET6_ADDRSTRLEN]; - char *wg_ptr = NULL; - struct cli_state *cli = NULL; - - print_sockaddr(addr, sizeof(addr), &ip_list[i].ss); - DEBUG(99, ("Found master browser %d of %d: %s\n", - i+1, MAX(count, max_lmb_count), - addr)); - - cli = get_ipc_connect_master_ip(talloc_tos(), - &ip_list[i], - &u_info, - &wg_ptr); - /* cli == NULL is the master browser refused to talk or - could not be found */ - if (!cli) { - continue; - } - - workgroup = talloc_strdup(frame, wg_ptr); - server = talloc_strdup(frame, cli->desthost); - - cli_shutdown(cli); - - if (!workgroup || !server) { - errno = ENOMEM; - TALLOC_FREE(frame); - return NULL; - } - - DEBUG(4, ("using workgroup %s %s\n", - workgroup, server)); - - /* - * For each returned master browser IP address, get a - * connection to IPC$ on the server if we do not - * already have one, and determine the - * workgroups/domains that it knows about. - */ - - srv = smbc_server(frame, context, True, server, "IPC$", - &workgroup, &user, &password); - if (!srv) { - continue; - } - - dir->srv = srv; - dir->dir_type = SMBC_WORKGROUP; - - /* Now, list the stuff ... */ - - if (!cli_NetServerEnum(srv->cli, - workgroup, - SV_TYPE_DOMAIN_ENUM, - list_unique_wg_fn, - (void *)dir)) { - continue; - } - } - - SAFE_FREE(ip_list); - } else { - /* - * Server not an empty string ... Check the rest and see what - * gives - */ - if (*share == '\0') { - if (*path != '\0') { - - /* Should not have empty share with path */ - errno = EINVAL + 8197; - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - TALLOC_FREE(frame); - return NULL; - - } - - /* - * We don't know if <server> is really a server name - * or is a workgroup/domain name. If we already have - * a server structure for it, we'll use it. - * Otherwise, check to see if <server><1D>, - * <server><1B>, or <server><20> translates. We check - * to see if <server> is an IP address first. - */ - - /* - * See if we have an existing server. Do not - * establish a connection if one does not already - * exist. - */ - srv = smbc_server(frame, context, False, server, "IPC$", - &workgroup, &user, &password); - - /* - * If no existing server and not an IP addr, look for - * LMB or DMB - */ - if (!srv && - !is_ipaddress(server) && - (resolve_name(server, &rem_ss, 0x1d) || /* LMB */ - resolve_name(server, &rem_ss, 0x1b) )) { /* DMB */ - - fstring buserver; - - dir->dir_type = SMBC_SERVER; - - /* - * Get the backup list ... - */ - if (!name_status_find(server, 0, 0, - &rem_ss, buserver)) { - - DEBUG(0, ("Could not get name of " - "local/domain master browser " - "for server %s\n", server)); - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - errno = EPERM; - TALLOC_FREE(frame); - return NULL; - - } - - /* - * Get a connection to IPC$ on the server if - * we do not already have one - */ - srv = smbc_server(frame, context, True, - buserver, "IPC$", - &workgroup, &user, &password); - if (!srv) { - DEBUG(0, ("got no contact to IPC$\n")); - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - TALLOC_FREE(frame); - return NULL; - - } - - dir->srv = srv; - - /* Now, list the servers ... */ - if (!cli_NetServerEnum(srv->cli, server, - 0x0000FFFE, list_fn, - (void *)dir)) { - - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - TALLOC_FREE(frame); - return NULL; - } - } else if (srv || - (resolve_name(server, &rem_ss, 0x20))) { - - /* If we hadn't found the server, get one now */ - if (!srv) { - srv = smbc_server(frame, context, True, - server, "IPC$", - &workgroup, - &user, &password); - } - - if (!srv) { - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - TALLOC_FREE(frame); - return NULL; - - } - - dir->dir_type = SMBC_FILE_SHARE; - dir->srv = srv; - - /* List the shares ... */ - - if (net_share_enum_rpc( - srv->cli, - list_fn, - (void *) dir) < 0 && - cli_RNetShareEnum( - srv->cli, - list_fn, - (void *)dir) < 0) { - - errno = cli_errno(srv->cli); - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - TALLOC_FREE(frame); - return NULL; - - } - } else { - /* Neither the workgroup nor server exists */ - errno = ECONNREFUSED; - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - TALLOC_FREE(frame); - return NULL; - } - - } - else { - /* - * The server and share are specified ... work from - * there ... - */ - char *targetpath; - struct cli_state *targetcli; - - /* We connect to the server and list the directory */ - dir->dir_type = SMBC_FILE_SHARE; - - srv = smbc_server(frame, context, True, server, share, - &workgroup, &user, &password); - - if (!srv) { - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - TALLOC_FREE(frame); - return NULL; - } - - dir->srv = srv; - - /* Now, list the files ... */ - - p = path + strlen(path); - path = talloc_asprintf_append(path, "\\*"); - if (!path) { - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - TALLOC_FREE(frame); - return NULL; - } - - if (!cli_resolve_path(frame, "", srv->cli, path, - &targetcli, &targetpath)) { - d_printf("Could not resolve %s\n", path); - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - TALLOC_FREE(frame); - return NULL; - } - - if (cli_list(targetcli, targetpath, - aDIR | aSYSTEM | aHIDDEN, - dir_list_fn, (void *)dir) < 0) { - - if (dir) { - SAFE_FREE(dir->fname); - SAFE_FREE(dir); - } - saved_errno = smbc_errno(context, targetcli); - - if (saved_errno == EINVAL) { - /* - * See if they asked to opendir something - * other than a directory. If so, the - * converted error value we got would have - * been EINVAL rather than ENOTDIR. - */ - *p = '\0'; /* restore original path */ - - if (smbc_getatr(context, srv, path, - &mode, NULL, - NULL, NULL, NULL, NULL, - NULL) && - ! IS_DOS_DIR(mode)) { - - /* It is. Correct the error value */ - saved_errno = ENOTDIR; - } - } - - /* - * If there was an error and the server is no - * good any more... - */ - cb = &context->callbacks; - if (cli_is_error(targetcli) && - (cb->check_server_fn)(context, srv)) { - - /* ... then remove it. */ - if ((cb->remove_unused_server_fn)(context, - srv)) { - /* - * We could not remove the - * server completely, remove - * it from the cache so we - * will not get it again. It - * will be removed when the - * last file/dir is closed. - */ - (cb->remove_cached_srv_fn)(context, - srv); - } - } - - errno = saved_errno; - TALLOC_FREE(frame); - return NULL; - } - } - - } - - DLIST_ADD(context->internal->_files, dir); - TALLOC_FREE(frame); - return dir; - -} - -/* - * Routine to close a directory - */ - -static int -smbc_closedir_ctx(SMBCCTX *context, - SMBCFILE *dir) -{ - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) { - errno = EBADF; - TALLOC_FREE(frame); - return -1; - } - - smbc_remove_dir(dir); /* Clean it up */ - - DLIST_REMOVE(context->internal->_files, dir); - - if (dir) { - - SAFE_FREE(dir->fname); - SAFE_FREE(dir); /* Free the space too */ - } - - TALLOC_FREE(frame); - return 0; - -} - -static void -smbc_readdir_internal(SMBCCTX * context, - struct smbc_dirent *dest, - struct smbc_dirent *src, - int max_namebuf_len) -{ - if (context->options.urlencode_readdir_entries) { - - /* url-encode the name. get back remaining buffer space */ - max_namebuf_len = - smbc_urlencode(dest->name, src->name, max_namebuf_len); - - /* We now know the name length */ - dest->namelen = strlen(dest->name); - - /* Save the pointer to the beginning of the comment */ - dest->comment = dest->name + dest->namelen + 1; - - /* Copy the comment */ - strncpy(dest->comment, src->comment, max_namebuf_len - 1); - dest->comment[max_namebuf_len - 1] = '\0'; - - /* Save other fields */ - dest->smbc_type = src->smbc_type; - dest->commentlen = strlen(dest->comment); - dest->dirlen = ((dest->comment + dest->commentlen + 1) - - (char *) dest); - } else { - - /* No encoding. Just copy the entry as is. */ - memcpy(dest, src, src->dirlen); - dest->comment = (char *)(&dest->name + src->namelen + 1); - } - -} - -/* - * Routine to get a directory entry - */ - -struct smbc_dirent * -smbc_readdir_ctx(SMBCCTX *context, - SMBCFILE *dir) -{ - int maxlen; - struct smbc_dirent *dirp, *dirent; - TALLOC_CTX *frame = talloc_stackframe(); - - /* Check that all is ok first ... */ - - if (!context || !context->internal || - !context->internal->_initialized) { - - errno = EINVAL; - DEBUG(0, ("Invalid context in smbc_readdir_ctx()\n")); - TALLOC_FREE(frame); - return NULL; - - } - - if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) { - - errno = EBADF; - DEBUG(0, ("Invalid dir in smbc_readdir_ctx()\n")); - TALLOC_FREE(frame); - return NULL; - - } - - if (dir->file != False) { /* FIXME, should be dir, perhaps */ - - errno = ENOTDIR; - DEBUG(0, ("Found file vs directory in smbc_readdir_ctx()\n")); - TALLOC_FREE(frame); - return NULL; - - } - - if (!dir->dir_next) { - TALLOC_FREE(frame); - return NULL; - } - - dirent = dir->dir_next->dirent; - if (!dirent) { - - errno = ENOENT; - TALLOC_FREE(frame); - return NULL; - - } - - dirp = (struct smbc_dirent *)context->internal->_dirent; - maxlen = (sizeof(context->internal->_dirent) - - sizeof(struct smbc_dirent)); - - smbc_readdir_internal(context, dirp, dirent, maxlen); - - dir->dir_next = dir->dir_next->next; - - TALLOC_FREE(frame); - return dirp; -} - -/* - * Routine to get directory entries - */ - -static int -smbc_getdents_ctx(SMBCCTX *context, - SMBCFILE *dir, - struct smbc_dirent *dirp, - int count) -{ - int rem = count; - int reqd; - int maxlen; - char *ndir = (char *)dirp; - struct smbc_dir_list *dirlist; - TALLOC_CTX *frame = talloc_stackframe(); - - /* Check that all is ok first ... */ - - if (!context || !context->internal || - !context->internal->_initialized) { - - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - - } - - if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) { - - errno = EBADF; - TALLOC_FREE(frame); - return -1; - - } - - if (dir->file != False) { /* FIXME, should be dir, perhaps */ - - errno = ENOTDIR; - TALLOC_FREE(frame); - return -1; - - } - - /* - * Now, retrieve the number of entries that will fit in what was passed - * We have to figure out if the info is in the list, or we need to - * send a request to the server to get the info. - */ - - while ((dirlist = dir->dir_next)) { - struct smbc_dirent *dirent; - - if (!dirlist->dirent) { - - errno = ENOENT; /* Bad error */ - TALLOC_FREE(frame); - return -1; - - } - - /* Do urlencoding of next entry, if so selected */ - dirent = (struct smbc_dirent *)context->internal->_dirent; - maxlen = (sizeof(context->internal->_dirent) - - sizeof(struct smbc_dirent)); - smbc_readdir_internal(context, dirent, dirlist->dirent, maxlen); - - reqd = dirent->dirlen; - - if (rem < reqd) { - - if (rem < count) { /* We managed to copy something */ - - errno = 0; - TALLOC_FREE(frame); - return count - rem; - - } - else { /* Nothing copied ... */ - - errno = EINVAL; /* Not enough space ... */ - TALLOC_FREE(frame); - return -1; - - } - - } - - memcpy(ndir, dirent, reqd); /* Copy the data in ... */ - - ((struct smbc_dirent *)ndir)->comment = - (char *)(&((struct smbc_dirent *)ndir)->name + - dirent->namelen + - 1); - - ndir += reqd; - - rem -= reqd; - - dir->dir_next = dirlist = dirlist -> next; - } - - TALLOC_FREE(frame); - - if (rem == count) - return 0; - else - return count - rem; - -} - -/* - * Routine to create a directory ... - */ - -static int -smbc_mkdir_ctx(SMBCCTX *context, - const char *fname, - mode_t mode) -{ - SMBCSRV *srv = NULL; - char *server = NULL; - char *share = NULL; - char *user = NULL; - char *password = NULL; - char *workgroup = NULL; - char *path = NULL; - char *targetpath = NULL; - struct cli_state *targetcli = NULL; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!fname) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - DEBUG(4, ("smbc_mkdir(%s)\n", fname)); - - if (smbc_parse_path(frame, - context, - fname, - &workgroup, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!user || user[0] == (char)0) { - user = talloc_strdup(frame, context->user); - if (!user) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - } - - srv = smbc_server(frame, context, True, - server, share, &workgroup, &user, &password); - - if (!srv) { - - TALLOC_FREE(frame); - return -1; /* errno set by smbc_server */ - - } - - /*d_printf(">>>mkdir: resolving %s\n", path);*/ - if (!cli_resolve_path(frame, "", srv->cli, path, - &targetcli, &targetpath)) { - d_printf("Could not resolve %s\n", path); - TALLOC_FREE(frame); - return -1; - } - /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/ - - if (!cli_mkdir(targetcli, targetpath)) { - - errno = smbc_errno(context, targetcli); - TALLOC_FREE(frame); - return -1; - - } - - TALLOC_FREE(frame); - return 0; - -} - -/* - * Our list function simply checks to see if a directory is not empty - */ - -static int smbc_rmdir_dirempty = True; - -static void -rmdir_list_fn(const char *mnt, - file_info *finfo, - const char *mask, - void *state) -{ - if (strncmp(finfo->name, ".", 1) != 0 && - strncmp(finfo->name, "..", 2) != 0) { - smbc_rmdir_dirempty = False; - } -} - -/* - * Routine to remove a directory - */ - -static int -smbc_rmdir_ctx(SMBCCTX *context, - const char *fname) -{ - SMBCSRV *srv = NULL; - char *server = NULL; - char *share = NULL; - char *user = NULL; - char *password = NULL; - char *workgroup = NULL; - char *path = NULL; - char *targetpath = NULL; - struct cli_state *targetcli = NULL; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!fname) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - DEBUG(4, ("smbc_rmdir(%s)\n", fname)); - - if (smbc_parse_path(frame, - context, - fname, - &workgroup, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!user || user[0] == (char)0) { - user = talloc_strdup(frame, context->user); - if (!user) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - } - - srv = smbc_server(frame, context, True, - server, share, &workgroup, &user, &password); - - if (!srv) { - - TALLOC_FREE(frame); - return -1; /* errno set by smbc_server */ - - } - - /*d_printf(">>>rmdir: resolving %s\n", path);*/ - if (!cli_resolve_path(frame, "", srv->cli, path, - &targetcli, &targetpath)) { - d_printf("Could not resolve %s\n", path); - TALLOC_FREE(frame); - return -1; - } - /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/ - - - if (!cli_rmdir(targetcli, targetpath)) { - - errno = smbc_errno(context, targetcli); - - if (errno == EACCES) { /* Check if the dir empty or not */ - - /* Local storage to avoid buffer overflows */ - char *lpath; - - smbc_rmdir_dirempty = True; /* Make this so ... */ - - lpath = talloc_asprintf(frame, "%s\\*", - targetpath); - if (!lpath) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - - if (cli_list(targetcli, lpath, - aDIR | aSYSTEM | aHIDDEN, - rmdir_list_fn, NULL) < 0) { - - /* Fix errno to ignore latest error ... */ - DEBUG(5, ("smbc_rmdir: " - "cli_list returned an error: %d\n", - smbc_errno(context, targetcli))); - errno = EACCES; - - } - - if (smbc_rmdir_dirempty) - errno = EACCES; - else - errno = ENOTEMPTY; - - } - - TALLOC_FREE(frame); - return -1; - - } - - TALLOC_FREE(frame); - return 0; - -} - -/* - * Routine to return the current directory position - */ - -static off_t -smbc_telldir_ctx(SMBCCTX *context, - SMBCFILE *dir) -{ - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - - } - - if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) { - - errno = EBADF; - TALLOC_FREE(frame); - return -1; - - } - - if (dir->file != False) { /* FIXME, should be dir, perhaps */ - - errno = ENOTDIR; - TALLOC_FREE(frame); - return -1; - - } - - /* See if we're already at the end. */ - if (dir->dir_next == NULL) { - /* We are. */ - TALLOC_FREE(frame); - return -1; - } - - /* - * We return the pointer here as the offset - */ - TALLOC_FREE(frame); - return (off_t)(long)dir->dir_next->dirent; -} - -/* - * A routine to run down the list and see if the entry is OK - */ - -struct smbc_dir_list * -smbc_check_dir_ent(struct smbc_dir_list *list, - struct smbc_dirent *dirent) -{ - - /* Run down the list looking for what we want */ - - if (dirent) { - - struct smbc_dir_list *tmp = list; - - while (tmp) { - - if (tmp->dirent == dirent) - return tmp; - - tmp = tmp->next; - - } - - } - - return NULL; /* Not found, or an error */ - -} - - -/* - * Routine to seek on a directory - */ - -static int -smbc_lseekdir_ctx(SMBCCTX *context, - SMBCFILE *dir, - off_t offset) -{ - long int l_offset = offset; /* Handle problems of size */ - struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset; - struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - - } - - if (dir->file != False) { /* FIXME, should be dir, perhaps */ - - errno = ENOTDIR; - TALLOC_FREE(frame); - return -1; - - } - - /* Now, check what we were passed and see if it is OK ... */ - - if (dirent == NULL) { /* Seek to the begining of the list */ - - dir->dir_next = dir->dir_list; - TALLOC_FREE(frame); - return 0; - - } - - if (offset == -1) { /* Seek to the end of the list */ - dir->dir_next = NULL; - TALLOC_FREE(frame); - return 0; - } - - /* Now, run down the list and make sure that the entry is OK */ - /* This may need to be changed if we change the format of the list */ - - if ((list_ent = smbc_check_dir_ent(dir->dir_list, dirent)) == NULL) { - errno = EINVAL; /* Bad entry */ - TALLOC_FREE(frame); - return -1; - } - - dir->dir_next = list_ent; - - TALLOC_FREE(frame); - return 0; -} - -/* - * Routine to fstat a dir - */ - -static int -smbc_fstatdir_ctx(SMBCCTX *context, - SMBCFILE *dir, - struct stat *st) -{ - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; - return -1; - } - - /* No code yet ... */ - return 0; -} - -static int -smbc_chmod_ctx(SMBCCTX *context, - const char *fname, - mode_t newmode) -{ - SMBCSRV *srv = NULL; - char *server = NULL; - char *share = NULL; - char *user = NULL; - char *password = NULL; - char *workgroup = NULL; - char *path = NULL; - uint16 mode; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; /* Best I can think of ... */ - TALLOC_FREE(frame); - return -1; - } - - if (!fname) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode)); - - if (smbc_parse_path(frame, - context, - fname, - &workgroup, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!user || user[0] == (char)0) { - user = talloc_strdup(frame, context->user); - if (!user) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - } - - srv = smbc_server(frame, context, True, - server, share, &workgroup, &user, &password); - - if (!srv) { - TALLOC_FREE(frame); - return -1; /* errno set by smbc_server */ - } - - mode = 0; - - if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY; - if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH; - if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM; - if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN; - - if (!cli_setatr(srv->cli, path, mode, 0)) { - errno = smbc_errno(context, srv->cli); - TALLOC_FREE(frame); - return -1; - } - - TALLOC_FREE(frame); - return 0; -} - -static int -smbc_utimes_ctx(SMBCCTX *context, - const char *fname, - struct timeval *tbuf) -{ - SMBCSRV *srv = NULL; - char *server = NULL; - char *share = NULL; - char *user = NULL; - char *password = NULL; - char *workgroup = NULL; - char *path = NULL; - time_t access_time; - time_t write_time; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; /* Best I can think of ... */ - TALLOC_FREE(frame); - return -1; - } - - if (!fname) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (tbuf == NULL) { - access_time = write_time = time(NULL); - } else { - access_time = tbuf[0].tv_sec; - write_time = tbuf[1].tv_sec; - } - - if (DEBUGLVL(4)) { - char *p; - char atimebuf[32]; - char mtimebuf[32]; - - strncpy(atimebuf, ctime(&access_time), sizeof(atimebuf) - 1); - atimebuf[sizeof(atimebuf) - 1] = '\0'; - if ((p = strchr(atimebuf, '\n')) != NULL) { - *p = '\0'; - } - - strncpy(mtimebuf, ctime(&write_time), sizeof(mtimebuf) - 1); - mtimebuf[sizeof(mtimebuf) - 1] = '\0'; - if ((p = strchr(mtimebuf, '\n')) != NULL) { - *p = '\0'; - } - - dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n", - fname, atimebuf, mtimebuf); - } - - if (smbc_parse_path(frame, - context, - fname, - &workgroup, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!user || user[0] == (char)0) { - user = talloc_strdup(frame, context->user); - if (!user) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - } - - srv = smbc_server(frame, context, True, - server, share, &workgroup, &user, &password); - - if (!srv) { - TALLOC_FREE(frame); - return -1; /* errno set by smbc_server */ - } - - if (!smbc_setatr(context, srv, path, - 0, access_time, write_time, 0, 0)) { - TALLOC_FREE(frame); - return -1; /* errno set by smbc_setatr */ - } - - TALLOC_FREE(frame); - return 0; -} - - -/* - * Sort ACEs according to the documentation at - * http://support.microsoft.com/kb/269175, at least as far as it defines the - * order. - */ - -static int -ace_compare(SEC_ACE *ace1, - SEC_ACE *ace2) -{ - bool b1; - bool b2; - - /* If the ACEs are equal, we have nothing more to do. */ - if (sec_ace_equal(ace1, ace2)) { - return 0; - } - - /* Inherited follow non-inherited */ - b1 = ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0); - b2 = ((ace2->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0); - if (b1 != b2) { - return (b1 ? 1 : -1); - } - - /* - * What shall we do with AUDITs and ALARMs? It's undefined. We'll - * sort them after DENY and ALLOW. - */ - b1 = (ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED && - ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT && - ace1->type != SEC_ACE_TYPE_ACCESS_DENIED && - ace1->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT); - b2 = (ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED && - ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT && - ace2->type != SEC_ACE_TYPE_ACCESS_DENIED && - ace2->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT); - if (b1 != b2) { - return (b1 ? 1 : -1); - } - - /* Allowed ACEs follow denied ACEs */ - b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED || - ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT); - b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED || - ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT); - if (b1 != b2) { - return (b1 ? 1 : -1); - } - - /* - * ACEs applying to an entity's object follow those applying to the - * entity itself - */ - b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT || - ace1->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT); - b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT || - ace2->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT); - if (b1 != b2) { - return (b1 ? 1 : -1); - } - - /* - * If we get this far, the ACEs are similar as far as the - * characteristics we typically care about (those defined by the - * referenced MS document). We'll now sort by characteristics that - * just seems reasonable. - */ - - if (ace1->type != ace2->type) { - return ace2->type - ace1->type; - } - - if (sid_compare(&ace1->trustee, &ace2->trustee)) { - return sid_compare(&ace1->trustee, &ace2->trustee); - } - - if (ace1->flags != ace2->flags) { - return ace1->flags - ace2->flags; - } - - if (ace1->access_mask != ace2->access_mask) { - return ace1->access_mask - ace2->access_mask; - } - - if (ace1->size != ace2->size) { - return ace1->size - ace2->size; - } - - return memcmp(ace1, ace2, sizeof(SEC_ACE)); -} - - -static void -sort_acl(SEC_ACL *the_acl) -{ - uint32 i; - if (!the_acl) return; - - qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]), - QSORT_CAST ace_compare); - - for (i=1;i<the_acl->num_aces;) { - if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) { - int j; - for (j=i; j<the_acl->num_aces-1; j++) { - the_acl->aces[j] = the_acl->aces[j+1]; - } - the_acl->num_aces--; - } else { - i++; - } - } -} - -/* convert a SID to a string, either numeric or username/group */ -static void -convert_sid_to_string(struct cli_state *ipc_cli, - POLICY_HND *pol, - fstring str, - bool numeric, - DOM_SID *sid) -{ - char **domains = NULL; - char **names = NULL; - enum lsa_SidType *types = NULL; - struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli); - TALLOC_CTX *ctx; - - sid_to_fstring(str, sid); - - if (numeric) { - return; /* no lookup desired */ - } - - if (!pipe_hnd) { - return; - } - - /* Ask LSA to convert the sid to a name */ - - ctx = talloc_stackframe(); - - if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ctx, - pol, 1, sid, &domains, - &names, &types)) || - !domains || !domains[0] || !names || !names[0]) { - TALLOC_FREE(ctx); - return; - } - - TALLOC_FREE(ctx); - /* Converted OK */ - - slprintf(str, sizeof(fstring) - 1, "%s%s%s", - domains[0], lp_winbind_separator(), - names[0]); -} - -/* convert a string to a SID, either numeric or username/group */ -static bool -convert_string_to_sid(struct cli_state *ipc_cli, - POLICY_HND *pol, - bool numeric, - DOM_SID *sid, - const char *str) -{ - enum lsa_SidType *types = NULL; - DOM_SID *sids = NULL; - bool result = True; - TALLOC_CTX *ctx = NULL; - struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli); - - if (!pipe_hnd) { - return False; - } - - if (numeric) { - if (strncmp(str, "S-", 2) == 0) { - return string_to_sid(sid, str); - } - - result = False; - goto done; - } - - ctx = talloc_stackframe(); - if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ctx, - pol, 1, &str, NULL, 1, &sids, - &types))) { - result = False; - goto done; - } - - sid_copy(sid, &sids[0]); - done: - - TALLOC_FREE(ctx); - return result; -} - - -/* parse an ACE in the same format as print_ace() */ -static bool -parse_ace(struct cli_state *ipc_cli, - POLICY_HND *pol, - SEC_ACE *ace, - bool numeric, - char *str) -{ - char *p; - const char *cp; - char *tok; - unsigned int atype; - unsigned int aflags; - unsigned int amask; - DOM_SID sid; - SEC_ACCESS mask; - const struct perm_value *v; - struct perm_value { - const char *perm; - uint32 mask; - }; - TALLOC_CTX *frame = talloc_stackframe(); - - /* These values discovered by inspection */ - static const struct perm_value special_values[] = { - { "R", 0x00120089 }, - { "W", 0x00120116 }, - { "X", 0x001200a0 }, - { "D", 0x00010000 }, - { "P", 0x00040000 }, - { "O", 0x00080000 }, - { NULL, 0 }, - }; - - static const struct perm_value standard_values[] = { - { "READ", 0x001200a9 }, - { "CHANGE", 0x001301bf }, - { "FULL", 0x001f01ff }, - { NULL, 0 }, - }; - - - ZERO_STRUCTP(ace); - p = strchr_m(str,':'); - if (!p) { - TALLOC_FREE(frame); - return False; - } - *p = '\0'; - p++; - /* Try to parse numeric form */ - - if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 && - convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) { - goto done; - } - - /* Try to parse text form */ - - if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) { - TALLOC_FREE(frame); - return false; - } - - cp = p; - if (!next_token_talloc(frame, &cp, &tok, "/")) { - TALLOC_FREE(frame); - return false; - } - - if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) { - atype = SEC_ACE_TYPE_ACCESS_ALLOWED; - } else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) { - atype = SEC_ACE_TYPE_ACCESS_DENIED; - } else { - TALLOC_FREE(frame); - return false; - } - - /* Only numeric form accepted for flags at present */ - - if (!(next_token_talloc(frame, &cp, &tok, "/") && - sscanf(tok, "%i", &aflags))) { - TALLOC_FREE(frame); - return false; - } - - if (!next_token_talloc(frame, &cp, &tok, "/")) { - TALLOC_FREE(frame); - return false; - } - - if (strncmp(tok, "0x", 2) == 0) { - if (sscanf(tok, "%i", &amask) != 1) { - TALLOC_FREE(frame); - return false; - } - goto done; - } - - for (v = standard_values; v->perm; v++) { - if (strcmp(tok, v->perm) == 0) { - amask = v->mask; - goto done; - } - } - - p = tok; - - while(*p) { - bool found = False; - - for (v = special_values; v->perm; v++) { - if (v->perm[0] == *p) { - amask |= v->mask; - found = True; - } - } - - if (!found) { - TALLOC_FREE(frame); - return false; - } - p++; - } - - if (*p) { - TALLOC_FREE(frame); - return false; - } - - done: - mask = amask; - init_sec_ace(ace, &sid, atype, mask, aflags); - TALLOC_FREE(frame); - return true; -} - -/* add an ACE to a list of ACEs in a SEC_ACL */ -static bool -add_ace(SEC_ACL **the_acl, - SEC_ACE *ace, - TALLOC_CTX *ctx) -{ - SEC_ACL *newacl; - SEC_ACE *aces; - - if (! *the_acl) { - (*the_acl) = make_sec_acl(ctx, 3, 1, ace); - return True; - } - - if ((aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces)) == NULL) { - return False; - } - memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE)); - memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE)); - newacl = make_sec_acl(ctx, (*the_acl)->revision, - 1+(*the_acl)->num_aces, aces); - SAFE_FREE(aces); - (*the_acl) = newacl; - return True; -} - - -/* parse a ascii version of a security descriptor */ -static SEC_DESC * -sec_desc_parse(TALLOC_CTX *ctx, - struct cli_state *ipc_cli, - POLICY_HND *pol, - bool numeric, - char *str) -{ - const char *p = str; - char *tok; - SEC_DESC *ret = NULL; - size_t sd_size; - DOM_SID *group_sid=NULL; - DOM_SID *owner_sid=NULL; - SEC_ACL *dacl=NULL; - int revision=1; - - while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) { - - if (StrnCaseCmp(tok,"REVISION:", 9) == 0) { - revision = strtol(tok+9, NULL, 16); - continue; - } - - if (StrnCaseCmp(tok,"OWNER:", 6) == 0) { - if (owner_sid) { - DEBUG(5, ("OWNER specified more than once!\n")); - goto done; - } - owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1); - if (!owner_sid || - !convert_string_to_sid(ipc_cli, pol, - numeric, - owner_sid, tok+6)) { - DEBUG(5, ("Failed to parse owner sid\n")); - goto done; - } - continue; - } - - if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) { - if (owner_sid) { - DEBUG(5, ("OWNER specified more than once!\n")); - goto done; - } - owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1); - if (!owner_sid || - !convert_string_to_sid(ipc_cli, pol, - False, - owner_sid, tok+7)) { - DEBUG(5, ("Failed to parse owner sid\n")); - goto done; - } - continue; - } - - if (StrnCaseCmp(tok,"GROUP:", 6) == 0) { - if (group_sid) { - DEBUG(5, ("GROUP specified more than once!\n")); - goto done; - } - group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1); - if (!group_sid || - !convert_string_to_sid(ipc_cli, pol, - numeric, - group_sid, tok+6)) { - DEBUG(5, ("Failed to parse group sid\n")); - goto done; - } - continue; - } - - if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) { - if (group_sid) { - DEBUG(5, ("GROUP specified more than once!\n")); - goto done; - } - group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1); - if (!group_sid || - !convert_string_to_sid(ipc_cli, pol, - False, - group_sid, tok+6)) { - DEBUG(5, ("Failed to parse group sid\n")); - goto done; - } - continue; - } - - if (StrnCaseCmp(tok,"ACL:", 4) == 0) { - SEC_ACE ace; - if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) { - DEBUG(5, ("Failed to parse ACL %s\n", tok)); - goto done; - } - if(!add_ace(&dacl, &ace, ctx)) { - DEBUG(5, ("Failed to add ACL %s\n", tok)); - goto done; - } - continue; - } - - if (StrnCaseCmp(tok,"ACL+:", 5) == 0) { - SEC_ACE ace; - if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) { - DEBUG(5, ("Failed to parse ACL %s\n", tok)); - goto done; - } - if(!add_ace(&dacl, &ace, ctx)) { - DEBUG(5, ("Failed to add ACL %s\n", tok)); - goto done; - } - continue; - } - - DEBUG(5, ("Failed to parse security descriptor\n")); - goto done; - } - - ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE, - owner_sid, group_sid, NULL, dacl, &sd_size); - - done: - SAFE_FREE(group_sid); - SAFE_FREE(owner_sid); - - return ret; -} - - -/* Obtain the current dos attributes */ -static DOS_ATTR_DESC * -dos_attr_query(SMBCCTX *context, - TALLOC_CTX *ctx, - const char *filename, - SMBCSRV *srv) -{ - struct timespec create_time_ts; - struct timespec write_time_ts; - struct timespec access_time_ts; - struct timespec change_time_ts; - SMB_OFF_T size = 0; - uint16 mode = 0; - SMB_INO_T inode = 0; - DOS_ATTR_DESC *ret; - - ret = TALLOC_P(ctx, DOS_ATTR_DESC); - if (!ret) { - errno = ENOMEM; - return NULL; - } - - /* Obtain the DOS attributes */ - if (!smbc_getatr(context, srv, CONST_DISCARD(char *, filename), - &mode, &size, - &create_time_ts, - &access_time_ts, - &write_time_ts, - &change_time_ts, - &inode)) { - errno = smbc_errno(context, srv->cli); - DEBUG(5, ("dos_attr_query Failed to query old attributes\n")); - return NULL; - } - - ret->mode = mode; - ret->size = size; - ret->create_time = convert_timespec_to_time_t(create_time_ts); - ret->access_time = convert_timespec_to_time_t(access_time_ts); - ret->write_time = convert_timespec_to_time_t(write_time_ts); - ret->change_time = convert_timespec_to_time_t(change_time_ts); - ret->inode = inode; - - return ret; -} - - -/* parse a ascii version of a security descriptor */ -static void -dos_attr_parse(SMBCCTX *context, - DOS_ATTR_DESC *dad, - SMBCSRV *srv, - char *str) -{ - int n; - const char *p = str; - char *tok = NULL; - TALLOC_CTX *frame = NULL; - struct { - const char * create_time_attr; - const char * access_time_attr; - const char * write_time_attr; - const char * change_time_attr; - } attr_strings; - - /* Determine whether to use old-style or new-style attribute names */ - if (context->internal->_full_time_names) { - /* new-style names */ - attr_strings.create_time_attr = "CREATE_TIME"; - attr_strings.access_time_attr = "ACCESS_TIME"; - attr_strings.write_time_attr = "WRITE_TIME"; - attr_strings.change_time_attr = "CHANGE_TIME"; - } else { - /* old-style names */ - attr_strings.create_time_attr = NULL; - attr_strings.access_time_attr = "A_TIME"; - attr_strings.write_time_attr = "M_TIME"; - attr_strings.change_time_attr = "C_TIME"; - } - - /* if this is to set the entire ACL... */ - if (*str == '*') { - /* ... then increment past the first colon if there is one */ - if ((p = strchr(str, ':')) != NULL) { - ++p; - } else { - p = str; - } - } - - frame = talloc_stackframe(); - while (next_token_talloc(frame, &p, &tok, "\t,\r\n")) { - if (StrnCaseCmp(tok, "MODE:", 5) == 0) { - long request = strtol(tok+5, NULL, 16); - if (request == 0) { - dad->mode = (request | - (IS_DOS_DIR(dad->mode) - ? FILE_ATTRIBUTE_DIRECTORY - : FILE_ATTRIBUTE_NORMAL)); - } else { - dad->mode = request; - } - continue; - } - - if (StrnCaseCmp(tok, "SIZE:", 5) == 0) { - dad->size = (SMB_OFF_T)atof(tok+5); - continue; - } - - n = strlen(attr_strings.access_time_attr); - if (StrnCaseCmp(tok, attr_strings.access_time_attr, n) == 0) { - dad->access_time = (time_t)strtol(tok+n+1, NULL, 10); - continue; - } - - n = strlen(attr_strings.change_time_attr); - if (StrnCaseCmp(tok, attr_strings.change_time_attr, n) == 0) { - dad->change_time = (time_t)strtol(tok+n+1, NULL, 10); - continue; - } - - n = strlen(attr_strings.write_time_attr); - if (StrnCaseCmp(tok, attr_strings.write_time_attr, n) == 0) { - dad->write_time = (time_t)strtol(tok+n+1, NULL, 10); - continue; - } - - if (attr_strings.create_time_attr != NULL) { - n = strlen(attr_strings.create_time_attr); - if (StrnCaseCmp(tok, attr_strings.create_time_attr, - n) == 0) { - dad->create_time = (time_t)strtol(tok+n+1, - NULL, 10); - continue; - } - } - - if (StrnCaseCmp(tok, "INODE:", 6) == 0) { - dad->inode = (SMB_INO_T)atof(tok+6); - continue; - } - } - TALLOC_FREE(frame); -} - -/***************************************************** - Retrieve the acls for a file. -*******************************************************/ - -static int -cacl_get(SMBCCTX *context, - TALLOC_CTX *ctx, - SMBCSRV *srv, - struct cli_state *ipc_cli, - POLICY_HND *pol, - char *filename, - char *attr_name, - char *buf, - int bufsize) -{ - uint32 i; - int n = 0; - int n_used; - bool all; - bool all_nt; - bool all_nt_acls; - bool all_dos; - bool some_nt; - bool some_dos; - bool exclude_nt_revision = False; - bool exclude_nt_owner = False; - bool exclude_nt_group = False; - bool exclude_nt_acl = False; - bool exclude_dos_mode = False; - bool exclude_dos_size = False; - bool exclude_dos_create_time = False; - bool exclude_dos_access_time = False; - bool exclude_dos_write_time = False; - bool exclude_dos_change_time = False; - bool exclude_dos_inode = False; - bool numeric = True; - bool determine_size = (bufsize == 0); - int fnum = -1; - SEC_DESC *sd; - fstring sidstr; - fstring name_sandbox; - char *name; - char *pExclude; - char *p; - struct timespec create_time_ts; - struct timespec write_time_ts; - struct timespec access_time_ts; - struct timespec change_time_ts; - time_t create_time = (time_t)0; - time_t write_time = (time_t)0; - time_t access_time = (time_t)0; - time_t change_time = (time_t)0; - SMB_OFF_T size = 0; - uint16 mode = 0; - SMB_INO_T ino = 0; - struct cli_state *cli = srv->cli; - struct { - const char * create_time_attr; - const char * access_time_attr; - const char * write_time_attr; - const char * change_time_attr; - } attr_strings; - struct { - const char * create_time_attr; - const char * access_time_attr; - const char * write_time_attr; - const char * change_time_attr; - } excl_attr_strings; - - /* Determine whether to use old-style or new-style attribute names */ - if (context->internal->_full_time_names) { - /* new-style names */ - attr_strings.create_time_attr = "CREATE_TIME"; - attr_strings.access_time_attr = "ACCESS_TIME"; - attr_strings.write_time_attr = "WRITE_TIME"; - attr_strings.change_time_attr = "CHANGE_TIME"; - - excl_attr_strings.create_time_attr = "CREATE_TIME"; - excl_attr_strings.access_time_attr = "ACCESS_TIME"; - excl_attr_strings.write_time_attr = "WRITE_TIME"; - excl_attr_strings.change_time_attr = "CHANGE_TIME"; - } else { - /* old-style names */ - attr_strings.create_time_attr = NULL; - attr_strings.access_time_attr = "A_TIME"; - attr_strings.write_time_attr = "M_TIME"; - attr_strings.change_time_attr = "C_TIME"; - - excl_attr_strings.create_time_attr = NULL; - excl_attr_strings.access_time_attr = "dos_attr.A_TIME"; - excl_attr_strings.write_time_attr = "dos_attr.M_TIME"; - excl_attr_strings.change_time_attr = "dos_attr.C_TIME"; - } - - /* Copy name so we can strip off exclusions (if any are specified) */ - strncpy(name_sandbox, attr_name, sizeof(name_sandbox) - 1); - - /* Ensure name is null terminated */ - name_sandbox[sizeof(name_sandbox) - 1] = '\0'; - - /* Play in the sandbox */ - name = name_sandbox; - - /* If there are any exclusions, point to them and mask them from name */ - if ((pExclude = strchr(name, '!')) != NULL) - { - *pExclude++ = '\0'; - } - - all = (StrnCaseCmp(name, "system.*", 8) == 0); - all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0); - all_nt_acls = (StrnCaseCmp(name, "system.nt_sec_desc.acl.*", 24) == 0); - all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0); - some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0); - some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0); - numeric = (* (name + strlen(name) - 1) != '+'); - - /* Look for exclusions from "all" requests */ - if (all || all_nt || all_dos) { - - /* Exclusions are delimited by '!' */ - for (; - pExclude != NULL; - pExclude = (p == NULL ? NULL : p + 1)) { - - /* Find end of this exclusion name */ - if ((p = strchr(pExclude, '!')) != NULL) - { - *p = '\0'; - } - - /* Which exclusion name is this? */ - if (StrCaseCmp(pExclude, "nt_sec_desc.revision") == 0) { - exclude_nt_revision = True; - } - else if (StrCaseCmp(pExclude, "nt_sec_desc.owner") == 0) { - exclude_nt_owner = True; - } - else if (StrCaseCmp(pExclude, "nt_sec_desc.group") == 0) { - exclude_nt_group = True; - } - else if (StrCaseCmp(pExclude, "nt_sec_desc.acl") == 0) { - exclude_nt_acl = True; - } - else if (StrCaseCmp(pExclude, "dos_attr.mode") == 0) { - exclude_dos_mode = True; - } - else if (StrCaseCmp(pExclude, "dos_attr.size") == 0) { - exclude_dos_size = True; - } - else if (excl_attr_strings.create_time_attr != NULL && - StrCaseCmp(pExclude, - excl_attr_strings.change_time_attr) == 0) { - exclude_dos_create_time = True; - } - else if (StrCaseCmp(pExclude, - excl_attr_strings.access_time_attr) == 0) { - exclude_dos_access_time = True; - } - else if (StrCaseCmp(pExclude, - excl_attr_strings.write_time_attr) == 0) { - exclude_dos_write_time = True; - } - else if (StrCaseCmp(pExclude, - excl_attr_strings.change_time_attr) == 0) { - exclude_dos_change_time = True; - } - else if (StrCaseCmp(pExclude, "dos_attr.inode") == 0) { - exclude_dos_inode = True; - } - else { - DEBUG(5, ("cacl_get received unknown exclusion: %s\n", - pExclude)); - errno = ENOATTR; - return -1; - } - } - } - - n_used = 0; - - /* - * If we are (possibly) talking to an NT or new system and some NT - * attributes have been requested... - */ - if (ipc_cli && (all || some_nt || all_nt_acls)) { - /* Point to the portion after "system.nt_sec_desc." */ - name += 19; /* if (all) this will be invalid but unused */ - - /* ... then obtain any NT attributes which were requested */ - fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ); - - if (fnum == -1) { - DEBUG(5, ("cacl_get failed to open %s: %s\n", - filename, cli_errstr(cli))); - errno = 0; - return -1; - } - - sd = cli_query_secdesc(cli, fnum, ctx); - - if (!sd) { - DEBUG(5, - ("cacl_get Failed to query old descriptor\n")); - errno = 0; - return -1; - } - - cli_close(cli, fnum); - - if (! exclude_nt_revision) { - if (all || all_nt) { - if (determine_size) { - p = talloc_asprintf(ctx, - "REVISION:%d", - sd->revision); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - "REVISION:%d", - sd->revision); - } - } else if (StrCaseCmp(name, "revision") == 0) { - if (determine_size) { - p = talloc_asprintf(ctx, "%d", - sd->revision); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, "%d", - sd->revision); - } - } - - if (!determine_size && n > bufsize) { - errno = ERANGE; - return -1; - } - buf += n; - n_used += n; - bufsize -= n; - n = 0; - } - - if (! exclude_nt_owner) { - /* Get owner and group sid */ - if (sd->owner_sid) { - convert_sid_to_string(ipc_cli, pol, - sidstr, - numeric, - sd->owner_sid); - } else { - fstrcpy(sidstr, ""); - } - - if (all || all_nt) { - if (determine_size) { - p = talloc_asprintf(ctx, ",OWNER:%s", - sidstr); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else if (sidstr[0] != '\0') { - n = snprintf(buf, bufsize, - ",OWNER:%s", sidstr); - } - } else if (StrnCaseCmp(name, "owner", 5) == 0) { - if (determine_size) { - p = talloc_asprintf(ctx, "%s", sidstr); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, "%s", - sidstr); - } - } - - if (!determine_size && n > bufsize) { - errno = ERANGE; - return -1; - } - buf += n; - n_used += n; - bufsize -= n; - n = 0; - } - - if (! exclude_nt_group) { - if (sd->group_sid) { - convert_sid_to_string(ipc_cli, pol, - sidstr, numeric, - sd->group_sid); - } else { - fstrcpy(sidstr, ""); - } - - if (all || all_nt) { - if (determine_size) { - p = talloc_asprintf(ctx, ",GROUP:%s", - sidstr); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else if (sidstr[0] != '\0') { - n = snprintf(buf, bufsize, - ",GROUP:%s", sidstr); - } - } else if (StrnCaseCmp(name, "group", 5) == 0) { - if (determine_size) { - p = talloc_asprintf(ctx, "%s", sidstr); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - "%s", sidstr); - } - } - - if (!determine_size && n > bufsize) { - errno = ERANGE; - return -1; - } - buf += n; - n_used += n; - bufsize -= n; - n = 0; - } - - if (! exclude_nt_acl) { - /* Add aces to value buffer */ - for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) { - - SEC_ACE *ace = &sd->dacl->aces[i]; - convert_sid_to_string(ipc_cli, pol, - sidstr, numeric, - &ace->trustee); - - if (all || all_nt) { - if (determine_size) { - p = talloc_asprintf( - ctx, - ",ACL:" - "%s:%d/%d/0x%08x", - sidstr, - ace->type, - ace->flags, - ace->access_mask); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf( - buf, bufsize, - ",ACL:%s:%d/%d/0x%08x", - sidstr, - ace->type, - ace->flags, - ace->access_mask); - } - } else if ((StrnCaseCmp(name, "acl", 3) == 0 && - StrCaseCmp(name+3, sidstr) == 0) || - (StrnCaseCmp(name, "acl+", 4) == 0 && - StrCaseCmp(name+4, sidstr) == 0)) { - if (determine_size) { - p = talloc_asprintf( - ctx, - "%d/%d/0x%08x", - ace->type, - ace->flags, - ace->access_mask); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - "%d/%d/0x%08x", - ace->type, - ace->flags, - ace->access_mask); - } - } else if (all_nt_acls) { - if (determine_size) { - p = talloc_asprintf( - ctx, - "%s%s:%d/%d/0x%08x", - i ? "," : "", - sidstr, - ace->type, - ace->flags, - ace->access_mask); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - "%s%s:%d/%d/0x%08x", - i ? "," : "", - sidstr, - ace->type, - ace->flags, - ace->access_mask); - } - } - if (!determine_size && n > bufsize) { - errno = ERANGE; - return -1; - } - buf += n; - n_used += n; - bufsize -= n; - n = 0; - } - } - - /* Restore name pointer to its original value */ - name -= 19; - } - - if (all || some_dos) { - /* Point to the portion after "system.dos_attr." */ - name += 16; /* if (all) this will be invalid but unused */ - - /* Obtain the DOS attributes */ - if (!smbc_getatr(context, srv, filename, &mode, &size, - &create_time_ts, - &access_time_ts, - &write_time_ts, - &change_time_ts, - &ino)) { - - errno = smbc_errno(context, srv->cli); - return -1; - - } - - create_time = convert_timespec_to_time_t(create_time_ts); - access_time = convert_timespec_to_time_t(access_time_ts); - write_time = convert_timespec_to_time_t(write_time_ts); - change_time = convert_timespec_to_time_t(change_time_ts); - - if (! exclude_dos_mode) { - if (all || all_dos) { - if (determine_size) { - p = talloc_asprintf(ctx, - "%sMODE:0x%x", - (ipc_cli && - (all || some_nt) - ? "," - : ""), - mode); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - "%sMODE:0x%x", - (ipc_cli && - (all || some_nt) - ? "," - : ""), - mode); - } - } else if (StrCaseCmp(name, "mode") == 0) { - if (determine_size) { - p = talloc_asprintf(ctx, "0x%x", mode); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - "0x%x", mode); - } - } - - if (!determine_size && n > bufsize) { - errno = ERANGE; - return -1; - } - buf += n; - n_used += n; - bufsize -= n; - n = 0; - } - - if (! exclude_dos_size) { - if (all || all_dos) { - if (determine_size) { - p = talloc_asprintf( - ctx, - ",SIZE:%.0f", - (double)size); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - ",SIZE:%.0f", - (double)size); - } - } else if (StrCaseCmp(name, "size") == 0) { - if (determine_size) { - p = talloc_asprintf( - ctx, - "%.0f", - (double)size); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - "%.0f", - (double)size); - } - } - - if (!determine_size && n > bufsize) { - errno = ERANGE; - return -1; - } - buf += n; - n_used += n; - bufsize -= n; - n = 0; - } - - if (! exclude_dos_create_time && - attr_strings.create_time_attr != NULL) { - if (all || all_dos) { - if (determine_size) { - p = talloc_asprintf(ctx, - ",%s:%lu", - attr_strings.create_time_attr, - create_time); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - ",%s:%lu", - attr_strings.create_time_attr, - create_time); - } - } else if (StrCaseCmp(name, attr_strings.create_time_attr) == 0) { - if (determine_size) { - p = talloc_asprintf(ctx, "%lu", create_time); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - "%lu", create_time); - } - } - - if (!determine_size && n > bufsize) { - errno = ERANGE; - return -1; - } - buf += n; - n_used += n; - bufsize -= n; - n = 0; - } - - if (! exclude_dos_access_time) { - if (all || all_dos) { - if (determine_size) { - p = talloc_asprintf(ctx, - ",%s:%lu", - attr_strings.access_time_attr, - access_time); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - ",%s:%lu", - attr_strings.access_time_attr, - access_time); - } - } else if (StrCaseCmp(name, attr_strings.access_time_attr) == 0) { - if (determine_size) { - p = talloc_asprintf(ctx, "%lu", access_time); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - "%lu", access_time); - } - } - - if (!determine_size && n > bufsize) { - errno = ERANGE; - return -1; - } - buf += n; - n_used += n; - bufsize -= n; - n = 0; - } - - if (! exclude_dos_write_time) { - if (all || all_dos) { - if (determine_size) { - p = talloc_asprintf(ctx, - ",%s:%lu", - attr_strings.write_time_attr, - write_time); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - ",%s:%lu", - attr_strings.write_time_attr, - write_time); - } - } else if (StrCaseCmp(name, attr_strings.write_time_attr) == 0) { - if (determine_size) { - p = talloc_asprintf(ctx, "%lu", write_time); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - "%lu", write_time); - } - } - - if (!determine_size && n > bufsize) { - errno = ERANGE; - return -1; - } - buf += n; - n_used += n; - bufsize -= n; - n = 0; - } - - if (! exclude_dos_change_time) { - if (all || all_dos) { - if (determine_size) { - p = talloc_asprintf(ctx, - ",%s:%lu", - attr_strings.change_time_attr, - change_time); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - ",%s:%lu", - attr_strings.change_time_attr, - change_time); - } - } else if (StrCaseCmp(name, attr_strings.change_time_attr) == 0) { - if (determine_size) { - p = talloc_asprintf(ctx, "%lu", change_time); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - "%lu", change_time); - } - } - - if (!determine_size && n > bufsize) { - errno = ERANGE; - return -1; - } - buf += n; - n_used += n; - bufsize -= n; - n = 0; - } - - if (! exclude_dos_inode) { - if (all || all_dos) { - if (determine_size) { - p = talloc_asprintf( - ctx, - ",INODE:%.0f", - (double)ino); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - ",INODE:%.0f", - (double) ino); - } - } else if (StrCaseCmp(name, "inode") == 0) { - if (determine_size) { - p = talloc_asprintf( - ctx, - "%.0f", - (double) ino); - if (!p) { - errno = ENOMEM; - return -1; - } - n = strlen(p); - } else { - n = snprintf(buf, bufsize, - "%.0f", - (double) ino); - } - } - - if (!determine_size && n > bufsize) { - errno = ERANGE; - return -1; - } - buf += n; - n_used += n; - bufsize -= n; - n = 0; - } - - /* Restore name pointer to its original value */ - name -= 16; - } - - if (n_used == 0) { - errno = ENOATTR; - return -1; - } - - return n_used; -} - -/***************************************************** -set the ACLs on a file given an ascii description -*******************************************************/ -static int -cacl_set(TALLOC_CTX *ctx, - struct cli_state *cli, - struct cli_state *ipc_cli, - POLICY_HND *pol, - const char *filename, - const char *the_acl, - int mode, - int flags) -{ - int fnum; - int err = 0; - SEC_DESC *sd = NULL, *old; - SEC_ACL *dacl = NULL; - DOM_SID *owner_sid = NULL; - DOM_SID *group_sid = NULL; - uint32 i, j; - size_t sd_size; - int ret = 0; - char *p; - bool numeric = True; - - /* the_acl will be null for REMOVE_ALL operations */ - if (the_acl) { - numeric = ((p = strchr(the_acl, ':')) != NULL && - p > the_acl && - p[-1] != '+'); - - /* if this is to set the entire ACL... */ - if (*the_acl == '*') { - /* ... then increment past the first colon */ - the_acl = p + 1; - } - - sd = sec_desc_parse(ctx, ipc_cli, pol, numeric, - CONST_DISCARD(char *, the_acl)); - - if (!sd) { - errno = EINVAL; - return -1; - } - } - - /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller - that doesn't deref sd */ - - if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) { - errno = EINVAL; - return -1; - } - - /* The desired access below is the only one I could find that works - with NT4, W2KP and Samba */ - - fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ); - - if (fnum == -1) { - DEBUG(5, ("cacl_set failed to open %s: %s\n", - filename, cli_errstr(cli))); - errno = 0; - return -1; - } - - old = cli_query_secdesc(cli, fnum, ctx); - - if (!old) { - DEBUG(5, ("cacl_set Failed to query old descriptor\n")); - errno = 0; - return -1; - } - - cli_close(cli, fnum); - - switch (mode) { - case SMBC_XATTR_MODE_REMOVE_ALL: - old->dacl->num_aces = 0; - dacl = old->dacl; - break; - - case SMBC_XATTR_MODE_REMOVE: - for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { - bool found = False; - - for (j=0;old->dacl && j<old->dacl->num_aces;j++) { - if (sec_ace_equal(&sd->dacl->aces[i], - &old->dacl->aces[j])) { - uint32 k; - for (k=j; k<old->dacl->num_aces-1;k++) { - old->dacl->aces[k] = - old->dacl->aces[k+1]; - } - old->dacl->num_aces--; - found = True; - dacl = old->dacl; - break; - } - } - - if (!found) { - err = ENOATTR; - ret = -1; - goto failed; - } - } - break; - - case SMBC_XATTR_MODE_ADD: - for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { - bool found = False; - - for (j=0;old->dacl && j<old->dacl->num_aces;j++) { - if (sid_equal(&sd->dacl->aces[i].trustee, - &old->dacl->aces[j].trustee)) { - if (!(flags & SMBC_XATTR_FLAG_CREATE)) { - err = EEXIST; - ret = -1; - goto failed; - } - old->dacl->aces[j] = sd->dacl->aces[i]; - ret = -1; - found = True; - } - } - - if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) { - err = ENOATTR; - ret = -1; - goto failed; - } - - for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { - add_ace(&old->dacl, &sd->dacl->aces[i], ctx); - } - } - dacl = old->dacl; - break; - - case SMBC_XATTR_MODE_SET: - old = sd; - owner_sid = old->owner_sid; - group_sid = old->group_sid; - dacl = old->dacl; - break; - - case SMBC_XATTR_MODE_CHOWN: - owner_sid = sd->owner_sid; - break; - - case SMBC_XATTR_MODE_CHGRP: - group_sid = sd->group_sid; - break; - } - - /* Denied ACE entries must come before allowed ones */ - sort_acl(old->dacl); - - /* Create new security descriptor and set it */ - sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE, - owner_sid, group_sid, NULL, dacl, &sd_size); - - fnum = cli_nt_create(cli, filename, - WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS); - - if (fnum == -1) { - DEBUG(5, ("cacl_set failed to open %s: %s\n", - filename, cli_errstr(cli))); - errno = 0; - return -1; - } - - if (!cli_set_secdesc(cli, fnum, sd)) { - DEBUG(5, ("ERROR: secdesc set failed: %s\n", cli_errstr(cli))); - ret = -1; - } - - /* Clean up */ - - failed: - cli_close(cli, fnum); - - if (err != 0) { - errno = err; - } - - return ret; -} - - -static int -smbc_setxattr_ctx(SMBCCTX *context, - const char *fname, - const char *name, - const void *value, - size_t size, - int flags) -{ - int ret; - int ret2; - SMBCSRV *srv = NULL; - SMBCSRV *ipc_srv = NULL; - char *server = NULL; - char *share = NULL; - char *user = NULL; - char *password = NULL; - char *workgroup = NULL; - char *path = NULL; - DOS_ATTR_DESC *dad = NULL; - struct { - const char * create_time_attr; - const char * access_time_attr; - const char * write_time_attr; - const char * change_time_attr; - } attr_strings; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; /* Best I can think of ... */ - TALLOC_FREE(frame); - return -1; - } - - if (!fname) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n", - fname, name, (int) size, (const char*)value)); - - if (smbc_parse_path(frame, - context, - fname, - &workgroup, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!user || user[0] == (char)0) { - user = talloc_strdup(frame, context->user); - if (!user) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - } - - srv = smbc_server(frame, context, True, - server, share, &workgroup, &user, &password); - if (!srv) { - TALLOC_FREE(frame); - return -1; /* errno set by smbc_server */ - } - - if (! srv->no_nt_session) { - ipc_srv = smbc_attr_server(frame, context, server, share, - &workgroup, &user, &password); - if (! ipc_srv) { - srv->no_nt_session = True; - } - } else { - ipc_srv = NULL; - } - - /* - * Are they asking to set the entire set of known attributes? - */ - if (StrCaseCmp(name, "system.*") == 0 || - StrCaseCmp(name, "system.*+") == 0) { - /* Yup. */ - char *namevalue = - talloc_asprintf(talloc_tos(), "%s:%s", - name+7, (const char *) value); - if (! namevalue) { - errno = ENOMEM; - ret = -1; - TALLOC_FREE(frame); - return -1; - } - - if (ipc_srv) { - ret = cacl_set(talloc_tos(), srv->cli, - ipc_srv->cli, &ipc_srv->pol, path, - namevalue, - (*namevalue == '*' - ? SMBC_XATTR_MODE_SET - : SMBC_XATTR_MODE_ADD), - flags); - } else { - ret = 0; - } - - /* get a DOS Attribute Descriptor with current attributes */ - dad = dos_attr_query(context, talloc_tos(), path, srv); - if (dad) { - /* Overwrite old with new, using what was provided */ - dos_attr_parse(context, dad, srv, namevalue); - - /* Set the new DOS attributes */ - if (! smbc_setatr(context, srv, path, - dad->create_time, - dad->access_time, - dad->write_time, - dad->change_time, - dad->mode)) { - - /* cause failure if NT failed too */ - dad = NULL; - } - } - - /* we only fail if both NT and DOS sets failed */ - if (ret < 0 && ! dad) { - ret = -1; /* in case dad was null */ - } - else { - ret = 0; - } - - TALLOC_FREE(frame); - return ret; - } - - /* - * Are they asking to set an access control element or to set - * the entire access control list? - */ - if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 || - StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 || - StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 || - StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 || - StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) { - - /* Yup. */ - char *namevalue = - talloc_asprintf(talloc_tos(), "%s:%s", - name+19, (const char *) value); - - if (! ipc_srv) { - ret = -1; /* errno set by smbc_server() */ - } - else if (! namevalue) { - errno = ENOMEM; - ret = -1; - } else { - ret = cacl_set(talloc_tos(), srv->cli, - ipc_srv->cli, &ipc_srv->pol, path, - namevalue, - (*namevalue == '*' - ? SMBC_XATTR_MODE_SET - : SMBC_XATTR_MODE_ADD), - flags); - } - TALLOC_FREE(frame); - return ret; - } - - /* - * Are they asking to set the owner? - */ - if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 || - StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) { - - /* Yup. */ - char *namevalue = - talloc_asprintf(talloc_tos(), "%s:%s", - name+19, (const char *) value); - - if (! ipc_srv) { - ret = -1; /* errno set by smbc_server() */ - } - else if (! namevalue) { - errno = ENOMEM; - ret = -1; - } else { - ret = cacl_set(talloc_tos(), srv->cli, - ipc_srv->cli, &ipc_srv->pol, path, - namevalue, SMBC_XATTR_MODE_CHOWN, 0); - } - TALLOC_FREE(frame); - return ret; - } - - /* - * Are they asking to set the group? - */ - if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 || - StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) { - - /* Yup. */ - char *namevalue = - talloc_asprintf(talloc_tos(), "%s:%s", - name+19, (const char *) value); - - if (! ipc_srv) { - /* errno set by smbc_server() */ - ret = -1; - } - else if (! namevalue) { - errno = ENOMEM; - ret = -1; - } else { - ret = cacl_set(talloc_tos(), srv->cli, - ipc_srv->cli, &ipc_srv->pol, path, - namevalue, SMBC_XATTR_MODE_CHGRP, 0); - } - TALLOC_FREE(frame); - return ret; - } - - /* Determine whether to use old-style or new-style attribute names */ - if (context->internal->_full_time_names) { - /* new-style names */ - attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME"; - attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME"; - attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME"; - attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME"; - } else { - /* old-style names */ - attr_strings.create_time_attr = NULL; - attr_strings.access_time_attr = "system.dos_attr.A_TIME"; - attr_strings.write_time_attr = "system.dos_attr.M_TIME"; - attr_strings.change_time_attr = "system.dos_attr.C_TIME"; - } - - /* - * Are they asking to set a DOS attribute? - */ - if (StrCaseCmp(name, "system.dos_attr.*") == 0 || - StrCaseCmp(name, "system.dos_attr.mode") == 0 || - (attr_strings.create_time_attr != NULL && - StrCaseCmp(name, attr_strings.create_time_attr) == 0) || - StrCaseCmp(name, attr_strings.access_time_attr) == 0 || - StrCaseCmp(name, attr_strings.write_time_attr) == 0 || - StrCaseCmp(name, attr_strings.change_time_attr) == 0) { - - /* get a DOS Attribute Descriptor with current attributes */ - dad = dos_attr_query(context, talloc_tos(), path, srv); - if (dad) { - char *namevalue = - talloc_asprintf(talloc_tos(), "%s:%s", - name+16, (const char *) value); - if (! namevalue) { - errno = ENOMEM; - ret = -1; - } else { - /* Overwrite old with provided new params */ - dos_attr_parse(context, dad, srv, namevalue); - - /* Set the new DOS attributes */ - ret2 = smbc_setatr(context, srv, path, - dad->create_time, - dad->access_time, - dad->write_time, - dad->change_time, - dad->mode); - - /* ret2 has True (success) / False (failure) */ - if (ret2) { - ret = 0; - } else { - ret = -1; - } - } - } else { - ret = -1; - } - - TALLOC_FREE(frame); - return ret; - } - - /* Unsupported attribute name */ - errno = EINVAL; - TALLOC_FREE(frame); - return -1; -} - -static int -smbc_getxattr_ctx(SMBCCTX *context, - const char *fname, - const char *name, - const void *value, - size_t size) -{ - int ret; - SMBCSRV *srv = NULL; - SMBCSRV *ipc_srv = NULL; - char *server = NULL; - char *share = NULL; - char *user = NULL; - char *password = NULL; - char *workgroup = NULL; - char *path = NULL; - struct { - const char * create_time_attr; - const char * access_time_attr; - const char * write_time_attr; - const char * change_time_attr; - } attr_strings; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; /* Best I can think of ... */ - TALLOC_FREE(frame); - return -1; - } - - if (!fname) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name)); - - if (smbc_parse_path(frame, - context, - fname, - &workgroup, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!user || user[0] == (char)0) { - user = talloc_strdup(frame, context->user); - if (!user) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - } - - srv = smbc_server(frame, context, True, - server, share, &workgroup, &user, &password); - if (!srv) { - TALLOC_FREE(frame); - return -1; /* errno set by smbc_server */ - } - - if (! srv->no_nt_session) { - ipc_srv = smbc_attr_server(frame, context, server, share, - &workgroup, &user, &password); - if (! ipc_srv) { - srv->no_nt_session = True; - } - } else { - ipc_srv = NULL; - } - - /* Determine whether to use old-style or new-style attribute names */ - if (context->internal->_full_time_names) { - /* new-style names */ - attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME"; - attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME"; - attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME"; - attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME"; - } else { - /* old-style names */ - attr_strings.create_time_attr = NULL; - attr_strings.access_time_attr = "system.dos_attr.A_TIME"; - attr_strings.write_time_attr = "system.dos_attr.M_TIME"; - attr_strings.change_time_attr = "system.dos_attr.C_TIME"; - } - - /* Are they requesting a supported attribute? */ - if (StrCaseCmp(name, "system.*") == 0 || - StrnCaseCmp(name, "system.*!", 9) == 0 || - StrCaseCmp(name, "system.*+") == 0 || - StrnCaseCmp(name, "system.*+!", 10) == 0 || - StrCaseCmp(name, "system.nt_sec_desc.*") == 0 || - StrnCaseCmp(name, "system.nt_sec_desc.*!", 21) == 0 || - StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 || - StrnCaseCmp(name, "system.nt_sec_desc.*+!", 22) == 0 || - StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 || - StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 || - StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 || - StrCaseCmp(name, "system.nt_sec_desc.group") == 0 || - StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 || - StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 || - StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 || - StrCaseCmp(name, "system.dos_attr.*") == 0 || - StrnCaseCmp(name, "system.dos_attr.*!", 18) == 0 || - StrCaseCmp(name, "system.dos_attr.mode") == 0 || - StrCaseCmp(name, "system.dos_attr.size") == 0 || - (attr_strings.create_time_attr != NULL && - StrCaseCmp(name, attr_strings.create_time_attr) == 0) || - StrCaseCmp(name, attr_strings.access_time_attr) == 0 || - StrCaseCmp(name, attr_strings.write_time_attr) == 0 || - StrCaseCmp(name, attr_strings.change_time_attr) == 0 || - StrCaseCmp(name, "system.dos_attr.inode") == 0) { - - /* Yup. */ - ret = cacl_get(context, talloc_tos(), srv, - ipc_srv == NULL ? NULL : ipc_srv->cli, - &ipc_srv->pol, path, - CONST_DISCARD(char *, name), - CONST_DISCARD(char *, value), size); - if (ret < 0 && errno == 0) { - errno = smbc_errno(context, srv->cli); - } - TALLOC_FREE(frame); - return ret; - } - - /* Unsupported attribute name */ - errno = EINVAL; - TALLOC_FREE(frame); - return -1; -} - - -static int -smbc_removexattr_ctx(SMBCCTX *context, - const char *fname, - const char *name) -{ - int ret; - SMBCSRV *srv = NULL; - SMBCSRV *ipc_srv = NULL; - char *server = NULL; - char *share = NULL; - char *user = NULL; - char *password = NULL; - char *workgroup = NULL; - char *path = NULL; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; /* Best I can think of ... */ - TALLOC_FREE(frame); - return -1; - } - - if (!fname) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name)); - - if (smbc_parse_path(frame, - context, - fname, - &workgroup, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!user || user[0] == (char)0) { - user = talloc_strdup(frame, context->user); - if (!user) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - } - - srv = smbc_server(frame, context, True, - server, share, &workgroup, &user, &password); - if (!srv) { - TALLOC_FREE(frame); - return -1; /* errno set by smbc_server */ - } - - if (! srv->no_nt_session) { - ipc_srv = smbc_attr_server(frame, context, server, share, - &workgroup, &user, &password); - if (! ipc_srv) { - srv->no_nt_session = True; - } - } else { - ipc_srv = NULL; - } - - if (! ipc_srv) { - TALLOC_FREE(frame); - return -1; /* errno set by smbc_attr_server */ - } - - /* Are they asking to set the entire ACL? */ - if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 || - StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) { - - /* Yup. */ - ret = cacl_set(talloc_tos(), srv->cli, - ipc_srv->cli, &ipc_srv->pol, path, - NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0); - TALLOC_FREE(frame); - return ret; - } - - /* - * Are they asking to remove one or more spceific security descriptor - * attributes? - */ - if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 || - StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 || - StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 || - StrCaseCmp(name, "system.nt_sec_desc.group") == 0 || - StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 || - StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 || - StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) { - - /* Yup. */ - ret = cacl_set(talloc_tos(), srv->cli, - ipc_srv->cli, &ipc_srv->pol, path, - name + 19, SMBC_XATTR_MODE_REMOVE, 0); - TALLOC_FREE(frame); - return ret; - } - - /* Unsupported attribute name */ - errno = EINVAL; - TALLOC_FREE(frame); - return -1; -} - -static int -smbc_listxattr_ctx(SMBCCTX *context, - const char *fname, - char *list, - size_t size) -{ - /* - * This isn't quite what listxattr() is supposed to do. This returns - * the complete set of attribute names, always, rather than only those - * attribute names which actually exist for a file. Hmmm... - */ - size_t retsize; - const char supported_old[] = - "system.*\0" - "system.*+\0" - "system.nt_sec_desc.revision\0" - "system.nt_sec_desc.owner\0" - "system.nt_sec_desc.owner+\0" - "system.nt_sec_desc.group\0" - "system.nt_sec_desc.group+\0" - "system.nt_sec_desc.acl.*\0" - "system.nt_sec_desc.acl\0" - "system.nt_sec_desc.acl+\0" - "system.nt_sec_desc.*\0" - "system.nt_sec_desc.*+\0" - "system.dos_attr.*\0" - "system.dos_attr.mode\0" - "system.dos_attr.c_time\0" - "system.dos_attr.a_time\0" - "system.dos_attr.m_time\0" - ; - const char supported_new[] = - "system.*\0" - "system.*+\0" - "system.nt_sec_desc.revision\0" - "system.nt_sec_desc.owner\0" - "system.nt_sec_desc.owner+\0" - "system.nt_sec_desc.group\0" - "system.nt_sec_desc.group+\0" - "system.nt_sec_desc.acl.*\0" - "system.nt_sec_desc.acl\0" - "system.nt_sec_desc.acl+\0" - "system.nt_sec_desc.*\0" - "system.nt_sec_desc.*+\0" - "system.dos_attr.*\0" - "system.dos_attr.mode\0" - "system.dos_attr.create_time\0" - "system.dos_attr.access_time\0" - "system.dos_attr.write_time\0" - "system.dos_attr.change_time\0" - ; - const char * supported; - - if (context->internal->_full_time_names) { - supported = supported_new; - retsize = sizeof(supported_new); - } else { - supported = supported_old; - retsize = sizeof(supported_old); - } - - if (size == 0) { - return retsize; - } - - if (retsize > size) { - errno = ERANGE; - return -1; - } - - /* this can't be strcpy() because there are embedded null characters */ - memcpy(list, supported, retsize); - return retsize; -} - - -/* - * Open a print file to be written to by other calls - */ - -static SMBCFILE * -smbc_open_print_job_ctx(SMBCCTX *context, - const char *fname) -{ - char *server = NULL; - char *share = NULL; - char *user = NULL; - char *password = NULL; - char *path = NULL; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; - TALLOC_FREE(frame); - return NULL; - } - - if (!fname) { - errno = EINVAL; - TALLOC_FREE(frame); - return NULL; - } - - DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname)); - - if (smbc_parse_path(frame, - context, - fname, - NULL, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return NULL; - } - - /* What if the path is empty, or the file exists? */ - - TALLOC_FREE(frame); - return (context->open)(context, fname, O_WRONLY, 666); -} - -/* - * Routine to print a file on a remote server ... - * - * We open the file, which we assume to be on a remote server, and then - * copy it to a print file on the share specified by printq. - */ - -static int -smbc_print_file_ctx(SMBCCTX *c_file, - const char *fname, - SMBCCTX *c_print, - const char *printq) -{ - SMBCFILE *fid1; - SMBCFILE *fid2; - int bytes; - int saverr; - int tot_bytes = 0; - char buf[4096]; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!c_file || !c_file->internal->_initialized || !c_print || - !c_print->internal->_initialized) { - - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - - } - - if (!fname && !printq) { - - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - - } - - /* Try to open the file for reading ... */ - - if ((long)(fid1 = (c_file->open)(c_file, fname, O_RDONLY, 0666)) < 0) { - DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno)); - TALLOC_FREE(frame); - return -1; /* smbc_open sets errno */ - } - - /* Now, try to open the printer file for writing */ - - if ((long)(fid2 = (c_print->open_print_job)(c_print, printq)) < 0) { - - saverr = errno; /* Save errno */ - (c_file->close_fn)(c_file, fid1); - errno = saverr; - TALLOC_FREE(frame); - return -1; - - } - - while ((bytes = (c_file->read)(c_file, fid1, buf, sizeof(buf))) > 0) { - - tot_bytes += bytes; - - if (((c_print->write)(c_print, fid2, buf, bytes)) < 0) { - - saverr = errno; - (c_file->close_fn)(c_file, fid1); - (c_print->close_fn)(c_print, fid2); - errno = saverr; - - } - - } - - saverr = errno; - - (c_file->close_fn)(c_file, fid1); /* We have to close these anyway */ - (c_print->close_fn)(c_print, fid2); - - if (bytes < 0) { - - errno = saverr; - TALLOC_FREE(frame); - return -1; - - } - - TALLOC_FREE(frame); - return tot_bytes; - -} - -/* - * Routine to list print jobs on a printer share ... - */ - -static int -smbc_list_print_jobs_ctx(SMBCCTX *context, - const char *fname, - smbc_list_print_job_fn fn) -{ - SMBCSRV *srv = NULL; - char *server = NULL; - char *share = NULL; - char *user = NULL; - char *password = NULL; - char *workgroup = NULL; - char *path = NULL; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!fname) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname)); - - if (smbc_parse_path(frame, - context, - fname, - &workgroup, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!user || user[0] == (char)0) { - user = talloc_strdup(frame, context->user); - if (!user) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - } - - srv = smbc_server(frame, context, True, - server, share, &workgroup, &user, &password); - - if (!srv) { - TALLOC_FREE(frame); - return -1; /* errno set by smbc_server */ - } - - if (cli_print_queue(srv->cli, - (void (*)(struct print_job_info *))fn) < 0) { - errno = smbc_errno(context, srv->cli); - TALLOC_FREE(frame); - return -1; - } - - TALLOC_FREE(frame); - return 0; - -} - -/* - * Delete a print job from a remote printer share - */ - -static int -smbc_unlink_print_job_ctx(SMBCCTX *context, - const char *fname, - int id) -{ - SMBCSRV *srv = NULL; - char *server = NULL; - char *share = NULL; - char *user = NULL; - char *password = NULL; - char *workgroup = NULL; - char *path = NULL; - int err; - TALLOC_CTX *frame = talloc_stackframe(); - - if (!context || !context->internal || - !context->internal->_initialized) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!fname) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname)); - - if (smbc_parse_path(frame, - context, - fname, - &workgroup, - &server, - &share, - &path, - &user, - &password, - NULL)) { - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - } - - if (!user || user[0] == (char)0) { - user = talloc_strdup(frame, context->user); - if (!user) { - errno = ENOMEM; - TALLOC_FREE(frame); - return -1; - } - } - - srv = smbc_server(frame, context, True, - server, share, &workgroup, &user, &password); - - if (!srv) { - - TALLOC_FREE(frame); - return -1; /* errno set by smbc_server */ - - } - - if ((err = cli_printjob_del(srv->cli, id)) != 0) { - - if (err < 0) - errno = smbc_errno(context, srv->cli); - else if (err == ERRnosuchprintjob) - errno = EINVAL; - TALLOC_FREE(frame); - return -1; - - } - - TALLOC_FREE(frame); - return 0; - -} - -/* - * Get a new empty handle to fill in with your own info - */ -SMBCCTX * -smbc_new_context(void) -{ - SMBCCTX *context; - - context = SMB_MALLOC_P(SMBCCTX); - if (!context) { - errno = ENOMEM; - return NULL; - } - - ZERO_STRUCTP(context); - - context->internal = SMB_MALLOC_P(struct smbc_internal_data); - if (!context->internal) { - SAFE_FREE(context); - errno = ENOMEM; - return NULL; - } - - ZERO_STRUCTP(context->internal); - - /* ADD REASONABLE DEFAULTS */ - context->debug = 0; - context->timeout = 20000; /* 20 seconds */ - - context->options.browse_max_lmb_count = 3; /* # LMBs to query */ - context->options.urlencode_readdir_entries = False;/* backward compat */ - context->options.one_share_per_server = False;/* backward compat */ - context->internal->_share_mode = SMBC_SHAREMODE_DENY_NONE; - /* backward compat */ - - context->open = smbc_open_ctx; - context->creat = smbc_creat_ctx; - context->read = smbc_read_ctx; - context->write = smbc_write_ctx; - context->close_fn = smbc_close_ctx; - context->unlink = smbc_unlink_ctx; - context->rename = smbc_rename_ctx; - context->lseek = smbc_lseek_ctx; - context->stat = smbc_stat_ctx; - context->fstat = smbc_fstat_ctx; - context->opendir = smbc_opendir_ctx; - context->closedir = smbc_closedir_ctx; - context->readdir = smbc_readdir_ctx; - context->getdents = smbc_getdents_ctx; - context->mkdir = smbc_mkdir_ctx; - context->rmdir = smbc_rmdir_ctx; - context->telldir = smbc_telldir_ctx; - context->lseekdir = smbc_lseekdir_ctx; - context->fstatdir = smbc_fstatdir_ctx; - context->ftruncate = smbc_ftruncate_ctx; - context->chmod = smbc_chmod_ctx; - context->utimes = smbc_utimes_ctx; - context->setxattr = smbc_setxattr_ctx; - context->getxattr = smbc_getxattr_ctx; - context->removexattr = smbc_removexattr_ctx; - context->listxattr = smbc_listxattr_ctx; - context->open_print_job = smbc_open_print_job_ctx; - context->print_file = smbc_print_file_ctx; - context->list_print_jobs = smbc_list_print_jobs_ctx; - context->unlink_print_job = smbc_unlink_print_job_ctx; - - context->callbacks.check_server_fn = smbc_check_server; - context->callbacks.remove_unused_server_fn = smbc_remove_unused_server; - - smbc_default_cache_functions(context); - - return context; -} - -/* - * Free a context - * - * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed - * and thus you'll be leaking memory if not handled properly. - * - */ -int -smbc_free_context(SMBCCTX *context, - int shutdown_ctx) -{ - if (!context) { - errno = EBADF; - return 1; - } - - if (shutdown_ctx) { - SMBCFILE * f; - DEBUG(1,("Performing aggressive shutdown.\n")); - - f = context->internal->_files; - while (f) { - (context->close_fn)(context, f); - f = f->next; - } - context->internal->_files = NULL; - - /* First try to remove the servers the nice way. */ - if (context->callbacks.purge_cached_fn(context)) { - SMBCSRV * s; - SMBCSRV * next; - DEBUG(1, ("Could not purge all servers, " - "Nice way shutdown failed.\n")); - s = context->internal->_servers; - while (s) { - DEBUG(1, ("Forced shutdown: %p (fd=%d)\n", - s, s->cli->fd)); - cli_shutdown(s->cli); - (context->callbacks.remove_cached_srv_fn)(context, - s); - next = s->next; - DLIST_REMOVE(context->internal->_servers, s); - SAFE_FREE(s); - s = next; - } - context->internal->_servers = NULL; - } - } - else { - /* This is the polite way */ - if ((context->callbacks.purge_cached_fn)(context)) { - DEBUG(1, ("Could not purge all servers, " - "free_context failed.\n")); - errno = EBUSY; - return 1; - } - if (context->internal->_servers) { - DEBUG(1, ("Active servers in context, " - "free_context failed.\n")); - errno = EBUSY; - return 1; - } - if (context->internal->_files) { - DEBUG(1, ("Active files in context, " - "free_context failed.\n")); - errno = EBUSY; - return 1; - } - } - - /* Things we have to clean up */ - SAFE_FREE(context->workgroup); - SAFE_FREE(context->netbios_name); - SAFE_FREE(context->user); - - DEBUG(3, ("Context %p successfully freed\n", context)); - SAFE_FREE(context->internal); - SAFE_FREE(context); - return 0; -} - - -/* - * Each time the context structure is changed, we have binary backward - * compatibility issues. Instead of modifying the public portions of the - * context structure to add new options, instead, we put them in the internal - * portion of the context structure and provide a set function for these new - * options. - */ -void -smbc_option_set(SMBCCTX *context, - char *option_name, - ... /* option_value */) -{ - va_list ap; - union { - int i; - bool b; - smbc_get_auth_data_with_context_fn auth_fn; - void *v; - const char *s; - } option_value; - - va_start(ap, option_name); - - if (strcmp(option_name, "debug_to_stderr") == 0) { - /* - * Log to standard error instead of standard output. - */ - option_value.b = (bool) va_arg(ap, int); - context->internal->_debug_stderr = option_value.b; - - } else if (strcmp(option_name, "full_time_names") == 0) { - /* - * Use new-style time attribute names, e.g. WRITE_TIME rather - * than the old-style names such as M_TIME. This allows also - * setting/getting CREATE_TIME which was previously - * unimplemented. (Note that the old C_TIME was supposed to - * be CHANGE_TIME but was confused and sometimes referred to - * CREATE_TIME.) - */ - option_value.b = (bool) va_arg(ap, int); - context->internal->_full_time_names = option_value.b; - - } else if (strcmp(option_name, "open_share_mode") == 0) { - /* - * The share mode to use for files opened with - * smbc_open_ctx(). The default is SMBC_SHAREMODE_DENY_NONE. - */ - option_value.i = va_arg(ap, int); - context->internal->_share_mode = - (smbc_share_mode) option_value.i; - - } else if (strcmp(option_name, "auth_function") == 0) { - /* - * Use the new-style authentication function which includes - * the context. - */ - option_value.auth_fn = - va_arg(ap, smbc_get_auth_data_with_context_fn); - context->internal->_auth_fn_with_context = - option_value.auth_fn; - } else if (strcmp(option_name, "user_data") == 0) { - /* - * Save a user data handle which may be retrieved by the user - * with smbc_option_get() - */ - option_value.v = va_arg(ap, void *); - context->internal->_user_data = option_value.v; - } else if (strcmp(option_name, "smb_encrypt_level") == 0) { - /* - * Save an encoded value for encryption level. - * 0 = off, 1 = attempt, 2 = required. - */ - option_value.s = va_arg(ap, const char *); - if (strcmp(option_value.s, "none") == 0) { - context->internal->_smb_encryption_level = 0; - } else if (strcmp(option_value.s, "request") == 0) { - context->internal->_smb_encryption_level = 1; - } else if (strcmp(option_value.s, "require") == 0) { - context->internal->_smb_encryption_level = 2; - } - } - - va_end(ap); -} - - -/* - * Retrieve the current value of an option - */ -void * -smbc_option_get(SMBCCTX *context, - char *option_name) -{ - if (strcmp(option_name, "debug_stderr") == 0) { - /* - * Log to standard error instead of standard output. - */ -#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) - return (void *) (intptr_t) context->internal->_debug_stderr; -#else - return (void *) context->internal->_debug_stderr; -#endif - } else if (strcmp(option_name, "full_time_names") == 0) { - /* - * Use new-style time attribute names, e.g. WRITE_TIME rather - * than the old-style names such as M_TIME. This allows also - * setting/getting CREATE_TIME which was previously - * unimplemented. (Note that the old C_TIME was supposed to - * be CHANGE_TIME but was confused and sometimes referred to - * CREATE_TIME.) - */ -#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) - return (void *) (intptr_t) context->internal->_full_time_names; -#else - return (void *) context->internal->_full_time_names; -#endif - - } else if (strcmp(option_name, "auth_function") == 0) { - /* - * Use the new-style authentication function which includes - * the context. - */ - return (void *) context->internal->_auth_fn_with_context; - } else if (strcmp(option_name, "user_data") == 0) { - /* - * Save a user data handle which may be retrieved by the user - * with smbc_option_get() - */ - return context->internal->_user_data; - } else if (strcmp(option_name, "smb_encrypt_level") == 0) { - /* - * Return the current smb encrypt negotiate option as a string. - */ - switch (context->internal->_smb_encryption_level) { - case 0: - return (void *) "none"; - case 1: - return (void *) "request"; - case 2: - return (void *) "require"; - } - } else if (strcmp(option_name, "smb_encrypt_on") == 0) { - /* - * Return the current smb encrypt status option as a bool. - * false = off, true = on. We don't know what server is - * being requested, so we only return true if all servers - * are using an encrypted connection. - */ - SMBCSRV *s; - unsigned int num_servers = 0; - - for (s = context->internal->_servers; s; s = s->next) { - num_servers++; - if (s->cli->trans_enc_state == NULL) { - return (void *)false; - } - } - return (void *) (bool) (num_servers > 0); - } - - return NULL; -} - - -/* - * Initialise the library etc - * - * We accept a struct containing handle information. - * valid values for info->debug from 0 to 100, - * and insist that info->fn must be non-null. - */ -SMBCCTX * -smbc_init_context(SMBCCTX *context) -{ - int pid; - char *user = NULL; - char *home = NULL; - - if (!context || !context->internal) { - errno = EBADF; - return NULL; - } - - /* Do not initialise the same client twice */ - if (context->internal->_initialized) { - return 0; - } - - if ((!context->callbacks.auth_fn && - !context->internal->_auth_fn_with_context) || - context->debug < 0 || - context->debug > 100) { - - errno = EINVAL; - return NULL; - - } - - if (!smbc_initialized) { - /* - * Do some library-wide intializations the first time we get - * called - */ - bool conf_loaded = False; - TALLOC_CTX *frame = talloc_stackframe(); - - /* Set this to what the user wants */ - DEBUGLEVEL = context->debug; - - load_case_tables(); - - setup_logging("libsmbclient", True); - if (context->internal->_debug_stderr) { - dbf = x_stderr; - x_setbuf(x_stderr, NULL); - } - - /* Here we would open the smb.conf file if needed ... */ - - in_client = True; /* FIXME, make a param */ - - home = getenv("HOME"); - if (home) { - char *conf = NULL; - if (asprintf(&conf, "%s/.smb/smb.conf", home) > 0) { - if (lp_load(conf, True, False, False, True)) { - conf_loaded = True; - } else { - DEBUG(5, ("Could not load config file: %s\n", - conf)); - } - SAFE_FREE(conf); - } - } - - if (!conf_loaded) { - /* - * Well, if that failed, try the get_dyn_CONFIGFILE - * Which points to the standard locn, and if that - * fails, silently ignore it and use the internal - * defaults ... - */ - - if (!lp_load(get_dyn_CONFIGFILE(), True, False, False, False)) { - DEBUG(5, ("Could not load config file: %s\n", - get_dyn_CONFIGFILE())); - } else if (home) { - char *conf; - /* - * We loaded the global config file. Now lets - * load user-specific modifications to the - * global config. - */ - if (asprintf(&conf, - "%s/.smb/smb.conf.append", - home) > 0) { - if (!lp_load(conf, True, False, False, False)) { - DEBUG(10, - ("Could not append config file: " - "%s\n", - conf)); - } - SAFE_FREE(conf); - } - } - } - - load_interfaces(); /* Load the list of interfaces ... */ - - reopen_logs(); /* Get logging working ... */ - - /* - * Block SIGPIPE (from lib/util_sock.c: write()) - * It is not needed and should not stop execution - */ - BlockSignals(True, SIGPIPE); - - /* Done with one-time initialisation */ - smbc_initialized = 1; - - TALLOC_FREE(frame); - } - - if (!context->user) { - /* - * FIXME: Is this the best way to get the user info? - */ - user = getenv("USER"); - /* walk around as "guest" if no username can be found */ - if (!user) context->user = SMB_STRDUP("guest"); - else context->user = SMB_STRDUP(user); - } - - if (!context->netbios_name) { - /* - * We try to get our netbios name from the config. If that - * fails we fall back on constructing our netbios name from - * our hostname etc - */ - if (global_myname()) { - context->netbios_name = SMB_STRDUP(global_myname()); - } - else { - /* - * Hmmm, I want to get hostname as well, but I am too - * lazy for the moment - */ - pid = sys_getpid(); - context->netbios_name = (char *)SMB_MALLOC(17); - if (!context->netbios_name) { - errno = ENOMEM; - return NULL; - } - slprintf(context->netbios_name, 16, - "smbc%s%d", context->user, pid); - } - } - - DEBUG(1, ("Using netbios name %s.\n", context->netbios_name)); - - if (!context->workgroup) { - if (lp_workgroup()) { - context->workgroup = SMB_STRDUP(lp_workgroup()); - } - else { - /* TODO: Think about a decent default workgroup */ - context->workgroup = SMB_STRDUP("samba"); - } - } - - DEBUG(1, ("Using workgroup %s.\n", context->workgroup)); - - /* shortest timeout is 1 second */ - if (context->timeout > 0 && context->timeout < 1000) - context->timeout = 1000; - - /* - * FIXME: Should we check the function pointers here? - */ - - context->internal->_initialized = True; - - return context; -} - - -/* Return the verion of samba, and thus libsmbclient */ -const char * -smbc_version(void) -{ - return samba_version_string(); -} |