Exemplo n.º 1
0
ssize_t
SMBC_read_ctx(SMBCCTX *context,
              SMBCFILE *file,
              void *buf,
              size_t count)
{
	size_t ret;
	TALLOC_CTX *frame = talloc_stackframe();
	NTSTATUS status;

        /*
         * 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->initialized) {
		errno = EINVAL;
		TALLOC_FREE(frame);
		return -1;
	}

	DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));

	if (!file || !SMBC_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;
	}

	status = cli_read(file->targetcli, file->cli_fd, (char *)buf, offset,
			  count, &ret);
	if (!NT_STATUS_IS_OK(status)) {
		errno = SMBC_errno(context, file->targetcli);
		TALLOC_FREE(frame);
		return -1;
	}

	file->offset += ret;

	DEBUG(4, ("  --> %ld\n", (unsigned long)ret));

	TALLOC_FREE(frame);
	return ret;  /* Success, ret bytes of data ... */
}
Exemplo n.º 2
0
off_t
SMBC_splice_ctx(SMBCCTX *context,
                SMBCFILE *srcfile,
                SMBCFILE *dstfile,
                off_t count,
                int (*splice_cb)(off_t n, void *priv),
                void *priv)
{
	off_t written;
	TALLOC_CTX *frame = talloc_stackframe();
	NTSTATUS status;

	if (!context || !context->internal->initialized) {
		errno = EINVAL;
		TALLOC_FREE(frame);
		return -1;
	}

	if (!srcfile ||
	    !SMBC_dlist_contains(context->internal->files, srcfile))
	{
		errno = EBADF;
		TALLOC_FREE(frame);
		return -1;
	}

	if (!dstfile ||
	    !SMBC_dlist_contains(context->internal->files, dstfile))
	{
		errno = EBADF;
		TALLOC_FREE(frame);
		return -1;
	}

	status = cli_splice(srcfile->targetcli, dstfile->targetcli,
			    srcfile->cli_fd, dstfile->cli_fd,
			    count, srcfile->offset, dstfile->offset, &written,
			    splice_cb, priv);
	if (!NT_STATUS_IS_OK(status)) {
		errno = SMBC_errno(context, srcfile->targetcli);
		TALLOC_FREE(frame);
		return -1;
	}

	srcfile->offset += written;
	dstfile->offset += written;

	TALLOC_FREE(frame);
	return written;
}
Exemplo n.º 3
0
int
SMBC_close_ctx(SMBCCTX *context,
               SMBCFILE *file)
{
	TALLOC_CTX *frame = talloc_stackframe();

	if (!context || !context->internal->initialized) {
		errno = EINVAL;
		TALLOC_FREE(frame);
		return -1;
	}

	if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
		errno = EBADF;
		TALLOC_FREE(frame);
		return -1;
	}

	/* IS a dir ... */
	if (!file->file) {
		TALLOC_FREE(frame);
		return smbc_getFunctionClosedir(context)(context, file);
	}

	if (!NT_STATUS_IS_OK(cli_close(file->targetcli, file->cli_fd))) {
		SMBCSRV *srv;
		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, file->targetcli);
		srv = file->srv;
		DLIST_REMOVE(context->internal->files, file);
		SAFE_FREE(file->fname);
		SAFE_FREE(file);
		smbc_getFunctionRemoveUnusedServer(context)(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;
}
Exemplo n.º 4
0
/*
 * 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,
                 uint16_t port,
                 const char *share,
                 char **pp_workgroup,
                 char **pp_username,
                 char **pp_password)
{
        int flags;
	struct cli_state *ipc_cli = NULL;
	struct rpc_pipe_client *pipe_hnd = NULL;
        NTSTATUS nt_status;
	SMBCSRV *srv=NULL;
	SMBCSRV *ipc_srv=NULL;

	/*
	 * Use srv->cli->desthost and srv->cli->share instead of
	 * server and share below to connect to the actual share,
	 * i.e., a normal share or a referred share from
	 * 'msdfs proxy' share.
	 */
	srv = SMBC_server(ctx, context, true, server, port, share,
			pp_workgroup, pp_username, pp_password);
	if (!srv) {
		return NULL;
	}
	server = smbXcli_conn_remote_name(srv->cli->conn);
	share = srv->cli->share;

        /*
         * 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) {
		int signing_state = SMB_SIGNING_DEFAULT;

                /* 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 (smbc_getOptionUseKerberos(context)) {
                        flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
                }
                if (smbc_getOptionUseCCache(context)) {
                        flags |= CLI_FULL_CONNECTION_USE_CCACHE;
                }
		if (context->internal->smb_encryption_level != SMBC_ENCRYPTLEVEL_NONE) {
			signing_state = SMB_SIGNING_REQUIRED;
		}

                nt_status = cli_full_connection(&ipc_cli,
						lp_netbios_name(), server,
						NULL, 0, "IPC$", "?????",
						*pp_username,
						*pp_workgroup,
						*pp_password,
						flags,
						signing_state);
                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->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);
                DLIST_ADD(ipc_srv->cli, ipc_cli);

                nt_status = cli_rpc_pipe_open_noauth(
			ipc_srv->cli, &ndr_table_lsarpc, &pipe_hnd);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        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_FLAG_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);
                        free(ipc_srv);
                        return NULL;
                }

                /* now add it to the cache (internal or external) */

                errno = 0;      /* let cache function set errno if it likes */
                if (smbc_getFunctionAddCachedServer(context)(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;
}
Exemplo n.º 5
0
/*
 * 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_t mode)
{
        uint16_t 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 ||
            !NT_STATUS_IS_OK(cli_setpathinfo_basic(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 (!NT_STATUS_IS_OK(cli_open(srv->cli, path, O_RDWR, DENY_NONE, &fd))) {
                        errno = SMBC_errno(context, srv->cli);
			TALLOC_FREE(frame);
                        return -1;
                }

                /* Set the new attributes */
                ret = NT_STATUS_IS_OK(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_t) -1) {
                        ret = NT_STATUS_IS_OK(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;
}
Exemplo n.º 6
0
SMBCFILE *
SMBC_open_ctx(SMBCCTX *context,
              const char *fname,
              int flags,
              mode_t mode)
{
	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;
	SMBCSRV *srv   = NULL;
	SMBCFILE *file = NULL;
	uint16_t fd;
	uint16_t port = 0;
	NTSTATUS status = NT_STATUS_OBJECT_PATH_INVALID;
	TALLOC_CTX *frame = talloc_stackframe();

	if (!context || !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,
                            &port,
                            &share,
                            &path,
                            &user,
                            &password,
                            NULL)) {
		errno = EINVAL;
		TALLOC_FREE(frame);
		return NULL;
        }

	if (!user || user[0] == (char)0) {
		user = talloc_strdup(frame, smbc_getUser(context));
		if (!user) {
                	errno = ENOMEM;
			TALLOC_FREE(frame);
			return NULL;
		}
	}

	srv = SMBC_server(frame, context, True,
                          server, port, 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] == '\\') {
		status = NT_STATUS_OBJECT_PATH_INVALID;
	} 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);*/
		status = cli_resolve_path(
			frame, "", context->internal->auth_info,
			srv->cli, path, &targetcli, &targetpath);
		if (!NT_STATUS_IS_OK(status)) {
			d_printf("Could not resolve %s\n", path);
                        errno = ENOENT;
			SAFE_FREE(file);
			TALLOC_FREE(frame);
			return NULL;
		}
		/*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/

		status = cli_open(targetcli, targetpath, flags,
                                   context->internal->share_mode, &fd);
		if (!NT_STATUS_IS_OK(status)) {

			/* 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;
		/*
		 * targetcli is either equal to srv->cli or
		 * is a subsidiary DFS connection. Either way
		 * file->cli_fd belongs to it so we must cache
		 * it for read/write/close, not re-resolve each time.
		 * Re-resolving is both slow and incorrect.
		 */
		file->targetcli = targetcli;

		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 (!NT_STATUS_IS_OK(status)) {
		int eno = 0;

		eno = SMBC_errno(context, srv->cli);
		file = smbc_getFunctionOpendir(context)(context, fname);
		if (!file) errno = eno;
		TALLOC_FREE(frame);
		return file;
	}

	errno = EINVAL; /* FIXME, correct errno ? */
	TALLOC_FREE(frame);
	return NULL;
}
Exemplo n.º 7
0
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();
	NTSTATUS status;

	if (!context || !context->internal->initialized) {
		errno = EINVAL;
		TALLOC_FREE(frame);
		return -1;
	}

	if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
		errno = EBADF;
		TALLOC_FREE(frame);
		return -1;
	}

	/* IS a dir ... */
	if (!file->file) {
		TALLOC_FREE(frame);
		return smbc_getFunctionClosedir(context)(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);*/
	status = cli_resolve_path(frame, "", context->internal->auth_info,
				  file->srv->cli, path,
				  &targetcli, &targetpath);
	if (!NT_STATUS_IS_OK(status)) {
		d_printf("Could not resolve %s\n", path);
                errno = ENOENT;
		TALLOC_FREE(frame);
		return -1;
	}
	/*d_printf(">>>close: resolved path as %s\n", targetpath);*/

	if (!NT_STATUS_IS_OK(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);
		smbc_getFunctionRemoveUnusedServer(context)(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;
}
Exemplo n.º 8
0
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->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, smbc_getUser(context));
		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, fname, size, mode);

	st->st_atime = convert_timespec_to_time_t(access_time_ts);
	st->st_ctime = convert_timespec_to_time_t(change_time_ts);
	st->st_mtime = convert_timespec_to_time_t(write_time_ts);
	st->st_dev   = srv->dev;

	TALLOC_FREE(frame);
	return 0;
}
Exemplo n.º 9
0
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->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, smbc_getUser(context));
		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;
        
}
Exemplo n.º 10
0
ssize_t
SMBC_read_ctx(SMBCCTX *context,
              SMBCFILE *file,
              void *buf,
              size_t count)
{
	size_t 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();
	NTSTATUS status;

        /*
         * 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->initialized) {
		errno = EINVAL;
		TALLOC_FREE(frame);
		return -1;
	}

	DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));

	if (!file || !SMBC_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);*/
	status = cli_resolve_path(frame, "", context->internal->auth_info,
				  file->srv->cli, path,
				  &targetcli, &targetpath);
	if (!NT_STATUS_IS_OK(status)) {
		d_printf("Could not resolve %s\n", path);
                errno = ENOENT;
		TALLOC_FREE(frame);
		return -1;
	}
	/*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/

	status = cli_read(targetcli, file->cli_fd, (char *)buf, offset,
			  count, &ret);
	if (!NT_STATUS_IS_OK(status)) {
		errno = SMBC_errno(context, targetcli);
		TALLOC_FREE(frame);
		return -1;
	}

	file->offset += ret;

	DEBUG(4, ("  --> %ld\n", (unsigned long)ret));

	TALLOC_FREE(frame);
	return ret;  /* Success, ret bytes of data ... */
}
Exemplo n.º 11
0
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->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, smbc_getUser(context));
		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;
        
}
ssize_t
SMBC_write_ctx(SMBCCTX *context,
               SMBCFILE *file,
               const 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->initialized) {
                
		errno = EINVAL;
		TALLOC_FREE(frame);
		return -1;
                
	}
        
	if (!file || !SMBC_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);
                errno = ENOENT;
		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 ... */
}
Exemplo n.º 13
0
/*
 * 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 (smbc_getOptionUseKerberos(context)) {
                        flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
                }
                
                zero_sockaddr(&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->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;
                
                nt_status = cli_rpc_pipe_open_noauth(
			ipc_srv->cli, &ndr_table_lsarpc.syntax_id, &pipe_hnd);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        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 (smbc_getFunctionAddCachedServer(context)(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;
}
Exemplo n.º 14
0
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;
	char *workgroup = 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;
        int is_ipc = (share != NULL && strcmp(share, "IPC$") == 0);
	uint32 fs_attrs = 0;
        const char *username_used;
 	NTSTATUS status;
        
	zero_sockaddr(&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' &&
            smbc_getOptionOneSharePerServer(context)) {
                
                /*
                 * ... then if there's no current connection to the share,
                 * connect to it.  SMBC_find_server(), or rather the function
                 * pointed to by context->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;
				smbc_getFunctionRemoveCachedServer(context)(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;
                                smbc_getFunctionRemoveCachedServer(context)(context,
                                                                            srv);
                                srv = NULL;
                        }
                        
                        /* Determine if this share supports case sensitivity */
                        if (is_ipc) {
                                DEBUG(4,
                                      ("IPC$ so ignore case sensitivity\n"));
                        } else if (!cli_get_fs_attr_info(c, &fs_attrs)) {
                                DEBUG(4, ("Could not retrieve "
                                          "case sensitivity flag: %s.\n",
                                          cli_errstr(c)));

                                /*
                                 * We can't determine the case sensitivity of
                                 * the share. We have no choice but to use the
                                 * user-specified case sensitivity setting.
                                 */
                                if (smbc_getOptionCaseSensitive(context)) {
                                        cli_set_case_sensitive(c, True);
                                } else {
                                        cli_set_case_sensitive(c, False);
                                }
                        } else {
                                DEBUG(4,
                                      ("Case sensitive: %s\n",
                                       (fs_attrs & FILE_CASE_SENSITIVE_SEARCH
                                        ? "True"
                                        : "False")));
                                cli_set_case_sensitive(
                                        c,
                                        (fs_attrs & FILE_CASE_SENSITIVE_SEARCH
                                         ? True
                                         : False));
                        }

                        /*
                         * 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. */
                goto done;
        }
        
        /* 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, smbc_getNetbiosName(context), 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_sockaddr(&ss);
        
	/* have to open a new connection */
	if ((c = cli_initialise()) == NULL) {
		errno = ENOMEM;
		return NULL;
	}

        if (smbc_getOptionUseKerberos(context)) {
		c->use_kerberos = True;
	}

        if (smbc_getOptionFallbackAfterKerberos(context)) {
		c->fallback_after_kerberos = True;
	}
        
	c->timeout = smbc_getTimeout(context);
        
        /*
         * 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' || is_ipc) {
                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 (smbc_getOptionNoAutoAnonymousLogin(context) ||
                    !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;
                }
	}

	cli_init_creds(c, username_used, *pp_workgroup, *pp_password);
        
	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"));
        
        /* Determine if this share supports case sensitivity */
	if (is_ipc) {
                DEBUG(4, ("IPC$ so ignore case sensitivity\n"));
        } else if (!cli_get_fs_attr_info(c, &fs_attrs)) {
                DEBUG(4, ("Could not retrieve case sensitivity flag: %s.\n",
                          cli_errstr(c)));

                /*
                 * We can't determine the case sensitivity of the share. We
                 * have no choice but to use the user-specified case
                 * sensitivity setting.
                 */
                if (smbc_getOptionCaseSensitive(context)) {
                        cli_set_case_sensitive(c, True);
                } else {
                        cli_set_case_sensitive(c, False);
                }
	} else {
                DEBUG(4, ("Case sensitive: %s\n",
                          (fs_attrs & FILE_CASE_SENSITIVE_SEARCH
                           ? "True"
                           : "False")));
                cli_set_case_sensitive(c,
                                       (fs_attrs & FILE_CASE_SENSITIVE_SEARCH
                                        ? True
                                        : False));
        }

	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->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 (smbc_getFunctionAddCachedServer(context)(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);
done:
	if (!pp_workgroup || !*pp_workgroup || !**pp_workgroup) {
		workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context));
	} else {
		workgroup = *pp_workgroup;
	}
	if(!workgroup) {
		return NULL;
	}
	
	/* set the credentials to make DFS work */
	smbc_set_credentials_with_fallback(context,
					   workgroup,
				    	   *pp_username,
				   	   *pp_password);
	
	return srv;
        
