diff --git a/configure.ac b/configure.ac index bf22dfdbf..4d71f3501 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/doc/INSTALL b/doc/INSTALL index 500f67177..d27624143 100644 --- a/doc/INSTALL +++ b/doc/INSTALL @@ -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 diff --git a/doc/man/mc.1.in b/doc/man/mc.1.in index 64070cae5..26ae6c629 100644 --- a/doc/man/mc.1.in +++ b/doc/man/mc.1.in @@ -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 diff --git a/doc/man/ru/mc.1.in b/doc/man/ru/mc.1.in index 29b70fe66..6ec8133d5 100644 --- a/doc/man/ru/mc.1.in +++ b/doc/man/ru/mc.1.in @@ -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, используемую diff --git a/lib/keybind.c b/lib/keybind.c index e6fd74b01..df417f24b 100644 --- a/lib/keybind.c +++ b/lib/keybind.c @@ -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 diff --git a/lib/keybind.h b/lib/keybind.h index 4bf9b6bf7..dc86cbc4b 100644 --- a/lib/keybind.h +++ b/lib/keybind.h @@ -150,6 +150,7 @@ enum CK_Filter, CK_ConnectFish, CK_ConnectFtp, + CK_ConnectSftp, CK_ConnectSmb, CK_PanelInfo, CK_Jobs, diff --git a/m4.include/mc-vfs.m4 b/m4.include/mc-vfs.m4 index 7d7325958..df2ac6528 100644 --- a/m4.include/mc-vfs.m4 +++ b/m4.include/mc-vfs.m4 @@ -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 diff --git a/m4.include/vfs/mc-vfs-sftp.m4 b/m4.include/vfs/mc-vfs-sftp.m4 new file mode 100644 index 000000000..a977c0339 --- /dev/null +++ b/m4.include/vfs/mc-vfs-sftp.m4 @@ -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"]) +]) diff --git a/src/filemanager/cmd.c b/src/filemanager/cmd.c index f3419708f..db42d72be 100644 --- a/src/filemanager/cmd.c +++ b/src/filemanager/cmd.c @@ -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) diff --git a/src/filemanager/cmd.h b/src/filemanager/cmd.h index 30d6364bc..9a6493a4b 100644 --- a/src/filemanager/cmd.h +++ b/src/filemanager/cmd.h @@ -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 diff --git a/src/filemanager/midnight.c b/src/filemanager/midnight.c index 8559978c0..6134f4f55 100644 --- a/src/filemanager/midnight.c +++ b/src/filemanager/midnight.c @@ -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 (); diff --git a/src/vfs/Makefile.am b/src/vfs/Makefile.am index 7418bddf1..0fa1f8044 100644 --- a/src/vfs/Makefile.am +++ b/src/vfs/Makefile.am @@ -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 diff --git a/src/vfs/plugins_init.c b/src/vfs/plugins_init.c index 6c60f1ec2..e77b7d7f3 100644 --- a/src/vfs/plugins_init.c +++ b/src/vfs/plugins_init.c @@ -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 */ diff --git a/src/vfs/sftpfs/Makefile.am b/src/vfs/sftpfs/Makefile.am new file mode 100644 index 000000000..a53e4cac9 --- /dev/null +++ b/src/vfs/sftpfs/Makefile.am @@ -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 diff --git a/src/vfs/sftpfs/config_parcer.c b/src/vfs/sftpfs/config_parcer.c new file mode 100644 index 000000000..2295c1c6b --- /dev/null +++ b/src/vfs/sftpfs/config_parcer.c @@ -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 , 2011 + Slava Zanko , 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 . + */ + +#include +#include +#include +#include /* 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; + } +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/connection.c b/src/vfs/sftpfs/connection.c new file mode 100644 index 000000000..1df88279f --- /dev/null +++ b/src/vfs/sftpfs/connection.c @@ -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 , 2011 + Slava Zanko , 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 . + */ + +#include +#include + +#include /* struct hostent */ +#include /* AF_INET */ +#include /* struct in_addr */ +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#include +#include + +#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; + } +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/dir.c b/src/vfs/sftpfs/dir.c new file mode 100644 index 000000000..f6d67d27d --- /dev/null +++ b/src/vfs/sftpfs/dir.c @@ -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 , 2011 + Slava Zanko , 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 . + */ + +#include + +#include +#include + +#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; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/file.c b/src/vfs/sftpfs/file.c new file mode 100644 index 000000000..94ec70d4a --- /dev/null +++ b/src/vfs/sftpfs/file.c @@ -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 , 2011 + Slava Zanko , 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 . + */ + +#include + +#include +#include + +#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; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/init.c b/src/vfs/sftpfs/init.c new file mode 100644 index 000000000..7019385ab --- /dev/null +++ b/src/vfs/sftpfs/init.c @@ -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 , 2011 + Slava Zanko , 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 . + */ + +#include + +#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); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/init.h b/src/vfs/sftpfs/init.h new file mode 100644 index 000000000..d74467df9 --- /dev/null +++ b/src/vfs/sftpfs/init.h @@ -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 */ diff --git a/src/vfs/sftpfs/internal.c b/src/vfs/sftpfs/internal.c new file mode 100644 index 000000000..856f88ecd --- /dev/null +++ b/src/vfs/sftpfs/internal.c @@ -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 , 2011 + Slava Zanko , 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 . + */ + +#include +#include + +#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; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/internal.h b/src/vfs/sftpfs/internal.h new file mode 100644 index 000000000..7c0f457a0 --- /dev/null +++ b/src/vfs/sftpfs/internal.h @@ -0,0 +1,100 @@ +/** + * \file + * \brief Header: SFTP FS + */ + +#ifndef MC__VFS_SFTPFS_INTERNAL_H +#define MC__VFS_SFTPFS_INTERNAL_H + +#include +#include + +#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 */ diff --git a/src/vfs/sftpfs/vfs_class.c b/src/vfs/sftpfs/vfs_class.c new file mode 100644 index 000000000..cc5795861 --- /dev/null +++ b/src/vfs/sftpfs/vfs_class.c @@ -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 , 2011 + Slava Zanko , 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 . + */ + +#include +#include + +#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; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/vfs_subclass.c b/src/vfs/sftpfs/vfs_subclass.c new file mode 100644 index 000000000..49d9bbcda --- /dev/null +++ b/src/vfs/sftpfs/vfs_subclass.c @@ -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 , 2011 + Slava Zanko , 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 . + */ + +#include +#include + +#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; +} + +/* --------------------------------------------------------------------------------------------- */