Ticket #1535: SFTP support

Signed-off-by: Slava Zanko <slavazanko@gmail.com>
This commit is contained in:
Slava Zanko 2012-04-13 13:40:53 +03:00
parent 7893cbf9b1
commit da03697e84
24 changed files with 3435 additions and 0 deletions

View File

@ -573,6 +573,8 @@ src/vfs/fish/helpers/Makefile
src/vfs/ftpfs/Makefile
src/vfs/sftpfs/Makefile
src/vfs/local/Makefile
src/vfs/sfs/Makefile

View File

@ -20,6 +20,7 @@ Build requirements for GNU Midnight Commander
- slang or ncurses
- gettext
- cvs
- libssh2 >= 1.2.5 is required only for sftp vfs (1.2.7 if you need ssh-agent support)
Installation instructions for GNU Midnight Commander
@ -170,6 +171,10 @@ VFS options:
(on by default)
Support for FISH vfs
`--enable-vfs-sftp'
(off by default)
Support for SFTP vfs
`--enable-vfs-extfs'
(on by default)
Support for extfs

View File

@ -3097,6 +3097,9 @@ system for Linux systems),
.I fish
(for manipulating files over shell connections such as rsh and ssh).
If the code was compiled with
.I sftpfs
(for manipulating files over SFTP connections).
If the code was compiled with
.I smbfs
support, you can manipulate files on remote systems with the SMB (CIFS)
protocol.
@ -3224,6 +3227,41 @@ Examples:
sh://joe@noncompressed.ssh.edu/private
sh://joe@somehost.ssh.edu:2222/private
.fi
.\"NODE " SFTP (SSH File Transfer Protocol) filesystem"
.SH " SFTP (SSH File Transfer Protocol) filesystem"
The SFTP file system is a network based file system that allows you to
manipulate the files in a remote machine as if they were local.
.PP
To connect to a remote machine, you just need to chdir
into a special directory which name is in the following
format:
.PP
.I sftp://[user@]machine:[port]/[remote\-dir]
.PP
The
.I user,
.I port
and
.I remote\-dir
elements are optional. If you specify the
.I user
element, the Midnight Commander will try to login on the remote
machine as that user, otherwise it will use your login name.
.I port
\- specify the port used by remote server (22 by default).
If the
.I remote\-dir
element is present, your current directory on the remote machine will be
set to this one.
.PP
Examples:
.PP
.nf
sftp://onlyrsh.mx/linux/local
sftp://joe:password@want.compression.edu/private
sftp://joe@noncompressed.ssh.edu/private
sftp://joe@somehost.ssh.edu:2222/private
.fi
.\"NODE " Undelete File System"
.SH " Undelete File System"
On Linux systems, if you asked configure to use the ext2fs undelete

View File

@ -3415,6 +3415,10 @@ MC может быть собран с поддержкой файловой с
.\"FIle transfer over SHell filesystem"
.br
.\"LINK2"
Файловая система SFTP (SSH File Transfer Protocol)
.\"SFTP (SSH File Transfer Protocol) filesystem"
.br
.\"LINK2"
Файловая система UFS (Undelete File System)
.\"Undelete File System"
.br
@ -3575,6 +3579,43 @@ bash\-совместимая оболочка shell.
sh://joe@noncompressed.ssh.edu/private
sh://joe@somehost.ssh.edu:2222/private
.fi
.\"NODE " SFTP (SSH File Transfer Protocol) filesystem"
.SH " Файловая система SFTP (SSH File Transfer Protocol)"
Файловая система SFTP \- это сетевая файловая система, которая позволяет
работать с файлами на удаленном компьютере так, как если бы они были
расположены на вашем диске. Для того, чтобы это было возможно, на
удаленном компьютере должен быть запущен sftp\-сервер.
.PP
Для соединения с удаленным компьютером нужно выполнить команду
перехода в каталог (chdir), имя которого задается в следующем формате:
.PP
.I sftp://[user@]machine[:port]/[remote\-dir]
.PP
Элементы
.IR user ", " port
и
.I remote\-dir
не обязательны. Если задан элемент
.IR user ,
то Midnight Commander будет регистрироваться на удаленный компьютер под
этим именем, в противном случае \- под тем именем, с которым вы
зарегистрированы в локальной системе.
Если задан
.I port
то он будет использован для поключения к удалённому компьютеру.
Если задан элемент
.IR remote\-dir ,
то указанный каталог станет текущим после соединения с удаленным
компьютером.
.PP
Примеры:
.PP
.nf
sftp://onlyrsh.mx/linux/local
sftp://joe:password@want.compression.edu/private
sftp://joe@noncompressed.ssh.edu/private
sftp://joe@somehost.ssh.edu:2222/private
.fi
.\"NODE " Undelete File System"
.SH " Файловая система UFS (Undelete File System)"
В ОС Linux можно сконфигурировать файловую систему ext2fs, используемую

View File

