mirror of
https://github.com/MidnightCommander/mc
synced 2024-12-22 12:32:40 +03:00
Ticket #1535: SFTP support
Signed-off-by: Slava Zanko <slavazanko@gmail.com>
This commit is contained in:
parent
7893cbf9b1
commit
da03697e84
@ -573,6 +573,8 @@ src/vfs/fish/helpers/Makefile
|
|||||||
|
|
||||||
src/vfs/ftpfs/Makefile
|
src/vfs/ftpfs/Makefile
|
||||||
|
|
||||||
|
src/vfs/sftpfs/Makefile
|
||||||
|
|
||||||
src/vfs/local/Makefile
|
src/vfs/local/Makefile
|
||||||
|
|
||||||
src/vfs/sfs/Makefile
|
src/vfs/sfs/Makefile
|
||||||
|
@ -20,6 +20,7 @@ Build requirements for GNU Midnight Commander
|
|||||||
- slang or ncurses
|
- slang or ncurses
|
||||||
- gettext
|
- gettext
|
||||||
- cvs
|
- 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
|
Installation instructions for GNU Midnight Commander
|
||||||
@ -170,6 +171,10 @@ VFS options:
|
|||||||
(on by default)
|
(on by default)
|
||||||
Support for FISH vfs
|
Support for FISH vfs
|
||||||
|
|
||||||
|
`--enable-vfs-sftp'
|
||||||
|
(off by default)
|
||||||
|
Support for SFTP vfs
|
||||||
|
|
||||||
`--enable-vfs-extfs'
|
`--enable-vfs-extfs'
|
||||||
(on by default)
|
(on by default)
|
||||||
Support for extfs
|
Support for extfs
|
||||||
|
@ -3097,6 +3097,9 @@ system for Linux systems),
|
|||||||
.I fish
|
.I fish
|
||||||
(for manipulating files over shell connections such as rsh and ssh).
|
(for manipulating files over shell connections such as rsh and ssh).
|
||||||
If the code was compiled with
|
If the code was compiled with
|
||||||
|
.I sftpfs
|
||||||
|
(for manipulating files over SFTP connections).
|
||||||
|
If the code was compiled with
|
||||||
.I smbfs
|
.I smbfs
|
||||||
support, you can manipulate files on remote systems with the SMB (CIFS)
|
support, you can manipulate files on remote systems with the SMB (CIFS)
|
||||||
protocol.
|
protocol.
|
||||||
@ -3224,6 +3227,41 @@ Examples:
|
|||||||
sh://joe@noncompressed.ssh.edu/private
|
sh://joe@noncompressed.ssh.edu/private
|
||||||
sh://joe@somehost.ssh.edu:2222/private
|
sh://joe@somehost.ssh.edu:2222/private
|
||||||
.fi
|
.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"
|
.\"NODE " Undelete File System"
|
||||||
.SH " Undelete File System"
|
.SH " Undelete File System"
|
||||||
On Linux systems, if you asked configure to use the ext2fs undelete
|
On Linux systems, if you asked configure to use the ext2fs undelete
|
||||||
|
@ -3415,6 +3415,10 @@ MC может быть собран с поддержкой файловой с
|
|||||||
.\"FIle transfer over SHell filesystem"
|
.\"FIle transfer over SHell filesystem"
|
||||||
.br
|
.br
|
||||||
.\"LINK2"
|
.\"LINK2"
|
||||||
|
Файловая система SFTP (SSH File Transfer Protocol)
|
||||||
|
.\"SFTP (SSH File Transfer Protocol) filesystem"
|
||||||
|
.br
|
||||||
|
.\"LINK2"
|
||||||
Файловая система UFS (Undelete File System)
|
Файловая система UFS (Undelete File System)
|
||||||
.\"Undelete File System"
|
.\"Undelete File System"
|
||||||
.br
|
.br
|
||||||
@ -3575,6 +3579,43 @@ bash\-совместимая оболочка shell.
|
|||||||
sh://joe@noncompressed.ssh.edu/private
|
sh://joe@noncompressed.ssh.edu/private
|
||||||
sh://joe@somehost.ssh.edu:2222/private
|
sh://joe@somehost.ssh.edu:2222/private
|
||||||
.fi
|
.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"
|
.\"NODE " Undelete File System"
|
||||||
.SH " Файловая система UFS (Undelete File System)"
|
.SH " Файловая система UFS (Undelete File System)"
|
||||||
В ОС Linux можно сконфигурировать файловую систему ext2fs, используемую
|
В ОС Linux можно сконфигурировать файловую систему ext2fs, используемую
|
||||||
|
@ -165,6 +165,9 @@ static name_keymap_t command_names[] = {
|
|||||||
#ifdef ENABLE_VFS_FTP
|
#ifdef ENABLE_VFS_FTP
|
||||||
{"ConnectFtp", CK_ConnectFtp},
|
{"ConnectFtp", CK_ConnectFtp},
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_VFS_SFTP
|
||||||
|
{"ConnectSftp", CK_ConnectSftp},
|
||||||
|
#endif
|
||||||
#ifdef ENABLE_VFS_SMB
|
#ifdef ENABLE_VFS_SMB
|
||||||
{"ConnectSmb", CK_ConnectSmb},
|
{"ConnectSmb", CK_ConnectSmb},
|
||||||
#endif
|
#endif
|
||||||
|
@ -150,6 +150,7 @@ enum
|
|||||||
CK_Filter,
|
CK_Filter,
|
||||||
CK_ConnectFish,
|
CK_ConnectFish,
|
||||||
CK_ConnectFtp,
|
CK_ConnectFtp,
|
||||||
|
CK_ConnectSftp,
|
||||||
CK_ConnectSmb,
|
CK_ConnectSmb,
|
||||||
CK_PanelInfo,
|
CK_PanelInfo,
|
||||||
CK_Jobs,
|
CK_Jobs,
|
||||||
|
@ -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-extfs.m4])
|
||||||
m4_include([m4.include/vfs/mc-vfs-sfs.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-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-fish.m4])
|
||||||
m4_include([m4.include/vfs/mc-vfs-undelfs.m4])
|
m4_include([m4.include/vfs/mc-vfs-undelfs.m4])
|
||||||
m4_include([m4.include/vfs/mc-vfs-tarfs.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_EXTFS
|
||||||
AC_MC_VFS_UNDELFS
|
AC_MC_VFS_UNDELFS
|
||||||
AC_MC_VFS_FTP
|
AC_MC_VFS_FTP
|
||||||
|
AC_MC_VFS_SFTP
|
||||||
AC_MC_VFS_FISH
|
AC_MC_VFS_FISH
|
||||||
AC_MC_VFS_SMB
|
AC_MC_VFS_SMB
|
||||||
|
|
||||||
|
19
m4.include/vfs/mc-vfs-sftp.m4
Normal file
19
m4.include/vfs/mc-vfs-sftp.m4
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
dnl Enable SFTP filesystem
|
||||||
|
AC_DEFUN([AC_MC_VFS_SFTP],
|
||||||
|
[
|
||||||
|
AC_ARG_ENABLE([vfs-sftp],
|
||||||
|
AS_HELP_STRING([--enable-vfs-sftp], [Support for SFTP filesystem [[yes]]]))
|
||||||
|
if test "$enable_vfs" != "no" -a x"$enable_vfs_sftp" != x"no"; then
|
||||||
|
PKG_CHECK_MODULES(LIBSSH, [libssh2 >= 1.2.5], [found_libssh=yes], [:])
|
||||||
|
if test x"$found_libssh" = "xyes"; then
|
||||||
|
AC_MC_VFS_ADDNAME([sftp])
|
||||||
|
AC_DEFINE([ENABLE_VFS_SFTP], [1], [Support for SFTP filesystem])
|
||||||
|
MCLIBS="$MCLIBS $LIBSSH_LIBS"
|
||||||
|
enable_vfs_sftp="yes"
|
||||||
|
else
|
||||||
|
enable_vfs_sftp="no"
|
||||||
|
AC_ERROR([libssh2 >= 1.2.5 library not found])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
AM_CONDITIONAL([ENABLE_VFS_SFTP], [test "$enable_vfs" = "yes" -a x"$enable_vfs_sftp" = x"yes"])
|
||||||
|
])
|
@ -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
|
#ifdef ENABLE_VFS_FISH
|
||||||
void
|
void
|
||||||
fishlink_cmd (void)
|
fishlink_cmd (void)
|
||||||
|
@ -38,6 +38,9 @@ typedef enum
|
|||||||
#ifdef ENABLE_VFS_FTP
|
#ifdef ENABLE_VFS_FTP
|
||||||
void ftplink_cmd (void);
|
void ftplink_cmd (void);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_VFS_SFTP
|
||||||
|
void sftplink_cmd (void);
|
||||||
|
#endif
|
||||||
#ifdef ENABLE_VFS_FISH
|
#ifdef ENABLE_VFS_FISH
|
||||||
void fishlink_cmd (void);
|
void fishlink_cmd (void);
|
||||||
#endif
|
#endif
|
||||||
|
@ -212,6 +212,9 @@ create_panel_menu (void)
|
|||||||
#ifdef ENABLE_VFS_FISH
|
#ifdef ENABLE_VFS_FISH
|
||||||
entries = g_list_prepend (entries, menu_entry_create (_("S&hell link..."), CK_ConnectFish));
|
entries = g_list_prepend (entries, menu_entry_create (_("S&hell link..."), CK_ConnectFish));
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_VFS_SFTP
|
||||||
|
entries = g_list_prepend (entries, menu_entry_create (_("S&FTP link..."), CK_ConnectSftp));
|
||||||
|
#endif
|
||||||
#ifdef ENABLE_VFS_SMB
|
#ifdef ENABLE_VFS_SMB
|
||||||
entries = g_list_prepend (entries, menu_entry_create (_("SM&B link..."), CK_ConnectSmb));
|
entries = g_list_prepend (entries, menu_entry_create (_("SM&B link..."), CK_ConnectSmb));
|
||||||
#endif
|
#endif
|
||||||
@ -1204,6 +1207,11 @@ midnight_execute_cmd (Widget * sender, unsigned long command)
|
|||||||
ftplink_cmd ();
|
ftplink_cmd ();
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_VFS_SFTP
|
||||||
|
case CK_ConnectSftp:
|
||||||
|
sftplink_cmd ();
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
#ifdef ENABLE_VFS_SMB
|
#ifdef ENABLE_VFS_SMB
|
||||||
case CK_ConnectSmb:
|
case CK_ConnectSmb:
|
||||||
smblink_cmd ();
|
smblink_cmd ();
|
||||||
|
@ -26,6 +26,11 @@ SUBDIRS += ftpfs
|
|||||||
libmc_vfs_la_LIBADD += ftpfs/libvfs-ftpfs.la
|
libmc_vfs_la_LIBADD += ftpfs/libvfs-ftpfs.la
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if ENABLE_VFS_SFTP
|
||||||
|
SUBDIRS += sftpfs
|
||||||
|
libmc_vfs_la_LIBADD += sftpfs/libvfs-sftpfs.la
|
||||||
|
endif
|
||||||
|
|
||||||
if ENABLE_VFS_SFS
|
if ENABLE_VFS_SFS
|
||||||
SUBDIRS += sfs
|
SUBDIRS += sfs
|
||||||
libmc_vfs_la_LIBADD += sfs/libvfs-sfs.la
|
libmc_vfs_la_LIBADD += sfs/libvfs-sfs.la
|
||||||
|
@ -54,6 +54,10 @@
|
|||||||
#include "ftpfs/ftpfs.h"
|
#include "ftpfs/ftpfs.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_VFS_SFTP
|
||||||
|
#include "sftpfs/init.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_VFS_SFS
|
#ifdef ENABLE_VFS_SFS
|
||||||
#include "sfs/sfs.h"
|
#include "sfs/sfs.h"
|
||||||
#endif
|
#endif
|
||||||
@ -112,6 +116,9 @@ vfs_plugins_init (void)
|
|||||||
#ifdef ENABLE_VFS_FTP
|
#ifdef ENABLE_VFS_FTP
|
||||||
init_ftpfs ();
|
init_ftpfs ();
|
||||||
#endif /* ENABLE_VFS_FTP */
|
#endif /* ENABLE_VFS_FTP */
|
||||||
|
#ifdef ENABLE_VFS_SFTP
|
||||||
|
init_sftpfs ();
|
||||||
|
#endif /* ENABLE_VFS_SFTP */
|
||||||
#ifdef ENABLE_VFS_FISH
|
#ifdef ENABLE_VFS_FISH
|
||||||
init_fish ();
|
init_fish ();
|
||||||
#endif /* ENABLE_VFS_FISH */
|
#endif /* ENABLE_VFS_FISH */
|
||||||
|
14
src/vfs/sftpfs/Makefile.am
Normal file
14
src/vfs/sftpfs/Makefile.am
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
AM_CFLAGS = $(GLIB_CFLAGS) $(LIBSSH_CFLAGS) -I$(top_srcdir)
|
||||||
|
AM_CPPFLAGS =
|
||||||
|
|
||||||
|
noinst_LTLIBRARIES = libvfs-sftpfs.la
|
||||||
|
|
||||||
|
libvfs_sftpfs_la_SOURCES = \
|
||||||
|
config_parcer.c \
|
||||||
|
connection.c \
|
||||||
|
dir.c \
|
||||||
|
file.c \
|
||||||
|
init.c init.h \
|
||||||
|
internal.c internal.h \
|
||||||
|
vfs_class.c \
|
||||||
|
vfs_subclass.c
|
420
src/vfs/sftpfs/config_parcer.c
Normal file
420
src/vfs/sftpfs/config_parcer.c
Normal file
@ -0,0 +1,420 @@
|
|||||||
|
/* Virtual File System: SFTP file system.
|
||||||
|
The SSH config parser
|
||||||
|
|
||||||
|
Copyright (C) 2011
|
||||||
|
The Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Written by:
|
||||||
|
Ilia Maslakov <il.smind@gmail.com>, 2011
|
||||||
|
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
|
||||||
|
|
||||||
|
This file is part of the Midnight Commander.
|
||||||
|
|
||||||
|
The Midnight Commander is free software: you can redistribute it
|
||||||
|
and/or modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the License,
|
||||||
|
or (at your option) any later version.
|
||||||
|
|
||||||
|
The Midnight Commander is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h> /* atoi() */
|
||||||
|
|
||||||
|
#include "lib/global.h"
|
||||||
|
|
||||||
|
#include "lib/search.h"
|
||||||
|
#include "lib/vfs/utilvfs.h"
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
/*** global variables ****************************************************************************/
|
||||||
|
|
||||||
|
/*** file scope macro definitions ****************************************************************/
|
||||||
|
|
||||||
|
#define SFTP_DEFAULT_PORT 22
|
||||||
|
|
||||||
|
#ifndef SFTPFS_SSH_CONFIG
|
||||||
|
#define SFTPFS_SSH_CONFIG "~/.ssh/config"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*** file scope type declarations ****************************************************************/
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *real_host; /* host DNS name or ip address */
|
||||||
|
int port; /* port for connect to host */
|
||||||
|
char *user; /* the user to log in as */
|
||||||
|
gboolean password_auth; /* FALSE - no passwords allowed (default TRUE) */
|
||||||
|
gboolean identities_only; /* TRUE - no ssh agent (default FALSE) */
|
||||||
|
gboolean pubkey_auth; /* FALSE - disable public key authentication (default TRUE) */
|
||||||
|
char *identity_file; /* A file from which the user's DSA, ECDSA or DSA authentication identity is read. */
|
||||||
|
} sftpfs_ssh_config_entity_t;
|
||||||
|
|
||||||
|
enum config_var_type
|
||||||
|
{
|
||||||
|
STRING,
|
||||||
|
INTEGER,
|
||||||
|
BOOLEAN,
|
||||||
|
FILENAME
|
||||||
|
};
|
||||||
|
|
||||||
|
/*** file scope variables ************************************************************************/
|
||||||
|
|
||||||
|
/* *INDENT-OFF* */
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
const char *pattern;
|
||||||
|
mc_search_t *pattern_regexp;
|
||||||
|
enum config_var_type type;
|
||||||
|
size_t offset;
|
||||||
|
} config_variables[] =
|
||||||
|
{
|
||||||
|
{"^\\s*User\\s+(.*)$", NULL, STRING, 0},
|
||||||
|
{"^\\s*HostName\\s+(.*)$", NULL, STRING, 0},
|
||||||
|
{"^\\s*IdentitiesOnly\\s+(.*)$", NULL, BOOLEAN, 0},
|
||||||
|
{"^\\s*IdentityFile\\s+(.*)$", NULL, FILENAME, 0},
|
||||||
|
{"^\\s*Port\\s+(.*)$", NULL, INTEGER, 0},
|
||||||
|
{"^\\s*PasswordAuthentication\\s+(.*)$", NULL, BOOLEAN, 0},
|
||||||
|
{"^\\s*PubkeyAuthentication\\s+(.*)$", NULL, STRING, 0},
|
||||||
|
{NULL, NULL, 0, 0}
|
||||||
|
};
|
||||||
|
/* *INDENT-ON* */
|
||||||
|
|
||||||
|
/*** file scope functions ************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Free one config entity.
|
||||||
|
*
|
||||||
|
* @param config_entity config entity structure
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
sftpfs_ssh_config_entity_free (sftpfs_ssh_config_entity_t * config_entity)
|
||||||
|
{
|
||||||
|
g_free (config_entity->real_host);
|
||||||
|
g_free (config_entity->user);
|
||||||
|
g_free (config_entity->identity_file);
|
||||||
|
g_free (config_entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Transform tilda (~) to full home dirname.
|
||||||
|
*
|
||||||
|
* @param filename file name with tilda
|
||||||
|
* @return newly allocated file name with full home dirname
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *
|
||||||
|
sftpfs_correct_file_name (const char *filename)
|
||||||
|
{
|
||||||
|
vfs_path_t *vpath;
|
||||||
|
char *ret_value;
|
||||||
|
|
||||||
|
vpath = vfs_path_from_str (filename);
|
||||||
|
ret_value = vfs_path_to_str (vpath);
|
||||||
|
vfs_path_free (vpath);
|
||||||
|
return ret_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Parse string and filling one config entity by parsed data.
|
||||||
|
*
|
||||||
|
* @param config_entity config entity structure
|
||||||
|
* @param buffer string for parce
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define POINTER_TO_STRUCTURE_MEMBER(type) \
|
||||||
|
((type) ((void *) config_entity + (off_t) config_variables[i].offset))
|
||||||
|
|
||||||
|
static void
|
||||||
|
sftpfs_fill_config_entity_from_string (sftpfs_ssh_config_entity_t * config_entity, char *buffer)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; config_variables[i].pattern != NULL; i++)
|
||||||
|
{
|
||||||
|
if (mc_search_run (config_variables[i].pattern_regexp, buffer, 0, strlen (buffer), NULL))
|
||||||
|
{
|
||||||
|
int value_offset;
|
||||||
|
char *value;
|
||||||
|
|
||||||
|
int *pointer_int;
|
||||||
|
char **pointer_str;
|
||||||
|
gboolean *pointer_bool;
|
||||||
|
|
||||||
|
/* Calculate start of value in string */
|
||||||
|
value_offset = mc_search_getstart_result_by_num (config_variables[i].pattern_regexp, 1);
|
||||||
|
value = &buffer[value_offset];
|
||||||
|
|
||||||
|
switch (config_variables[i].type)
|
||||||
|
{
|
||||||
|
case STRING:
|
||||||
|
pointer_str = POINTER_TO_STRUCTURE_MEMBER (char **);
|
||||||
|
*pointer_str = g_strdup (value);
|
||||||
|
break;
|
||||||
|
case FILENAME:
|
||||||
|
pointer_str = POINTER_TO_STRUCTURE_MEMBER (char **);
|
||||||
|
*pointer_str = sftpfs_correct_file_name (value);
|
||||||
|
break;
|
||||||
|
case INTEGER:
|
||||||
|
pointer_int = POINTER_TO_STRUCTURE_MEMBER (int *);
|
||||||
|
*pointer_int = atoi (value);
|
||||||
|
break;
|
||||||
|
case BOOLEAN:
|
||||||
|
pointer_bool = POINTER_TO_STRUCTURE_MEMBER (gboolean *);
|
||||||
|
*pointer_bool = strcasecmp (value, "True") == 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef POINTER_TO_STRUCTURE_MEMBER
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Fill one config entity from config file.
|
||||||
|
*
|
||||||
|
* @param ssh_config_handler file descriptor for the ssh config file
|
||||||
|
* @param config_entity config entity structure
|
||||||
|
* @param vpath_element path element with host data (hostname, port)
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return TRUE if config entity was filled sucessfully, FALSE otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
sftpfs_fill_config_entity_from_config (FILE * ssh_config_handler,
|
||||||
|
sftpfs_ssh_config_entity_t * config_entity,
|
||||||
|
const vfs_path_element_t * vpath_element, GError ** error)
|
||||||
|
{
|
||||||
|
char buffer[BUF_MEDIUM];
|
||||||
|
gboolean host_block_hit = FALSE;
|
||||||
|
gboolean pattern_block_hit = FALSE;
|
||||||
|
mc_search_t *host_regexp;
|
||||||
|
|
||||||
|
host_regexp = mc_search_new ("^\\s*host\\s+(.*)$", -1);
|
||||||
|
host_regexp->search_type = MC_SEARCH_T_REGEX;
|
||||||
|
host_regexp->is_case_sensitive = FALSE;
|
||||||
|
|
||||||
|
while (!feof (ssh_config_handler))
|
||||||
|
{
|
||||||
|
char *cr;
|
||||||
|
if (fgets (buffer, BUF_MEDIUM, ssh_config_handler) == NULL)
|
||||||
|
{
|
||||||
|
if (errno != 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, MC_ERROR, errno,
|
||||||
|
_("sftp: an error occured while reading %s: %s"), SFTPFS_SSH_CONFIG,
|
||||||
|
strerror (errno));
|
||||||
|
mc_search_free (host_regexp);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cr = strrchr (buffer, '\n');
|
||||||
|
if (cr != NULL)
|
||||||
|
*cr = '\0';
|
||||||
|
|
||||||
|
if (mc_search_run (host_regexp, buffer, 0, strlen (buffer), NULL))
|
||||||
|
{
|
||||||
|
const char *host_pattern;
|
||||||
|
int host_pattern_offset;
|
||||||
|
|
||||||
|
/* if previous host block exactly describe our connection */
|
||||||
|
if (host_block_hit)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
host_pattern_offset = mc_search_getstart_result_by_num (host_regexp, 1);
|
||||||
|
host_pattern = &buffer[host_pattern_offset];
|
||||||
|
if (strcmp (host_pattern, vpath_element->host) == 0)
|
||||||
|
{
|
||||||
|
/* current host block describe our connection */
|
||||||
|
host_block_hit = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mc_search_t *pattern_regexp;
|
||||||
|
|
||||||
|
pattern_block_hit = FALSE;
|
||||||
|
pattern_regexp = mc_search_new (host_pattern, -1);
|
||||||
|
pattern_regexp->search_type = MC_SEARCH_T_GLOB;
|
||||||
|
pattern_regexp->is_case_sensitive = FALSE;
|
||||||
|
pattern_regexp->is_entire_line = TRUE;
|
||||||
|
pattern_block_hit =
|
||||||
|
mc_search_run (pattern_regexp, vpath_element->host, 0,
|
||||||
|
strlen (vpath_element->host), NULL);
|
||||||
|
mc_search_free (pattern_regexp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pattern_block_hit || host_block_hit)
|
||||||
|
{
|
||||||
|
sftpfs_fill_config_entity_from_string (config_entity, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mc_search_free (host_regexp);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Open the ssh config file and fill config entity.
|
||||||
|
*
|
||||||
|
* @param vpath_element path element with host data (hostname, port)
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return newly allocated config entity structure
|
||||||
|
*/
|
||||||
|
|
||||||
|
static sftpfs_ssh_config_entity_t *
|
||||||
|
sftpfs_get_config_entity (const vfs_path_element_t * vpath_element, GError ** error)
|
||||||
|
{
|
||||||
|
sftpfs_ssh_config_entity_t *config_entity;
|
||||||
|
FILE *ssh_config_handler;
|
||||||
|
char *config_filename;
|
||||||
|
|
||||||
|
config_entity = g_new0 (sftpfs_ssh_config_entity_t, 1);
|
||||||
|
config_entity->password_auth = TRUE;
|
||||||
|
config_entity->identities_only = FALSE;
|
||||||
|
config_entity->pubkey_auth = TRUE;
|
||||||
|
config_entity->port = SFTP_DEFAULT_PORT;
|
||||||
|
|
||||||
|
config_filename = sftpfs_correct_file_name (SFTPFS_SSH_CONFIG);
|
||||||
|
ssh_config_handler = fopen (config_filename, "r");
|
||||||
|
g_free (config_filename);
|
||||||
|
|
||||||
|
if (ssh_config_handler != NULL)
|
||||||
|
{
|
||||||
|
gboolean ok;
|
||||||
|
|
||||||
|
ok = sftpfs_fill_config_entity_from_config
|
||||||
|
(ssh_config_handler, config_entity, vpath_element, error);
|
||||||
|
fclose (ssh_config_handler);
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
sftpfs_ssh_config_entity_free (config_entity);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_entity->user == NULL)
|
||||||
|
{
|
||||||
|
config_entity->user = vfs_get_local_username ();
|
||||||
|
if (config_entity->user == NULL)
|
||||||
|
{
|
||||||
|
sftpfs_ssh_config_entity_free (config_entity);
|
||||||
|
config_entity = NULL;
|
||||||
|
g_set_error (error, MC_ERROR, EPERM, _("sftp: Unable to get current user name."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config_entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/*** public functions ****************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Reads data from the ssh config file related to connection.
|
||||||
|
*
|
||||||
|
* @param super connection data
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
sftpfs_fill_connection_data_from_config (struct vfs_s_super *super, GError ** error)
|
||||||
|
{
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
sftpfs_ssh_config_entity_t *config_entity;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
|
||||||
|
config_entity = sftpfs_get_config_entity (super->path_element, error);
|
||||||
|
if (config_entity == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
super_data->config_auth_type = NONE;
|
||||||
|
super_data->config_auth_type |= (config_entity->pubkey_auth) ? PUBKEY : 0;
|
||||||
|
super_data->config_auth_type |= (config_entity->identities_only) ? 0 : AGENT;
|
||||||
|
super_data->config_auth_type |= (config_entity->password_auth) ? PASSWORD : 0;
|
||||||
|
|
||||||
|
if (super->path_element->port == 0)
|
||||||
|
super->path_element->port = config_entity->port;
|
||||||
|
|
||||||
|
if (super->path_element->user == NULL)
|
||||||
|
super->path_element->user = g_strdup (config_entity->user);
|
||||||
|
|
||||||
|
if (config_entity->real_host != NULL)
|
||||||
|
{
|
||||||
|
g_free (super->path_element->host);
|
||||||
|
super->path_element->host = g_strdup (config_entity->real_host);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_entity->identity_file != NULL)
|
||||||
|
{
|
||||||
|
super_data->privkey = g_strdup (config_entity->identity_file);
|
||||||
|
super_data->pubkey = g_strdup_printf ("%s.pub", config_entity->identity_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_ssh_config_entity_free (config_entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Initialize the SSH config parser.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
sftpfs_init_config_variables_patterns (void)
|
||||||
|
{
|
||||||
|
size_t structure_offsets[] = {
|
||||||
|
offsetof(sftpfs_ssh_config_entity_t, user),
|
||||||
|
offsetof(sftpfs_ssh_config_entity_t, real_host),
|
||||||
|
offsetof(sftpfs_ssh_config_entity_t, identities_only),
|
||||||
|
offsetof(sftpfs_ssh_config_entity_t, identity_file),
|
||||||
|
offsetof(sftpfs_ssh_config_entity_t, port),
|
||||||
|
offsetof(sftpfs_ssh_config_entity_t, password_auth),
|
||||||
|
offsetof(sftpfs_ssh_config_entity_t, pubkey_auth)
|
||||||
|
};
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; config_variables[i].pattern != NULL; i++)
|
||||||
|
{
|
||||||
|
config_variables[i].pattern_regexp = mc_search_new (config_variables[i].pattern, -1);
|
||||||
|
config_variables[i].pattern_regexp->search_type = MC_SEARCH_T_REGEX;
|
||||||
|
config_variables[i].pattern_regexp->is_case_sensitive = FALSE;
|
||||||
|
config_variables[i].offset = structure_offsets[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Deinitialize the SSH config parser.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
sftpfs_deinit_config_variables_patterns (void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; config_variables[i].pattern != NULL; i++)
|
||||||
|
{
|
||||||
|
mc_search_free (config_variables[i].pattern_regexp);
|
||||||
|
config_variables[i].pattern_regexp = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
468
src/vfs/sftpfs/connection.c
Normal file
468
src/vfs/sftpfs/connection.c
Normal file
@ -0,0 +1,468 @@
|
|||||||
|
/* Virtual File System: SFTP file system.
|
||||||
|
The internal functions: connections
|
||||||
|
|
||||||
|
Copyright (C) 2011
|
||||||
|
The Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Written by:
|
||||||
|
Ilia Maslakov <il.smind@gmail.com>, 2011
|
||||||
|
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
|
||||||
|
|
||||||
|
This file is part of the Midnight Commander.
|
||||||
|
|
||||||
|
The Midnight Commander is free software: you can redistribute it
|
||||||
|
and/or modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the License,
|
||||||
|
or (at your option) any later version.
|
||||||
|
|
||||||
|
The Midnight Commander is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <netdb.h> /* struct hostent */
|
||||||
|
#include <sys/socket.h> /* AF_INET */
|
||||||
|
#include <netinet/in.h> /* struct in_addr */
|
||||||
|
#ifdef HAVE_ARPA_INET_H
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <libssh2.h>
|
||||||
|
#include <libssh2_sftp.h>
|
||||||
|
|
||||||
|
#include "lib/global.h"
|
||||||
|
|
||||||
|
#include "lib/util.h"
|
||||||
|
#include "lib/tty/tty.h" /* tty_enable_interrupt_key () */
|
||||||
|
#include "lib/vfs/utilvfs.h"
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
/*** global variables ****************************************************************************/
|
||||||
|
|
||||||
|
/*** file scope macro definitions ****************************************************************/
|
||||||
|
|
||||||
|
/*** file scope type declarations ****************************************************************/
|
||||||
|
|
||||||
|
/*** file scope variables ************************************************************************/
|
||||||
|
|
||||||
|
/*** file scope functions ************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Create socket to host.
|
||||||
|
*
|
||||||
|
* @param super connection data
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return socket descriptor number, -1 if any error was occured
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_open_socket (struct vfs_s_super *super, GError ** error)
|
||||||
|
{
|
||||||
|
struct addrinfo hints, *res = NULL, *curr_res;
|
||||||
|
int my_socket = 0;
|
||||||
|
char port[BUF_TINY];
|
||||||
|
int e;
|
||||||
|
|
||||||
|
if (super->path_element->host == NULL || *super->path_element->host == '\0')
|
||||||
|
{
|
||||||
|
g_set_error (error, MC_ERROR, -1, _("sftp: Invalid host name."));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf (port, "%hu", (unsigned short) super->path_element->port);
|
||||||
|
if (port == NULL)
|
||||||
|
{
|
||||||
|
g_set_error (error, MC_ERROR, -1, _("sftp: Invalid port value."));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tty_enable_interrupt_key (); /* clear the interrupt flag */
|
||||||
|
|
||||||
|
memset (&hints, 0, sizeof (struct addrinfo));
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
#ifdef AI_ADDRCONFIG
|
||||||
|
/* By default, only look up addresses using address types for
|
||||||
|
* which a local interface is configured (i.e. no IPv6 if no IPv6
|
||||||
|
* interfaces, likewise for IPv4 (see RFC 3493 for details). */
|
||||||
|
hints.ai_flags = AI_ADDRCONFIG;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
e = getaddrinfo (super->path_element->host, port, &hints, &res);
|
||||||
|
|
||||||
|
#ifdef AI_ADDRCONFIG
|
||||||
|
if (e == EAI_BADFLAGS)
|
||||||
|
{
|
||||||
|
/* Retry with no flags if AI_ADDRCONFIG was rejected. */
|
||||||
|
hints.ai_flags = 0;
|
||||||
|
e = getaddrinfo (super->path_element->host, port, &hints, &res);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (e != 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, MC_ERROR, -1, _("sftp: %s"), gai_strerror (e));
|
||||||
|
my_socket = -1;
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next)
|
||||||
|
{
|
||||||
|
my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol);
|
||||||
|
|
||||||
|
if (my_socket < 0)
|
||||||
|
{
|
||||||
|
if (curr_res->ai_next != NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vfs_print_message (_("sftp: %s"), unix_error_string (errno));
|
||||||
|
my_socket = -1;
|
||||||
|
goto ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
vfs_print_message (_("sftp: making connection to %s"), super->path_element->host);
|
||||||
|
|
||||||
|
if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
close (my_socket);
|
||||||
|
|
||||||
|
if (errno == EINTR && tty_got_interrupt ())
|
||||||
|
g_set_error (error, MC_ERROR, -1, _("sftp: connection interrupted by user"));
|
||||||
|
else if (res->ai_next == NULL)
|
||||||
|
g_set_error (error, MC_ERROR, -1, _("sftp: connection to server failed: %s"),
|
||||||
|
unix_error_string (errno));
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
my_socket = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret:
|
||||||
|
if (res != NULL)
|
||||||
|
freeaddrinfo (res);
|
||||||
|
tty_disable_interrupt_key ();
|
||||||
|
return my_socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Recognize authenticaion types supported by remote side and filling internal 'super' structure by
|
||||||
|
* proper enum's values.
|
||||||
|
*
|
||||||
|
* @param super connection data
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
sftpfs_recognize_auth_types (struct vfs_s_super *super)
|
||||||
|
{
|
||||||
|
char *userauthlist;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
|
||||||
|
super_data->auth_type = NONE;
|
||||||
|
|
||||||
|
/* check what authentication methods are available */
|
||||||
|
userauthlist = libssh2_userauth_list (super_data->session, super->path_element->user,
|
||||||
|
strlen (super->path_element->user));
|
||||||
|
|
||||||
|
if ((strstr (userauthlist, "password") != NULL
|
||||||
|
|| strstr (userauthlist, "keyboard-interactive") != NULL)
|
||||||
|
&& (super_data->config_auth_type & PASSWORD) != 0)
|
||||||
|
super_data->auth_type |= PASSWORD;
|
||||||
|
|
||||||
|
if (strstr (userauthlist, "publickey") != NULL && (super_data->config_auth_type & PUBKEY) != 0)
|
||||||
|
super_data->auth_type |= PUBKEY;
|
||||||
|
|
||||||
|
if ((super_data->config_auth_type & AGENT) != 0)
|
||||||
|
super_data->auth_type |= AGENT;
|
||||||
|
|
||||||
|
g_free (userauthlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Open connection to host using SSH-agent helper.
|
||||||
|
*
|
||||||
|
* @param super connection data
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return TRUE if connection was sucessfully opened, FALSE otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
sftpfs_open_connection_ssh_agent (struct vfs_s_super *super, GError ** error)
|
||||||
|
{
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
struct libssh2_agent_publickey *identity, *prev_identity = NULL;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
(void) error;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
super_data->agent = NULL;
|
||||||
|
|
||||||
|
if ((super_data->auth_type & AGENT) == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Connect to the ssh-agent */
|
||||||
|
super_data->agent = libssh2_agent_init (super_data->session);
|
||||||
|
if (super_data->agent == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (libssh2_agent_connect (super_data->agent) != 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (libssh2_agent_list_identities (super_data->agent) != 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
rc = libssh2_agent_get_identity (super_data->agent, &identity, prev_identity);
|
||||||
|
if (rc == 1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (rc < 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (libssh2_agent_userauth (super_data->agent, super->path_element->user, identity) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
prev_identity = identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (rc == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Open connection to host using SSH-keypair.
|
||||||
|
*
|
||||||
|
* @param super connection data
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return TRUE if connection was sucessfully opened, FALSE otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
sftpfs_open_connection_ssh_key (struct vfs_s_super *super, GError ** error)
|
||||||
|
{
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
char *p, *passwd;
|
||||||
|
gboolean ret_value = FALSE;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
|
||||||
|
if ((super_data->auth_type & PUBKEY) == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (super_data->privkey == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (libssh2_userauth_publickey_fromfile (super_data->session, super->path_element->user,
|
||||||
|
super_data->pubkey, super_data->privkey,
|
||||||
|
super->path_element->password) == 0)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
p = g_strdup_printf (_("sftp: Enter passphrase for %s "), super->path_element->user);
|
||||||
|
passwd = vfs_get_password (p);
|
||||||
|
g_free (p);
|
||||||
|
|
||||||
|
if (passwd == NULL)
|
||||||
|
g_set_error (error, MC_ERROR, -1, _("sftp: Passphrase is empty."));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret_value = (libssh2_userauth_publickey_fromfile (super_data->session,
|
||||||
|
super->path_element->user,
|
||||||
|
super_data->pubkey, super_data->privkey,
|
||||||
|
passwd) == 0);
|
||||||
|
g_free (passwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Open connection to host using password.
|
||||||
|
*
|
||||||
|
* @param super connection data
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return TRUE if connection was sucessfully opened, FALSE otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
sftpfs_open_connection_ssh_password (struct vfs_s_super *super, GError ** error)
|
||||||
|
{
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
char *p, *passwd;
|
||||||
|
gboolean ret_value = FALSE;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
|
||||||
|
if ((super_data->auth_type & PASSWORD) == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (super->path_element->password != NULL)
|
||||||
|
{
|
||||||
|
while ((rc = libssh2_userauth_password (super_data->session, super->path_element->user,
|
||||||
|
super->path_element->password)) ==
|
||||||
|
LIBSSH2_ERROR_EAGAIN);
|
||||||
|
if (rc == 0)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = g_strdup_printf (_("sftp: Enter password for %s "), super->path_element->user);
|
||||||
|
passwd = vfs_get_password (p);
|
||||||
|
g_free (p);
|
||||||
|
|
||||||
|
if (passwd == NULL)
|
||||||
|
g_set_error (error, MC_ERROR, -1, _("sftp: Password is empty."));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while ((rc = libssh2_userauth_password (super_data->session, super->path_element->user,
|
||||||
|
passwd)) == LIBSSH2_ERROR_EAGAIN)
|
||||||
|
;
|
||||||
|
|
||||||
|
if (rc == 0)
|
||||||
|
{
|
||||||
|
ret_value = TRUE;
|
||||||
|
g_free (super->path_element->password);
|
||||||
|
super->path_element->password = passwd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
g_free (passwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/*** public functions ****************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Open new connection.
|
||||||
|
*
|
||||||
|
* @param super connection data
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return 0 if success, -1 otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
sftpfs_open_connection (struct vfs_s_super *super, GError ** error)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
|
||||||
|
/* Create a session instance */
|
||||||
|
super_data->session = libssh2_session_init ();
|
||||||
|
if (super_data->session == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The application code is responsible for creating the socket
|
||||||
|
* and establishing the connection
|
||||||
|
*/
|
||||||
|
super_data->socket_handle = sftpfs_open_socket (super, error);
|
||||||
|
if (super_data->socket_handle == -1)
|
||||||
|
goto deinit_by_error;
|
||||||
|
|
||||||
|
/* ... start it up. This will trade welcome banners, exchange keys,
|
||||||
|
* and setup crypto, compression, and MAC layers
|
||||||
|
*/
|
||||||
|
rc = libssh2_session_startup (super_data->session, super_data->socket_handle);
|
||||||
|
if (rc != 0)
|
||||||
|
{
|
||||||
|
g_set_error (error, MC_ERROR, -1, _("sftp: Failure establishing SSH session: (%d)"), rc);
|
||||||
|
goto deinit_by_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* At this point we havn't yet authenticated. The first thing to do
|
||||||
|
* is check the hostkey's fingerprint against our known hosts Your app
|
||||||
|
* may have it hard coded, may go to a file, may present it to the
|
||||||
|
* user, that's your call
|
||||||
|
*/
|
||||||
|
super_data->fingerprint = libssh2_hostkey_hash (super_data->session, LIBSSH2_HOSTKEY_HASH_SHA1);
|
||||||
|
|
||||||
|
sftpfs_recognize_auth_types (super);
|
||||||
|
|
||||||
|
if (!sftpfs_open_connection_ssh_agent (super, error)
|
||||||
|
&& !sftpfs_open_connection_ssh_key (super, error)
|
||||||
|
&& !sftpfs_open_connection_ssh_password (super, error))
|
||||||
|
goto deinit_by_error;
|
||||||
|
|
||||||
|
super_data->sftp_session = libssh2_sftp_init (super_data->session);
|
||||||
|
|
||||||
|
if (super_data->sftp_session == NULL)
|
||||||
|
goto deinit_by_error;
|
||||||
|
|
||||||
|
/* Since we have not set non-blocking, tell libssh2 we are blocking */
|
||||||
|
libssh2_session_set_blocking (super_data->session, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
deinit_by_error:
|
||||||
|
sftpfs_close_connection (super, "Shutdown with errors", NULL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Close connection.
|
||||||
|
*
|
||||||
|
* @param super connection data
|
||||||
|
* @param shutdown_message message for shutdown functions
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
sftpfs_close_connection (struct vfs_s_super *super, const char *shutdown_message, GError ** error)
|
||||||
|
{
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
|
||||||
|
(void) error;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
if (super_data == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (super_data->agent != NULL)
|
||||||
|
{
|
||||||
|
libssh2_agent_disconnect (super_data->agent);
|
||||||
|
libssh2_agent_free (super_data->agent);
|
||||||
|
super_data->agent = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (super_data->sftp_session != NULL)
|
||||||
|
{
|
||||||
|
libssh2_sftp_shutdown (super_data->sftp_session);
|
||||||
|
super_data->sftp_session = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (super_data->session != NULL)
|
||||||
|
{
|
||||||
|
libssh2_session_disconnect (super_data->session, shutdown_message);
|
||||||
|
super_data->session = NULL;
|
||||||
|
}
|
||||||
|
if (super_data->fingerprint != NULL)
|
||||||
|
super_data->fingerprint = NULL;
|
||||||
|
|
||||||
|
if (super_data->socket_handle != -1)
|
||||||
|
{
|
||||||
|
close (super_data->socket_handle);
|
||||||
|
super_data->socket_handle = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
286
src/vfs/sftpfs/dir.c
Normal file
286
src/vfs/sftpfs/dir.c
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
/* Virtual File System: SFTP file system.
|
||||||
|
The internal functions: dirs
|
||||||
|
|
||||||
|
Copyright (C) 2011
|
||||||
|
The Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Written by:
|
||||||
|
Ilia Maslakov <il.smind@gmail.com>, 2011
|
||||||
|
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
|
||||||
|
|
||||||
|
This file is part of the Midnight Commander.
|
||||||
|
|
||||||
|
The Midnight Commander is free software: you can redistribute it
|
||||||
|
and/or modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the License,
|
||||||
|
or (at your option) any later version.
|
||||||
|
|
||||||
|
The Midnight Commander is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <libssh2.h>
|
||||||
|
#include <libssh2_sftp.h>
|
||||||
|
|
||||||
|
#include "lib/global.h"
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
/*** global variables ****************************************************************************/
|
||||||
|
|
||||||
|
/*** file scope macro definitions ****************************************************************/
|
||||||
|
|
||||||
|
/*** file scope type declarations ****************************************************************/
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
LIBSSH2_SFTP_HANDLE *handle;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
} sftpfs_dir_data_t;
|
||||||
|
|
||||||
|
/*** file scope variables ************************************************************************/
|
||||||
|
|
||||||
|
/*** file scope functions ************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/*** public functions ****************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Open a directory stream corresponding to the directory name.
|
||||||
|
*
|
||||||
|
* @param vpath path to directory
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return directory data handler if success, NULL otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
void *
|
||||||
|
sftpfs_opendir (const vfs_path_t * vpath, GError ** error)
|
||||||
|
{
|
||||||
|
sftpfs_dir_data_t *sftpfs_dir;
|
||||||
|
struct vfs_s_super *super;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
const vfs_path_element_t *path_element;
|
||||||
|
LIBSSH2_SFTP_HANDLE *handle;
|
||||||
|
|
||||||
|
path_element = vfs_path_get_by_index (vpath, -1);
|
||||||
|
|
||||||
|
if (vfs_s_get_path (vpath, &super, 0) == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
int libssh_errno;
|
||||||
|
|
||||||
|
handle =
|
||||||
|
libssh2_sftp_opendir (super_data->sftp_session,
|
||||||
|
sftpfs_fix_filename (path_element->path));
|
||||||
|
if (handle != NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
libssh_errno = libssh2_session_last_errno (super_data->session);
|
||||||
|
if (libssh_errno != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, libssh_errno, error);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
sftpfs_waitsocket (super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_dir = g_new0 (sftpfs_dir_data_t, 1);
|
||||||
|
sftpfs_dir->handle = handle;
|
||||||
|
sftpfs_dir->super_data = super_data;
|
||||||
|
|
||||||
|
return (void *) sftpfs_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Get a pointer to a structure representing the next directory entry.
|
||||||
|
*
|
||||||
|
* @param data directory data handler
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return information about direntry if success, NULL otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
void *
|
||||||
|
sftpfs_readdir (void *data, GError ** error)
|
||||||
|
{
|
||||||
|
char mem[BUF_MEDIUM];
|
||||||
|
LIBSSH2_SFTP_ATTRIBUTES attrs;
|
||||||
|
sftpfs_dir_data_t *sftpfs_dir = (sftpfs_dir_data_t *) data;
|
||||||
|
static union vfs_dirent sftpfs_dirent;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
rc = libssh2_sftp_readdir (sftpfs_dir->handle, mem, sizeof (mem), &attrs);
|
||||||
|
if (rc >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (rc != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (sftpfs_dir->super_data, rc, error);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_waitsocket (sftpfs_dir->super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
while (rc == LIBSSH2_ERROR_EAGAIN);
|
||||||
|
|
||||||
|
if (rc == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
g_strlcpy (sftpfs_dirent.dent.d_name, mem, BUF_MEDIUM);
|
||||||
|
compute_namelen (&sftpfs_dirent.dent);
|
||||||
|
return &sftpfs_dirent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Close the directory stream.
|
||||||
|
*
|
||||||
|
* @param data directory data handler
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
sftpfs_closedir (void *data, GError ** error)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
sftpfs_dir_data_t *sftpfs_dir = (sftpfs_dir_data_t *) data;
|
||||||
|
|
||||||
|
(void) error;
|
||||||
|
|
||||||
|
rc = libssh2_sftp_closedir (sftpfs_dir->handle);
|
||||||
|
g_free (sftpfs_dir);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Create a new directory.
|
||||||
|
*
|
||||||
|
* @param vpath path directory
|
||||||
|
* @param mode mode (see man 2 open)
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
sftpfs_mkdir (const vfs_path_t * vpath, mode_t mode, GError ** error)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
struct vfs_s_super *super;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
const vfs_path_element_t *path_element;
|
||||||
|
|
||||||
|
path_element = vfs_path_get_by_index (vpath, -1);
|
||||||
|
|
||||||
|
if (vfs_s_get_path (vpath, &super, 0) == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (super == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
if (super_data->sftp_session == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const char *fixfname;
|
||||||
|
|
||||||
|
fixfname = sftpfs_fix_filename (path_element->path);
|
||||||
|
|
||||||
|
res =
|
||||||
|
libssh2_sftp_mkdir_ex (super_data->sftp_session,
|
||||||
|
fixfname, sftpfs_filename_buffer->len, mode);
|
||||||
|
if (res >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (res != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, res, error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_waitsocket (super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (res == LIBSSH2_ERROR_EAGAIN);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Remove a directory.
|
||||||
|
*
|
||||||
|
* @param vpath path directory
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
sftpfs_rmdir (const vfs_path_t * vpath, GError ** error)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
struct vfs_s_super *super;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
const vfs_path_element_t *path_element;
|
||||||
|
|
||||||
|
path_element = vfs_path_get_by_index (vpath, -1);
|
||||||
|
|
||||||
|
if (vfs_s_get_path (vpath, &super, 0) == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (super == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
if (super_data->sftp_session == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const char *fixfname;
|
||||||
|
|
||||||
|
fixfname = sftpfs_fix_filename (path_element->path);
|
||||||
|
|
||||||
|
res =
|
||||||
|
libssh2_sftp_rmdir_ex (super_data->sftp_session, fixfname, sftpfs_filename_buffer->len);
|
||||||
|
if (res >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (res != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, res, error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_waitsocket (super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (res == LIBSSH2_ERROR_EAGAIN);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
388
src/vfs/sftpfs/file.c
Normal file
388
src/vfs/sftpfs/file.c
Normal file
@ -0,0 +1,388 @@
|
|||||||
|
/* Virtual File System: SFTP file system.
|
||||||
|
The internal functions: files
|
||||||
|
|
||||||
|
Copyright (C) 2011
|
||||||
|
The Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Written by:
|
||||||
|
Ilia Maslakov <il.smind@gmail.com>, 2011
|
||||||
|
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
|
||||||
|
|
||||||
|
This file is part of the Midnight Commander.
|
||||||
|
|
||||||
|
The Midnight Commander is free software: you can redistribute it
|
||||||
|
and/or modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the License,
|
||||||
|
or (at your option) any later version.
|
||||||
|
|
||||||
|
The Midnight Commander is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <libssh2.h>
|
||||||
|
#include <libssh2_sftp.h>
|
||||||
|
|
||||||
|
#include "lib/global.h"
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
/*** global variables ****************************************************************************/
|
||||||
|
|
||||||
|
/*** file scope macro definitions ****************************************************************/
|
||||||
|
|
||||||
|
/*** file scope type declarations ****************************************************************/
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
LIBSSH2_SFTP_HANDLE *handle;
|
||||||
|
int flags;
|
||||||
|
mode_t mode;
|
||||||
|
} sftpfs_file_handler_data_t;
|
||||||
|
|
||||||
|
/*** file scope variables ************************************************************************/
|
||||||
|
|
||||||
|
/*** file scope functions ************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Reopen file by file handle.
|
||||||
|
*
|
||||||
|
* @param file_handler the file handler data
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
sftpfs_reopen (vfs_file_handler_t * file_handler, GError ** error)
|
||||||
|
{
|
||||||
|
sftpfs_file_handler_data_t *file_handler_data;
|
||||||
|
|
||||||
|
file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
|
||||||
|
|
||||||
|
sftpfs_close_file (file_handler, error);
|
||||||
|
if (error == NULL || *error == NULL)
|
||||||
|
sftpfs_open_file (file_handler, file_handler_data->flags, file_handler_data->mode, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/*** public functions ****************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Open new SFTP file.
|
||||||
|
*
|
||||||
|
* @param file_handler the file handler data
|
||||||
|
* @param flags flags (see man 2 open)
|
||||||
|
* @param mode mode (see man 2 open)
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return TRUE if connection was created successfully, FALSE otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
sftpfs_open_file (vfs_file_handler_t * file_handler, int flags, mode_t mode, GError ** error)
|
||||||
|
{
|
||||||
|
unsigned long sftp_open_flags = 0;
|
||||||
|
int sftp_open_mode = 0;
|
||||||
|
sftpfs_file_handler_data_t *file_handler_data;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
(void) mode;
|
||||||
|
|
||||||
|
name = vfs_s_fullpath (&sftpfs_class, file_handler->ino);
|
||||||
|
if (name == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
|
||||||
|
file_handler_data = g_new0 (sftpfs_file_handler_data_t, 1);
|
||||||
|
|
||||||
|
if ((flags & O_CREAT) != 0 || (flags & O_WRONLY) != 0)
|
||||||
|
{
|
||||||
|
sftp_open_flags = (flags & O_WRONLY) != 0 ? LIBSSH2_FXF_WRITE : 0;
|
||||||
|
sftp_open_flags |= (flags & O_CREAT) != 0 ? LIBSSH2_FXF_CREAT : 0;
|
||||||
|
sftp_open_flags |= (flags & O_APPEND) != 0 ? LIBSSH2_FXF_APPEND : 0;
|
||||||
|
sftp_open_flags |= (flags & O_TRUNC) != 0 ? LIBSSH2_FXF_TRUNC : 0;
|
||||||
|
|
||||||
|
sftp_open_mode = LIBSSH2_SFTP_S_IRUSR |
|
||||||
|
LIBSSH2_SFTP_S_IWUSR | LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sftp_open_flags = LIBSSH2_FXF_READ;
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
int libssh_errno;
|
||||||
|
|
||||||
|
file_handler_data->handle =
|
||||||
|
libssh2_sftp_open (super_data->sftp_session, sftpfs_fix_filename (name),
|
||||||
|
sftp_open_flags, sftp_open_mode);
|
||||||
|
|
||||||
|
if (file_handler_data->handle != NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
libssh_errno = libssh2_session_last_errno (super_data->session);
|
||||||
|
if (libssh_errno != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, libssh_errno, error);
|
||||||
|
g_free (name);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (name);
|
||||||
|
|
||||||
|
file_handler_data->flags = flags;
|
||||||
|
file_handler_data->mode = mode;
|
||||||
|
file_handler->data = file_handler_data;
|
||||||
|
|
||||||
|
if ((flags & O_APPEND) != 0)
|
||||||
|
{
|
||||||
|
struct stat file_info;
|
||||||
|
|
||||||
|
if (sftpfs_fstat (file_handler, &file_info, error) == 0)
|
||||||
|
libssh2_sftp_seek64 (file_handler_data->handle, file_info.st_size);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Stats the file specified by the file descriptor.
|
||||||
|
*
|
||||||
|
* @param data file data handler
|
||||||
|
* @param buf buffer for store stat-info
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
sftpfs_fstat (void *data, struct stat *buf, GError ** error)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
LIBSSH2_SFTP_ATTRIBUTES attrs;
|
||||||
|
vfs_file_handler_t *fh = (vfs_file_handler_t *) data;
|
||||||
|
sftpfs_file_handler_data_t *sftpfs_fh = fh->data;
|
||||||
|
struct vfs_s_super *super = fh->ino->super;
|
||||||
|
sftpfs_super_data_t *super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
|
||||||
|
if (sftpfs_fh->handle == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
res = libssh2_sftp_fstat_ex (sftpfs_fh->handle, &attrs, 0);
|
||||||
|
if (res >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (res != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, res, error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_waitsocket (super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (res == LIBSSH2_ERROR_EAGAIN);
|
||||||
|
|
||||||
|
if ((attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
|
||||||
|
{
|
||||||
|
buf->st_uid = attrs.uid;
|
||||||
|
buf->st_gid = attrs.gid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
|
||||||
|
{
|
||||||
|
buf->st_atime = attrs.atime;
|
||||||
|
buf->st_mtime = attrs.mtime;
|
||||||
|
buf->st_ctime = attrs.mtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
|
||||||
|
buf->st_size = attrs.filesize;
|
||||||
|
|
||||||
|
if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
|
||||||
|
buf->st_mode = attrs.permissions;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Read up to 'count' bytes from the file descriptor 'file_handler' to the buffer starting at 'buffer'.
|
||||||
|
*
|
||||||
|
* @param data file data handler
|
||||||
|
* @param buffer buffer for data
|
||||||
|
* @param count data size
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
sftpfs_read_file (vfs_file_handler_t * file_handler, char *buffer, size_t count, GError ** error)
|
||||||
|
{
|
||||||
|
ssize_t rc;
|
||||||
|
sftpfs_file_handler_data_t *file_handler_data;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
|
||||||
|
if (file_handler == NULL || file_handler->data == NULL)
|
||||||
|
{
|
||||||
|
g_set_error (error, MC_ERROR, -1, _("sftp: No file handler data present for reading file"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_handler_data = file_handler->data;
|
||||||
|
super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
rc = libssh2_sftp_read (file_handler_data->handle, buffer, count);
|
||||||
|
if (rc >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (rc != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, rc, error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_waitsocket (super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (rc == LIBSSH2_ERROR_EAGAIN);
|
||||||
|
|
||||||
|
file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Write up to 'count' bytes from the buffer starting at 'buffer' to the descriptor 'file_handler'.
|
||||||
|
*
|
||||||
|
* @param data file data handler
|
||||||
|
* @param buffer buffer for data
|
||||||
|
* @param count data size
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
sftpfs_write_file (vfs_file_handler_t * file_handler, const char *buffer, size_t count,
|
||||||
|
GError ** error)
|
||||||
|
{
|
||||||
|
ssize_t rc;
|
||||||
|
sftpfs_file_handler_data_t *file_handler_data;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
|
||||||
|
file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
|
||||||
|
super_data = (sftpfs_super_data_t *) file_handler->ino->super->data;
|
||||||
|
|
||||||
|
file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
rc = libssh2_sftp_write (file_handler_data->handle, buffer, count);
|
||||||
|
if (rc >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (rc != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, rc, error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_waitsocket (super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (rc == LIBSSH2_ERROR_EAGAIN);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Close a file descriptor.
|
||||||
|
*
|
||||||
|
* @param data file data handler
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
sftpfs_close_file (vfs_file_handler_t * file_handler, GError ** error)
|
||||||
|
{
|
||||||
|
sftpfs_file_handler_data_t *file_handler_data;
|
||||||
|
|
||||||
|
(void) error;
|
||||||
|
|
||||||
|
file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
|
||||||
|
if (file_handler_data == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
libssh2_sftp_close (file_handler_data->handle);
|
||||||
|
|
||||||
|
g_free (file_handler_data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Reposition the offset of the open file associated with the file descriptor.
|
||||||
|
*
|
||||||
|
* @param data file data handler
|
||||||
|
* @param offset file offset
|
||||||
|
* @param whence method of seek (at begin, at current, at end)
|
||||||
|
* @param error pointer to the error handler
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
off_t
|
||||||
|
sftpfs_lseek (vfs_file_handler_t * file_handler, off_t offset, int whence, GError ** error)
|
||||||
|
{
|
||||||
|
sftpfs_file_handler_data_t *file_handler_data;
|
||||||
|
|
||||||
|
file_handler_data = (sftpfs_file_handler_data_t *) file_handler->data;
|
||||||
|
|
||||||
|
switch (whence)
|
||||||
|
{
|
||||||
|
case SEEK_SET:
|
||||||
|
/* Need reopen file because:
|
||||||
|
"You MUST NOT seek during writing or reading a file with SFTP, as the internals use
|
||||||
|
outstanding packets and changing the "file position" during transit will results in
|
||||||
|
badness." */
|
||||||
|
if (file_handler->pos > offset || offset == 0)
|
||||||
|
{
|
||||||
|
sftpfs_reopen (file_handler, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
file_handler->pos = offset;
|
||||||
|
break;
|
||||||
|
case SEEK_CUR:
|
||||||
|
file_handler->pos += offset;
|
||||||
|
break;
|
||||||
|
case SEEK_END:
|
||||||
|
if (file_handler->pos > file_handler->ino->st.st_size - offset)
|
||||||
|
{
|
||||||
|
sftpfs_reopen (file_handler, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
file_handler->pos = file_handler->ino->st.st_size - offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
libssh2_sftp_seek64 (file_handler_data->handle, file_handler->pos);
|
||||||
|
file_handler->pos = (off_t) libssh2_sftp_tell64 (file_handler_data->handle);
|
||||||
|
|
||||||
|
return file_handler->pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
69
src/vfs/sftpfs/init.c
Normal file
69
src/vfs/sftpfs/init.c
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/* Virtual File System: SFTP file system.
|
||||||
|
The interface function
|
||||||
|
|
||||||
|
Copyright (C) 2011
|
||||||
|
The Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Written by:
|
||||||
|
Ilia Maslakov <il.smind@gmail.com>, 2011
|
||||||
|
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
|
||||||
|
|
||||||
|
This file is part of the Midnight Commander.
|
||||||
|
|
||||||
|
The Midnight Commander is free software: you can redistribute it
|
||||||
|
and/or modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the License,
|
||||||
|
or (at your option) any later version.
|
||||||
|
|
||||||
|
The Midnight Commander is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include "lib/global.h"
|
||||||
|
#include "lib/vfs/netutil.h"
|
||||||
|
|
||||||
|
#include "init.h"
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
/*** global variables ****************************************************************************/
|
||||||
|
|
||||||
|
/*** file scope macro definitions ****************************************************************/
|
||||||
|
|
||||||
|
/*** file scope type declarations ****************************************************************/
|
||||||
|
|
||||||
|
/*** file scope variables ************************************************************************/
|
||||||
|
|
||||||
|
/*** file scope functions ************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/*** public functions ****************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Initialization of SFTP Virtual File Sysytem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
init_sftpfs (void)
|
||||||
|
{
|
||||||
|
tcp_init ();
|
||||||
|
|
||||||
|
sftpfs_init_class ();
|
||||||
|
sftpfs_init_subclass ();
|
||||||
|
|
||||||
|
vfs_s_init_class (&sftpfs_class, &sftpfs_subclass);
|
||||||
|
|
||||||
|
sftpfs_init_class_callbacks ();
|
||||||
|
sftpfs_init_subclass_callbacks ();
|
||||||
|
|
||||||
|
vfs_register_class (&sftpfs_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
23
src/vfs/sftpfs/init.h
Normal file
23
src/vfs/sftpfs/init.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief Header: SFTP FS
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MC__VFS_SFTPFS_INIT_H
|
||||||
|
#define MC__VFS_SFTPFS_INIT_H
|
||||||
|
|
||||||
|
/*** typedefs(not structures) and defined constants **********************************************/
|
||||||
|
|
||||||
|
/*** enums ***************************************************************************************/
|
||||||
|
|
||||||
|
/*** structures declarations (and typedefs of structures)*****************************************/
|
||||||
|
|
||||||
|
/*** global variables defined in .c file *********************************************************/
|
||||||
|
|
||||||
|
/*** declarations of public functions ************************************************************/
|
||||||
|
|
||||||
|
void init_sftpfs (void);
|
||||||
|
|
||||||
|
/*** inline functions ****************************************************************************/
|
||||||
|
|
||||||
|
#endif /* MC__VFS_SFTPFS_INIT_H */
|
624
src/vfs/sftpfs/internal.c
Normal file
624
src/vfs/sftpfs/internal.c
Normal file
@ -0,0 +1,624 @@
|
|||||||
|
/* Virtual File System: SFTP file system.
|
||||||
|
The internal functions
|
||||||
|
|
||||||
|
Copyright (C) 2011
|
||||||
|
The Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Written by:
|
||||||
|
Ilia Maslakov <il.smind@gmail.com>, 2011
|
||||||
|
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
|
||||||
|
|
||||||
|
This file is part of the Midnight Commander.
|
||||||
|
|
||||||
|
The Midnight Commander is free software: you can redistribute it
|
||||||
|
and/or modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the License,
|
||||||
|
or (at your option) any later version.
|
||||||
|
|
||||||
|
The Midnight Commander is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "lib/global.h"
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
/*** global variables ****************************************************************************/
|
||||||
|
|
||||||
|
GString *sftpfs_filename_buffer = NULL;
|
||||||
|
|
||||||
|
/*** file scope macro definitions ****************************************************************/
|
||||||
|
|
||||||
|
/*** file scope type declarations ****************************************************************/
|
||||||
|
|
||||||
|
/*** file scope variables ************************************************************************/
|
||||||
|
|
||||||
|
/*** file scope functions ************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/*** public functions ****************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Show error message (if error have raised) and cleanup GError object.
|
||||||
|
*
|
||||||
|
* @param error pointer to object contains error message
|
||||||
|
* @return TRUE if error message was printed, FALSE otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
sftpfs_show_error (GError ** error)
|
||||||
|
{
|
||||||
|
if (error == NULL || *error == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
vfs_print_message ("(%d) %s", (*error)->code, (*error)->message);
|
||||||
|
g_error_free (*error);
|
||||||
|
*error = NULL;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Convert libssh error to GError object.
|
||||||
|
*
|
||||||
|
* @param super_data extra data for SFTP connection
|
||||||
|
* @param libssh_errno errno from libssh
|
||||||
|
* @param error pointer to the error object
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
sftpfs_ssherror_to_gliberror (sftpfs_super_data_t * super_data, int libssh_errno, GError ** error)
|
||||||
|
{
|
||||||
|
char *err = NULL;
|
||||||
|
int err_len;
|
||||||
|
|
||||||
|
g_return_if_fail (error == NULL || *error == NULL);
|
||||||
|
|
||||||
|
libssh2_session_last_error (super_data->session, &err, &err_len, 1);
|
||||||
|
g_set_error (error, MC_ERROR, libssh_errno, "%s", err);
|
||||||
|
g_free (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Fix filename for SFTP operations: add leading slash to file name.
|
||||||
|
*
|
||||||
|
* @param file_name file name
|
||||||
|
* @return newly allocated string contains the file name with leading slash
|
||||||
|
*/
|
||||||
|
|
||||||
|
const char *
|
||||||
|
sftpfs_fix_filename (const char *file_name)
|
||||||
|
{
|
||||||
|
g_string_printf (sftpfs_filename_buffer, "%c%s", PATH_SEP, file_name);
|
||||||
|
return sftpfs_filename_buffer->str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Awaiting for any activity on socket.
|
||||||
|
*
|
||||||
|
* @param super_data extra data for SFTP connection
|
||||||
|
* @param error unused
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
sftpfs_waitsocket (sftpfs_super_data_t * super_data, GError ** error)
|
||||||
|
{
|
||||||
|
struct timeval timeout = { 10, 0 };
|
||||||
|
fd_set fd;
|
||||||
|
fd_set *writefd = NULL;
|
||||||
|
fd_set *readfd = NULL;
|
||||||
|
int dir;
|
||||||
|
|
||||||
|
(void) error;
|
||||||
|
|
||||||
|
FD_ZERO (&fd);
|
||||||
|
FD_SET (super_data->socket_handle, &fd);
|
||||||
|
|
||||||
|
/* now make sure we wait in the correct direction */
|
||||||
|
dir = libssh2_session_block_directions (super_data->session);
|
||||||
|
|
||||||
|
if ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) != 0)
|
||||||
|
readfd = &fd;
|
||||||
|
|
||||||
|
if ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) != 0)
|
||||||
|
writefd = &fd;
|
||||||
|
|
||||||
|
return select (super_data->socket_handle + 1, readfd, writefd, NULL, &timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Getting information about a symbolic link.
|
||||||
|
*
|
||||||
|
* @param vpath path to file, directory or symbolic link
|
||||||
|
* @param buf buffer for store stat-info
|
||||||
|
* @param error pointer to error object
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
sftpfs_lstat (const vfs_path_t * vpath, struct stat *buf, GError ** error)
|
||||||
|
{
|
||||||
|
struct vfs_s_super *super;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
LIBSSH2_SFTP_ATTRIBUTES attrs;
|
||||||
|
int res;
|
||||||
|
const vfs_path_element_t *path_element;
|
||||||
|
|
||||||
|
path_element = vfs_path_get_by_index (vpath, -1);
|
||||||
|
|
||||||
|
if (vfs_s_get_path (vpath, &super, 0) == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (super == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
if (super_data->sftp_session == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const char *fixfname;
|
||||||
|
|
||||||
|
fixfname = sftpfs_fix_filename (path_element->path);
|
||||||
|
|
||||||
|
res = libssh2_sftp_stat_ex (super_data->sftp_session, fixfname,
|
||||||
|
sftpfs_filename_buffer->len, LIBSSH2_SFTP_LSTAT, &attrs);
|
||||||
|
if (res >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (res != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, res, error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_waitsocket (super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (res == LIBSSH2_ERROR_EAGAIN);
|
||||||
|
|
||||||
|
if ((attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
|
||||||
|
{
|
||||||
|
buf->st_uid = attrs.uid;
|
||||||
|
buf->st_gid = attrs.gid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
|
||||||
|
{
|
||||||
|
buf->st_atime = attrs.atime;
|
||||||
|
buf->st_mtime = attrs.mtime;
|
||||||
|
buf->st_ctime = attrs.mtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
|
||||||
|
buf->st_size = attrs.filesize;
|
||||||
|
|
||||||
|
if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
|
||||||
|
buf->st_mode = attrs.permissions;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Getting information about a file or directory.
|
||||||
|
*
|
||||||
|
* @param vpath path to file or directory
|
||||||
|
* @param buf buffer for store stat-info
|
||||||
|
* @param error pointer to error object
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
sftpfs_stat (const vfs_path_t * vpath, struct stat *buf, GError ** error)
|
||||||
|
{
|
||||||
|
struct vfs_s_super *super;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
LIBSSH2_SFTP_ATTRIBUTES attrs;
|
||||||
|
int res;
|
||||||
|
const vfs_path_element_t *path_element;
|
||||||
|
|
||||||
|
path_element = vfs_path_get_by_index (vpath, -1);
|
||||||
|
|
||||||
|
if (vfs_s_get_path (vpath, &super, 0) == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (super == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
if (super_data->sftp_session == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const char *fixfname;
|
||||||
|
|
||||||
|
fixfname = sftpfs_fix_filename (path_element->path);
|
||||||
|
|
||||||
|
res =
|
||||||
|
libssh2_sftp_stat_ex (super_data->sftp_session,
|
||||||
|
fixfname, sftpfs_filename_buffer->len, LIBSSH2_SFTP_STAT, &attrs);
|
||||||
|
if (res >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (res != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, res, error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_waitsocket (super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (res == LIBSSH2_ERROR_EAGAIN);
|
||||||
|
|
||||||
|
buf->st_nlink = 1;
|
||||||
|
if ((attrs.flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
|
||||||
|
{
|
||||||
|
buf->st_uid = attrs.uid;
|
||||||
|
buf->st_gid = attrs.gid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((attrs.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
|
||||||
|
{
|
||||||
|
buf->st_atime = attrs.atime;
|
||||||
|
buf->st_mtime = attrs.mtime;
|
||||||
|
buf->st_ctime = attrs.mtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
|
||||||
|
buf->st_size = attrs.filesize;
|
||||||
|
|
||||||
|
if ((attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
|
||||||
|
buf->st_mode = attrs.permissions;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Read value of a symbolic link.
|
||||||
|
*
|
||||||
|
* @param vpath path to file or directory
|
||||||
|
* @param buf buffer for store stat-info
|
||||||
|
* @param size buffer size
|
||||||
|
* @param error pointer to error object
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
sftpfs_readlink (const vfs_path_t * vpath, char *buf, size_t size, GError ** error)
|
||||||
|
{
|
||||||
|
struct vfs_s_super *super;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
int res;
|
||||||
|
const vfs_path_element_t *path_element;
|
||||||
|
|
||||||
|
path_element = vfs_path_get_by_index (vpath, -1);
|
||||||
|
|
||||||
|
if (vfs_s_get_path (vpath, &super, 0) == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (super == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
if (super_data->sftp_session == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const char *fixfname;
|
||||||
|
|
||||||
|
fixfname = sftpfs_fix_filename (path_element->path);
|
||||||
|
|
||||||
|
res = libssh2_sftp_readlink (super_data->sftp_session, fixfname, buf, size);
|
||||||
|
if (res >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (res != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, res, error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_waitsocket (super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (res == LIBSSH2_ERROR_EAGAIN);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Create symlink to file or directory
|
||||||
|
*
|
||||||
|
* @param vpath1 path to file or directory
|
||||||
|
* @param vpath2 path to symlink
|
||||||
|
* @param error pointer to error object
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
sftpfs_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** error)
|
||||||
|
{
|
||||||
|
struct vfs_s_super *super;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
const vfs_path_element_t *path_element1;
|
||||||
|
const vfs_path_element_t *path_element2;
|
||||||
|
char *tmp_path;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
path_element2 = vfs_path_get_by_index (vpath2, -1);
|
||||||
|
|
||||||
|
if (vfs_s_get_path (vpath2, &super, 0) == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (super == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
if (super_data->sftp_session == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
tmp_path = g_strdup_printf ("%c%s", PATH_SEP, path_element2->path);
|
||||||
|
path_element1 = vfs_path_get_by_index (vpath1, -1);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const char *fixfname;
|
||||||
|
|
||||||
|
fixfname = sftpfs_fix_filename (path_element1->path);
|
||||||
|
|
||||||
|
res =
|
||||||
|
libssh2_sftp_symlink_ex (super_data->sftp_session,
|
||||||
|
fixfname,
|
||||||
|
sftpfs_filename_buffer->len, tmp_path, strlen (tmp_path),
|
||||||
|
LIBSSH2_SFTP_SYMLINK);
|
||||||
|
if (res >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (res != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, res, error);
|
||||||
|
g_free (tmp_path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_waitsocket (super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
{
|
||||||
|
g_free (tmp_path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (res == LIBSSH2_ERROR_EAGAIN);
|
||||||
|
g_free (tmp_path);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Changes the permissions of the file.
|
||||||
|
*
|
||||||
|
* @param vpath path to file or directory
|
||||||
|
* @param mode mode (see man 2 open)
|
||||||
|
* @param error pointer to error object
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
sftpfs_chmod (const vfs_path_t * vpath, mode_t mode, GError ** error)
|
||||||
|
{
|
||||||
|
struct vfs_s_super *super;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
LIBSSH2_SFTP_ATTRIBUTES attrs;
|
||||||
|
int res;
|
||||||
|
const vfs_path_element_t *path_element;
|
||||||
|
|
||||||
|
path_element = vfs_path_get_by_index (vpath, -1);
|
||||||
|
|
||||||
|
if (vfs_s_get_path (vpath, &super, 0) == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (super == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
if (super_data->sftp_session == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const char *fixfname;
|
||||||
|
|
||||||
|
fixfname = sftpfs_fix_filename (path_element->path);
|
||||||
|
|
||||||
|
res = libssh2_sftp_stat_ex (super_data->sftp_session, fixfname,
|
||||||
|
sftpfs_filename_buffer->len, LIBSSH2_SFTP_LSTAT, &attrs);
|
||||||
|
if (res >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (res != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, res, error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_waitsocket (super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (res == LIBSSH2_ERROR_EAGAIN);
|
||||||
|
|
||||||
|
attrs.permissions = mode;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const char *fixfname;
|
||||||
|
|
||||||
|
fixfname = sftpfs_fix_filename (path_element->path);
|
||||||
|
|
||||||
|
res = libssh2_sftp_stat_ex (super_data->sftp_session, fixfname,
|
||||||
|
sftpfs_filename_buffer->len, LIBSSH2_SFTP_SETSTAT, &attrs);
|
||||||
|
if (res >= 0)
|
||||||
|
break;
|
||||||
|
else if (res != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, res, error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_waitsocket (super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (res == LIBSSH2_ERROR_EAGAIN);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Delete a name from the file system.
|
||||||
|
*
|
||||||
|
* @param vpath path to file or directory
|
||||||
|
* @param error pointer to error object
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
sftpfs_unlink (const vfs_path_t * vpath, GError ** error)
|
||||||
|
{
|
||||||
|
struct vfs_s_super *super;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
int res;
|
||||||
|
const vfs_path_element_t *path_element;
|
||||||
|
|
||||||
|
path_element = vfs_path_get_by_index (vpath, -1);
|
||||||
|
|
||||||
|
if (vfs_s_get_path (vpath, &super, 0) == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (super == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
if (super_data->sftp_session == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const char *fixfname;
|
||||||
|
|
||||||
|
fixfname = sftpfs_fix_filename (path_element->path);
|
||||||
|
|
||||||
|
res =
|
||||||
|
libssh2_sftp_unlink_ex (super_data->sftp_session, fixfname,
|
||||||
|
sftpfs_filename_buffer->len);
|
||||||
|
if (res >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (res != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, res, error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_waitsocket (super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while (res == LIBSSH2_ERROR_EAGAIN);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Rename a file, moving it between directories if required.
|
||||||
|
*
|
||||||
|
* @param vpath1 path to source file or directory
|
||||||
|
* @param vpath2 path to destination file or directory
|
||||||
|
* @param error pointer to error object
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
sftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** error)
|
||||||
|
{
|
||||||
|
struct vfs_s_super *super;
|
||||||
|
sftpfs_super_data_t *super_data;
|
||||||
|
const vfs_path_element_t *path_element1;
|
||||||
|
const vfs_path_element_t *path_element2;
|
||||||
|
char *tmp_path;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
path_element2 = vfs_path_get_by_index (vpath2, -1);
|
||||||
|
|
||||||
|
if (vfs_s_get_path (vpath2, &super, 0) == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (super == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
super_data = (sftpfs_super_data_t *) super->data;
|
||||||
|
if (super_data->sftp_session == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
tmp_path = g_strdup_printf ("%c%s", PATH_SEP, path_element2->path);
|
||||||
|
path_element1 = vfs_path_get_by_index (vpath1, -1);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const char *fixfname;
|
||||||
|
|
||||||
|
fixfname = sftpfs_fix_filename (path_element1->path);
|
||||||
|
|
||||||
|
res = libssh2_sftp_rename_ex
|
||||||
|
(super_data->sftp_session,
|
||||||
|
fixfname,
|
||||||
|
sftpfs_filename_buffer->len, tmp_path, strlen (tmp_path), LIBSSH2_SFTP_SYMLINK);
|
||||||
|
if (res >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (res != LIBSSH2_ERROR_EAGAIN)
|
||||||
|
{
|
||||||
|
sftpfs_ssherror_to_gliberror (super_data, res, error);
|
||||||
|
g_free (tmp_path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_waitsocket (super_data, error);
|
||||||
|
if (error != NULL && *error != NULL)
|
||||||
|
{
|
||||||
|
g_free (tmp_path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (res == LIBSSH2_ERROR_EAGAIN);
|
||||||
|
g_free (tmp_path);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
100
src/vfs/sftpfs/internal.h
Normal file
100
src/vfs/sftpfs/internal.h
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* \file
|
||||||
|
* \brief Header: SFTP FS
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MC__VFS_SFTPFS_INTERNAL_H
|
||||||
|
#define MC__VFS_SFTPFS_INTERNAL_H
|
||||||
|
|
||||||
|
#include <libssh2.h>
|
||||||
|
#include <libssh2_sftp.h>
|
||||||
|
|
||||||
|
#include "lib/vfs/vfs.h"
|
||||||
|
#include "lib/vfs/xdirentry.h"
|
||||||
|
|
||||||
|
/*** typedefs(not structures) and defined constants **********************************************/
|
||||||
|
|
||||||
|
#define SFTP_DEFAULT_PORT 22
|
||||||
|
|
||||||
|
/*** enums ***************************************************************************************/
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
NONE = 0,
|
||||||
|
PUBKEY = (1 << 0),
|
||||||
|
PASSWORD = (1 << 1),
|
||||||
|
AGENT = (1 << 2)
|
||||||
|
} sftpfs_auth_type_t;
|
||||||
|
|
||||||
|
/*** structures declarations (and typedefs of structures)*****************************************/
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
sftpfs_auth_type_t auth_type;
|
||||||
|
sftpfs_auth_type_t config_auth_type;
|
||||||
|
|
||||||
|
LIBSSH2_SESSION *session;
|
||||||
|
LIBSSH2_SFTP *sftp_session;
|
||||||
|
|
||||||
|
LIBSSH2_AGENT *agent;
|
||||||
|
|
||||||
|
char *pubkey;
|
||||||
|
char *privkey;
|
||||||
|
|
||||||
|
int socket_handle;
|
||||||
|
const char *fingerprint;
|
||||||
|
} sftpfs_super_data_t;
|
||||||
|
|
||||||
|
/*** global variables defined in .c file *********************************************************/
|
||||||
|
|
||||||
|
extern GString *sftpfs_filename_buffer;
|
||||||
|
extern struct vfs_class sftpfs_class;
|
||||||
|
extern struct vfs_s_subclass sftpfs_subclass;
|
||||||
|
|
||||||
|
/*** declarations of public functions ************************************************************/
|
||||||
|
|
||||||
|
void sftpfs_init_class (void);
|
||||||
|
void sftpfs_init_subclass (void);
|
||||||
|
void sftpfs_init_class_callbacks (void);
|
||||||
|
void sftpfs_init_subclass_callbacks (void);
|
||||||
|
void sftpfs_init_config_variables_patterns (void);
|
||||||
|
void sftpfs_deinit_config_variables_patterns (void);
|
||||||
|
|
||||||
|
gboolean sftpfs_show_error (GError ** error);
|
||||||
|
void sftpfs_ssherror_to_gliberror (sftpfs_super_data_t * super_data, int libssh_errno,
|
||||||
|
GError ** error);
|
||||||
|
int sftpfs_waitsocket (sftpfs_super_data_t * super_data, GError ** error);
|
||||||
|
|
||||||
|
const char *sftpfs_fix_filename (const char *file_name);
|
||||||
|
int sftpfs_lstat (const vfs_path_t * vpath, struct stat *buf, GError ** error);
|
||||||
|
int sftpfs_stat (const vfs_path_t * vpath, struct stat *buf, GError ** error);
|
||||||
|
int sftpfs_readlink (const vfs_path_t * vpath, char *buf, size_t size, GError ** error);
|
||||||
|
int sftpfs_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** error);
|
||||||
|
int sftpfs_chmod (const vfs_path_t * vpath, mode_t mode, GError ** error);
|
||||||
|
int sftpfs_unlink (const vfs_path_t * vpath, GError ** error);
|
||||||
|
int sftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** error);
|
||||||
|
|
||||||
|
void sftpfs_fill_connection_data_from_config (struct vfs_s_super *super, GError ** error);
|
||||||
|
int sftpfs_open_connection (struct vfs_s_super *super, GError ** error);
|
||||||
|
void sftpfs_close_connection (struct vfs_s_super *super, const char *shutdown_message,
|
||||||
|
GError ** error);
|
||||||
|
|
||||||
|
void *sftpfs_opendir (const vfs_path_t * vpath, GError ** error);
|
||||||
|
void *sftpfs_readdir (void *data, GError ** error);
|
||||||
|
int sftpfs_closedir (void *data, GError ** error);
|
||||||
|
int sftpfs_mkdir (const vfs_path_t * vpath, mode_t mode, GError ** error);
|
||||||
|
int sftpfs_rmdir (const vfs_path_t * vpath, GError ** error);
|
||||||
|
|
||||||
|
gboolean sftpfs_open_file (vfs_file_handler_t * file_handler, int flags, mode_t mode,
|
||||||
|
GError ** error);
|
||||||
|
ssize_t sftpfs_read_file (vfs_file_handler_t * file_handler, char *buffer, size_t count,
|
||||||
|
GError ** error);
|
||||||
|
ssize_t sftpfs_write_file (vfs_file_handler_t * file_handler, const char *buffer, size_t count,
|
||||||
|
GError ** error);
|
||||||
|
int sftpfs_close_file (vfs_file_handler_t * file_handler, GError ** error);
|
||||||
|
int sftpfs_fstat (void *data, struct stat *buf, GError ** error);
|
||||||
|
off_t sftpfs_lseek (vfs_file_handler_t * file_handler, off_t offset, int whence, GError ** error);
|
||||||
|
|
||||||
|
/*** inline functions ****************************************************************************/
|
||||||
|
|
||||||
|
#endif /* MC__VFS_SFTPFS_INTERNAL_H */
|
693
src/vfs/sftpfs/vfs_class.c
Normal file
693
src/vfs/sftpfs/vfs_class.c
Normal file
@ -0,0 +1,693 @@
|
|||||||
|
/* Virtual File System: SFTP file system.
|
||||||
|
The VFS class functions
|
||||||
|
|
||||||
|
Copyright (C) 2011
|
||||||
|
The Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Written by:
|
||||||
|
Ilia Maslakov <il.smind@gmail.com>, 2011
|
||||||
|
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
|
||||||
|
|
||||||
|
This file is part of the Midnight Commander.
|
||||||
|
|
||||||
|
The Midnight Commander is free software: you can redistribute it
|
||||||
|
and/or modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the License,
|
||||||
|
or (at your option) any later version.
|
||||||
|
|
||||||
|
The Midnight Commander is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "lib/global.h"
|
||||||
|
#include "lib/vfs/gc.h"
|
||||||
|
#include "lib/tty/tty.h" /* tty_enable_interrupt_key () */
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
/*** global variables ****************************************************************************/
|
||||||
|
|
||||||
|
struct vfs_class sftpfs_class;
|
||||||
|
|
||||||
|
/*** file scope macro definitions ****************************************************************/
|
||||||
|
|
||||||
|
/*** file scope type declarations ****************************************************************/
|
||||||
|
|
||||||
|
/*** file scope variables ************************************************************************/
|
||||||
|
|
||||||
|
/*** file scope functions ************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for VFS-class init action.
|
||||||
|
*
|
||||||
|
* @param me structure of VFS class
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_init (struct vfs_class *me)
|
||||||
|
{
|
||||||
|
(void) me;
|
||||||
|
|
||||||
|
if (libssh2_init (0) != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
sftpfs_filename_buffer = g_string_new ("");
|
||||||
|
sftpfs_init_config_variables_patterns ();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for VFS-class deinit action.
|
||||||
|
*
|
||||||
|
* @param me structure of VFS class
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
sftpfs_cb_done (struct vfs_class *me)
|
||||||
|
{
|
||||||
|
(void) me;
|
||||||
|
|
||||||
|
sftpfs_deinit_config_variables_patterns ();
|
||||||
|
g_string_free (sftpfs_filename_buffer, TRUE);
|
||||||
|
libssh2_exit ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for opening file.
|
||||||
|
*
|
||||||
|
* @param vpath path to file
|
||||||
|
* @param flags flags (see man 2 open)
|
||||||
|
* @param mode mode (see man 2 open)
|
||||||
|
* @return file data handler if success, NULL otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void *
|
||||||
|
sftpfs_cb_open (const vfs_path_t * vpath, int flags, mode_t mode)
|
||||||
|
{
|
||||||
|
vfs_file_handler_t *file_handler;
|
||||||
|
const vfs_path_element_t *path_element;
|
||||||
|
struct vfs_s_super *super;
|
||||||
|
const char *path_super;
|
||||||
|
struct vfs_s_inode *path_inode;
|
||||||
|
GError *error = NULL;
|
||||||
|
gboolean is_changed = FALSE;
|
||||||
|
|
||||||
|
path_element = vfs_path_get_by_index (vpath, -1);
|
||||||
|
|
||||||
|
path_super = vfs_s_get_path (vpath, &super, 0);
|
||||||
|
if (path_super == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
path_inode = vfs_s_find_inode (path_element->class, super, path_super, LINK_FOLLOW, FL_NONE);
|
||||||
|
if (path_inode != NULL && ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)))
|
||||||
|
{
|
||||||
|
path_element->class->verrno = EEXIST;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path_inode == NULL)
|
||||||
|
{
|
||||||
|
char *dirname, *name;
|
||||||
|
struct vfs_s_entry *ent;
|
||||||
|
struct vfs_s_inode *dir;
|
||||||
|
|
||||||
|
dirname = g_path_get_dirname (path_super);
|
||||||
|
name = g_path_get_basename (path_super);
|
||||||
|
dir = vfs_s_find_inode (path_element->class, super, dirname, LINK_FOLLOW, FL_DIR);
|
||||||
|
if (dir == NULL)
|
||||||
|
{
|
||||||
|
g_free (dirname);
|
||||||
|
g_free (name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ent = vfs_s_generate_entry (path_element->class, name, dir, 0755);
|
||||||
|
path_inode = ent->ino;
|
||||||
|
vfs_s_insert_entry (path_element->class, dir, ent);
|
||||||
|
g_free (dirname);
|
||||||
|
g_free (name);
|
||||||
|
is_changed = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISDIR (path_inode->st.st_mode))
|
||||||
|
{
|
||||||
|
path_element->class->verrno = EISDIR;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_handler = g_new0 (vfs_file_handler_t, 1);
|
||||||
|
file_handler->pos = 0;
|
||||||
|
file_handler->ino = path_inode;
|
||||||
|
file_handler->handle = -1;
|
||||||
|
file_handler->changed = is_changed;
|
||||||
|
file_handler->linear = 0;
|
||||||
|
file_handler->data = NULL;
|
||||||
|
|
||||||
|
if (!sftpfs_open_file (file_handler, flags, mode, &error))
|
||||||
|
{
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
g_free (file_handler);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vfs_rmstamp (path_element->class, (vfsid) super);
|
||||||
|
super->fd_usage++;
|
||||||
|
file_handler->ino->st.st_nlink++;
|
||||||
|
return file_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for opening directory.
|
||||||
|
*
|
||||||
|
* @param vpath path to directory
|
||||||
|
* @return directory data handler if success, NULL otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void *
|
||||||
|
sftpfs_cb_opendir (const vfs_path_t * vpath)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
void *ret_value;
|
||||||
|
|
||||||
|
/* reset interrupt flag */
|
||||||
|
tty_got_interrupt ();
|
||||||
|
|
||||||
|
ret_value = sftpfs_opendir (vpath, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return ret_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for reading directory entry.
|
||||||
|
*
|
||||||
|
* @param data directory data handler
|
||||||
|
* @return information about direntry if success, NULL otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void *
|
||||||
|
sftpfs_cb_readdir (void *data)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
union vfs_dirent *sftpfs_dirent;
|
||||||
|
|
||||||
|
if (tty_got_interrupt ())
|
||||||
|
{
|
||||||
|
tty_disable_interrupt_key ();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftpfs_dirent = sftpfs_readdir (data, &error);
|
||||||
|
if (!sftpfs_show_error (&error))
|
||||||
|
{
|
||||||
|
if (sftpfs_dirent != NULL)
|
||||||
|
vfs_print_message (_("sftp: (Ctrl-G break) Listing... %s"), sftpfs_dirent->dent.d_name);
|
||||||
|
else
|
||||||
|
vfs_print_message (_("sftp: Listing done."));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sftpfs_dirent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for closing directory.
|
||||||
|
*
|
||||||
|
* @param data directory data handler
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_closedir (void *data)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
rc = sftpfs_closedir (data, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for lstat VFS-function.
|
||||||
|
*
|
||||||
|
* @param vpath path to file or directory
|
||||||
|
* @param buf buffer for store stat-info
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_lstat (const vfs_path_t * vpath, struct stat *buf)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
rc = sftpfs_lstat (vpath, buf, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for stat VFS-function.
|
||||||
|
*
|
||||||
|
* @param vpath path to file or directory
|
||||||
|
* @param buf buffer for store stat-info
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_stat (const vfs_path_t * vpath, struct stat *buf)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
rc = sftpfs_stat (vpath, buf, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for fstat VFS-function.
|
||||||
|
*
|
||||||
|
* @param data file data handler
|
||||||
|
* @param buf buffer for store stat-info
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_fstat (void *data, struct stat *buf)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
rc = sftpfs_fstat (data, buf, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for readlink VFS-function.
|
||||||
|
*
|
||||||
|
* @param vpath path to file or directory
|
||||||
|
* @param buf buffer for store stat-info
|
||||||
|
* @param size buffer size
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_readlink (const vfs_path_t * vpath, char *buf, size_t size)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
rc = sftpfs_readlink (vpath, buf, size, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for utime VFS-function.
|
||||||
|
*
|
||||||
|
* @param vpath unused
|
||||||
|
* @param times unused
|
||||||
|
* @return always 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_utime (const vfs_path_t * vpath, struct utimbuf *times)
|
||||||
|
{
|
||||||
|
(void) vpath;
|
||||||
|
(void) times;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for symlink VFS-function.
|
||||||
|
*
|
||||||
|
* @param vpath1 path to file or directory
|
||||||
|
* @param vpath2 path to symlink
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
rc = sftpfs_symlink (vpath1, vpath2, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for symlink VFS-function.
|
||||||
|
*
|
||||||
|
* @param vpath unused
|
||||||
|
* @param mode unused
|
||||||
|
* @param dev unused
|
||||||
|
* @return always 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_mknod (const vfs_path_t * vpath, mode_t mode, dev_t dev)
|
||||||
|
{
|
||||||
|
(void) vpath;
|
||||||
|
(void) mode;
|
||||||
|
(void) dev;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for link VFS-function.
|
||||||
|
*
|
||||||
|
* @param vpath1 unused
|
||||||
|
* @param vpath2 unused
|
||||||
|
* @return always 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_link (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
|
||||||
|
{
|
||||||
|
(void) vpath1;
|
||||||
|
(void) vpath2;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for chown VFS-function.
|
||||||
|
*
|
||||||
|
* @param vpath unused
|
||||||
|
* @param owner unused
|
||||||
|
* @param group unused
|
||||||
|
* @return always 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
|
||||||
|
{
|
||||||
|
(void) vpath;
|
||||||
|
(void) owner;
|
||||||
|
(void) group;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for reading file content.
|
||||||
|
*
|
||||||
|
* @param data file data handler
|
||||||
|
* @param buffer buffer for data
|
||||||
|
* @param count data size
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
sftpfs_cb_read (void *data, char *buffer, size_t count)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GError *error = NULL;
|
||||||
|
vfs_file_handler_t *fh = (vfs_file_handler_t *) data;
|
||||||
|
|
||||||
|
if (tty_got_interrupt ())
|
||||||
|
{
|
||||||
|
tty_disable_interrupt_key ();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = sftpfs_read_file (fh, buffer, count, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for writing file content.
|
||||||
|
*
|
||||||
|
* @param data file data handler
|
||||||
|
* @param buf buffer for data
|
||||||
|
* @param count data size
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
sftpfs_cb_write (void *data, const char *buf, size_t nbyte)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GError *error = NULL;
|
||||||
|
vfs_file_handler_t *fh = (vfs_file_handler_t *) data;
|
||||||
|
|
||||||
|
rc = sftpfs_write_file (fh, buf, nbyte, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for close file.
|
||||||
|
*
|
||||||
|
* @param data file data handler
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_close (void *data)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GError *error = NULL;
|
||||||
|
struct vfs_s_super *super;
|
||||||
|
vfs_file_handler_t *file_handler = (vfs_file_handler_t *) data;
|
||||||
|
|
||||||
|
super = file_handler->ino->super;
|
||||||
|
|
||||||
|
super->fd_usage--;
|
||||||
|
if (super->fd_usage == 0)
|
||||||
|
vfs_stamp_create (&sftpfs_class, super);
|
||||||
|
|
||||||
|
rc = sftpfs_close_file (file_handler, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
|
||||||
|
if (file_handler->handle != -1)
|
||||||
|
close (file_handler->handle);
|
||||||
|
|
||||||
|
vfs_s_free_inode (&sftpfs_class, file_handler->ino);
|
||||||
|
g_free (file_handler);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for chmod VFS-function.
|
||||||
|
*
|
||||||
|
* @param vpath path to file or directory
|
||||||
|
* @param mode mode (see man 2 open)
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_chmod (const vfs_path_t * vpath, mode_t mode)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
rc = sftpfs_chmod (vpath, mode, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for mkdir VFS-function.
|
||||||
|
*
|
||||||
|
* @param vpath path directory
|
||||||
|
* @param mode mode (see man 2 open)
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_mkdir (const vfs_path_t * vpath, mode_t mode)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
rc = sftpfs_mkdir (vpath, mode, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for rmdir VFS-function.
|
||||||
|
*
|
||||||
|
* @param vpath path directory
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_rmdir (const vfs_path_t * vpath)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
rc = sftpfs_rmdir (vpath, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for lseek VFS-function.
|
||||||
|
*
|
||||||
|
* @param data file data handler
|
||||||
|
* @param offset file offset
|
||||||
|
* @param whence method of seek (at begin, at current, at end)
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static off_t
|
||||||
|
sftpfs_cb_lseek (void *data, off_t offset, int whence)
|
||||||
|
{
|
||||||
|
off_t ret_offset;
|
||||||
|
vfs_file_handler_t *file_handler = (vfs_file_handler_t *) data;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
ret_offset = sftpfs_lseek (file_handler, offset, whence, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return ret_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for unlink VFS-function.
|
||||||
|
*
|
||||||
|
* @param vpath path to file or directory
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_unlink (const vfs_path_t * vpath)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
rc = sftpfs_unlink (vpath, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for rename VFS-function.
|
||||||
|
*
|
||||||
|
* @param vpath1 path to source file or directory
|
||||||
|
* @param vpath2 path to destination file or directory
|
||||||
|
* @return 0 if sucess, negative value otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
rc = sftpfs_rename (vpath1, vpath2, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for errno VFS-function.
|
||||||
|
*
|
||||||
|
* @param me unused
|
||||||
|
* @return value of errno global variable
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_errno (struct vfs_class *me)
|
||||||
|
{
|
||||||
|
(void) me;
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/*** public functions ****************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Initialization of VFS class structure.
|
||||||
|
*
|
||||||
|
* @return the VFS class structure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
sftpfs_init_class (void)
|
||||||
|
{
|
||||||
|
memset (&sftpfs_class, 0, sizeof (struct vfs_class));
|
||||||
|
sftpfs_class.name = "sftpfs";
|
||||||
|
sftpfs_class.prefix = "sftp";
|
||||||
|
sftpfs_class.flags = VFSF_NOLINKS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Initialization of VFS class callbacks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
sftpfs_init_class_callbacks (void)
|
||||||
|
{
|
||||||
|
sftpfs_class.init = sftpfs_cb_init;
|
||||||
|
sftpfs_class.done = sftpfs_cb_done;
|
||||||
|
|
||||||
|
sftpfs_class.opendir = sftpfs_cb_opendir;
|
||||||
|
sftpfs_class.readdir = sftpfs_cb_readdir;
|
||||||
|
sftpfs_class.closedir = sftpfs_cb_closedir;
|
||||||
|
sftpfs_class.mkdir = sftpfs_cb_mkdir;
|
||||||
|
sftpfs_class.rmdir = sftpfs_cb_rmdir;
|
||||||
|
|
||||||
|
sftpfs_class.stat = sftpfs_cb_stat;
|
||||||
|
sftpfs_class.lstat = sftpfs_cb_lstat;
|
||||||
|
sftpfs_class.fstat = sftpfs_cb_fstat;
|
||||||
|
sftpfs_class.readlink = sftpfs_cb_readlink;
|
||||||
|
sftpfs_class.symlink = sftpfs_cb_symlink;
|
||||||
|
sftpfs_class.link = sftpfs_cb_link;
|
||||||
|
sftpfs_class.utime = sftpfs_cb_utime;
|
||||||
|
sftpfs_class.mknod = sftpfs_cb_mknod;
|
||||||
|
sftpfs_class.chown = sftpfs_cb_chown;
|
||||||
|
sftpfs_class.chmod = sftpfs_cb_chmod;
|
||||||
|
|
||||||
|
sftpfs_class.open = sftpfs_cb_open;
|
||||||
|
sftpfs_class.read = sftpfs_cb_read;
|
||||||
|
sftpfs_class.write = sftpfs_cb_write;
|
||||||
|
sftpfs_class.close = sftpfs_cb_close;
|
||||||
|
sftpfs_class.lseek = sftpfs_cb_lseek;
|
||||||
|
sftpfs_class.unlink = sftpfs_cb_unlink;
|
||||||
|
sftpfs_class.rename = sftpfs_cb_rename;
|
||||||
|
sftpfs_class.ferrno = sftpfs_cb_errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
204
src/vfs/sftpfs/vfs_subclass.c
Normal file
204
src/vfs/sftpfs/vfs_subclass.c
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
/* Virtual File System: SFTP file system.
|
||||||
|
The VFS subclass functions
|
||||||
|
|
||||||
|
Copyright (C) 2011
|
||||||
|
The Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Written by:
|
||||||
|
Ilia Maslakov <il.smind@gmail.com>, 2011
|
||||||
|
Slava Zanko <slavazanko@gmail.com>, 2011, 2012
|
||||||
|
|
||||||
|
This file is part of the Midnight Commander.
|
||||||
|
|
||||||
|
The Midnight Commander is free software: you can redistribute it
|
||||||
|
and/or modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the License,
|
||||||
|
or (at your option) any later version.
|
||||||
|
|
||||||
|
The Midnight Commander is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "lib/global.h"
|
||||||
|
#include "lib/vfs/utilvfs.h"
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
/*** global variables ****************************************************************************/
|
||||||
|
|
||||||
|
struct vfs_s_subclass sftpfs_subclass;
|
||||||
|
|
||||||
|
/*** file scope macro definitions ****************************************************************/
|
||||||
|
|
||||||
|
/*** file scope type declarations ****************************************************************/
|
||||||
|
|
||||||
|
/*** file scope variables ************************************************************************/
|
||||||
|
|
||||||
|
/*** file scope functions ************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for checking if connection is equal to existing connection.
|
||||||
|
*
|
||||||
|
* @param vpath_element path element with connetion data
|
||||||
|
* @param super data with exists connection
|
||||||
|
* @param vpath unused
|
||||||
|
* @param cookie unused
|
||||||
|
* @return TRUE if connections is equal, FALSE otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
sftpfs_cb_is_equal_connection (const vfs_path_element_t * vpath_element, struct vfs_s_super *super,
|
||||||
|
const vfs_path_t * vpath, void *cookie)
|
||||||
|
{
|
||||||
|
int port;
|
||||||
|
char *user_name;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
(void) vpath;
|
||||||
|
(void) cookie;
|
||||||
|
|
||||||
|
if (vpath_element->user != NULL)
|
||||||
|
user_name = vpath_element->user;
|
||||||
|
else
|
||||||
|
user_name = vfs_get_local_username ();
|
||||||
|
|
||||||
|
if (vpath_element->port != 0)
|
||||||
|
port = vpath_element->port;
|
||||||
|
else
|
||||||
|
port = SFTP_DEFAULT_PORT;
|
||||||
|
|
||||||
|
result = ((strcmp (vpath_element->host, super->path_element->host) == 0)
|
||||||
|
&& (strcmp (user_name, super->path_element->user) == 0)
|
||||||
|
&& (port == super->path_element->port));
|
||||||
|
|
||||||
|
if (user_name != vpath_element->user)
|
||||||
|
g_free (user_name);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for opening new connection.
|
||||||
|
*
|
||||||
|
* @param super connection data
|
||||||
|
* @param vpath unused
|
||||||
|
* @param vpath_element path element with connetion data
|
||||||
|
* @return 0 if success, -1 otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_open_connection (struct vfs_s_super *super,
|
||||||
|
const vfs_path_t * vpath, const vfs_path_element_t * vpath_element)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
int ret_value;
|
||||||
|
|
||||||
|
(void) vpath;
|
||||||
|
|
||||||
|
if (vpath_element->host == NULL || *vpath_element->host == '\0')
|
||||||
|
{
|
||||||
|
vfs_print_message (_("sftp: Invalid host name."));
|
||||||
|
vpath_element->class->verrno = EPERM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
super->data = g_new0 (sftpfs_super_data_t, 1);
|
||||||
|
super->path_element = vfs_path_element_clone (vpath_element);
|
||||||
|
|
||||||
|
sftpfs_fill_connection_data_from_config (super, &error);
|
||||||
|
if (error != NULL)
|
||||||
|
{
|
||||||
|
vpath_element->class->verrno = error->code;
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
super->name = g_strdup (PATH_SEP_STR);
|
||||||
|
super->root =
|
||||||
|
vfs_s_new_inode (vpath_element->class, super,
|
||||||
|
vfs_s_default_stat (vpath_element->class, S_IFDIR | 0755));
|
||||||
|
|
||||||
|
ret_value = sftpfs_open_connection (super, &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
return ret_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for closing connection.
|
||||||
|
*
|
||||||
|
* @param me unused
|
||||||
|
* @param super connection data
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
sftpfs_cb_close_connection (struct vfs_class *me, struct vfs_s_super *super)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
(void) me;
|
||||||
|
sftpfs_close_connection (super, "Normal Shutdown", &error);
|
||||||
|
sftpfs_show_error (&error);
|
||||||
|
g_free (super->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Callback for getting directory content.
|
||||||
|
*
|
||||||
|
* @param me unused
|
||||||
|
* @param dir unused
|
||||||
|
* @param remote_path unused
|
||||||
|
* @return always 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
sftpfs_cb_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
|
||||||
|
{
|
||||||
|
(void) me;
|
||||||
|
(void) dir;
|
||||||
|
(void) remote_path;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/*** public functions ****************************************************************************/
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Initialization of VFS subclass structure.
|
||||||
|
*
|
||||||
|
* @return VFS subclass structure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
sftpfs_init_subclass (void)
|
||||||
|
{
|
||||||
|
memset (&sftpfs_subclass, 0, sizeof (struct vfs_s_subclass));
|
||||||
|
sftpfs_subclass.flags = VFS_S_REMOTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Initialization of VFS subclass callbacks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
sftpfs_init_subclass_callbacks (void)
|
||||||
|
{
|
||||||
|
sftpfs_subclass.archive_same = sftpfs_cb_is_equal_connection;
|
||||||
|
sftpfs_subclass.open_archive = sftpfs_cb_open_connection;
|
||||||
|
sftpfs_subclass.free_archive = sftpfs_cb_close_connection;
|
||||||
|
sftpfs_subclass.dir_load = sftpfs_cb_dir_load;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
Loading…
Reference in New Issue
Block a user