failed:
	cli_shutdown(c);
	if (!srv) {
		return NULL;
	}
        
	SAFE_FREE(srv);
	return NULL;
}
Exemplo n.º 15
0
off_t
SMBC_splice_ctx(SMBCCTX *context,
                SMBCFILE *srcfile,
                SMBCFILE *dstfile,
                off_t count,
                int (*splice_cb)(off_t n, void *priv),
                void *priv)
{
	off_t written;
	char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
	char *path = NULL;
	char *targetpath = NULL;
	struct cli_state *srccli = NULL;
	struct cli_state *dstcli = NULL;
	uint16_t port = 0;
	TALLOC_CTX *frame = talloc_stackframe();
	NTSTATUS status;

	if (!context || !context->internal->initialized) {
		errno = EINVAL;
		TALLOC_FREE(frame);
		return -1;
	}

	if (!srcfile ||
	    !SMBC_dlist_contains(context->internal->files, srcfile))
	{
		errno = EBADF;
		TALLOC_FREE(frame);
		return -1;
	}

	if (!dstfile ||
	    !SMBC_dlist_contains(context->internal->files, dstfile))
	{
		errno = EBADF;
		TALLOC_FREE(frame);
		return -1;
	}

	if (SMBC_parse_path(frame,
                            context,
                            srcfile->fname,
                            NULL,
                            &server,
                            &port,
                            &share,
                            &path,
                            &user,
                            &password,
                            NULL)) {
                errno = EINVAL;
		TALLOC_FREE(frame);
                return -1;
        }

	status = cli_resolve_path(frame, "", context->internal->auth_info,
				  srcfile->srv->cli, path,
				  &srccli, &targetpath);
	if (!NT_STATUS_IS_OK(status)) {
		d_printf("Could not resolve %s\n", path);
                errno = ENOENT;
		TALLOC_FREE(frame);
		return -1;
	}

	if (SMBC_parse_path(frame,
                            context,
                            dstfile->fname,
                            NULL,
                            &server,
                            &port,
                            &share,
                            &path,
                            &user,
                            &password,
                            NULL)) {
                errno = EINVAL;
		TALLOC_FREE(frame);
                return -1;
        }

	status = cli_resolve_path(frame, "", context->internal->auth_info,
				  dstfile->srv->cli, path,
				  &dstcli, &targetpath);
	if (!NT_STATUS_IS_OK(status)) {
		d_printf("Could not resolve %s\n", path);
                errno = ENOENT;
		TALLOC_FREE(frame);
		return -1;
	}

	status = cli_splice(srccli, dstcli,
			    srcfile->cli_fd, dstfile->cli_fd,
			    count, srcfile->offset, dstfile->offset, &written,
			    splice_cb, priv);
	if (!NT_STATUS_IS_OK(status)) {
		errno = SMBC_errno(context, srccli);
		TALLOC_FREE(frame);
		return -1;
	}

	srcfile->offset += written;
	dstfile->offset += written;

	TALLOC_FREE(frame);
	return written;
}