123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- /* sftp-file-type.c -- SFTP file type.
- *
- * Copyright (C) 2015, 2016 Artyom V. Poptsov <poptsov.artyom@gmail.com>
- * Copyright (C) 2017 Ludovic Courtès <ludo@gnu.org>
- *
- * This file is part of Guile-SSH.
- *
- * Guile-SSH 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.
- *
- * Guile-SSH 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 Guile-SSH. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <config.h>
- #include <libguile.h>
- #include <libssh/libssh.h>
- #include "common.h"
- #include "error.h"
- #include "sftp-session-type.h"
- #include "sftp-file-type.h"
- /* The SFTP file port type. Guile 2.2 introduced a new port API, so we have a
- separate implementation for these newer versions. */
- #if USING_GUILE_BEFORE_2_2
- static scm_t_bits sftp_file_tag; /* Smob tag. */
- #else
- static scm_t_port_type *sftp_file_tag;
- #endif
- enum {
- DEFAULT_PORT_R_BUFSZ = 256, /* Default read buffer size */
- DEFAULT_PORT_W_BUFSZ = 1 /* Default write buffer size */
- };
- /* Ptob callbacks. */
- #if USING_GUILE_BEFORE_2_2
- /* Read data from the channel. Return EOF if no data is available or
- throw `guile-ssh-error' if an error occured. */
- static int
- ptob_fill_input (SCM file)
- #define FUNC_NAME "ptob_fill_input"
- {
- struct sftp_file_data *fd = _scm_to_sftp_file_data (file);
- scm_port *pt = SCM_PTAB_ENTRY (file);
- ssize_t res;
- res = sftp_read (fd->file, pt->read_buf, pt->read_buf_size);
- if (! res)
- return EOF;
- else if (res < 0)
- guile_ssh_error1 (FUNC_NAME, "Error reading the file", file);
- pt->read_pos = pt->read_buf;
- pt->read_end = pt->read_buf + res;
- return *pt->read_buf;
- }
- #undef FUNC_NAME
- static void
- ptob_write (SCM file, const void* data, size_t sz)
- #define FUNC_NAME "ptob_write"
- {
- struct sftp_file_data *fd = _scm_to_sftp_file_data (file);
- ssize_t nwritten = sftp_write (fd->file, data, sz);
- if (nwritten != sz)
- guile_ssh_error1 (FUNC_NAME, "Error writing the file", file);
- }
- #undef FUNC_NAME
- #else /* !USING_GUILE_BEFORE_2_2 */
- static size_t
- read_from_sftp_file_port (SCM file, SCM dst, size_t start, size_t count)
- #define FUNC_NAME "read_from_sftp_file_port"
- {
- char *data = (char *) SCM_BYTEVECTOR_CONTENTS (dst) + start;
- struct sftp_file_data *fd = _scm_to_sftp_file_data (file);
- ssize_t res;
- res = sftp_read (fd->file, data, count);
- if (res < 0)
- guile_ssh_error1 (FUNC_NAME, "Error reading the file", file);
- return res;
- }
- #undef FUNC_NAME
- static size_t
- write_to_sftp_file_port (SCM file, SCM src, size_t start, size_t count)
- #define FUNC_NAME "write_to_sftp_file_port"
- {
- char *data = (char *) SCM_BYTEVECTOR_CONTENTS (src) + start;
- struct sftp_file_data *fd = _scm_to_sftp_file_data (file);
- ssize_t nwritten = sftp_write (fd->file, data, count);
- if (nwritten < 0)
- guile_ssh_error1 (FUNC_NAME, "Error reading the file", file);
- return nwritten;
- }
- #undef FUNC_NAME
- #endif /* !USING_GUILE_BEFORE_2_2 */
- static int
- ptob_input_waiting (SCM file)
- #define FUNC_NAME "ptob_input_waiting"
- {
- struct sftp_file_data *fd = _scm_to_sftp_file_data (file);
- sftp_attributes attr = sftp_fstat (fd->file);
- uint64_t pos = sftp_tell64 (fd->file);
- return attr->size - pos;
- }
- #undef FUNC_NAME
- #if USING_GUILE_BEFORE_2_2
- static SCM
- equalp_sftp_file (SCM x1, SCM x2)
- {
- struct sftp_file_data *fd1 = _scm_to_sftp_file_data (x1);
- struct sftp_file_data *fd2 = _scm_to_sftp_file_data (x2);
- if ((! fd1) || (! fd2))
- return SCM_BOOL_F;
- else if (fd1 != fd2)
- return SCM_BOOL_F;
- else
- return SCM_BOOL_T;
- }
- #endif
- static int
- print_sftp_file (SCM sftp_file, SCM port, scm_print_state *pstate)
- {
- struct sftp_file_data *fd = _scm_to_sftp_file_data (sftp_file);
- ssh_session session = fd->file->sftp->session;
- char *user = NULL;
- char *host = NULL;
- unsigned int ssh_port;
- int res;
- scm_puts ("#<sftp-file ", port);
- res = ssh_options_get (session, SSH_OPTIONS_USER, &user);
- scm_display ((res == SSH_OK) ? scm_from_locale_string (user) : SCM_UNDEFINED,
- port);
- ssh_string_free_char (user);
- scm_putc ('@', port);
- res = ssh_options_get (session, SSH_OPTIONS_HOST, &host);
- scm_display ((res == SSH_OK) ? scm_from_locale_string (host) : SCM_UNDEFINED,
- port);
- ssh_string_free_char (host);
- scm_putc (':', port);
- res = ssh_options_get_port (session, &ssh_port);
- scm_display ((res == SSH_OK) ? scm_from_int (ssh_port) : SCM_UNDEFINED,
- port);
- scm_putc (' ', port);
- scm_display (scm_port_filename (sftp_file), port);
- scm_putc (' ', port);
- scm_display (_scm_object_hex_address (sftp_file), port);
- scm_puts (">", port);
- return 1;
- }
- #if USING_GUILE_BEFORE_2_2
- /* Complete the processing of buffered output data. Currently this callback
- makes no effect because a SFTP_FILE uses unbuffered output. */
- static void
- ptob_flush (SCM sftp_file)
- #define FUNC_NAME "ptob_flush"
- {
- scm_port *pt = SCM_PTAB_ENTRY (sftp_file);
- size_t wrsize = pt->write_pos - pt->write_buf;
- if (wrsize)
- ptob_write (sftp_file, pt->write_buf, wrsize);
- pt->write_pos = pt->write_buf;
- }
- #undef FUNC_NAME
- #endif
- #if USING_GUILE_BEFORE_2_2
- static int
- #else
- static void
- #endif
- ptob_close (SCM sftp_file)
- {
- struct sftp_file_data *fd = _scm_to_sftp_file_data (sftp_file);
- #if USING_GUILE_BEFORE_2_2
- scm_port *pt = SCM_PTAB_ENTRY (sftp_file);
- ptob_flush (sftp_file);
- #endif
- if (fd)
- {
- sftp_close (fd->file);
- }
- #if USING_GUILE_BEFORE_2_2
- scm_gc_free (pt->write_buf, pt->write_buf_size, "port write buffer");
- scm_gc_free (pt->read_buf, pt->read_buf_size, "port read buffer");
- SCM_SETSTREAM (sftp_file, NULL);
- return 1;
- #endif
- }
- static scm_t_off
- ptob_seek (SCM port, scm_t_off offset, int whence)
- #define FUNC_NAME "ptob_seek"
- {
- struct sftp_file_data *fd = _scm_to_sftp_file_data (port);
- scm_t_off target;
- /* In Guile 2.2, PORT is flushed before this function is called; in 2.0 that
- wasn't the case. */
- #if USING_GUILE_BEFORE_2_2
- {
- scm_t_port *pt = SCM_PTAB_ENTRY (port);
- if (pt->rw_active == SCM_PORT_WRITE)
- ptob_flush (port);
- if (pt->rw_active == SCM_PORT_READ)
- scm_end_input (port);
- }
- #endif
- switch (whence)
- {
- case SEEK_CUR:
- {
- uint64_t current_pos = sftp_tell64 (fd->file);
- target = current_pos + offset;
- }
- break;
- case SEEK_END:
- {
- sftp_attributes attr = sftp_fstat (fd->file);
- if (! attr)
- {
- guile_ssh_error1 (FUNC_NAME,
- "Could not get file attributes",
- port);
- }
- target = attr->size - offset;
- }
- break;
- default: /* SEEK_SET */
- target = offset;
- break;
- }
- if (target < 0)
- scm_misc_error (FUNC_NAME, "negative offset", SCM_EOL);
- if (sftp_seek64 (fd->file, target))
- guile_ssh_error1 (FUNC_NAME, "Could not seek a file", port);
- return target;
- }
- #undef FUNC_NAME
- /* Public Scheme procedures */
- SCM_GSSH_DEFINE (gssh_sftp_open, "%gssh-sftp-open", 4,
- (SCM sftp_session, SCM path, SCM access_type, SCM mode))
- #define FUNC_NAME s_gssh_sftp_open
- {
- struct sftp_session_data *sftp_sd = _scm_to_sftp_session_data (sftp_session);
- sftp_file file;
- char* c_path;
- int c_access_type;
- mode_t c_mode;
- SCM_ASSERT (scm_is_string (path), path, SCM_ARG2, FUNC_NAME);
- SCM_ASSERT (scm_is_number (access_type), access_type, SCM_ARG3, FUNC_NAME);
- SCM_ASSERT (scm_is_number (mode), mode, SCM_ARG4, FUNC_NAME);
- scm_dynwind_begin (0);
- c_path = scm_to_locale_string (path);
- scm_dynwind_free (c_path);
- c_access_type = scm_to_uint (access_type);
- c_mode = scm_to_uint (mode);
- file = sftp_open (sftp_sd->sftp_session, c_path, c_access_type, c_mode);
- if (file == NULL)
- {
- guile_ssh_error1 (FUNC_NAME, "Could not open a file",
- scm_list_4 (sftp_session, path, access_type, mode));
- }
- scm_dynwind_end ();
- return _scm_from_sftp_file (file, path, sftp_session);
- }
- #undef FUNC_NAME
- SCM_GSSH_DEFINE (gssh_sftp_file_p, "%gssh-sftp-file?", 1, (SCM x))
- {
- #if USING_GUILE_BEFORE_2_2
- return scm_from_bool (SCM_SMOB_PREDICATE (sftp_file_tag, x));
- #else
- return scm_from_bool (SCM_PORTP (x)
- && SCM_PORT_TYPE (x) == sftp_file_tag);
- #endif
- }
- /* Public C procedures */
- /* Convert SCM object X to a SFTP file data object. */
- struct sftp_file_data *
- _scm_to_sftp_file_data (SCM x)
- {
- #if USING_GUILE_BEFORE_2_2
- scm_assert_smob_type (sftp_file_tag, x);
- #else
- SCM_ASSERT_TYPE (SCM_PORTP (x) && SCM_PORT_TYPE (x) == sftp_file_tag,
- x, 1, __func__, "sftp-file-port");
- #endif
- return (struct sftp_file_data *) SCM_STREAM (x);
- }
- /* Convert SFTP file FD to a SCM object; set SFTP_SESSION as a parent of the
- object. */
- SCM
- _scm_from_sftp_file (const sftp_file file, const SCM name, SCM sftp_session)
- {
- SCM ptob;
- struct sftp_file_data *fd = scm_gc_malloc (sizeof (struct sftp_file_data),
- "sftp file");
- fd->sftp_session = sftp_session;
- fd->file = file;
- #if USING_GUILE_BEFORE_2_2
- {
- scm_port *pt;
- ptob = scm_new_port_table_entry (sftp_file_tag);
- pt = SCM_PTAB_ENTRY (ptob);
- /* Output init */
- pt->write_buf_size = DEFAULT_PORT_R_BUFSZ;
- pt->write_buf = scm_gc_malloc (pt->write_buf_size, "port write buffer");
- pt->write_pos = pt->write_end = pt->write_buf;
- /* Input init */
- pt->read_buf_size = DEFAULT_PORT_W_BUFSZ;
- pt->read_buf = scm_gc_malloc (pt->read_buf_size, "port read buffer");
- pt->read_pos = pt->read_end = pt->read_buf;
- pt->rw_random = 1;
- SCM_SET_CELL_TYPE (ptob, sftp_file_tag | SCM_RDNG | SCM_WRTNG | SCM_OPN);
- SCM_SETSTREAM (ptob, fd);
- }
- #else
- ptob = scm_c_make_port (sftp_file_tag, SCM_RDNG | SCM_WRTNG | SCM_OPN,
- (scm_t_bits) fd);
- #endif
- scm_set_port_filename_x (ptob, name);
- return ptob;
- }
- /* Ptob initialization. */
- void
- init_sftp_file_type (void)
- {
- sftp_file_tag = scm_make_port_type ("sftp-file",
- #if USING_GUILE_BEFORE_2_2
- &ptob_fill_input,
- &ptob_write
- #else
- read_from_sftp_file_port,
- write_to_sftp_file_port
- #endif
- );
- #if USING_GUILE_BEFORE_2_2
- scm_set_port_flush (sftp_file_tag, ptob_flush);
- scm_set_port_equalp (sftp_file_tag, equalp_sftp_file);
- #endif
- scm_set_port_close (sftp_file_tag, ptob_close);
- scm_set_port_input_waiting (sftp_file_tag, ptob_input_waiting);
- scm_set_port_print (sftp_file_tag, print_sftp_file);
- scm_set_port_seek (sftp_file_tag, ptob_seek);
- #include "sftp-file-type.x"
- }
- /* sftp-file-type.c ends here. */
|