@ -165,6 +165,9 @@ static name_keymap_t command_names[] = {
#ifdef ENABLE_VFS_FTP
{"ConnectFtp", CK_ConnectFtp},
#endif
#ifdef ENABLE_VFS_SFTP
{"ConnectSftp", CK_ConnectSftp},
#endif
#ifdef ENABLE_VFS_SMB
{"ConnectSmb", CK_ConnectSmb},
#endif

View File

@ -150,6 +150,7 @@ enum
CK_Filter,
CK_ConnectFish,
CK_ConnectFtp,
CK_ConnectSftp,
CK_ConnectSmb,
CK_PanelInfo,
CK_Jobs,

View File

@ -12,6 +12,7 @@ m4_include([m4.include/vfs/socket.m4])
m4_include([m4.include/vfs/mc-vfs-extfs.m4])
m4_include([m4.include/vfs/mc-vfs-sfs.m4])
m4_include([m4.include/vfs/mc-vfs-ftp.m4])
m4_include([m4.include/vfs/mc-vfs-sftp.m4])
m4_include([m4.include/vfs/mc-vfs-fish.m4])
m4_include([m4.include/vfs/mc-vfs-undelfs.m4])
m4_include([m4.include/vfs/mc-vfs-tarfs.m4])
@ -74,6 +75,7 @@ AC_DEFUN([AC_MC_VFS_CHECKS],
AC_MC_VFS_EXTFS
AC_MC_VFS_UNDELFS
AC_MC_VFS_FTP
AC_MC_VFS_SFTP
AC_MC_VFS_FISH
AC_MC_VFS_SMB

View File

@ -0,0 +1,19 @@
dnl Enable SFTP filesystem
AC_DEFUN([AC_MC_VFS_SFTP],
[
AC_ARG_ENABLE([vfs-sftp],
AS_HELP_STRING([--enable-vfs-sftp], [Support for SFTP filesystem [[yes]]]))
if test "$enable_vfs" != "no" -a x"$enable_vfs_sftp" != x"no"; then
PKG_CHECK_MODULES(LIBSSH, [libssh2 >= 1.2.5], [found_libssh=yes], [:])
if test x"$found_libssh" = "xyes"; then
AC_MC_VFS_ADDNAME([sftp])
AC_DEFINE([ENABLE_VFS_SFTP], [1], [Support for SFTP filesystem])
MCLIBS="$MCLIBS $LIBSSH_LIBS"
enable_vfs_sftp="yes"
else
enable_vfs_sftp="no"
AC_ERROR([libssh2 >= 1.2.5 library not found])
fi
fi
AM_CONDITIONAL([ENABLE_VFS_SFTP], [test "$enable_vfs" = "yes" -a x"$enable_vfs_sftp" = x"yes"])
])

View File

@ -1455,6 +1455,18 @@ ftplink_cmd (void)
/* --------------------------------------------------------------------------------------------- */
#ifdef ENABLE_VFS_SFTP
void
sftplink_cmd (void)
{
nice_cd (_("SFTP to machine"), _(machine_str),
"[SFTP (SSH File Transfer Protocol) filesystem]",
":sftplink_cmd: SFTP to machine ", "sftp://", 1, TRUE);
}
#endif /* ENABLE_VFS_SFTP */
/* --------------------------------------------------------------------------------------------- */
#ifdef ENABLE_VFS_FISH
void
fishlink_cmd (void)

View File

@ -38,6 +38,9 @@ typedef enum
#ifdef ENABLE_VFS_FTP
void ftplink_cmd (void);
#endif
#ifdef ENABLE_VFS_SFTP
void sftplink_cmd (void);
#endif
#ifdef ENABLE_VFS_FISH
void fishlink_cmd (void);
#endif

View File

@ -212,6 +212,9 @@ create_panel_menu (void)
#ifdef ENABLE_VFS_FISH
entries = g_list_prepend (entries, menu_entry_create (_("S&hell link..."), CK_ConnectFish));
#endif
#ifdef ENABLE_VFS_SFTP
entries = g_list_prepend (entries, menu_entry_create (_("S&FTP link..."), CK_ConnectSftp));
#endif
#ifdef ENABLE_VFS_SMB
entries = g_list_prepend (entries, menu_entry_create (_("SM&B link..."), CK_ConnectSmb));
#endif
@ -1204,6 +1207,11 @@ midnight_execute_cmd (Widget * sender, unsigned long command)
ftplink_cmd ();
break;
#endif
#ifdef ENABLE_VFS_SFTP
case CK_ConnectSftp:
sftplink_cmd ();
break;
#endif
#ifdef ENABLE_VFS_SMB
case CK_ConnectSmb:
smblink_cmd ();

View File

@ -26,6 +26,11 @@ SUBDIRS += ftpfs
libmc_vfs_la_LIBADD += ftpfs/libvfs-ftpfs.la
endif
if ENABLE_VFS_SFTP
SUBDIRS += sftpfs
libmc_vfs_la_LIBADD += sftpfs/libvfs-sftpfs.la
endif
if ENABLE_VFS_SFS
SUBDIRS += sfs
libmc_vfs_la_LIBADD += sfs/libvfs-sfs.la

View File

@ -54,6 +54,10 @@
#include "ftpfs/ftpfs.h"
#endif
#ifdef ENABLE_VFS_SFTP
#include "sftpfs/init.h"
#endif
#ifdef ENABLE_VFS_SFS
#include "sfs/sfs.h"
#endif
@ -112,6 +116,9 @@ vfs_plugins_init (void)
#ifdef ENABLE_VFS_FTP
init_ftpfs ();
#endif /* ENABLE_VFS_FTP */
#ifdef ENABLE_VFS_SFTP
init_sftpfs ();
#endif /* ENABLE_VFS_SFTP */
#ifdef ENABLE_VFS_FISH
init_fish ();
#endif /* ENABLE_VFS_FISH */

View File

@ -0,0 +1,14 @@
AM_CFLAGS = $(GLIB_CFLAGS) $(LIBSSH_CFLAGS) -I$(top_srcdir)
AM_CPPFLAGS =
noinst_LTLIBRARIES = libvfs-sftpfs.la
libvfs_sftpfs_la_SOURCES = \
config_parcer.c \
connection.c \
dir.c \
file.c \
init.c init.h \
internal.c internal.h \
vfs_class.c \
vfs_subclass.c

View File

@ -0,0 +1,420 @@
/* Virtual File System: SFTP file system.
The SSH config parser
Copyright (C) 2011
The Free Software Foundation, Inc.
Written by:
Ilia Maslakov <il.smind@gmail.com>, 2011
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
This file is part of the Midnight Commander.
The Midnight Commander 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.
The Midnight Commander 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 <config.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h> /* atoi() */
#include "lib/global.h"
#include "lib/search.h"
#include "lib/vfs/utilvfs.h"
#include "internal.h"
/*** global variables ****************************************************************************/
/*** file scope macro definitions ****************************************************************/
#define SFTP_DEFAULT_PORT 22
#ifndef SFTPFS_SSH_CONFIG
#define SFTPFS_SSH_CONFIG "~/.ssh/config"
#endif
/*** file scope type declarations ****************************************************************/
typedef struct
{
char *real_host; /* host DNS name or ip address */
int port; /* port for connect to host */
char *user; /* the user to log in as */
gboolean password_auth; /* FALSE - no passwords allowed (default TRUE) */
gboolean identities_only; /* TRUE - no ssh agent (default FALSE) */
gboolean pubkey_auth; /* FALSE - disable public key authentication (default TRUE) */
char *identity_file; /* A file from which the user's DSA, ECDSA or DSA authentication identity is read. */
} sftpfs_ssh_config_entity_t;
enum config_var_type
{
STRING,
INTEGER,
BOOLEAN,
FILENAME
};
/*** file scope variables ************************************************************************/
/* *INDENT-OFF* */
struct
{
const char *pattern;
mc_search_t *pattern_regexp;
enum config_var_type type;
size_t offset;
} config_variables[] =
{
{"^\\s*User\\s+(.*)$", NULL, STRING, 0},
{"^\\s*HostName\\s+(.*)$", NULL, STRING, 0},
{"^\\s*IdentitiesOnly\\s+(.*)$", NULL, BOOLEAN, 0},
{"^\\s*IdentityFile\\s+(.*)$", NULL, FILENAME, 0},
{"^\\s*Port\\s+(.*)$", NULL, INTEGER, 0},
{"^\\s*PasswordAuthentication\\s+(.*)$", NULL, BOOLEAN, 0},
{"^\\s*PubkeyAuthentication\\s+(.*)$", NULL, STRING, 0},
{NULL, NULL, 0, 0}
};
/* *INDENT-ON* */
/*** file scope functions ************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/**
* Free one config entity.
*
* @param config_entity config entity structure
*/
static void
sftpfs_ssh_config_entity_free (sftpfs_ssh_config_entity_t * config_entity)
{
g_free (config_entity->real_host);
g_free (config_entity->user);
g_free (config_entity->identity_file);
g_free (config_entity);
}
/* --------------------------------------------------------------------------------------------- */
/**
* Transform tilda (~) to full home dirname.
*
* @param filename file name with tilda
* @return newly allocated file name with full home dirname
*/
static char *
sftpfs_correct_file_name (const char *filename)
{
vfs_path_t *vpath;
char *ret_value;
vpath = vfs_path_from_str (filename);
ret_value = vfs_path_to_str (vpath);
vfs_path_free (vpath);
return ret_value;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Parse string and filling one config entity by parsed data.
*
* @param config_entity config entity structure
* @param buffer string for parce
*/
#define POINTER_TO_STRUCTURE_MEMBER(type) \
((type) ((void *) config_entity + (off_t) config_variables[i].offset))
static void
sftpfs_fill_config_entity_from_string (sftpfs_ssh_config_entity_t * config_entity, char *buffer)
{
int i;
for (i = 0; config_variables[i].pattern != NULL; i++)
{
if (mc_search_run (config_variables[i].pattern_regexp, buffer, 0, strlen (buffer), NULL))
{
int value_offset;
char *value;
int *pointer_int;
char **pointer_str;
gboolean *pointer_bool;
/* Calculate start of value in string */
value_offset = mc_search_getstart_result_by_num (config_variables[i].pattern_regexp, 1);
value = &buffer[value_offset];
switch (config_variables[i].type)
{
case STRING:
pointer_str = POINTER_TO_STRUCTURE_MEMBER (char **);
*pointer_str = g_strdup (value);
break;
case FILENAME:
pointer_str = POINTER_TO_STRUCTURE_MEMBER (char **);
*pointer_str = sftpfs_correct_file_name (value);
break;
case INTEGER:
pointer_int = POINTER_TO_STRUCTURE_MEMBER (int *);
*pointer_int = atoi (value);
break;
case BOOLEAN:
pointer_bool = POINTER_TO_STRUCTURE_MEMBER (gboolean *);
*pointer_bool = strcasecmp (value, "True") == 0;
break;
default:
continue;
}
return;
}
}
}
#undef POINTER_TO_STRUCTURE_MEMBER
/* --------------------------------------------------------------------------------------------- */
/**
* Fill one config entity from config file.
*
* @param ssh_config_handler file descriptor for the ssh config file
* @param config_entity config entity structure
* @param vpath_element path element with host data (hostname, port)
* @param error pointer to the error handler
* @return TRUE if config entity was filled sucessfully, FALSE otherwise
*/
static gboolean
sftpfs_fill_config_entity_from_config (FILE * ssh_config_handler,
sftpfs_ssh_config_entity_t * config_entity,
const vfs_path_element_t * vpath_element, GError ** error)
{
char buffer[BUF_MEDIUM];
gboolean host_block_hit = FALSE;
gboolean pattern_block_hit = FALSE;
mc_search_t *host_regexp;
host_regexp = mc_search_new ("^\\s*host\\s+(.*)$", -1);
host_regexp->search_type = MC_SEARCH_T_REGEX;
host_regexp->is_case_sensitive = FALSE;
while (!feof (ssh_config_handler))
{
char *cr;
if (fgets (buffer, BUF_MEDIUM, ssh_config_handler) == NULL)
{
if (errno != 0)
{
g_set_error (error, MC_ERROR, errno,
_("sftp: an error occured while reading %s: %s"), SFTPFS_SSH_CONFIG,
strerror (errno));
mc_search_free (host_regexp);
return FALSE;
}
break;
}
cr = strrchr (buffer, '\n');
if (cr != NULL)
*cr = '\0';
if (mc_search_run (host_regexp, buffer, 0, strlen (buffer), NULL))
{
const char *host_pattern;
int host_pattern_offset;
/* if previous host block exactly describe our connection */
if (host_block_hit)
return TRUE;
host_pattern_offset = mc_search_getstart_result_by_num (host_regexp, 1);
host_pattern = &buffer[host_pattern_offset];
if (strcmp (host_pattern, vpath_element->host) == 0)
{
/* current host block describe our connection */
host_block_hit = TRUE;
}
else
{
mc_search_t *pattern_regexp;
pattern_block_hit = FALSE;
pattern_regexp = mc_search_new (host_pattern, -1);
pattern_regexp->search_type = MC_SEARCH_T_GLOB;
pattern_regexp->is_case_sensitive = FALSE;
pattern_regexp->is_entire_line = TRUE;
pattern_block_hit =
mc_search_run (pattern_regexp, vpath_element->host, 0,
strlen (vpath_element->host), NULL);
mc_search_free (pattern_regexp);
}
}
else if (pattern_block_hit || host_block_hit)
{
sftpfs_fill_config_entity_from_string (config_entity, buffer);
}
}
mc_search_free (host_regexp);
return TRUE;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Open the ssh config file and fill config entity.
*
* @param vpath_element path element with host data (hostname, port)
* @param error pointer to the error handler
* @return newly allocated config entity structure
*/
static sftpfs_ssh_config_entity_t *
sftpfs_get_config_entity (const vfs_path_element_t * vpath_element, GError ** error)
{
sftpfs_ssh_config_entity_t *config_entity;
FILE *ssh_config_handler;
char *config_filename;
config_entity = g_new0 (sftpfs_ssh_config_entity_t, 1);
config_entity->password_auth = TRUE;
config_entity->identities_only = FALSE;
config_entity->pubkey_auth = TRUE;
config_entity->port = SFTP_DEFAULT_PORT;
config_filename = sftpfs_correct_file_name (SFTPFS_SSH_CONFIG);
ssh_config_handler = fopen (config_filename, "r");
g_free (config_filename);
if (ssh_config_handler != NULL)
{
gboolean ok;
ok = sftpfs_fill_config_entity_from_config
(ssh_config_handler, config_entity, vpath_element, error);
fclose (ssh_config_handler);
if (!ok)
{
sftpfs_ssh_config_entity_free (config_entity);
return NULL;
}
}
if (config_entity->user == NULL)
{
config_entity->user = vfs_get_local_username ();
if (config_entity->user == NULL)
{
sftpfs_ssh_config_entity_free (config_entity);
config_entity = NULL;
g_set_error (error, MC_ERROR, EPERM, _("sftp: Unable to get current user name."));
}
}
return config_entity;
}
/* --------------------------------------------------------------------------------------------- */
/*** public functions ****************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/**
* Reads data from the ssh config file related to connection.
*
* @param super connection data
* @param error pointer to the error handler
*/
void
sftpfs_fill_connection_data_from_config (struct vfs_s_super *super, GError ** error)
{
sftpfs_super_data_t *super_data;
sftpfs_ssh_config_entity_t *config_entity;
super_data = (sftpfs_super_data_t *) super->data;
config_entity = sftpfs_get_config_entity (super->path_element, error);
if (config_entity == NULL)
return;
super_data->config_auth_type = NONE;
super_data->config_auth_type |= (config_entity->pubkey_auth) ? PUBKEY : 0;
super_data->config_auth_type |= (config_entity->identities_only) ? 0 : AGENT;
super_data->config_auth_type |= (config_entity->password_auth) ? PASSWORD : 0;
if (super->path_element->port == 0)
super->path_element->port = config_entity->port;
if (super->path_element->user == NULL)
super->path_element->user = g_strdup (config_entity->user);
if (config_entity->real_host != NULL)
{
g_free (super->path_element->host);
super->path_element->host = g_strdup (config_entity->real_host);
}
if (config_entity->identity_file != NULL)
{
super_data->privkey = g_strdup (config_entity->identity_file);
super_data->pubkey = g_strdup_printf ("%s.pub", config_entity->identity_file);
}
sftpfs_ssh_config_entity_free (config_entity);
}
/* --------------------------------------------------------------------------------------------- */
/**
* Initialize the SSH config parser.
*/
void
sftpfs_init_config_variables_patterns (void)
{
size_t structure_offsets[] = {
offsetof(sftpfs_ssh_config_entity_t, user),
offsetof(sftpfs_ssh_config_entity_t, real_host),
offsetof(sftpfs_ssh_config_entity_t, identities_only),
offsetof(sftpfs_ssh_config_entity_t, identity_file),
offsetof(sftpfs_ssh_config_entity_t, port),
offsetof(sftpfs_ssh_config_entity_t, password_auth),
offsetof(sftpfs_ssh_config_entity_t, pubkey_auth)
};
int i;
for (i = 0; config_variables[i].pattern != NULL; i++)
{
config_variables[i].pattern_regexp = mc_search_new (config_variables[i].pattern, -1);
config_variables[i].pattern_regexp->search_type = MC_SEARCH_T_REGEX;
config_variables[i].pattern_regexp->is_case_sensitive = FALSE;
config_variables[i].offset = structure_offsets[i];
}
}
/* --------------------------------------------------------------------------------------------- */
/**
* Deinitialize the SSH config parser.
*/
void
sftpfs_deinit_config_variables_patterns (void)
{
int i;
for (i = 0; config_variables[i].pattern != NULL; i++)
{
mc_search_free (config_variables[i].pattern_regexp);
config_variables[i].pattern_regexp = NULL;
}
}
/* --------------------------------------------------------------------------------------------- */

468
src/vfs/sftpfs/connection.c Normal file
View File

@ -0,0 +1,468 @@
/* Virtual File System: SFTP file system.
The internal functions: connections
Copyright (C) 2011
The Free Software Foundation, Inc.
Written by:
Ilia Maslakov <il.smind@gmail.com>, 2011
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
This file is part of the Midnight Commander.
The Midnight Commander 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.
The Midnight Commander 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 <config.h>
#include <errno.h>
#include <netdb.h> /* struct hostent */
#include <sys/socket.h> /* AF_INET */
#include <netinet/in.h> /* struct in_addr */
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include <libssh2.h>
#include <libssh2_sftp.h>
#include "lib/global.h"
#include "lib/util.h"
#include "lib/tty/tty.h" /* tty_enable_interrupt_key () */
#include "lib/vfs/utilvfs.h"
#include "internal.h"
/*** global variables ****************************************************************************/
/*** file scope macro definitions ****************************************************************/
/*** file scope type declarations ****************************************************************/
/*** file scope variables ************************************************************************/
/*** file scope functions ************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/**
* Create socket to host.
*
* @param super connection data
* @param error pointer to the error handler
* @return socket descriptor number, -1 if any error was occured
*/
static int
sftpfs_open_socket (struct vfs_s_super *super, GError ** error)
{
struct addrinfo hints, *res = NULL, *curr_res;
int my_socket = 0;
char port[BUF_TINY];
int e;
if (super->path_element->host == NULL || *super->path_element->host == '\0')
{
g_set_error (error, MC_ERROR, -1, _("sftp: Invalid host name."));
return -1;
}
sprintf (port, "%hu", (unsigned short) super->path_element->port);
if (port == NULL)
{
g_set_error (error, MC_ERROR, -1, _("sftp: Invalid port value."));
return -1;
}
tty_enable_interrupt_key (); /* clear the interrupt flag */
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
#ifdef AI_ADDRCONFIG
/* By default, only look up addresses using address types for
* which a local interface is configured (i.e. no IPv6 if no IPv6
* interfaces, likewise for IPv4 (see RFC 3493 for details). */
hints.ai_flags = AI_ADDRCONFIG;
#endif
e = getaddrinfo (super->path_element->host, port, &hints, &res);
#ifdef AI_ADDRCONFIG
if (e == EAI_BADFLAGS)
{
/* Retry with no flags if AI_ADDRCONFIG was rejected. */
hints.ai_flags = 0;
e = getaddrinfo (super->path_element->host, port, &hints, &res);
}
#endif
if (e != 0)
{
g_set_error (error, MC_ERROR, -1, _("sftp: %s"), gai_strerror (e));
my_socket = -1;
goto ret;
}
for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
{
my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
if (my_socket < 0)
{
if (curr_res->ai_next != NULL)
continue;
vfs_print_message (_("sftp: %s"), unix_error_string (errno));
my_socket = -1;
goto ret;
}
vfs_print_message (_("sftp: making connection to %s"), super->path_element->host);
if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
break;
close (my_socket);
if (errno == EINTR && tty_got_interrupt ())
g_set_error (error, MC_ERROR, -1, _("sftp: connection interrupted by user"));
else if (res->ai_next == NULL)
g_set_error (error, MC_ERROR, -1, _("sftp: connection to server failed: %s"),
unix_error_string (errno));
else
continue;
my_socket = -1;
break;
}
ret:
if (res != NULL)
freeaddrinfo (res);
tty_disable_interrupt_key ();
return my_socket;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Recognize authenticaion types supported by remote side and filling internal 'super' structure by
* proper enum's values.
*
* @param super connection data
*/
static void
sftpfs_recognize_auth_types (struct vfs_s_super *super)
{
char *userauthlist;
sftpfs_super_data_t *super_data;
super_data = (sftpfs_super_data_t *) super->data;
super_data->auth_type = NONE;
/* check what authentication methods are available */
userauthlist = libssh2_userauth_list (super_data->session, super->path_element->user,
strlen (super->path_element->user));
if ((strstr (userauthlist, "password") != NULL
|| strstr (userauthlist, "keyboard-interactive") != NULL)
&& (super_data->config_auth_type & PASSWORD) != 0)
super_data->auth_type |= PASSWORD;
if (strstr (userauthlist, "publickey") != NULL && (super_data->config_auth_type & PUBKEY) != 0)
super_data->auth_type |= PUBKEY;
if ((super_data->config_auth_type & AGENT) != 0)
super_data->auth_type |= AGENT;
g_free (userauthlist);
}
/* --------------------------------------------------------------------------------------------- */
/**
* Open connection to host using SSH-agent helper.
*
* @param super connection data
* @param error pointer to the error handler
* @return TRUE if connection was sucessfully opened, FALSE otherwise
*/
static gboolean
sftpfs_open_connection_ssh_agent (struct vfs_s_super *super, GError ** error)
{
sftpfs_super_data_t *super_data;
struct libssh2_agent_publickey *identity, *prev_identity = NULL;
int rc;
(void) error;
super_data = (sftpfs_super_data_t *) super->data;
super_data->agent = NULL;
if ((super_data->auth_type & AGENT) == 0)
return FALSE;
/* Connect to the ssh-agent */
super_data->agent = libssh2_agent_init (super_data->session);
if (super_data->agent == NULL)
return FALSE;
if (libssh2_agent_connect (super_data->agent) != 0)
return FALSE;
if (libssh2_agent_list_identities (super_data->agent) != 0)
return FALSE;
while (TRUE)
{
rc = libssh2_agent_get_identity (super_data->agent, &identity, prev_identity);
if (rc == 1)
break;
if (rc < 0)
return FALSE;
if (libssh2_agent_userauth (super_data->agent, super->path_element->user, identity) == 0)
break;
prev_identity = identity;
}
return (rc == 0);
}
/* --------------------------------------------------------------------------------------------- */
/**
* Open connection to host using SSH-keypair.
*
* @param super connection data
* @param error pointer to the error handler
* @return TRUE if connection was sucessfully opened, FALSE otherwise
*/
static gboolean
sftpfs_open_connection_ssh_key (struct vfs_s_super *super, GError ** error)
{
sftpfs_super_data_t *super_data;
char *p, *passwd;
gboolean ret_value = FALSE;
super_data = (sftpfs_super_data_t *) super->data;
if ((super_data->auth_type & PUBKEY) == 0)
return FALSE;
if (super_data->privkey == NULL)
return FALSE;
if (libssh2_userauth_publickey_fromfile (super_data->session, super->path_element->user,
super_data->pubkey, super_data->privkey,
super->path_element->password) == 0)
return TRUE;
p = g_strdup_printf (_("sftp: Enter passphrase for %s "), super->path_element->user);
passwd = vfs_get_password (p);
g_free (p);
if (passwd == NULL)
g_set_error (error, MC_ERROR, -1, _("sftp: Passphrase is empty."));
else
{
ret_value = (libssh2_userauth_publickey_fromfile (super_data->session,
super->path_element->user,
super_data->pubkey, super_data->privkey,
passwd) == 0);
g_free (passwd);
}
return ret_value;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Open connection to host using password.
*
* @param super connection data
* @param error pointer to the error handler
* @return TRUE if connection was sucessfully opened, FALSE otherwise
*/
static gboolean
sftpfs_open_connection_ssh_password (struct vfs_s_super *super, GError ** error)
{
sftpfs_super_data_t *super_data;
char *p, *passwd;
gboolean ret_value = FALSE;
int rc;
super_data = (sftpfs_super_data_t *) super->data;
if ((super_data->auth_type & PASSWORD) == 0)
return FALSE;
if (super->path_element->password != NULL)
{
while ((rc = libssh2_userauth_password (super_data->session, super->path_element->user,
super->path_element->password)) ==
LIBSSH2_ERROR_EAGAIN);
if (rc == 0)
return TRUE;
}
p = g_strdup_printf (_("sftp: Enter password for %s "), super->path_element->user);
passwd = vfs_get_password (p);
g_free (p);
if (passwd == NULL)
g_set_error (error, MC_ERROR, -1, _("sftp: Password is empty."));
else
{
while ((rc = libssh2_userauth_password (super_data->session, super->path_element->user,
passwd)) == LIBSSH2_ERROR_EAGAIN)
;
if (rc == 0)
{
ret_value = TRUE;
g_free (super->path_element->password);
super->path_element->password = passwd;
}
else
g_free (passwd);
}
return ret_value;
}
/* --------------------------------------------------------------------------------------------- */
/*** public functions ****************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/**
* Open new connection.
*
* @param super connection data
* @param error pointer to the error handler
* @return 0 if success, -1 otherwise
*/
int
sftpfs_open_connection (struct vfs_s_super *super, GError ** error)
{
int rc;
sftpfs_super_data_t *super_data;
super_data = (sftpfs_super_data_t *) super->data;
/* Create a session instance */
super_data->session = libssh2_session_init ();
if (super_data->session == NULL)
return -1;
/*
* The application code is responsible for creating the socket
* and establishing the connection
*/
super_data->socket_handle = sftpfs_open_socket (super, error);
if (super_data->socket_handle == -1)
goto deinit_by_error;
/* ... start it up. This will trade welcome banners, exchange keys,
* and setup crypto, compression, and MAC layers
*/
rc = libssh2_session_startup (super_data->session, super_data->socket_handle);
if (rc != 0)
{
g_set_error (error, MC_ERROR, -1, _("sftp: Failure establishing SSH session: (%d)"), rc);
goto deinit_by_error;
}
/* At this point we havn't yet authenticated. The first thing to do
* is check the hostkey's fingerprint against our known hosts Your app
* may have it hard coded, may go to a file, may present it to the
* user, that's your call
*/
super_data->fingerprint = libssh2_hostkey_hash (super_data->session, LIBSSH2_HOSTKEY_HASH_SHA1);
sftpfs_recognize_auth_types (super);
if (!sftpfs_open_connection_ssh_agent (super, error)
&& !sftpfs_open_connection_ssh_key (super, error)
&& !sftpfs_open_connection_ssh_password (super, error))
goto deinit_by_error;
super_data->sftp_session = libssh2_sftp_init (super_data->session);
if (super_data->sftp_session == NULL)
goto deinit_by_error;
/* Since we have not set non-blocking, tell libssh2 we are blocking */
libssh2_session_set_blocking (super_data->session, 1);
return 0;
deinit_by_error:
sftpfs_close_connection (super, "Shutdown with errors", NULL);
return -1;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Close connection.
*
* @param super connection data
* @param shutdown_message message for shutdown functions
* @param error pointer to the error handler
*/
void
sftpfs_close_connection (struct vfs_s_super *super, const char *shutdown_message, GError ** error)
{
sftpfs_super_data_t *super_data;
(void) error;
super_data = (sftpfs_super_data_t *) super->data;
if (super_data == NULL)
return;
if (super_data->agent != NULL)
{
libssh2_agent_disconnect (super_data->agent);
libssh2_agent_free (super_data->agent);
super_data->agent = NULL;
}
if (super_data->sftp_session != NULL)
{
libssh2_sftp_shutdown (super_data->sftp_session);
super_data->sftp_session = NULL;
}
if (super_data->session != NULL)
{
libssh2_session_disconnect (super_data->session, shutdown_message);
super_data->session = NULL;
}
if (super_data->fingerprint != NULL)
super_data->fingerprint = NULL;
if (super_data->socket_handle != -1)
{
close (super_data->socket_handle);
super_data->socket_handle = -1;
}
}
/* --------------------------------------------------------------------------------------------- */

286
src/vfs/sftpfs/dir.c Normal file
View File

@ -0,0 +1,286 @@
/* Virtual File System: SFTP file system.
The internal functions: dirs
Copyright (C) 2011
The Free Software Foundation, Inc.
Written by:
Ilia Maslakov <il.smind@gmail.com>, 2011
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
This file is part of the Midnight Commander.
The Midnight Commander 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.
The Midnight Commander 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 <config.h>
#include <libssh2.h>
#include <libssh2_sftp.h>
#include "lib/global.h"
#include "internal.h"
/*** global variables ****************************************************************************/
/*** file scope macro definitions ****************************************************************/
/*** file scope type declarations ****************************************************************/
typedef struct
{
LIBSSH2_SFTP_HANDLE *handle;
sftpfs_super_data_t *super_data;
} sftpfs_dir_data_t;
/*** file scope variables ************************************************************************/
/*** file scope functions ************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------------------- */
/*** public functions ****************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/**
* Open a directory stream corresponding to the directory name.
*
* @param vpath path to directory
* @param error pointer to the error handler
* @return directory data handler if success, NULL otherwise
*/
void *
sftpfs_opendir (const vfs_path_t * vpath, GError ** error)
{
sftpfs_dir_data_t *sftpfs_dir;
struct vfs_s_super *super;
sftpfs_super_data_t *super_data;
const vfs_path_element_t *path_element;
LIBSSH2_SFTP_HANDLE *handle;
path_element = vfs_path_get_by_index (vpath, -1);
if (vfs_s_get_path (vpath, &super, 0) == NULL)
return NULL;
super_data = (sftpfs_super_data_t *) super->data;
while (TRUE)
{
int libssh_errno;
handle =
libssh2_sftp_opendir (super_data->sftp_session,
sftpfs_fix_filename (path_element->path));
if (handle != NULL)
break;
libssh_errno = libssh2_session_last_errno (super_data->session);
if (libssh_errno != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, libssh_errno, error);
return NULL;
}
sftpfs_waitsocket (super_data, error);
if (error != NULL && *error != NULL)
return NULL;
}
sftpfs_dir = g_new0 (sftpfs_dir_data_t, 1);
sftpfs_dir->handle = handle;
sftpfs_dir->super_data = super_data;
return (void *) sftpfs_dir;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Get a pointer to a structure representing the next directory entry.
*
* @param data directory data handler
* @param error pointer to the error handler
* @return information about direntry if success, NULL otherwise
*/
void *
sftpfs_readdir (void *data, GError ** error)
{
char mem[BUF_MEDIUM];
LIBSSH2_SFTP_ATTRIBUTES attrs;
sftpfs_dir_data_t *sftpfs_dir = (sftpfs_dir_data_t *) data;
static union vfs_dirent sftpfs_dirent;
int rc;
do
{
rc = libssh2_sftp_readdir (sftpfs_dir->handle, mem, sizeof (mem), &attrs);
if (rc >= 0)
break;
if (rc != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (sftpfs_dir->super_data, rc, error);
return NULL;
}
sftpfs_waitsocket (sftpfs_dir->super_data, error);
if (error != NULL && *error != NULL)
return NULL;
}
while (rc == LIBSSH2_ERROR_EAGAIN);
if (rc == 0)
return NULL;
g_strlcpy (sftpfs_dirent.dent.d_name, mem, BUF_MEDIUM);
compute_namelen (&sftpfs_dirent.dent);
return &sftpfs_dirent;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Close the directory stream.
*
* @param data directory data handler
* @param error pointer to the error handler
* @return 0 if sucess, negative value otherwise
*/
int
sftpfs_closedir (void *data, GError ** error)
{
int rc;
sftpfs_dir_data_t *sftpfs_dir = (sftpfs_dir_data_t *) data;
(void) error;
rc = libssh2_sftp_closedir (sftpfs_dir->handle);
g_free (sftpfs_dir);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Create a new directory.
*
* @param vpath path directory
* @param mode mode (see man 2 open)
* @param error pointer to the error handler
* @return 0 if sucess, negative value otherwise
*/
int
sftpfs_mkdir (const vfs_path_t * vpath, mode_t mode, GError ** error)
{
int res;
struct vfs_s_super *super;
sftpfs_super_data_t *super_data;
const vfs_path_element_t *path_element;
path_element = vfs_path_get_by_index (vpath, -1);
if (vfs_s_get_path (vpath, &super, 0) == NULL)
return -1;
if (super == NULL)
return -1;
super_data = (sftpfs_super_data_t *) super->data;
if (super_data->sftp_session == NULL)
return -1;
do
{
const char *fixfname;
fixfname = sftpfs_fix_filename (path_element->path);
res =
libssh2_sftp_mkdir_ex (super_data->sftp_session,
fixfname, sftpfs_filename_buffer->len, mode);
if (res >= 0)
break;
if (res != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, res, error);
return -1;
}
sftpfs_waitsocket (super_data, error);
if (error != NULL && *error != NULL)
return -1;
}
while (res == LIBSSH2_ERROR_EAGAIN);
return res;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Remove a directory.
*
* @param vpath path directory
* @param error pointer to the error handler
* @return 0 if sucess, negative value otherwise
*/
int
sftpfs_rmdir (const vfs_path_t * vpath, GError ** error)
{
int res;
struct vfs_s_super *super;
sftpfs_super_data_t *super_data;
const vfs_path_element_t *path_element;
path_element = vfs_path_get_by_index (vpath, -1);
if (vfs_s_get_path (vpath, &super, 0) == NULL)
return -1;
if (super == NULL)
return -1;
super_data = (sftpfs_super_data_t *) super->data;
if (super_data->sftp_session == NULL)
return -1;
do
{
const char *fixfname;
fixfname = sftpfs_fix_filename (path_element->path);
res =
libssh2_sftp_rmdir_ex (super_data->sftp_session, fixfname, sftpfs_filename_buffer->len);
if (res >= 0)
break;
if (res != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, res, error);
return -1;
}
sftpfs_waitsocket (super_data, error);
if (error != NULL && *error != NULL)
return -1;
}
while (res == LIBSSH2_ERROR_EAGAIN);
return res;
}
/* --------------------------------------------------------------------------------------------- */

388
src/vfs/sftpfs/file.c Normal file
View File

@ -0,0 +1,388 @@
/* Virtual File System: SFTP file system.
The internal functions: files
Copyright (C) 2011
The Free Software Foundation, Inc.
Written by:
Ilia Maslakov <il.smind@gmail.com>, 2011
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
This file is part of the Midnight Commander.
The Midnight Commander 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.
The Midnight Commander 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 <config.h>
#include <libssh2.h>
#include <libssh2_sftp.h>
#include "lib/global.h"
#include "internal.h"
/*** global variables ****************************************************************************/
/*** file scope macro definitions ****************************************************************/
/*** file scope type declarations ****************************************************************/
typedef struct
{
LIBSSH2_SFTP_HANDLE *handle;
int flags;
mode_t mode;
} sftpfs_file_handler_data_t;
/*** file scope variables ************************************************************************/
/*** file scope functions ************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/**
* Reopen file by file handle.
*
* @param file_handler the file handler data
* @param error pointer to the error handler
*/
static void
sftpfs_reopen (vfs_file_handler_t * file_handler, GError ** error)
{
sftpfs_file_handler_data_t *file_handler_data;
file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
sftpfs_close_file (file_handler, error);
if (error == NULL || *error == NULL)
sftpfs_open_file (file_handler, file_handler_data->flags, file_handler_data->mode, error);
}
/* --------------------------------------------------------------------------------------------- */
/*** public functions ****************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/**
* Open new SFTP file.
*
* @param file_handler the file handler data
* @param flags flags (see man 2 open)
* @param mode mode (see man 2 open)
* @param error pointer to the error handler
* @return TRUE if connection was created successfully, FALSE otherwise
*/
gboolean
sftpfs_open_file (vfs_file_handler_t * file_handler, int flags, mode_t mode, GError ** error)
{
unsigned long sftp_open_flags = 0;
int sftp_open_mode = 0;
sftpfs_file_handler_data_t *file_handler_data;
sftpfs_super_data_t *super_data;
char *name;
(void) mode;
name = vfs_s_fullpath (&sftpfs_class, file_handler->ino);
if (name == NULL)
return FALSE;
super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
file_handler_data = g_new0 (sftpfs_file_handler_data_t, 1);
if ((flags & O_CREAT) != 0 || (flags & O_WRONLY) != 0)
{
sftp_open_flags = (flags & O_WRONLY) != 0 ? LIBSSH2_FXF_WRITE : 0;
sftp_open_flags |= (flags & O_CREAT) != 0 ? LIBSSH2_FXF_CREAT : 0;
sftp_open_flags |= (flags & O_APPEND) != 0 ? LIBSSH2_FXF_APPEND : 0;
sftp_open_flags |= (flags & O_TRUNC) != 0 ? LIBSSH2_FXF_TRUNC : 0;
sftp_open_mode = LIBSSH2_SFTP_S_IRUSR |
LIBSSH2_SFTP_S_IWUSR | LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH;
}
else
sftp_open_flags = LIBSSH2_FXF_READ;
while (TRUE)
{
int libssh_errno;
file_handler_data->handle =
libssh2_sftp_open (super_data->sftp_session, sftpfs_fix_filename (name),
sftp_open_flags, sftp_open_mode);
if (file_handler_data->handle != NULL)
break;
libssh_errno = libssh2_session_last_errno (super_data->session);
if (libssh_errno != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, libssh_errno, error);
g_free (name);
return FALSE;
}
}
g_free (name);
file_handler_data->flags = flags;
file_handler_data->mode = mode;
file_handler->data = file_handler_data;
if ((flags & O_APPEND) != 0)
{
struct stat file_info;
if (sftpfs_fstat (file_handler, &file_info, error) == 0)
libssh2_sftp_seek64 (file_handler_data->handle, file_info.st_size);
}
return TRUE;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Stats the file specified by the file descriptor.
*
* @param data file data handler
* @param buf buffer for store stat-info
* @param error pointer to the error handler
* @return 0 if sucess, negative value otherwise
*/
int
sftpfs_fstat (void *data, struct stat *buf, GError ** error)
{
int res;
LIBSSH2_SFTP_ATTRIBUTES attrs;
vfs_file_handler_t *fh = (vfs_file_handler_t *) data;
sftpfs_file_handler_data_t *sftpfs_fh = fh->data;
struct vfs_s_super *super = fh->ino->super;
sftpfs_super_data_t *super_data = (sftpfs_super_data_t *) super->data;
if (sftpfs_fh->handle == NULL)
return -1;
do
{
res = libssh2_sftp_fstat_ex (sftpfs_fh->handle, &attrs, 0);
if (res >= 0)
break;
if (res != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, res, error);
return -1;
}
sftpfs_waitsocket (super_data, error);
if (error != NULL && *error != NULL)
return -1;
}
while (res == LIBSSH2_ERROR_EAGAIN);
if ((attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
{
buf->st_uid = attrs.uid;
buf->st_gid = attrs.gid;
}
if ((attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
{
buf->st_atime = attrs.atime;
buf->st_mtime = attrs.mtime;
buf->st_ctime = attrs.mtime;
}
if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
buf->st_size = attrs.filesize;
if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
buf->st_mode = attrs.permissions;
return 0;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Read up to 'count' bytes from the file descriptor 'file_handler' to the buffer starting at 'buffer'.
*
* @param data file data handler
* @param buffer buffer for data
* @param count data size
* @param error pointer to the error handler
* @return 0 if sucess, negative value otherwise
*/
ssize_t
sftpfs_read_file (vfs_file_handler_t * file_handler, char *buffer, size_t count, GError ** error)
{
ssize_t rc;
sftpfs_file_handler_data_t *file_handler_data;
sftpfs_super_data_t *super_data;
if (file_handler == NULL || file_handler->data == NULL)
{
g_set_error (error, MC_ERROR, -1, _("sftp: No file handler data present for reading file"));
return -1;
}
file_handler_data = file_handler->data;
super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
do
{
rc = libssh2_sftp_read (file_handler_data->handle, buffer, count);
if (rc >= 0)
break;
if (rc != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, rc, error);
return -1;
}
sftpfs_waitsocket (super_data, error);
if (error != NULL && *error != NULL)
return -1;
}
while (rc == LIBSSH2_ERROR_EAGAIN);
file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Write up to 'count' bytes from the buffer starting at 'buffer' to the descriptor 'file_handler'.
*
* @param data file data handler
* @param buffer buffer for data
* @param count data size
* @param error pointer to the error handler
* @return 0 if sucess, negative value otherwise
*/
ssize_t
sftpfs_write_file (vfs_file_handler_t * file_handler, const char *buffer, size_t count,
GError ** error)
{
ssize_t rc;
sftpfs_file_handler_data_t *file_handler_data;
sftpfs_super_data_t *super_data;
file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
do
{
rc = libssh2_sftp_write (file_handler_data->handle, buffer, count);
if (rc >= 0)
break;
if (rc != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, rc, error);
return -1;
}
sftpfs_waitsocket (super_data, error);
if (error != NULL && *error != NULL)
return -1;
}
while (rc == LIBSSH2_ERROR_EAGAIN);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Close a file descriptor.
*
* @param data file data handler
* @param error pointer to the error handler
* @return 0 if sucess, negative value otherwise
*/
int
sftpfs_close_file (vfs_file_handler_t * file_handler, GError ** error)
{
sftpfs_file_handler_data_t *file_handler_data;
(void) error;
file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
if (file_handler_data == NULL)
return -1;
libssh2_sftp_close (file_handler_data->handle);
g_free (file_handler_data);
return 0;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Reposition the offset of the open file associated with the file descriptor.
*
* @param data file data handler
* @param offset file offset
* @param whence method of seek (at begin, at current, at end)
* @param error pointer to the error handler
* @return 0 if sucess, negative value otherwise
*/
off_t
sftpfs_lseek (vfs_file_handler_t * file_handler, off_t offset, int whence, GError ** error)
{
sftpfs_file_handler_data_t *file_handler_data;
file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
switch (whence)
{
case SEEK_SET:
/* Need reopen file because:
"You MUST NOT seek during writing or reading a file with SFTP, as the internals use
outstanding packets and changing the "file position" during transit will results in
badness." */
if (file_handler->pos > offset || offset == 0)
{
sftpfs_reopen (file_handler, error);
if (error != NULL && *error != NULL)
return 0;
}
file_handler->pos = offset;
break;
case SEEK_CUR:
file_handler->pos += offset;
break;
case SEEK_END:
if (file_handler->pos > file_handler->ino->st.st_size - offset)
{
sftpfs_reopen (file_handler, error);
if (error != NULL && *error != NULL)
return 0;
}
file_handler->pos = file_handler->ino->st.st_size - offset;
break;
}
libssh2_sftp_seek64 (file_handler_data->handle, file_handler->pos);
file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
return file_handler->pos;
}
/* --------------------------------------------------------------------------------------------- */

69
src/vfs/sftpfs/init.c Normal file
View File

@ -0,0 +1,69 @@
/* Virtual File System: SFTP file system.
The interface function
Copyright (C) 2011
The Free Software Foundation, Inc.
Written by:
Ilia Maslakov <il.smind@gmail.com>, 2011
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
This file is part of the Midnight Commander.
The Midnight Commander 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.
The Midnight Commander 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 <config.h>
#include "lib/global.h"
#include "lib/vfs/netutil.h"
#include "init.h"
#include "internal.h"
/*** global variables ****************************************************************************/
/*** file scope macro definitions ****************************************************************/
/*** file scope type declarations ****************************************************************/
/*** file scope variables ************************************************************************/
/*** file scope functions ************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------------------- */
/*** public functions ****************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/**
* Initialization of SFTP Virtual File Sysytem.
*/
void
init_sftpfs (void)
{
tcp_init ();
sftpfs_init_class ();
sftpfs_init_subclass ();
vfs_s_init_class (&sftpfs_class, &sftpfs_subclass);
sftpfs_init_class_callbacks ();
sftpfs_init_subclass_callbacks ();
vfs_register_class (&sftpfs_class);
}
/* --------------------------------------------------------------------------------------------- */

23
src/vfs/sftpfs/init.h Normal file
View File

@ -0,0 +1,23 @@
/**
* \file
* \brief Header: SFTP FS
*/
#ifndef MC__VFS_SFTPFS_INIT_H
#define MC__VFS_SFTPFS_INIT_H
/*** typedefs(not structures) and defined constants **********************************************/
/*** enums ***************************************************************************************/
/*** structures declarations (and typedefs of structures)*****************************************/
/*** global variables defined in .c file *********************************************************/
/*** declarations of public functions ************************************************************/
void init_sftpfs (void);
/*** inline functions ****************************************************************************/
#endif /* MC__VFS_SFTPFS_INIT_H */

624
src/vfs/sftpfs/internal.c Normal file
View File

@ -0,0 +1,624 @@
/* Virtual File System: SFTP file system.
The internal functions
Copyright (C) 2011
The Free Software Foundation, Inc.
Written by:
Ilia Maslakov <il.smind@gmail.com>, 2011
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
This file is part of the Midnight Commander.
The Midnight Commander 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.
The Midnight Commander 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 <config.h>
#include <errno.h>
#include "lib/global.h"
#include "internal.h"
/*** global variables ****************************************************************************/
GString *sftpfs_filename_buffer = NULL;
/*** file scope macro definitions ****************************************************************/
/*** file scope type declarations ****************************************************************/
/*** file scope variables ************************************************************************/
/*** file scope functions ************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------------------------- */
/*** public functions ****************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/**
* Show error message (if error have raised) and cleanup GError object.
*
* @param error pointer to object contains error message
* @return TRUE if error message was printed, FALSE otherwise
*/
gboolean
sftpfs_show_error (GError ** error)
{
if (error == NULL || *error == NULL)
return FALSE;
vfs_print_message ("(%d) %s", (*error)->code, (*error)->message);
g_error_free (*error);
*error = NULL;
return TRUE;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Convert libssh error to GError object.
*
* @param super_data extra data for SFTP connection
* @param libssh_errno errno from libssh
* @param error pointer to the error object
*/
void
sftpfs_ssherror_to_gliberror (sftpfs_super_data_t * super_data, int libssh_errno, GError ** error)
{
char *err = NULL;
int err_len;
g_return_if_fail (error == NULL || *error == NULL);
libssh2_session_last_error (super_data->session, &err, &err_len, 1);
g_set_error (error, MC_ERROR, libssh_errno, "%s", err);
g_free (err);
}
/* --------------------------------------------------------------------------------------------- */
/**
* Fix filename for SFTP operations: add leading slash to file name.
*
* @param file_name file name
* @return newly allocated string contains the file name with leading slash
*/
const char *
sftpfs_fix_filename (const char *file_name)
{
g_string_printf (sftpfs_filename_buffer, "%c%s", PATH_SEP, file_name);
return sftpfs_filename_buffer->str;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Awaiting for any activity on socket.
*
* @param super_data extra data for SFTP connection
* @param error unused
* @return 0 if sucess, negative value otherwise
*/
int
sftpfs_waitsocket (sftpfs_super_data_t * super_data, GError ** error)
{
struct timeval timeout = { 10, 0 };
fd_set fd;
fd_set *writefd = NULL;
fd_set *readfd = NULL;
int dir;
(void) error;
FD_ZERO (&fd);
FD_SET (super_data->socket_handle, &fd);
/* now make sure we wait in the correct direction */
dir = libssh2_session_block_directions (super_data->session);
if ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) != 0)
readfd = &fd;
if ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) != 0)
writefd = &fd;
return select (super_data->socket_handle + 1, readfd, writefd, NULL, &timeout);
}
/* --------------------------------------------------------------------------------------------- */
/**
* Getting information about a symbolic link.
*
* @param vpath path to file, directory or symbolic link
* @param buf buffer for store stat-info
* @param error pointer to error object
* @return 0 if sucess, negative value otherwise
*/
int
sftpfs_lstat (const vfs_path_t * vpath, struct stat *buf, GError ** error)
{
struct vfs_s_super *super;
sftpfs_super_data_t *super_data;
LIBSSH2_SFTP_ATTRIBUTES attrs;
int res;
const vfs_path_element_t *path_element;
path_element = vfs_path_get_by_index (vpath, -1);
if (vfs_s_get_path (vpath, &super, 0) == NULL)
return -1;
if (super == NULL)
return -1;
super_data = (sftpfs_super_data_t *) super->data;
if (super_data->sftp_session == NULL)
return -1;
do
{
const char *fixfname;
fixfname = sftpfs_fix_filename (path_element->path);
res = libssh2_sftp_stat_ex (super_data->sftp_session, fixfname,
sftpfs_filename_buffer->len, LIBSSH2_SFTP_LSTAT, &attrs);
if (res >= 0)
break;
if (res != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, res, error);
return -1;
}
sftpfs_waitsocket (super_data, error);
if (error != NULL && *error != NULL)
return -1;
}
while (res == LIBSSH2_ERROR_EAGAIN);
if ((attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
{
buf->st_uid = attrs.uid;
buf->st_gid = attrs.gid;
}
if ((attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
{
buf->st_atime = attrs.atime;
buf->st_mtime = attrs.mtime;
buf->st_ctime = attrs.mtime;
}
if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
buf->st_size = attrs.filesize;
if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
buf->st_mode = attrs.permissions;
return 0;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Getting information about a file or directory.
*
* @param vpath path to file or directory
* @param buf buffer for store stat-info
* @param error pointer to error object
* @return 0 if sucess, negative value otherwise
*/
int
sftpfs_stat (const vfs_path_t * vpath, struct stat *buf, GError ** error)
{
struct vfs_s_super *super;
sftpfs_super_data_t *super_data;
LIBSSH2_SFTP_ATTRIBUTES attrs;
int res;
const vfs_path_element_t *path_element;
path_element = vfs_path_get_by_index (vpath, -1);
if (vfs_s_get_path (vpath, &super, 0) == NULL)
return -1;
if (super == NULL)
return -1;
super_data = (sftpfs_super_data_t *) super->data;
if (super_data->sftp_session == NULL)
return -1;
do
{
const char *fixfname;
fixfname = sftpfs_fix_filename (path_element->path);
res =
libssh2_sftp_stat_ex (super_data->sftp_session,
fixfname, sftpfs_filename_buffer->len, LIBSSH2_SFTP_STAT, &attrs);
if (res >= 0)
break;
if (res != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, res, error);
return -1;
}
sftpfs_waitsocket (super_data, error);
if (error != NULL && *error != NULL)
return -1;
}
while (res == LIBSSH2_ERROR_EAGAIN);
buf->st_nlink = 1;
if ((attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
{
buf->st_uid = attrs.uid;
buf->st_gid = attrs.gid;
}
if ((attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
{
buf->st_atime = attrs.atime;
buf->st_mtime = attrs.mtime;
buf->st_ctime = attrs.mtime;
}
if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
buf->st_size = attrs.filesize;
if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
buf->st_mode = attrs.permissions;
return 0;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Read value of a symbolic link.
*
* @param vpath path to file or directory
* @param buf buffer for store stat-info
* @param size buffer size
* @param error pointer to error object
* @return 0 if sucess, negative value otherwise
*/
int
sftpfs_readlink (const vfs_path_t * vpath, char *buf, size_t size, GError ** error)
{
struct vfs_s_super *super;
sftpfs_super_data_t *super_data;
int res;
const vfs_path_element_t *path_element;
path_element = vfs_path_get_by_index (vpath, -1);
if (vfs_s_get_path (vpath, &super, 0) == NULL)
return -1;
if (super == NULL)
return -1;
super_data = (sftpfs_super_data_t *) super->data;
if (super_data->sftp_session == NULL)
return -1;
do
{
const char *fixfname;
fixfname = sftpfs_fix_filename (path_element->path);
res = libssh2_sftp_readlink (super_data->sftp_session, fixfname, buf, size);
if (res >= 0)
break;
if (res != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, res, error);
return -1;
}
sftpfs_waitsocket (super_data, error);
if (error != NULL && *error != NULL)
return -1;
}
while (res == LIBSSH2_ERROR_EAGAIN);
return res;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Create symlink to file or directory
*
* @param vpath1 path to file or directory
* @param vpath2 path to symlink
* @param error pointer to error object
* @return 0 if sucess, negative value otherwise
*/
int
sftpfs_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** error)
{
struct vfs_s_super *super;
sftpfs_super_data_t *super_data;
const vfs_path_element_t *path_element1;
const vfs_path_element_t *path_element2;
char *tmp_path;
int res;
path_element2 = vfs_path_get_by_index (vpath2, -1);
if (vfs_s_get_path (vpath2, &super, 0) == NULL)
return -1;
if (super == NULL)
return -1;
super_data = (sftpfs_super_data_t *) super->data;
if (super_data->sftp_session == NULL)
return -1;
tmp_path = g_strdup_printf ("%c%s", PATH_SEP, path_element2->path);
path_element1 = vfs_path_get_by_index (vpath1, -1);
do
{
const char *fixfname;
fixfname = sftpfs_fix_filename (path_element1->path);
res =
libssh2_sftp_symlink_ex (super_data->sftp_session,
fixfname,
sftpfs_filename_buffer->len, tmp_path, strlen (tmp_path),
LIBSSH2_SFTP_SYMLINK);
if (res >= 0)
break;
if (res != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, res, error);
g_free (tmp_path);
return -1;
}
sftpfs_waitsocket (super_data, error);
if (error != NULL && *error != NULL)
{
g_free (tmp_path);
return -1;
}
}
while (res == LIBSSH2_ERROR_EAGAIN);
g_free (tmp_path);
return 0;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Changes the permissions of the file.
*
* @param vpath path to file or directory
* @param mode mode (see man 2 open)
* @param error pointer to error object
* @return 0 if sucess, negative value otherwise
*/
int
sftpfs_chmod (const vfs_path_t * vpath, mode_t mode, GError ** error)
{
struct vfs_s_super *super;
sftpfs_super_data_t *super_data;
LIBSSH2_SFTP_ATTRIBUTES attrs;
int res;
const vfs_path_element_t *path_element;
path_element = vfs_path_get_by_index (vpath, -1);
if (vfs_s_get_path (vpath, &super, 0) == NULL)
return -1;
if (super == NULL)
return -1;
super_data = (sftpfs_super_data_t *) super->data;
if (super_data->sftp_session == NULL)
return -1;
do
{
const char *fixfname;
fixfname = sftpfs_fix_filename (path_element->path);
res = libssh2_sftp_stat_ex (super_data->sftp_session, fixfname,
sftpfs_filename_buffer->len, LIBSSH2_SFTP_LSTAT, &attrs);
if (res >= 0)
break;
if (res != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, res, error);
return -1;
}
sftpfs_waitsocket (super_data, error);
if (error != NULL && *error != NULL)
return -1;
}
while (res == LIBSSH2_ERROR_EAGAIN);
attrs.permissions = mode;
do
{
const char *fixfname;
fixfname = sftpfs_fix_filename (path_element->path);
res = libssh2_sftp_stat_ex (super_data->sftp_session, fixfname,
sftpfs_filename_buffer->len, LIBSSH2_SFTP_SETSTAT, &attrs);
if (res >= 0)
break;
else if (res != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, res, error);
return -1;
}
sftpfs_waitsocket (super_data, error);
if (error != NULL && *error != NULL)
return -1;
}
while (res == LIBSSH2_ERROR_EAGAIN);
return res;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Delete a name from the file system.
*
* @param vpath path to file or directory
* @param error pointer to error object
* @return 0 if sucess, negative value otherwise
*/
int
sftpfs_unlink (const vfs_path_t * vpath, GError ** error)
{
struct vfs_s_super *super;
sftpfs_super_data_t *super_data;
int res;
const vfs_path_element_t *path_element;
path_element = vfs_path_get_by_index (vpath, -1);
if (vfs_s_get_path (vpath, &super, 0) == NULL)
return -1;
if (super == NULL)
return -1;
super_data = (sftpfs_super_data_t *) super->data;
if (super_data->sftp_session == NULL)
return -1;
do
{
const char *fixfname;
fixfname = sftpfs_fix_filename (path_element->path);
res =
libssh2_sftp_unlink_ex (super_data->sftp_session, fixfname,
sftpfs_filename_buffer->len);
if (res >= 0)
break;
if (res != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, res, error);
return -1;
}
sftpfs_waitsocket (super_data, error);
if (error != NULL && *error != NULL)
return -1;
}
while (res == LIBSSH2_ERROR_EAGAIN);
return res;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Rename a file, moving it between directories if required.
*
* @param vpath1 path to source file or directory
* @param vpath2 path to destination file or directory
* @param error pointer to error object
* @return 0 if sucess, negative value otherwise
*/
int
sftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** error)
{
struct vfs_s_super *super;
sftpfs_super_data_t *super_data;
const vfs_path_element_t *path_element1;
const vfs_path_element_t *path_element2;
char *tmp_path;
int res;
path_element2 = vfs_path_get_by_index (vpath2, -1);
if (vfs_s_get_path (vpath2, &super, 0) == NULL)
return -1;
if (super == NULL)
return -1;
super_data = (sftpfs_super_data_t *) super->data;
if (super_data->sftp_session == NULL)
return -1;
tmp_path = g_strdup_printf ("%c%s", PATH_SEP, path_element2->path);
path_element1 = vfs_path_get_by_index (vpath1, -1);
do
{
const char *fixfname;
fixfname = sftpfs_fix_filename (path_element1->path);
res = libssh2_sftp_rename_ex
(super_data->sftp_session,
fixfname,
sftpfs_filename_buffer->len, tmp_path, strlen (tmp_path), LIBSSH2_SFTP_SYMLINK);
if (res >= 0)
break;
if (res != LIBSSH2_ERROR_EAGAIN)
{
sftpfs_ssherror_to_gliberror (super_data, res, error);
g_free (tmp_path);
return -1;
}
sftpfs_waitsocket (super_data, error);
if (error != NULL && *error != NULL)
{
g_free (tmp_path);
return -1;
}
}
while (res == LIBSSH2_ERROR_EAGAIN);
g_free (tmp_path);
return 0;
}
/* --------------------------------------------------------------------------------------------- */

100
src/vfs/sftpfs/internal.h Normal file
View File

@ -0,0 +1,100 @@
/**
* \file
* \brief Header: SFTP FS
*/
#ifndef MC__VFS_SFTPFS_INTERNAL_H
#define MC__VFS_SFTPFS_INTERNAL_H
#include <libssh2.h>
#include <libssh2_sftp.h>
#include "lib/vfs/vfs.h"
#include "lib/vfs/xdirentry.h"
/*** typedefs(not structures) and defined constants **********************************************/
#define SFTP_DEFAULT_PORT 22
/*** enums ***************************************************************************************/
typedef enum
{
NONE = 0,
PUBKEY = (1 << 0),
PASSWORD = (1 << 1),
AGENT = (1 << 2)
} sftpfs_auth_type_t;
/*** structures declarations (and typedefs of structures)*****************************************/
typedef struct
{
sftpfs_auth_type_t auth_type;
sftpfs_auth_type_t config_auth_type;
LIBSSH2_SESSION *session;
LIBSSH2_SFTP *sftp_session;
LIBSSH2_AGENT *agent;
char *pubkey;
char *privkey;
int socket_handle;
const char *fingerprint;
} sftpfs_super_data_t;
/*** global variables defined in .c file *********************************************************/
extern GString *sftpfs_filename_buffer;
extern struct vfs_class sftpfs_class;
extern struct vfs_s_subclass sftpfs_subclass;
/*** declarations of public functions ************************************************************/
void sftpfs_init_class (void);
void sftpfs_init_subclass (void);
void sftpfs_init_class_callbacks (void);
void sftpfs_init_subclass_callbacks (void);
void sftpfs_init_config_variables_patterns (void);
void sftpfs_deinit_config_variables_patterns (void);
gboolean sftpfs_show_error (GError ** error);
void sftpfs_ssherror_to_gliberror (sftpfs_super_data_t * super_data, int libssh_errno,
GError ** error);
int sftpfs_waitsocket (sftpfs_super_data_t * super_data, GError ** error);
const char *sftpfs_fix_filename (const char *file_name);
int sftpfs_lstat (const vfs_path_t * vpath, struct stat *buf, GError ** error);
int sftpfs_stat (const vfs_path_t * vpath, struct stat *buf, GError ** error);
int sftpfs_readlink (const vfs_path_t * vpath, char *buf, size_t size, GError ** error);
int sftpfs_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** error);
int sftpfs_chmod (const vfs_path_t * vpath, mode_t mode, GError ** error);
int sftpfs_unlink (const vfs_path_t * vpath, GError ** error);
int sftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** error);
void sftpfs_fill_connection_data_from_config (struct vfs_s_super *super, GError ** error);
int sftpfs_open_connection (struct vfs_s_super *super, GError ** error);
void sftpfs_close_connection (struct vfs_s_super *super, const char *shutdown_message,
GError ** error);
void *sftpfs_opendir (const vfs_path_t * vpath, GError ** error);
void *sftpfs_readdir (void *data, GError ** error);
int sftpfs_closedir (void *data, GError ** error);
int sftpfs_mkdir (const vfs_path_t * vpath, mode_t mode, GError ** error);
int sftpfs_rmdir (const vfs_path_t * vpath, GError ** error);
gboolean sftpfs_open_file (vfs_file_handler_t * file_handler, int flags, mode_t mode,
GError ** error);
ssize_t sftpfs_read_file (vfs_file_handler_t * file_handler, char *buffer, size_t count,
GError ** error);
ssize_t sftpfs_write_file (vfs_file_handler_t * file_handler, const char *buffer, size_t count,
GError ** error);
int sftpfs_close_file (vfs_file_handler_t * file_handler, GError ** error);
int sftpfs_fstat (void *data, struct stat *buf, GError ** error);
off_t sftpfs_lseek (vfs_file_handler_t * file_handler, off_t offset, int whence, GError ** error);
/*** inline functions ****************************************************************************/
#endif /* MC__VFS_SFTPFS_INTERNAL_H */

693
src/vfs/sftpfs/vfs_class.c Normal file
View File

@ -0,0 +1,693 @@
/* Virtual File System: SFTP file system.
The VFS class functions
Copyright (C) 2011
The Free Software Foundation, Inc.
Written by:
Ilia Maslakov <il.smind@gmail.com>, 2011
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
This file is part of the Midnight Commander.
The Midnight Commander 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.
The Midnight Commander 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 <config.h>
#include <errno.h>
#include "lib/global.h"
#include "lib/vfs/gc.h"
#include "lib/tty/tty.h" /* tty_enable_interrupt_key () */
#include "internal.h"
/*** global variables ****************************************************************************/
struct vfs_class sftpfs_class;
/*** file scope macro definitions ****************************************************************/
/*** file scope type declarations ****************************************************************/
/*** file scope variables ************************************************************************/
/*** file scope functions ************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for VFS-class init action.
*
* @param me structure of VFS class
*/
static int
sftpfs_cb_init (struct vfs_class *me)
{
(void) me;
if (libssh2_init (0) != 0)
return 0;
sftpfs_filename_buffer = g_string_new ("");
sftpfs_init_config_variables_patterns ();
return 1;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for VFS-class deinit action.
*
* @param me structure of VFS class
*/
static void
sftpfs_cb_done (struct vfs_class *me)
{
(void) me;
sftpfs_deinit_config_variables_patterns ();
g_string_free (sftpfs_filename_buffer, TRUE);
libssh2_exit ();
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for opening file.
*
* @param vpath path to file
* @param flags flags (see man 2 open)
* @param mode mode (see man 2 open)
* @return file data handler if success, NULL otherwise
*/
static void *
sftpfs_cb_open (const vfs_path_t * vpath, int flags, mode_t mode)
{
vfs_file_handler_t *file_handler;
const vfs_path_element_t *path_element;
struct vfs_s_super *super;
const char *path_super;
struct vfs_s_inode *path_inode;
GError *error = NULL;
gboolean is_changed = FALSE;
path_element = vfs_path_get_by_index (vpath, -1);
path_super = vfs_s_get_path (vpath, &super, 0);
if (path_super == NULL)
return NULL;
path_inode = vfs_s_find_inode (path_element->class, super, path_super, LINK_FOLLOW, FL_NONE);
if (path_inode != NULL && ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)))
{
path_element->class->verrno = EEXIST;
return NULL;
}
if (path_inode == NULL)
{
char *dirname, *name;
struct vfs_s_entry *ent;
struct vfs_s_inode *dir;
dirname = g_path_get_dirname (path_super);
name = g_path_get_basename (path_super);
dir = vfs_s_find_inode (path_element->class, super, dirname, LINK_FOLLOW, FL_DIR);
if (dir == NULL)
{
g_free (dirname);
g_free (name);
return NULL;
}
ent = vfs_s_generate_entry (path_element->class, name, dir, 0755);
path_inode = ent->ino;
vfs_s_insert_entry (path_element->class, dir, ent);
g_free (dirname);
g_free (name);
is_changed = TRUE;
}
if (S_ISDIR (path_inode->st.st_mode))
{
path_element->class->verrno = EISDIR;
return NULL;
}
file_handler = g_new0 (vfs_file_handler_t, 1);
file_handler->pos = 0;
file_handler->ino = path_inode;
file_handler->handle = -1;
file_handler->changed = is_changed;
file_handler->linear = 0;
file_handler->data = NULL;
if (!sftpfs_open_file (file_handler, flags, mode, &error))
{
sftpfs_show_error (&error);
g_free (file_handler);
return NULL;
}
vfs_rmstamp (path_element->class, (vfsid) super);
super->fd_usage++;
file_handler->ino->st.st_nlink++;
return file_handler;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for opening directory.
*
* @param vpath path to directory
* @return directory data handler if success, NULL otherwise
*/
static void *
sftpfs_cb_opendir (const vfs_path_t * vpath)
{
GError *error = NULL;
void *ret_value;
/* reset interrupt flag */
tty_got_interrupt ();
ret_value = sftpfs_opendir (vpath, &error);
sftpfs_show_error (&error);
return ret_value;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for reading directory entry.
*
* @param data directory data handler
* @return information about direntry if success, NULL otherwise
*/
static void *
sftpfs_cb_readdir (void *data)
{
GError *error = NULL;
union vfs_dirent *sftpfs_dirent;
if (tty_got_interrupt ())
{
tty_disable_interrupt_key ();
return NULL;
}
sftpfs_dirent = sftpfs_readdir (data, &error);
if (!sftpfs_show_error (&error))
{
if (sftpfs_dirent != NULL)
vfs_print_message (_("sftp: (Ctrl-G break) Listing... %s"), sftpfs_dirent->dent.d_name);
else
vfs_print_message (_("sftp: Listing done."));
}
return sftpfs_dirent;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for closing directory.
*
* @param data directory data handler
* @return 0 if sucess, negative value otherwise
*/
static int
sftpfs_cb_closedir (void *data)
{
int rc;
GError *error = NULL;
rc = sftpfs_closedir (data, &error);
sftpfs_show_error (&error);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for lstat VFS-function.
*
* @param vpath path to file or directory
* @param buf buffer for store stat-info
* @return 0 if sucess, negative value otherwise
*/
static int
sftpfs_cb_lstat (const vfs_path_t * vpath, struct stat *buf)
{
int rc;
GError *error = NULL;
rc = sftpfs_lstat (vpath, buf, &error);
sftpfs_show_error (&error);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for stat VFS-function.
*
* @param vpath path to file or directory
* @param buf buffer for store stat-info
* @return 0 if sucess, negative value otherwise
*/
static int
sftpfs_cb_stat (const vfs_path_t * vpath, struct stat *buf)
{
int rc;
GError *error = NULL;
rc = sftpfs_stat (vpath, buf, &error);
sftpfs_show_error (&error);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for fstat VFS-function.
*
* @param data file data handler
* @param buf buffer for store stat-info
* @return 0 if sucess, negative value otherwise
*/
static int
sftpfs_cb_fstat (void *data, struct stat *buf)
{
int rc;
GError *error = NULL;
rc = sftpfs_fstat (data, buf, &error);
sftpfs_show_error (&error);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for readlink VFS-function.
*
* @param vpath path to file or directory
* @param buf buffer for store stat-info
* @param size buffer size
* @return 0 if sucess, negative value otherwise
*/
static int
sftpfs_cb_readlink (const vfs_path_t * vpath, char *buf, size_t size)
{
int rc;
GError *error = NULL;
rc = sftpfs_readlink (vpath, buf, size, &error);
sftpfs_show_error (&error);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for utime VFS-function.
*
* @param vpath unused
* @param times unused
* @return always 0
*/
static int
sftpfs_cb_utime (const vfs_path_t * vpath, struct utimbuf *times)
{
(void) vpath;
(void) times;
return 0;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for symlink VFS-function.
*
* @param vpath1 path to file or directory
* @param vpath2 path to symlink
* @return 0 if sucess, negative value otherwise
*/
static int
sftpfs_cb_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
{
int rc;
GError *error = NULL;
rc = sftpfs_symlink (vpath1, vpath2, &error);
sftpfs_show_error (&error);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for symlink VFS-function.
*
* @param vpath unused
* @param mode unused
* @param dev unused
* @return always 0
*/
static int
sftpfs_cb_mknod (const vfs_path_t * vpath, mode_t mode, dev_t dev)
{
(void) vpath;
(void) mode;
(void) dev;
return 0;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for link VFS-function.
*
* @param vpath1 unused
* @param vpath2 unused
* @return always 0
*/
static int
sftpfs_cb_link (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
{
(void) vpath1;
(void) vpath2;
return 0;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for chown VFS-function.
*
* @param vpath unused
* @param owner unused
* @param group unused
* @return always 0
*/
static int
sftpfs_cb_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
{
(void) vpath;
(void) owner;
(void) group;
return 0;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for reading file content.
*
* @param data file data handler
* @param buffer buffer for data
* @param count data size
* @return 0 if sucess, negative value otherwise
*/
static ssize_t
sftpfs_cb_read (void *data, char *buffer, size_t count)
{
int rc;
GError *error = NULL;
vfs_file_handler_t *fh = (vfs_file_handler_t *) data;
if (tty_got_interrupt ())
{
tty_disable_interrupt_key ();
return 0;
}
rc = sftpfs_read_file (fh, buffer, count, &error);
sftpfs_show_error (&error);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for writing file content.
*
* @param data file data handler
* @param buf buffer for data
* @param count data size
* @return 0 if sucess, negative value otherwise
*/
static ssize_t
sftpfs_cb_write (void *data, const char *buf, size_t nbyte)
{
int rc;
GError *error = NULL;
vfs_file_handler_t *fh = (vfs_file_handler_t *) data;
rc = sftpfs_write_file (fh, buf, nbyte, &error);
sftpfs_show_error (&error);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for close file.
*
* @param data file data handler
* @return 0 if sucess, negative value otherwise
*/
static int
sftpfs_cb_close (void *data)
{
int rc;
GError *error = NULL;
struct vfs_s_super *super;
vfs_file_handler_t *file_handler = (vfs_file_handler_t *) data;
super = file_handler->ino->super;
super->fd_usage--;
if (super->fd_usage == 0)
vfs_stamp_create (&sftpfs_class, super);
rc = sftpfs_close_file (file_handler, &error);
sftpfs_show_error (&error);
if (file_handler->handle != -1)
close (file_handler->handle);
vfs_s_free_inode (&sftpfs_class, file_handler->ino);
g_free (file_handler);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for chmod VFS-function.
*
* @param vpath path to file or directory
* @param mode mode (see man 2 open)
* @return 0 if sucess, negative value otherwise
*/
static int
sftpfs_cb_chmod (const vfs_path_t * vpath, mode_t mode)
{
int rc;
GError *error = NULL;
rc = sftpfs_chmod (vpath, mode, &error);
sftpfs_show_error (&error);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for mkdir VFS-function.
*
* @param vpath path directory
* @param mode mode (see man 2 open)
* @return 0 if sucess, negative value otherwise
*/
static int
sftpfs_cb_mkdir (const vfs_path_t * vpath, mode_t mode)
{
int rc;
GError *error = NULL;
rc = sftpfs_mkdir (vpath, mode, &error);
sftpfs_show_error (&error);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for rmdir VFS-function.
*
* @param vpath path directory
* @return 0 if sucess, negative value otherwise
*/
static int
sftpfs_cb_rmdir (const vfs_path_t * vpath)
{
int rc;
GError *error = NULL;
rc = sftpfs_rmdir (vpath, &error);
sftpfs_show_error (&error);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for lseek VFS-function.
*
* @param data file data handler
* @param offset file offset
* @param whence method of seek (at begin, at current, at end)
* @return 0 if sucess, negative value otherwise
*/
static off_t
sftpfs_cb_lseek (void *data, off_t offset, int whence)
{
off_t ret_offset;
vfs_file_handler_t *file_handler = (vfs_file_handler_t *) data;
GError *error = NULL;
ret_offset = sftpfs_lseek (file_handler, offset, whence, &error);
sftpfs_show_error (&error);
return ret_offset;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for unlink VFS-function.
*
* @param vpath path to file or directory
* @return 0 if sucess, negative value otherwise
*/
static int
sftpfs_cb_unlink (const vfs_path_t * vpath)
{
int rc;
GError *error = NULL;
rc = sftpfs_unlink (vpath, &error);
sftpfs_show_error (&error);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for rename VFS-function.
*
* @param vpath1 path to source file or directory
* @param vpath2 path to destination file or directory
* @return 0 if sucess, negative value otherwise
*/
static int
sftpfs_cb_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
{
int rc;
GError *error = NULL;
rc = sftpfs_rename (vpath1, vpath2, &error);
sftpfs_show_error (&error);
return rc;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for errno VFS-function.
*
* @param me unused
* @return value of errno global variable
*/
static int
sftpfs_cb_errno (struct vfs_class *me)
{
(void) me;
return errno;
}
/* --------------------------------------------------------------------------------------------- */
/*** public functions ****************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/**
* Initialization of VFS class structure.
*
* @return the VFS class structure.
*/
void
sftpfs_init_class (void)
{
memset (&sftpfs_class, 0, sizeof (struct vfs_class));
sftpfs_class.name = "sftpfs";
sftpfs_class.prefix = "sftp";
sftpfs_class.flags = VFSF_NOLINKS;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Initialization of VFS class callbacks.
*/
void
sftpfs_init_class_callbacks (void)
{
sftpfs_class.init = sftpfs_cb_init;
sftpfs_class.done = sftpfs_cb_done;
sftpfs_class.opendir = sftpfs_cb_opendir;
sftpfs_class.readdir = sftpfs_cb_readdir;
sftpfs_class.closedir = sftpfs_cb_closedir;
sftpfs_class.mkdir = sftpfs_cb_mkdir;
sftpfs_class.rmdir = sftpfs_cb_rmdir;
sftpfs_class.stat = sftpfs_cb_stat;
sftpfs_class.lstat = sftpfs_cb_lstat;
sftpfs_class.fstat = sftpfs_cb_fstat;
sftpfs_class.readlink = sftpfs_cb_readlink;
sftpfs_class.symlink = sftpfs_cb_symlink;
sftpfs_class.link = sftpfs_cb_link;
sftpfs_class.utime = sftpfs_cb_utime;
sftpfs_class.mknod = sftpfs_cb_mknod;
sftpfs_class.chown = sftpfs_cb_chown;
sftpfs_class.chmod = sftpfs_cb_chmod;
sftpfs_class.open = sftpfs_cb_open;
sftpfs_class.read = sftpfs_cb_read;
sftpfs_class.write = sftpfs_cb_write;
sftpfs_class.close = sftpfs_cb_close;
sftpfs_class.lseek = sftpfs_cb_lseek;
sftpfs_class.unlink = sftpfs_cb_unlink;
sftpfs_class.rename = sftpfs_cb_rename;
sftpfs_class.ferrno = sftpfs_cb_errno;
}
/* --------------------------------------------------------------------------------------------- */

View File

@ -0,0 +1,204 @@
/* Virtual File System: SFTP file system.
The VFS subclass functions
Copyright (C) 2011
The Free Software Foundation, Inc.
Written by:
Ilia Maslakov <il.smind@gmail.com>, 2011
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
This file is part of the Midnight Commander.
The Midnight Commander 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.
The Midnight Commander 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 <config.h>
#include <errno.h>
#include "lib/global.h"
#include "lib/vfs/utilvfs.h"
#include "internal.h"
/*** global variables ****************************************************************************/
struct vfs_s_subclass sftpfs_subclass;
/*** file scope macro definitions ****************************************************************/
/*** file scope type declarations ****************************************************************/
/*** file scope variables ************************************************************************/
/*** file scope functions ************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for checking if connection is equal to existing connection.
*
* @param vpath_element path element with connetion data
* @param super data with exists connection
* @param vpath unused
* @param cookie unused
* @return TRUE if connections is equal, FALSE otherwise
*/
static gboolean
sftpfs_cb_is_equal_connection (const vfs_path_element_t * vpath_element, struct vfs_s_super *super,
const vfs_path_t * vpath, void *cookie)
{
int port;
char *user_name;
int result;
(void) vpath;
(void) cookie;
if (vpath_element->user != NULL)
user_name = vpath_element->user;
else
user_name = vfs_get_local_username ();
if (vpath_element->port != 0)
port = vpath_element->port;
else
port = SFTP_DEFAULT_PORT;
result = ((strcmp (vpath_element->host, super->path_element->host) == 0)
&& (strcmp (user_name, super->path_element->user) == 0)
&& (port == super->path_element->port));
if (user_name != vpath_element->user)
g_free (user_name);
return result;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for opening new connection.
*
* @param super connection data
* @param vpath unused
* @param vpath_element path element with connetion data
* @return 0 if success, -1 otherwise
*/
static int
sftpfs_cb_open_connection (struct vfs_s_super *super,
const vfs_path_t * vpath, const vfs_path_element_t * vpath_element)
{
GError *error = NULL;
int ret_value;
(void) vpath;
if (vpath_element->host == NULL || *vpath_element->host == '\0')
{
vfs_print_message (_("sftp: Invalid host name."));
vpath_element->class->verrno = EPERM;
return -1;
}
super->data = g_new0 (sftpfs_super_data_t, 1);
super->path_element = vfs_path_element_clone (vpath_element);
sftpfs_fill_connection_data_from_config (super, &error);
if (error != NULL)
{
vpath_element->class->verrno = error->code;
sftpfs_show_error (&error);
return -1;
}
super->name = g_strdup (PATH_SEP_STR);
super->root =
vfs_s_new_inode (vpath_element->class, super,
vfs_s_default_stat (vpath_element->class, S_IFDIR | 0755));
ret_value = sftpfs_open_connection (super, &error);
sftpfs_show_error (&error);
return ret_value;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for closing connection.
*
* @param me unused
* @param super connection data
*/
static void
sftpfs_cb_close_connection (struct vfs_class *me, struct vfs_s_super *super)
{
GError *error = NULL;
(void) me;
sftpfs_close_connection (super, "Normal Shutdown", &error);
sftpfs_show_error (&error);
g_free (super->data);
}
/* --------------------------------------------------------------------------------------------- */
/**
* Callback for getting directory content.
*
* @param me unused
* @param dir unused
* @param remote_path unused
* @return always 0
*/
static int
sftpfs_cb_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
{
(void) me;
(void) dir;
(void) remote_path;
return 0;
}
/* --------------------------------------------------------------------------------------------- */
/*** public functions ****************************************************************************/
/* --------------------------------------------------------------------------------------------- */
/**
* Initialization of VFS subclass structure.
*
* @return VFS subclass structure.
*/
void
sftpfs_init_subclass (void)
{
memset (&sftpfs_subclass, 0, sizeof (struct vfs_s_subclass));
sftpfs_subclass.flags = VFS_S_REMOTE;
}
/* --------------------------------------------------------------------------------------------- */
/**
* Initialization of VFS subclass callbacks.
*/
void
sftpfs_init_subclass_callbacks (void)
{
sftpfs_subclass.archive_same = sftpfs_cb_is_equal_connection;
sftpfs_subclass.open_archive = sftpfs_cb_open_connection;
sftpfs_subclass.free_archive = sftpfs_cb_close_connection;
sftpfs_subclass.dir_load = sftpfs_cb_dir_load;
}
/* --------------------------------------------------------------------------------------------- */