diff options
Diffstat (limited to 'src/vfs/sftpfs')
-rw-r--r-- | src/vfs/sftpfs/Makefile.am | 12 | ||||
-rw-r--r-- | src/vfs/sftpfs/Makefile.in | 759 | ||||
-rw-r--r-- | src/vfs/sftpfs/config_parser.c | 427 | ||||
-rw-r--r-- | src/vfs/sftpfs/connection.c | 970 | ||||
-rw-r--r-- | src/vfs/sftpfs/dir.c | 230 | ||||
-rw-r--r-- | src/vfs/sftpfs/file.c | 424 | ||||
-rw-r--r-- | src/vfs/sftpfs/internal.c | 621 | ||||
-rw-r--r-- | src/vfs/sftpfs/internal.h | 114 | ||||
-rw-r--r-- | src/vfs/sftpfs/sftpfs.c | 866 | ||||
-rw-r--r-- | src/vfs/sftpfs/sftpfs.h | 23 |
10 files changed, 4446 insertions, 0 deletions
diff --git a/src/vfs/sftpfs/Makefile.am b/src/vfs/sftpfs/Makefile.am new file mode 100644 index 0000000..12905d1 --- /dev/null +++ b/src/vfs/sftpfs/Makefile.am @@ -0,0 +1,12 @@ + +AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) $(LIBSSH_CFLAGS) + +noinst_LTLIBRARIES = libvfs-sftpfs.la + +libvfs_sftpfs_la_SOURCES = \ + config_parser.c \ + connection.c \ + dir.c \ + file.c \ + internal.c internal.h \ + sftpfs.c sftpfs.h diff --git a/src/vfs/sftpfs/Makefile.in b/src/vfs/sftpfs/Makefile.in new file mode 100644 index 0000000..e59e875 --- /dev/null +++ b/src/vfs/sftpfs/Makefile.in @@ -0,0 +1,759 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/vfs/sftpfs +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/longlong.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/m4.include/gnulib/mode_t.m4 \ + $(top_srcdir)/m4.include/gnulib/stat-size.m4 \ + $(top_srcdir)/m4.include/gnulib/fstypename.m4 \ + $(top_srcdir)/m4.include/gnulib/fsusage.m4 \ + $(top_srcdir)/m4.include/gnulib/mountlist.m4 \ + $(top_srcdir)/m4.include/gnulib/windows-stat-inodes.m4 \ + $(top_srcdir)/m4.include/gnulib/sys_types_h.m4 \ + $(top_srcdir)/m4.include/ax_path_lib_pcre.m4 \ + $(top_srcdir)/m4.include/ax_check_pcre2.m4 \ + $(top_srcdir)/m4.include/dx_doxygen.m4 \ + $(top_srcdir)/m4.include/ax_require_defined.m4 \ + $(top_srcdir)/m4.include/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4.include/ax_append_flag.m4 \ + $(top_srcdir)/m4.include/ax_append_compile_flags.m4 \ + $(top_srcdir)/m4.include/mc-cflags.m4 \ + $(top_srcdir)/m4.include/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4.include/mc-check-search-type.m4 \ + $(top_srcdir)/m4.include/mc-get-fs-info.m4 \ + $(top_srcdir)/m4.include/mc-with-x.m4 \ + $(top_srcdir)/m4.include/mc-use-termcap.m4 \ + $(top_srcdir)/m4.include/mc-with-screen.m4 \ + $(top_srcdir)/m4.include/mc-with-screen-ncurses.m4 \ + $(top_srcdir)/m4.include/mc-with-screen-slang.m4 \ + $(top_srcdir)/m4.include/mc-with-internal-edit.m4 \ + $(top_srcdir)/m4.include/mc-subshell.m4 \ + $(top_srcdir)/m4.include/mc-background.m4 \ + $(top_srcdir)/m4.include/mc-ext2fs-attr.m4 \ + $(top_srcdir)/m4.include/mc-glib.m4 \ + $(top_srcdir)/m4.include/mc-vfs.m4 \ + $(top_srcdir)/m4.include/vfs/rpc.m4 \ + $(top_srcdir)/m4.include/vfs/socket.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-extfs.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-sfs.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-ftp.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-sftp.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-fish.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-undelfs.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-tarfs.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-cpiofs.m4 \ + $(top_srcdir)/m4.include/mc-version.m4 \ + $(top_srcdir)/m4.include/mc-tests.m4 \ + $(top_srcdir)/m4.include/mc-i18n.m4 \ + $(top_srcdir)/m4.include/mc-assert.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libvfs_sftpfs_la_LIBADD = +am_libvfs_sftpfs_la_OBJECTS = config_parser.lo connection.lo dir.lo \ + file.lo internal.lo sftpfs.lo +libvfs_sftpfs_la_OBJECTS = $(am_libvfs_sftpfs_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/config_parser.Plo \ + ./$(DEPDIR)/connection.Plo ./$(DEPDIR)/dir.Plo \ + ./$(DEPDIR)/file.Plo ./$(DEPDIR)/internal.Plo \ + ./$(DEPDIR)/sftpfs.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libvfs_sftpfs_la_SOURCES) +DIST_SOURCES = $(libvfs_sftpfs_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/config/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CHECK_CFLAGS = @CHECK_CFLAGS@ +CHECK_LIBS = @CHECK_LIBS@ +COM_ERR_CFLAGS = @COM_ERR_CFLAGS@ +COM_ERR_LIBS = @COM_ERR_LIBS@ +CP1251 = @CP1251@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOC_LINGUAS = @DOC_LINGUAS@ +DOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DX_CONFIG = @DX_CONFIG@ +DX_DOCDIR = @DX_DOCDIR@ +DX_DOT = @DX_DOT@ +DX_DOXYGEN = @DX_DOXYGEN@ +DX_DVIPS = @DX_DVIPS@ +DX_EGREP = @DX_EGREP@ +DX_ENV = @DX_ENV@ +DX_FLAG_chi = @DX_FLAG_chi@ +DX_FLAG_chm = @DX_FLAG_chm@ +DX_FLAG_doc = @DX_FLAG_doc@ +DX_FLAG_dot = @DX_FLAG_dot@ +DX_FLAG_html = @DX_FLAG_html@ +DX_FLAG_man = @DX_FLAG_man@ +DX_FLAG_pdf = @DX_FLAG_pdf@ +DX_FLAG_ps = @DX_FLAG_ps@ +DX_FLAG_rtf = @DX_FLAG_rtf@ +DX_FLAG_xml = @DX_FLAG_xml@ +DX_HHC = @DX_HHC@ +DX_LATEX = @DX_LATEX@ +DX_MAKEINDEX = @DX_MAKEINDEX@ +DX_PDFLATEX = @DX_PDFLATEX@ +DX_PERL = @DX_PERL@ +DX_PROJECT = @DX_PROJECT@ +E2P_CFLAGS = @E2P_CFLAGS@ +E2P_LIBS = @E2P_LIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +EXT2FS_CFLAGS = @EXT2FS_CFLAGS@ +EXT2FS_LIBS = @EXT2FS_LIBS@ +EXTHELPERSDIR = @EXTHELPERSDIR@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +HAVE_FILECMD = @HAVE_FILECMD@ +HAVE_ZIPINFO = @HAVE_ZIPINFO@ +HAVE_nroff = @HAVE_nroff@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBMC_RELEASE = @LIBMC_RELEASE@ +LIBMC_VERSION = @LIBMC_VERSION@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSSH_CFLAGS = @LIBSSH_CFLAGS@ +LIBSSH_LIBS = @LIBSSH_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANDOC = @MANDOC@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAN_DATE = @MAN_DATE@ +MAN_FLAGS = @MAN_FLAGS@ +MAN_VERSION = @MAN_VERSION@ +MCLIBS = @MCLIBS@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PCRE_CFLAGS = @PCRE_CFLAGS@ +PCRE_LIBS = @PCRE_LIBS@ +PERL = @PERL@ +PERL_FOR_BUILD = @PERL_FOR_BUILD@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POSUB = @POSUB@ +PYTHON = @PYTHON@ +RANLIB = @RANLIB@ +RUBY = @RUBY@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SLANG_CFLAGS = @SLANG_CFLAGS@ +SLANG_LIBS = @SLANG_LIBS@ +STRIP = @STRIP@ +TESTS_LDFLAGS = @TESTS_LDFLAGS@ +UNZIP = @UNZIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +X11_WWW = @X11_WWW@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +XMKMF = @XMKMF@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +ZIP = @ZIP@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) $(LIBSSH_CFLAGS) +noinst_LTLIBRARIES = libvfs-sftpfs.la +libvfs_sftpfs_la_SOURCES = \ + config_parser.c \ + connection.c \ + dir.c \ + file.c \ + internal.c internal.h \ + sftpfs.c sftpfs.h + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/vfs/sftpfs/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/vfs/sftpfs/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libvfs-sftpfs.la: $(libvfs_sftpfs_la_OBJECTS) $(libvfs_sftpfs_la_DEPENDENCIES) $(EXTRA_libvfs_sftpfs_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libvfs_sftpfs_la_OBJECTS) $(libvfs_sftpfs_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config_parser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dir.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/internal.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sftpfs.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/config_parser.Plo + -rm -f ./$(DEPDIR)/connection.Plo + -rm -f ./$(DEPDIR)/dir.Plo + -rm -f ./$(DEPDIR)/file.Plo + -rm -f ./$(DEPDIR)/internal.Plo + -rm -f ./$(DEPDIR)/sftpfs.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/config_parser.Plo + -rm -f ./$(DEPDIR)/connection.Plo + -rm -f ./$(DEPDIR)/dir.Plo + -rm -f ./$(DEPDIR)/file.Plo + -rm -f ./$(DEPDIR)/internal.Plo + -rm -f ./$(DEPDIR)/sftpfs.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/vfs/sftpfs/config_parser.c b/src/vfs/sftpfs/config_parser.c new file mode 100644 index 0000000..d3e2287 --- /dev/null +++ b/src/vfs/sftpfs/config_parser.c @@ -0,0 +1,427 @@ +/* Virtual File System: SFTP file system. + The SSH config parser + + Copyright (C) 2011-2023 + Free Software Foundation, Inc. + + Written by: + Ilia Maslakov <il.smind@gmail.com>, 2011 + Slava Zanko <slavazanko@gmail.com>, 2011, 2012, 2013 + + 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/util.h" /* tilde_expand() */ +#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 +}; + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +/* *INDENT-OFF* */ +static struct +{ + const char *pattern; + mc_search_t *pattern_regexp; + enum config_var_type type; + size_t offset; +} config_variables[] = +{ + {"^\\s*User\\s+(.*)$", NULL, STRING, offsetof (sftpfs_ssh_config_entity_t, user)}, + {"^\\s*HostName\\s+(.*)$", NULL, STRING, offsetof (sftpfs_ssh_config_entity_t, real_host)}, + {"^\\s*IdentitiesOnly\\s+(.*)$", NULL, BOOLEAN, offsetof (sftpfs_ssh_config_entity_t, identities_only)}, + {"^\\s*IdentityFile\\s+(.*)$", NULL, FILENAME, offsetof (sftpfs_ssh_config_entity_t, identity_file)}, + {"^\\s*Port\\s+(.*)$", NULL, INTEGER, offsetof (sftpfs_ssh_config_entity_t, port)}, + {"^\\s*PasswordAuthentication\\s+(.*)$", NULL, BOOLEAN, offsetof (sftpfs_ssh_config_entity_t, password_auth)}, + {"^\\s*PubkeyAuthentication\\s+(.*)$", NULL, STRING, offsetof (sftpfs_ssh_config_entity_t, pubkey_auth)}, + {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 *fn; + + fn = tilde_expand (filename); + vpath = vfs_path_from_str (fn); + g_free (fn); + return vfs_path_free (vpath, FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +#define POINTER_TO_STRUCTURE_MEMBER(type) \ + ((type) ((char *) config_entity + (size_t) config_variables[i].offset)) + +/** + * Parse string and filling one config entity by parsed data. + * + * @param config_entity config entity structure + * @param buffer string for parce + */ + +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 mcerror pointer to the error handler + * @return TRUE if config entity was filled successfully, 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 ** mcerror) +{ + char buffer[BUF_MEDIUM]; + gboolean host_block_hit = FALSE; + gboolean pattern_block_hit = FALSE; + mc_search_t *host_regexp; + gboolean ok = TRUE; + + mc_return_val_if_error (mcerror, FALSE); + + host_regexp = mc_search_new ("^\\s*host\\s+(.*)$", DEFAULT_CHARSET); + host_regexp->search_type = MC_SEARCH_T_REGEX; + host_regexp->is_case_sensitive = FALSE; + + while (TRUE) + { + char *cr; + + if (fgets (buffer, sizeof (buffer), ssh_config_handler) == NULL) + { + int e; + + e = errno; + + if (!feof (ssh_config_handler)) + { + mc_propagate_error (mcerror, e, + _("sftp: an error occurred while reading %s: %s"), + SFTPFS_SSH_CONFIG, strerror (e)); + ok = FALSE; + goto done; + } + + 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) + goto done; + + 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_regexp = mc_search_new (host_pattern, DEFAULT_CHARSET); + 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); + } + } + + done: + mc_search_free (host_regexp); + return ok; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Open the ssh config file and fill config entity. + * + * @param vpath_element path element with host data (hostname, port) + * @param mcerror 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 ** mcerror) +{ + sftpfs_ssh_config_entity_t *config_entity; + FILE *ssh_config_handler; + char *config_filename; + + mc_return_val_if_error (mcerror, FALSE); + + 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, mcerror); + 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; + mc_propagate_error (mcerror, EPERM, "%s", _("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 ** mcerror) +{ + sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); + sftpfs_ssh_config_entity_t *config_entity; + + mc_return_if_error (mcerror); + + config_entity = sftpfs_get_config_entity (super->path_element, mcerror); + if (config_entity == NULL) + return; + + sftpfs_super->config_auth_type = (config_entity->pubkey_auth) ? PUBKEY : 0; + sftpfs_super->config_auth_type |= (config_entity->identities_only) ? 0 : AGENT; + sftpfs_super->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) + { + sftpfs_super->privkey = g_strdup (config_entity->identity_file); + sftpfs_super->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) +{ + int i; + + for (i = 0; config_variables[i].pattern != NULL; i++) + { + config_variables[i].pattern_regexp = + mc_search_new (config_variables[i].pattern, DEFAULT_CHARSET); + config_variables[i].pattern_regexp->search_type = MC_SEARCH_T_REGEX; + config_variables[i].pattern_regexp->is_case_sensitive = FALSE; + } +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Deinitialize the SSH config parser. + */ + +void +sftpfs_deinit_config_variables_patterns (void) +{ + int i; + + for (i = 0; config_variables[i].pattern != NULL; i++) + { + mc_search_free (config_variables[i].pattern_regexp); + config_variables[i].pattern_regexp = NULL; + } +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/connection.c b/src/vfs/sftpfs/connection.c new file mode 100644 index 0000000..d2466de --- /dev/null +++ b/src/vfs/sftpfs/connection.c @@ -0,0 +1,970 @@ +/* Virtual File System: SFTP file system. + The internal functions: connections + + Copyright (C) 2011-2023 + Free Software Foundation, Inc. + + Written by: + Ilia Maslakov <il.smind@gmail.com>, 2011 + Slava Zanko <slavazanko@gmail.com>, 2011, 2012, 2013 + + 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 "lib/mcconfig.h" /* mc_config_get_home_dir () */ +#include "lib/widget.h" /* query_dialog () */ + +#include "internal.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#define SHA1_DIGEST_LENGTH 20 + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 +static const char *const hostkey_method_ssh_ed25519 = "ssh-ed25519"; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521 +static const char *const hostkey_method_ssh_ecdsa_521 = "ecdsa-sha2-nistp521"; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384 +static const char *const hostkey_method_ssh_ecdsa_384 = "ecdsa-sha2-nistp384"; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 +static const char *const hostkey_method_ssh_ecdsa_256 = "ecdsa-sha2-nistp256"; +#endif +static const char *const hostkey_method_ssh_rsa = "ssh-rsa"; +static const char *const hostkey_method_ssh_dss = "ssh-dss"; + +/** + * + * The current implementation of know host key checking has following limitations: + * + * - Only plain-text entries are supported (`HashKnownHosts no` OpenSSH option) + * - Only HEX-encoded SHA1 fingerprint display is supported (`FingerprintHash` OpenSSH option) + * - Resolved IP addresses are *not* saved/validated along with the hostnames + * + */ + +static const char *kbi_passwd = NULL; +static const struct vfs_s_super *kbi_super = NULL; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ +/** + * Create socket to host. + * + * @param super connection data + * @param mcerror pointer to the error handler + * @return socket descriptor number, -1 if any error was occurred + */ + +static int +sftpfs_open_socket (struct vfs_s_super *super, GError ** mcerror) +{ + sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); + struct addrinfo hints, *res = NULL, *curr_res; + int my_socket = 0; + char port[BUF_TINY]; + static char address_ipv4[INET_ADDRSTRLEN]; + static char address_ipv6[INET6_ADDRSTRLEN]; + int e; + + mc_return_val_if_error (mcerror, LIBSSH2_INVALID_SOCKET); + + if (super->path_element->host == NULL || *super->path_element->host == '\0') + { + mc_propagate_error (mcerror, 0, "%s", _("sftp: Invalid host name.")); + return LIBSSH2_INVALID_SOCKET; + } + + sprintf (port, "%hu", (unsigned short) super->path_element->port); + + tty_enable_interrupt_key (); /* clear the interrupt flag */ + + memset (&hints, 0, sizeof (hints)); + 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) + { + mc_propagate_error (mcerror, e, _("sftp: %s"), gai_strerror (e)); + my_socket = LIBSSH2_INVALID_SOCKET; + goto ret; + } + + for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next) + { + int save_errno; + + switch (curr_res->ai_addr->sa_family) + { + case AF_INET: + sftpfs_super->ip_address = + inet_ntop (AF_INET, &((struct sockaddr_in *) curr_res->ai_addr)->sin_addr, + address_ipv4, INET_ADDRSTRLEN); + break; + case AF_INET6: + sftpfs_super->ip_address = + inet_ntop (AF_INET6, &((struct sockaddr_in6 *) curr_res->ai_addr)->sin6_addr, + address_ipv6, INET6_ADDRSTRLEN); + break; + default: + sftpfs_super->ip_address = NULL; + } + + if (sftpfs_super->ip_address == NULL) + { + mc_propagate_error (mcerror, 0, "%s", + _("sftp: failed to convert remote host IP address into text form")); + my_socket = LIBSSH2_INVALID_SOCKET; + goto ret; + } + + 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 = LIBSSH2_INVALID_SOCKET; + 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; + + save_errno = errno; + + close (my_socket); + + if (save_errno == EINTR && tty_got_interrupt ()) + mc_propagate_error (mcerror, 0, "%s", _("sftp: connection interrupted by user")); + else if (res->ai_next == NULL) + mc_propagate_error (mcerror, save_errno, _("sftp: connection to server failed: %s"), + unix_error_string (save_errno)); + else + continue; + + my_socket = LIBSSH2_INVALID_SOCKET; + break; + } + + ret: + if (res != NULL) + freeaddrinfo (res); + tty_disable_interrupt_key (); + return my_socket; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Read ~/.ssh/known_hosts file. + * + * @param super connection data + * @param mcerror pointer to the error handler + * @return TRUE on success, FALSE otherwise + * + * Thanks the Curl project for the code used in this function. + */ +static gboolean +sftpfs_read_known_hosts (struct vfs_s_super *super, GError ** mcerror) +{ + sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); + struct libssh2_knownhost *store = NULL; + int rc; + gboolean found = FALSE; + + sftpfs_super->known_hosts = libssh2_knownhost_init (sftpfs_super->session); + if (sftpfs_super->known_hosts == NULL) + goto err; + + sftpfs_super->known_hosts_file = + mc_build_filename (mc_config_get_home_dir (), ".ssh", "known_hosts", (char *) NULL); + rc = libssh2_knownhost_readfile (sftpfs_super->known_hosts, sftpfs_super->known_hosts_file, + LIBSSH2_KNOWNHOST_FILE_OPENSSH); + if (rc > 0) + { + const char *kh_name_end = NULL; + + while (!found && libssh2_knownhost_get (sftpfs_super->known_hosts, &store, store) == 0) + { + /* For non-standard ports, the name will be enclosed in + * square brackets, followed by a colon and the port */ + if (store == NULL) + continue; + + if (store->name == NULL) + found = TRUE; + else if (store->name[0] != '[') + found = strcmp (store->name, super->path_element->host) == 0; + else + { + int port; + + kh_name_end = strstr (store->name, "]:"); + if (kh_name_end == NULL) + /* Invalid host pattern */ + continue; + + port = (int) g_ascii_strtoll (kh_name_end + 2, NULL, 10); + if (port == super->path_element->port) + { + size_t kh_name_size; + + kh_name_size = strlen (store->name) - 1 - strlen (kh_name_end); + found = strncmp (store->name + 1, super->path_element->host, kh_name_size) == 0; + } + } + } + } + + if (found) + { + int mask; + const char *hostkey_method = NULL; + + mask = store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK; + + switch (mask) + { +#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 + case LIBSSH2_KNOWNHOST_KEY_ED25519: + hostkey_method = hostkey_method_ssh_ed25519; + break; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521 + case LIBSSH2_KNOWNHOST_KEY_ECDSA_521: + hostkey_method = hostkey_method_ssh_ecdsa_521; + break; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384 + case LIBSSH2_KNOWNHOST_KEY_ECDSA_384: + hostkey_method = hostkey_method_ssh_ecdsa_384; + break; +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 + case LIBSSH2_KNOWNHOST_KEY_ECDSA_256: + hostkey_method = hostkey_method_ssh_ecdsa_256; + break; +#endif + case LIBSSH2_KNOWNHOST_KEY_SSHRSA: + hostkey_method = hostkey_method_ssh_rsa; + break; + case LIBSSH2_KNOWNHOST_KEY_SSHDSS: + hostkey_method = hostkey_method_ssh_dss; + break; + case LIBSSH2_KNOWNHOST_KEY_RSA1: + mc_propagate_error (mcerror, 0, "%s", + _("sftp: found host key of unsupported type: RSA1")); + return FALSE; + default: + mc_propagate_error (mcerror, 0, "%s 0x%x", _("sftp: unknown host key type:"), + (unsigned int) mask); + return FALSE; + } + + rc = libssh2_session_method_pref (sftpfs_super->session, LIBSSH2_METHOD_HOSTKEY, + hostkey_method); + if (rc < 0) + goto err; + } + + return TRUE; + + err: + { + int sftp_errno; + + sftp_errno = libssh2_session_last_errno (sftpfs_super->session); + sftpfs_ssherror_to_gliberror (sftpfs_super, sftp_errno, mcerror); + } + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Write new host + key pair to the ~/.ssh/known_hosts file. + * + * @param super connection data + * @param remote_key he key for the remote host + * @param remote_key_len length of @remote_key + * @param type_mask info about format of host name, key and key type + * @return 0 on success, regular libssh2 error code otherwise + * + * Thanks the Curl project for the code used in this function. + */ +static int +sftpfs_update_known_hosts (struct vfs_s_super *super, const char *remote_key, size_t remote_key_len, + int type_mask) +{ + sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); + int rc; + + /* add this host + key pair */ + rc = libssh2_knownhost_addc (sftpfs_super->known_hosts, super->path_element->host, NULL, + remote_key, remote_key_len, NULL, 0, type_mask, NULL); + if (rc < 0) + return rc; + + /* write the entire in-memory list of known hosts to the known_hosts file */ + rc = libssh2_knownhost_writefile (sftpfs_super->known_hosts, sftpfs_super->known_hosts_file, + LIBSSH2_KNOWNHOST_FILE_OPENSSH); + + if (rc < 0) + return rc; + + (void) message (D_NORMAL, _("Information"), + _("Permanently added\n%s (%s)\nto the list of known hosts."), + super->path_element->host, sftpfs_super->ip_address); + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Compute and return readable host key fingerprint hash. + * + * @param session libssh2 session handle + * @return pointer to static buffer on success, NULL otherwise + */ +static const char * +sftpfs_compute_fingerprint_hash (LIBSSH2_SESSION * session) +{ + static char result[SHA1_DIGEST_LENGTH * 3 + 1]; /* "XX:" for each byte, and EOL */ + const char *fingerprint; + size_t i; + + /* The fingerprint points to static storage (!), don't free() it. */ + fingerprint = libssh2_hostkey_hash (session, LIBSSH2_HOSTKEY_HASH_SHA1); + if (fingerprint == NULL) + return NULL; + + for (i = 0; i < SHA1_DIGEST_LENGTH && i * 3 < sizeof (result) - 1; i++) + g_snprintf ((gchar *) (result + i * 3), 4, "%02x:", (guint8) fingerprint[i]); + + /* remove last ":" */ + result[i * 3 - 1] = '\0'; + + return result; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Process host info found in ~/.ssh/known_hosts file. + * + * @param super connection data + * @param mcerror pointer to the error handler + * @return TRUE on success, FALSE otherwise + * + * Thanks the Curl project for the code used in this function. + */ +static gboolean +sftpfs_process_known_host (struct vfs_s_super *super, GError ** mcerror) +{ + sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); + const char *remote_key; + const char *key_type; + const char *fingerprint_hash; + size_t remote_key_len = 0; + int remote_key_type = LIBSSH2_HOSTKEY_TYPE_UNKNOWN; + int keybit = 0; + struct libssh2_knownhost *host = NULL; + int rc; + char *msg = NULL; + gboolean handle_query = FALSE; + + remote_key = libssh2_session_hostkey (sftpfs_super->session, &remote_key_len, &remote_key_type); + if (remote_key == NULL || remote_key_len == 0 + || remote_key_type == LIBSSH2_HOSTKEY_TYPE_UNKNOWN) + { + mc_propagate_error (mcerror, 0, "%s", _("sftp: cannot get the remote host key")); + return FALSE; + } + + switch (remote_key_type) + { + case LIBSSH2_HOSTKEY_TYPE_RSA: + keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA; + key_type = "RSA"; + break; + case LIBSSH2_HOSTKEY_TYPE_DSS: + keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS; + key_type = "DSS"; + break; +#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256 + case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256; + key_type = "ECDSA"; + break; +#endif +#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384 + case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384; + key_type = "ECDSA"; + break; +#endif +#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521 + case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521; + key_type = "ECDSA"; + break; +#endif +#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519 + case LIBSSH2_HOSTKEY_TYPE_ED25519: + keybit = LIBSSH2_KNOWNHOST_KEY_ED25519; + key_type = "ED25519"; + break; +#endif + default: + mc_propagate_error (mcerror, 0, "%s", + _("sftp: unsupported key type, can't check remote host key")); + return FALSE; + } + + fingerprint_hash = sftpfs_compute_fingerprint_hash (sftpfs_super->session); + if (fingerprint_hash == NULL) + { + mc_propagate_error (mcerror, 0, "%s", _("sftp: can't compute host key fingerprint hash")); + return FALSE; + } + + rc = libssh2_knownhost_checkp (sftpfs_super->known_hosts, super->path_element->host, + super->path_element->port, remote_key, remote_key_len, + LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | + keybit, &host); + + switch (rc) + { + default: + case LIBSSH2_KNOWNHOST_CHECK_FAILURE: + /* something prevented the check to be made */ + goto err; + + case LIBSSH2_KNOWNHOST_CHECK_MATCH: + /* host + key pair matched -- OK */ + break; + + case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND: + /* no host match was found -- add it to the known_hosts file */ + msg = g_strdup_printf (_("The authenticity of host\n%s (%s)\ncan't be established!\n" + "%s key fingerprint hash is\nSHA1:%s.\n" + "Do you want to add it to the list of known hosts and continue connecting?"), + super->path_element->host, sftpfs_super->ip_address, + key_type, fingerprint_hash); + /* Select "No" initially */ + query_set_sel (2); + rc = query_dialog (_("Warning"), msg, D_NORMAL, 3, _("&Yes"), _("&Ignore"), _("&No")); + g_free (msg); + handle_query = TRUE; + break; + + case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: + msg = g_strdup_printf (_("%s (%s)\nis found in the list of known hosts but\n" + "KEYS DO NOT MATCH! THIS COULD BE A MITM ATTACK!\n" + "Are you sure you want to add it to the list of known hosts and continue connecting?"), + super->path_element->host, sftpfs_super->ip_address); + /* Select "No" initially */ + query_set_sel (2); + rc = query_dialog (MSG_ERROR, msg, D_ERROR, 3, _("&Yes"), _("&Ignore"), _("&No")); + g_free (msg); + handle_query = TRUE; + break; + } + + if (handle_query) + switch (rc) + { + case 0: + /* Yes: add this host + key pair, continue connecting */ + if (sftpfs_update_known_hosts (super, remote_key, remote_key_len, + LIBSSH2_KNOWNHOST_TYPE_PLAIN + | LIBSSH2_KNOWNHOST_KEYENC_RAW | keybit) < 0) + goto err; + break; + case 1: + /* Ignore: do not add this host + key pair, continue connecting anyway */ + break; + case 2: + default: + mc_propagate_error (mcerror, 0, "%s", _("sftp: host key verification failed")); + /* No: abort connection */ + goto err; + } + + return TRUE; + + err: + { + int sftp_errno; + + sftp_errno = libssh2_session_last_errno (sftpfs_super->session); + sftpfs_ssherror_to_gliberror (sftpfs_super, sftp_errno, mcerror); + } + + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Recognize authentication types supported by remote side and filling internal 'super' structure by + * proper enum's values. + * + * @param super connection data + * @return TRUE if some of authentication methods is available, FALSE otherwise + */ +static gboolean +sftpfs_recognize_auth_types (struct vfs_s_super *super) +{ + char *userauthlist; + sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); + + /* check what authentication methods are available */ + /* userauthlist is internally managed by libssh2 and freed by libssh2_session_free() */ + userauthlist = libssh2_userauth_list (sftpfs_super->session, super->path_element->user, + strlen (super->path_element->user)); + + if (userauthlist == NULL) + return FALSE; + + if ((strstr (userauthlist, "password") != NULL + || strstr (userauthlist, "keyboard-interactive") != NULL) + && (sftpfs_super->config_auth_type & PASSWORD) != 0) + sftpfs_super->auth_type |= PASSWORD; + + if (strstr (userauthlist, "publickey") != NULL + && (sftpfs_super->config_auth_type & PUBKEY) != 0) + sftpfs_super->auth_type |= PUBKEY; + + if ((sftpfs_super->config_auth_type & AGENT) != 0) + sftpfs_super->auth_type |= AGENT; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Open connection to host using SSH-agent helper. + * + * @param super connection data + * @param mcerror pointer to the error handler + * @return TRUE if connection was successfully opened, FALSE otherwise + */ + +static gboolean +sftpfs_open_connection_ssh_agent (struct vfs_s_super *super, GError ** mcerror) +{ + sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); + struct libssh2_agent_publickey *identity, *prev_identity = NULL; + int rc; + + mc_return_val_if_error (mcerror, FALSE); + + sftpfs_super->agent = NULL; + + if ((sftpfs_super->auth_type & AGENT) == 0) + return FALSE; + + /* Connect to the ssh-agent */ + sftpfs_super->agent = libssh2_agent_init (sftpfs_super->session); + if (sftpfs_super->agent == NULL) + return FALSE; + + if (libssh2_agent_connect (sftpfs_super->agent) != 0) + return FALSE; + + if (libssh2_agent_list_identities (sftpfs_super->agent) != 0) + return FALSE; + + while (TRUE) + { + rc = libssh2_agent_get_identity (sftpfs_super->agent, &identity, prev_identity); + if (rc == 1) + break; + + if (rc < 0) + return FALSE; + + if (libssh2_agent_userauth (sftpfs_super->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 mcerror pointer to the error handler + * @return TRUE if connection was successfully opened, FALSE otherwise + */ + +static gboolean +sftpfs_open_connection_ssh_key (struct vfs_s_super *super, GError ** mcerror) +{ + sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); + char *p, *passwd; + gboolean ret_value = FALSE; + + mc_return_val_if_error (mcerror, FALSE); + + if ((sftpfs_super->auth_type & PUBKEY) == 0) + return FALSE; + + if (sftpfs_super->privkey == NULL) + return FALSE; + + if (libssh2_userauth_publickey_fromfile (sftpfs_super->session, super->path_element->user, + sftpfs_super->pubkey, sftpfs_super->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) + mc_propagate_error (mcerror, 0, "%s", _("sftp: Passphrase is empty.")); + else + { + ret_value = (libssh2_userauth_publickey_fromfile (sftpfs_super->session, + super->path_element->user, + sftpfs_super->pubkey, + sftpfs_super->privkey, passwd) == 0); + g_free (passwd); + } + + return ret_value; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Keyboard-interactive password helper for opening connection to host by + * sftpfs_open_connection_ssh_password + * + * Uses global kbi_super (data with existing connection) and kbi_passwd (password) + * + * @param name username + * @param name_len length of @name + * @param instruction unused + * @param instruction_len unused + * @param num_prompts number of possible problems to process + * @param prompts array of prompts to process + * @param responses array of responses, one per prompt + * @param abstract unused + */ + +static +LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC (sftpfs_keyboard_interactive_helper) +{ + int i; + size_t len; + + (void) instruction; + (void) instruction_len; + (void) abstract; + + if (kbi_super == NULL || kbi_passwd == NULL) + return; + + if (strncmp (name, kbi_super->path_element->user, name_len) != 0) + return; + + /* assume these are password prompts */ + len = strlen (kbi_passwd); + + for (i = 0; i < num_prompts; ++i) + if (strncmp (prompts[i].text, "Password: ", prompts[i].length) == 0) + { + responses[i].text = strdup (kbi_passwd); + responses[i].length = len; + } +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Open connection to host using password. + * + * @param super connection data + * @param mcerror pointer to the error handler + * @return TRUE if connection was successfully opened, FALSE otherwise + */ + +static gboolean +sftpfs_open_connection_ssh_password (struct vfs_s_super *super, GError ** mcerror) +{ + sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); + char *p, *passwd; + gboolean ret_value = FALSE; + int rc; + + mc_return_val_if_error (mcerror, FALSE); + + if ((sftpfs_super->auth_type & PASSWORD) == 0) + return FALSE; + + if (super->path_element->password != NULL) + { + while ((rc = libssh2_userauth_password (sftpfs_super->session, super->path_element->user, + super->path_element->password)) == + LIBSSH2_ERROR_EAGAIN); + if (rc == 0) + return TRUE; + + kbi_super = super; + kbi_passwd = super->path_element->password; + + while ((rc = + libssh2_userauth_keyboard_interactive (sftpfs_super->session, + super->path_element->user, + sftpfs_keyboard_interactive_helper)) == + LIBSSH2_ERROR_EAGAIN) + ; + + kbi_super = NULL; + kbi_passwd = NULL; + + 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) + mc_propagate_error (mcerror, 0, "%s", _("sftp: Password is empty.")); + else + { + while ((rc = libssh2_userauth_password (sftpfs_super->session, super->path_element->user, + passwd)) == LIBSSH2_ERROR_EAGAIN) + ; + + if (rc != 0) + { + kbi_super = super; + kbi_passwd = passwd; + + while ((rc = + libssh2_userauth_keyboard_interactive (sftpfs_super->session, + super->path_element->user, + sftpfs_keyboard_interactive_helper)) == + LIBSSH2_ERROR_EAGAIN) + ; + + kbi_super = NULL; + kbi_passwd = NULL; + } + + 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 mcerror pointer to the error handler + * @return 0 if success, -1 otherwise + */ + +int +sftpfs_open_connection (struct vfs_s_super *super, GError ** mcerror) +{ + int rc; + sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); + + mc_return_val_if_error (mcerror, -1); + + /* + * The application code is responsible for creating the socket + * and establishing the connection + */ + sftpfs_super->socket_handle = sftpfs_open_socket (super, mcerror); + if (sftpfs_super->socket_handle == LIBSSH2_INVALID_SOCKET) + return (-1); + + /* Create a session instance */ + sftpfs_super->session = libssh2_session_init (); + if (sftpfs_super->session == NULL) + return (-1); + + if (!sftpfs_read_known_hosts (super, mcerror)) + return (-1); + + /* ... start it up. This will trade welcome banners, exchange keys, + * and setup crypto, compression, and MAC layers + */ + while ((rc = + libssh2_session_handshake (sftpfs_super->session, + (libssh2_socket_t) sftpfs_super->socket_handle)) == + LIBSSH2_ERROR_EAGAIN) + ; + if (rc != 0) + { + mc_propagate_error (mcerror, rc, "%s", _("sftp: failure establishing SSH session")); + return (-1); + } + + if (!sftpfs_process_known_host (super, mcerror)) + return (-1); + + if (!sftpfs_recognize_auth_types (super)) + { + int sftp_errno; + + sftp_errno = libssh2_session_last_errno (sftpfs_super->session); + sftpfs_ssherror_to_gliberror (sftpfs_super, sftp_errno, mcerror); + return (-1); + } + + if (!sftpfs_open_connection_ssh_agent (super, mcerror) + && !sftpfs_open_connection_ssh_key (super, mcerror) + && !sftpfs_open_connection_ssh_password (super, mcerror)) + return (-1); + + sftpfs_super->sftp_session = libssh2_sftp_init (sftpfs_super->session); + + if (sftpfs_super->sftp_session == NULL) + return (-1); + + /* Since we have not set non-blocking, tell libssh2 we are blocking */ + libssh2_session_set_blocking (sftpfs_super->session, 1); + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Close connection. + * + * @param super connection data + * @param shutdown_message message for shutdown functions + * @param mcerror pointer to the error handler + */ + +void +sftpfs_close_connection (struct vfs_s_super *super, const char *shutdown_message, GError ** mcerror) +{ + sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); + + /* no mc_return_*_if_error() here because of abort open_connection handling too */ + (void) mcerror; + + if (sftpfs_super->sftp_session != NULL) + { + libssh2_sftp_shutdown (sftpfs_super->sftp_session); + sftpfs_super->sftp_session = NULL; + } + + if (sftpfs_super->agent != NULL) + { + libssh2_agent_disconnect (sftpfs_super->agent); + libssh2_agent_free (sftpfs_super->agent); + sftpfs_super->agent = NULL; + } + + if (sftpfs_super->known_hosts != NULL) + { + libssh2_knownhost_free (sftpfs_super->known_hosts); + sftpfs_super->known_hosts = NULL; + } + + MC_PTR_FREE (sftpfs_super->known_hosts_file); + + if (sftpfs_super->session != NULL) + { + libssh2_session_disconnect (sftpfs_super->session, shutdown_message); + libssh2_session_free (sftpfs_super->session); + sftpfs_super->session = NULL; + } + + if (sftpfs_super->socket_handle != LIBSSH2_INVALID_SOCKET) + { + close (sftpfs_super->socket_handle); + sftpfs_super->socket_handle = LIBSSH2_INVALID_SOCKET; + } +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/dir.c b/src/vfs/sftpfs/dir.c new file mode 100644 index 0000000..a19a31f --- /dev/null +++ b/src/vfs/sftpfs/dir.c @@ -0,0 +1,230 @@ +/* Virtual File System: SFTP file system. + The internal functions: dirs + + Copyright (C) 2011-2023 + 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 "lib/util.h" + +#include "internal.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +typedef struct +{ + LIBSSH2_SFTP_HANDLE *handle; + sftpfs_super_t *super; +} 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 mcerror pointer to the error handler + * @return directory data handler if success, NULL otherwise + */ + +void * +sftpfs_opendir (const vfs_path_t * vpath, GError ** mcerror) +{ + sftpfs_dir_data_t *sftpfs_dir; + sftpfs_super_t *sftpfs_super; + const vfs_path_element_t *path_element; + LIBSSH2_SFTP_HANDLE *handle; + const GString *fixfname; + + if (!sftpfs_op_init (&sftpfs_super, &path_element, vpath, mcerror)) + return NULL; + + fixfname = sftpfs_fix_filename (path_element->path); + + while (TRUE) + { + int libssh_errno; + + handle = + libssh2_sftp_open_ex (sftpfs_super->sftp_session, fixfname->str, fixfname->len, 0, 0, + LIBSSH2_SFTP_OPENDIR); + if (handle != NULL) + break; + + libssh_errno = libssh2_session_last_errno (sftpfs_super->session); + if (!sftpfs_waitsocket (sftpfs_super, libssh_errno, mcerror)) + return NULL; + } + + sftpfs_dir = g_new0 (sftpfs_dir_data_t, 1); + sftpfs_dir->handle = handle; + sftpfs_dir->super = sftpfs_super; + + return (void *) sftpfs_dir; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Get a pointer to a structure representing the next directory entry. + * + * @param data directory data handler + * @param mcerror pointer to the error handler + * @return information about direntry if success, NULL otherwise + */ + +struct vfs_dirent * +sftpfs_readdir (void *data, GError ** mcerror) +{ + char mem[BUF_MEDIUM]; + LIBSSH2_SFTP_ATTRIBUTES attrs; + sftpfs_dir_data_t *sftpfs_dir = (sftpfs_dir_data_t *) data; + int rc; + + mc_return_val_if_error (mcerror, NULL); + + do + { + rc = libssh2_sftp_readdir (sftpfs_dir->handle, mem, sizeof (mem), &attrs); + if (rc >= 0) + break; + + if (!sftpfs_waitsocket (sftpfs_dir->super, rc, mcerror)) + return NULL; + } + while (rc == LIBSSH2_ERROR_EAGAIN); + + return (rc != 0 ? vfs_dirent_init (NULL, mem, 0) : NULL); /* FIXME: inode */ +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Close the directory stream. + * + * @param data directory data handler + * @param mcerror pointer to the error handler + * @return 0 if success, negative value otherwise + */ + +int +sftpfs_closedir (void *data, GError ** mcerror) +{ + int rc; + sftpfs_dir_data_t *sftpfs_dir = (sftpfs_dir_data_t *) data; + + mc_return_val_if_error (mcerror, -1); + + 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 mcerror pointer to the error handler + * @return 0 if success, negative value otherwise + */ + +int +sftpfs_mkdir (const vfs_path_t * vpath, mode_t mode, GError ** mcerror) +{ + int res; + sftpfs_super_t *sftpfs_super; + const vfs_path_element_t *path_element; + const GString *fixfname; + + if (!sftpfs_op_init (&sftpfs_super, &path_element, vpath, mcerror)) + return -1; + + fixfname = sftpfs_fix_filename (path_element->path); + + do + { + res = + libssh2_sftp_mkdir_ex (sftpfs_super->sftp_session, fixfname->str, fixfname->len, mode); + if (res >= 0) + break; + + if (!sftpfs_waitsocket (sftpfs_super, res, mcerror)) + return -1; + } + while (res == LIBSSH2_ERROR_EAGAIN); + + return res; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Remove a directory. + * + * @param vpath path directory + * @param mcerror pointer to the error handler + * @return 0 if success, negative value otherwise + */ + +int +sftpfs_rmdir (const vfs_path_t * vpath, GError ** mcerror) +{ + int res; + sftpfs_super_t *sftpfs_super; + const vfs_path_element_t *path_element; + const GString *fixfname; + + if (!sftpfs_op_init (&sftpfs_super, &path_element, vpath, mcerror)) + return -1; + + fixfname = sftpfs_fix_filename (path_element->path); + + do + { + res = libssh2_sftp_rmdir_ex (sftpfs_super->sftp_session, fixfname->str, fixfname->len); + if (res >= 0) + break; + + if (!sftpfs_waitsocket (sftpfs_super, res, mcerror)) + return -1; + } + while (res == LIBSSH2_ERROR_EAGAIN); + + return res; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/file.c b/src/vfs/sftpfs/file.c new file mode 100644 index 0000000..4146239 --- /dev/null +++ b/src/vfs/sftpfs/file.c @@ -0,0 +1,424 @@ +/* Virtual File System: SFTP file system. + The internal functions: files + + Copyright (C) 2011-2023 + 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> /* ENOENT, EACCES */ + +#include <libssh2.h> +#include <libssh2_sftp.h> + +#include "lib/global.h" +#include "lib/util.h" + +#include "internal.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#define SFTP_FILE_HANDLER(a) ((sftpfs_file_handler_t *) a) + +/*** file scope type declarations ****************************************************************/ + +typedef struct +{ + vfs_file_handler_t base; /* base class */ + + LIBSSH2_SFTP_HANDLE *handle; + int flags; + mode_t mode; +} sftpfs_file_handler_t; + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ +/** + * Reopen file by file handle. + * + * @param fh the file handler + * @param mcerror pointer to the error handler + */ +static void +sftpfs_reopen (vfs_file_handler_t * fh, GError ** mcerror) +{ + sftpfs_file_handler_t *file = SFTP_FILE_HANDLER (fh); + int flags; + mode_t mode; + + g_return_if_fail (mcerror == NULL || *mcerror == NULL); + + flags = file->flags; + mode = file->mode; + + sftpfs_close_file (fh, mcerror); + sftpfs_open_file (fh, flags, mode, mcerror); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +sftpfs_file__handle_error (sftpfs_super_t * super, int sftp_res, GError ** mcerror) +{ + if (sftpfs_is_sftp_error (super->sftp_session, sftp_res, LIBSSH2_FX_PERMISSION_DENIED)) + return -EACCES; + + if (sftpfs_is_sftp_error (super->sftp_session, sftp_res, LIBSSH2_FX_NO_SUCH_FILE)) + return -ENOENT; + + if (!sftpfs_waitsocket (super, sftp_res, mcerror)) + return -1; + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +vfs_file_handler_t * +sftpfs_fh_new (struct vfs_s_inode * ino, gboolean changed) +{ + sftpfs_file_handler_t *fh; + + fh = g_new0 (sftpfs_file_handler_t, 1); + vfs_s_init_fh (VFS_FILE_HANDLER (fh), ino, changed); + + return VFS_FILE_HANDLER (fh); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Open new SFTP file. + * + * @param fh the file handler + * @param flags flags (see man 2 open) + * @param mode mode (see man 2 open) + * @param mcerror pointer to the error handler + * @return TRUE if connection was created successfully, FALSE otherwise + */ + +gboolean +sftpfs_open_file (vfs_file_handler_t * fh, int flags, mode_t mode, GError ** mcerror) +{ + unsigned long sftp_open_flags = 0; + int sftp_open_mode = 0; + gboolean do_append = FALSE; + sftpfs_file_handler_t *file = SFTP_FILE_HANDLER (fh); + sftpfs_super_t *super = SFTP_SUPER (fh->ino->super); + char *name; + const GString *fixfname; + + (void) mode; + mc_return_val_if_error (mcerror, FALSE); + + name = vfs_s_fullpath (vfs_sftpfs_ops, fh->ino); + if (name == NULL) + return FALSE; + + 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; + if ((flags & O_APPEND) != 0) + { + sftp_open_flags |= LIBSSH2_FXF_APPEND; + do_append = TRUE; + } + 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; + + fixfname = sftpfs_fix_filename (name); + + while (TRUE) + { + int libssh_errno; + + file->handle = + libssh2_sftp_open_ex (super->sftp_session, fixfname->str, fixfname->len, + sftp_open_flags, sftp_open_mode, LIBSSH2_SFTP_OPENFILE); + if (file->handle != NULL) + break; + + libssh_errno = libssh2_session_last_errno (super->session); + if (libssh_errno != LIBSSH2_ERROR_EAGAIN) + { + sftpfs_ssherror_to_gliberror (super, libssh_errno, mcerror); + g_free (name); + return FALSE; + } + } + + g_free (name); + + file->flags = flags; + file->mode = mode; + + if (do_append) + { + struct stat file_info = { + .st_dev = 0 + }; + /* In case of + + struct stat file_info = { 0 }; + + gcc < 4.7 [1] generates the following: + + error: missing initializer [-Werror=missing-field-initializers] + error: (near initialization for 'file_info.st_dev') [-Werror=missing-field-initializers] + + [1] http://stackoverflow.com/questions/13373695/how-to-remove-the-warning-in-gcc-4-6-missing-initializer-wmissing-field-initi/27461062#27461062 + */ + + if (sftpfs_fstat (fh, &file_info, mcerror) == 0) + libssh2_sftp_seek64 (file->handle, file_info.st_size); + } + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Stats the file specified by the file descriptor. + * + * @param data file handler + * @param buf buffer for store stat-info + * @param mcerror pointer to the error handler + * @return 0 if success, negative value otherwise + */ + +int +sftpfs_fstat (void *data, struct stat *buf, GError ** mcerror) +{ + int res; + LIBSSH2_SFTP_ATTRIBUTES attrs; + vfs_file_handler_t *fh = VFS_FILE_HANDLER (data); + sftpfs_file_handler_t *sftpfs_fh = (sftpfs_file_handler_t *) data; + struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh); + sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); + + mc_return_val_if_error (mcerror, -1); + + if (sftpfs_fh->handle == NULL) + return -1; + + do + { + int err; + + res = libssh2_sftp_fstat_ex (sftpfs_fh->handle, &attrs, 0); + if (res >= 0) + break; + + err = sftpfs_file__handle_error (sftpfs_super, res, mcerror); + if (err < 0) + return err; + } + while (res == LIBSSH2_ERROR_EAGAIN); + + sftpfs_attr_to_stat (&attrs, buf); + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Read up to 'count' bytes from the file descriptor 'fh' to the buffer starting at 'buffer'. + * + * @param fh file handler + * @param buffer buffer for data + * @param count data size + * @param mcerror pointer to the error handler + * + * @return 0 on success, negative value otherwise + */ + +ssize_t +sftpfs_read_file (vfs_file_handler_t * fh, char *buffer, size_t count, GError ** mcerror) +{ + ssize_t rc; + sftpfs_file_handler_t *file = SFTP_FILE_HANDLER (fh); + sftpfs_super_t *super; + + mc_return_val_if_error (mcerror, -1); + + if (fh == NULL) + { + mc_propagate_error (mcerror, 0, "%s", + _("sftp: No file handler data present for reading file")); + return -1; + } + + super = SFTP_SUPER (VFS_FILE_HANDLER_SUPER (fh)); + + do + { + int err; + + rc = libssh2_sftp_read (file->handle, buffer, count); + if (rc >= 0) + break; + + err = sftpfs_file__handle_error (super, (int) rc, mcerror); + if (err < 0) + return err; + } + while (rc == LIBSSH2_ERROR_EAGAIN); + + fh->pos = (off_t) libssh2_sftp_tell64 (file->handle); + + return rc; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Write up to 'count' bytes from the buffer starting at 'buffer' to the descriptor 'fh'. + * + * @param fh file handler + * @param buffer buffer for data + * @param count data size + * @param mcerror pointer to the error handler + * + * @return 0 on success, negative value otherwise + */ + +ssize_t +sftpfs_write_file (vfs_file_handler_t * fh, const char *buffer, size_t count, GError ** mcerror) +{ + ssize_t rc; + sftpfs_file_handler_t *file = SFTP_FILE_HANDLER (fh); + sftpfs_super_t *super = SFTP_SUPER (VFS_FILE_HANDLER_SUPER (fh)); + + mc_return_val_if_error (mcerror, -1); + + fh->pos = (off_t) libssh2_sftp_tell64 (file->handle); + + do + { + int err; + + rc = libssh2_sftp_write (file->handle, buffer, count); + if (rc >= 0) + break; + + err = sftpfs_file__handle_error (super, (int) rc, mcerror); + if (err < 0) + return err; + } + while (rc == LIBSSH2_ERROR_EAGAIN); + + return rc; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Close a file descriptor. + * + * @param fh file handler + * @param mcerror pointer to the error handler + * + * @return 0 on success, negative value otherwise + */ + +int +sftpfs_close_file (vfs_file_handler_t * fh, GError ** mcerror) +{ + int ret; + + mc_return_val_if_error (mcerror, -1); + + ret = libssh2_sftp_close (SFTP_FILE_HANDLER (fh)->handle); + + return ret == 0 ? 0 : -1; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Reposition the offset of the open file associated with the file descriptor. + * + * @param fh file handler + * @param offset file offset + * @param whence method of seek (at begin, at current, at end) + * @param mcerror pointer to the error handler + * + * @return 0 on success, negative value otherwise + */ + +off_t +sftpfs_lseek (vfs_file_handler_t * fh, off_t offset, int whence, GError ** mcerror) +{ + sftpfs_file_handler_t *file = SFTP_FILE_HANDLER (fh); + + mc_return_val_if_error (mcerror, 0); + + 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 (fh->pos > offset || offset == 0) + { + sftpfs_reopen (fh, mcerror); + mc_return_val_if_error (mcerror, 0); + } + fh->pos = offset; + break; + case SEEK_CUR: + fh->pos += offset; + break; + case SEEK_END: + if (fh->pos > fh->ino->st.st_size - offset) + { + sftpfs_reopen (fh, mcerror); + mc_return_val_if_error (mcerror, 0); + } + fh->pos = fh->ino->st.st_size - offset; + break; + default: + break; + } + + libssh2_sftp_seek64 (file->handle, fh->pos); + fh->pos = (off_t) libssh2_sftp_tell64 (file->handle); + + return fh->pos; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/internal.c b/src/vfs/sftpfs/internal.c new file mode 100644 index 0000000..9faa76c --- /dev/null +++ b/src/vfs/sftpfs/internal.c @@ -0,0 +1,621 @@ +/* Virtual File System: SFTP file system. + The internal functions + + Copyright (C) 2011-2023 + 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> + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#else +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#endif + +#include "lib/global.h" +#include "lib/util.h" + +#include "internal.h" + +/*** global variables ****************************************************************************/ + +GString *sftpfs_filename_buffer = NULL; + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/* Adjust block size and number of blocks */ + +static void +sftpfs_blksize (struct stat *s) +{ +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + s->st_blksize = LIBSSH2_CHANNEL_WINDOW_DEFAULT; /* FIXME */ +#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ + vfs_adjust_stat (s); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Awaiting for any activity on socket. + * + * @param super extra data for SFTP connection + * @param mcerror pointer to the error object + * @return 0 if success, negative value otherwise + */ + +static int +sftpfs_internal_waitsocket (sftpfs_super_t * super, GError ** mcerror) +{ + struct timeval timeout = { 10, 0 }; + fd_set fd; + fd_set *writefd = NULL; + fd_set *readfd = NULL; + int dir, ret; + + mc_return_val_if_error (mcerror, -1); + + FD_ZERO (&fd); + FD_SET (super->socket_handle, &fd); + + /* now make sure we wait in the correct direction */ + dir = libssh2_session_block_directions (super->session); + + if ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) != 0) + readfd = &fd; + + if ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) != 0) + writefd = &fd; + + ret = select (super->socket_handle + 1, readfd, writefd, NULL, &timeout); + if (ret < 0) + { + int my_errno = errno; + + mc_propagate_error (mcerror, my_errno, _("sftp: socket error: %s"), + unix_error_string (my_errno)); + } + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +sftpfs_stat_init (sftpfs_super_t ** super, const vfs_path_element_t ** path_element, + const vfs_path_t * vpath, GError ** mcerror, int stat_type, + LIBSSH2_SFTP_ATTRIBUTES * attrs) +{ + const GString *fixfname; + int res; + + if (!sftpfs_op_init (super, path_element, vpath, mcerror)) + return -1; + + fixfname = sftpfs_fix_filename ((*path_element)->path); + + do + { + res = libssh2_sftp_stat_ex ((*super)->sftp_session, fixfname->str, fixfname->len, + stat_type, attrs); + if (res >= 0) + break; + + if (sftpfs_is_sftp_error ((*super)->sftp_session, res, LIBSSH2_FX_PERMISSION_DENIED)) + return -EACCES; + + if (sftpfs_is_sftp_error ((*super)->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE)) + return -ENOENT; + + if (!sftpfs_waitsocket (*super, res, mcerror)) + return -1; + } + while (res == LIBSSH2_ERROR_EAGAIN); + + return res; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +gboolean +sftpfs_waitsocket (sftpfs_super_t * super, int sftp_res, GError ** mcerror) +{ + if (sftp_res != LIBSSH2_ERROR_EAGAIN) + { + sftpfs_ssherror_to_gliberror (super, sftp_res, mcerror); + return FALSE; + } + + sftpfs_internal_waitsocket (super, mcerror); + + return (mcerror == NULL || *mcerror == NULL); +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +sftpfs_is_sftp_error (LIBSSH2_SFTP * sftp_session, int sftp_res, int sftp_error) +{ + return (sftp_res == LIBSSH2_ERROR_SFTP_PROTOCOL && + libssh2_sftp_last_error (sftp_session) == (unsigned long) sftp_error); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Convert libssh error to GError object. + * + * @param super extra data for SFTP connection + * @param libssh_errno errno from libssh + * @param mcerror pointer to the error object + */ + +void +sftpfs_ssherror_to_gliberror (sftpfs_super_t * super, int libssh_errno, GError ** mcerror) +{ + char *err = NULL; + int err_len; + + mc_return_if_error (mcerror); + + libssh2_session_last_error (super->session, &err, &err_len, 1); + if (libssh_errno == LIBSSH2_ERROR_SFTP_PROTOCOL && super->sftp_session != NULL) + mc_propagate_error (mcerror, libssh_errno, "%s %lu", err, + libssh2_sftp_last_error (super->sftp_session)); + else + mc_propagate_error (mcerror, libssh_errno, "%s", err); + g_free (err); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Fix filename for SFTP operations: add leading slash to file name. + * + * @param file_name file name + * @param length length of returned string + * + * @return pointer to string that contains the file name with leading slash + */ + +const GString * +sftpfs_fix_filename (const char *file_name) +{ + g_string_printf (sftpfs_filename_buffer, "%c%s", PATH_SEP, file_name); + return sftpfs_filename_buffer; +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +sftpfs_op_init (sftpfs_super_t ** super, const vfs_path_element_t ** path_element, + const vfs_path_t * vpath, GError ** mcerror) +{ + struct vfs_s_super *lc_super = NULL; + + mc_return_val_if_error (mcerror, FALSE); + + if (vfs_s_get_path (vpath, &lc_super, 0) == NULL) + return FALSE; + + if (lc_super == NULL) + return FALSE; + + *super = SFTP_SUPER (lc_super); + if ((*super)->sftp_session == NULL) + return FALSE; + + *path_element = vfs_path_get_by_index (vpath, -1); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +sftpfs_attr_to_stat (const LIBSSH2_SFTP_ATTRIBUTES * attrs, struct stat *s) +{ + if ((attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0) + { + s->st_uid = attrs->uid; + s->st_gid = attrs->gid; + } + + if ((attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0) + { + s->st_atime = attrs->atime; + s->st_mtime = attrs->mtime; + s->st_ctime = attrs->mtime; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + s->st_atim.tv_nsec = s->st_mtim.tv_nsec = s->st_ctim.tv_nsec = 0; +#endif + } + + if ((attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) != 0) + { + s->st_size = attrs->filesize; + sftpfs_blksize (s); + } + + if ((attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0) + s->st_mode = attrs->permissions; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Getting information about a symbolic link. + * + * @param vpath path to file, directory or symbolic link + * @param buf buffer for store stat-info + * @param mcerror pointer to error object + * @return 0 if success, negative value otherwise + */ + +int +sftpfs_lstat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror) +{ + sftpfs_super_t *super = NULL; + const vfs_path_element_t *path_element = NULL; + LIBSSH2_SFTP_ATTRIBUTES attrs; + int res; + + res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs); + if (res >= 0) + { + sftpfs_attr_to_stat (&attrs, buf); + res = 0; + } + + return res; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Getting information about a file or directory. + * + * @param vpath path to file or directory + * @param buf buffer for store stat-info + * @param mcerror pointer to error object + * @return 0 if success, negative value otherwise + */ + +int +sftpfs_stat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror) +{ + sftpfs_super_t *super = NULL; + const vfs_path_element_t *path_element = NULL; + LIBSSH2_SFTP_ATTRIBUTES attrs; + int res; + + res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_STAT, &attrs); + if (res >= 0) + { + buf->st_nlink = 1; + sftpfs_attr_to_stat (&attrs, buf); + res = 0; + } + + return res; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * 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 mcerror pointer to error object + * @return 0 if success, negative value otherwise + */ + +int +sftpfs_readlink (const vfs_path_t * vpath, char *buf, size_t size, GError ** mcerror) +{ + sftpfs_super_t *super = NULL; + const vfs_path_element_t *path_element = NULL; + const GString *fixfname; + int res; + + if (!sftpfs_op_init (&super, &path_element, vpath, mcerror)) + return -1; + + fixfname = sftpfs_fix_filename (path_element->path); + + do + { + res = + libssh2_sftp_symlink_ex (super->sftp_session, fixfname->str, fixfname->len, buf, size, + LIBSSH2_SFTP_READLINK); + if (res >= 0) + break; + + if (!sftpfs_waitsocket (super, res, mcerror)) + 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 mcerror pointer to error object + * @return 0 if success, negative value otherwise + */ + +int +sftpfs_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror) +{ + sftpfs_super_t *super = NULL; + const vfs_path_element_t *path_element2 = NULL; + const char *path1; + size_t path1_len; + const GString *ctmp_path; + char *tmp_path; + unsigned int tmp_path_len; + int res; + + if (!sftpfs_op_init (&super, &path_element2, vpath2, mcerror)) + return -1; + + ctmp_path = sftpfs_fix_filename (path_element2->path); + tmp_path = g_strndup (ctmp_path->str, ctmp_path->len); + tmp_path_len = ctmp_path->len; + + path1 = vfs_path_get_last_path_str (vpath1); + path1_len = strlen (path1); + + do + { + res = + libssh2_sftp_symlink_ex (super->sftp_session, path1, path1_len, tmp_path, tmp_path_len, + LIBSSH2_SFTP_SYMLINK); + if (res >= 0) + break; + + if (!sftpfs_waitsocket (super, res, mcerror)) + { + g_free (tmp_path); + return -1; + } + } + while (res == LIBSSH2_ERROR_EAGAIN); + g_free (tmp_path); + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Changes the times of the file. + * + * @param vpath path to file or directory + * @param atime access time + * @param mtime modification time + * @param mcerror pointer to error object + * @return 0 if success, negative value otherwise + */ + +int +sftpfs_utime (const vfs_path_t * vpath, time_t atime, time_t mtime, GError ** mcerror) +{ + sftpfs_super_t *super = NULL; + const vfs_path_element_t *path_element = NULL; + LIBSSH2_SFTP_ATTRIBUTES attrs; + const GString *fixfname; + int res; + + res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs); + if (res < 0) + return res; + + attrs.flags = LIBSSH2_SFTP_ATTR_ACMODTIME; + attrs.atime = atime; + attrs.mtime = mtime; + + fixfname = sftpfs_fix_filename (path_element->path); + + do + { + res = + libssh2_sftp_stat_ex (super->sftp_session, fixfname->str, fixfname->len, + LIBSSH2_SFTP_SETSTAT, &attrs); + if (res >= 0) + break; + + if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE)) + return -ENOENT; + + if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE)) + { + res = 0; /* need something like ftpfs_ignore_chattr_errors */ + break; + } + + if (!sftpfs_waitsocket (super, res, mcerror)) + return -1; + } + while (res == LIBSSH2_ERROR_EAGAIN); + + return res; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Changes the permissions of the file. + * + * @param vpath path to file or directory + * @param mode mode (see man 2 open) + * @param mcerror pointer to error object + * @return 0 if success, negative value otherwise + */ + +int +sftpfs_chmod (const vfs_path_t * vpath, mode_t mode, GError ** mcerror) +{ + sftpfs_super_t *super = NULL; + const vfs_path_element_t *path_element = NULL; + LIBSSH2_SFTP_ATTRIBUTES attrs; + const GString *fixfname; + int res; + + res = sftpfs_stat_init (&super, &path_element, vpath, mcerror, LIBSSH2_SFTP_LSTAT, &attrs); + if (res < 0) + return res; + + attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS; + attrs.permissions = mode; + + fixfname = sftpfs_fix_filename (path_element->path); + + do + { + res = + libssh2_sftp_stat_ex (super->sftp_session, fixfname->str, fixfname->len, + LIBSSH2_SFTP_SETSTAT, &attrs); + if (res >= 0) + break; + + if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_NO_SUCH_FILE)) + return -ENOENT; + + if (sftpfs_is_sftp_error (super->sftp_session, res, LIBSSH2_FX_FAILURE)) + { + res = 0; /* need something like ftpfs_ignore_chattr_errors */ + break; + } + + if (!sftpfs_waitsocket (super, res, mcerror)) + return -1; + } + while (res == LIBSSH2_ERROR_EAGAIN); + + return res; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Delete a name from the file system. + * + * @param vpath path to file or directory + * @param mcerror pointer to error object + * @return 0 if success, negative value otherwise + */ + +int +sftpfs_unlink (const vfs_path_t * vpath, GError ** mcerror) +{ + sftpfs_super_t *super = NULL; + const vfs_path_element_t *path_element = NULL; + const GString *fixfname; + int res; + + if (!sftpfs_op_init (&super, &path_element, vpath, mcerror)) + return -1; + + fixfname = sftpfs_fix_filename (path_element->path); + + do + { + res = libssh2_sftp_unlink_ex (super->sftp_session, fixfname->str, fixfname->len); + if (res >= 0) + break; + + if (!sftpfs_waitsocket (super, res, mcerror)) + 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 mcerror pointer to error object + * @return 0 if success, negative value otherwise + */ + +int +sftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror) +{ + sftpfs_super_t *super = NULL; + const char *path1; + const vfs_path_element_t *path_element2 = NULL; + const GString *ctmp_path; + char *tmp_path; + unsigned int tmp_path_len; + const GString *fixfname; + int res; + + if (!sftpfs_op_init (&super, &path_element2, vpath2, mcerror)) + return -1; + + ctmp_path = sftpfs_fix_filename (path_element2->path); + tmp_path = g_strndup (ctmp_path->str, ctmp_path->len); + tmp_path_len = ctmp_path->len; + + path1 = vfs_path_get_last_path_str (vpath1); + fixfname = sftpfs_fix_filename (path1); + + do + { + res = + libssh2_sftp_rename_ex (super->sftp_session, fixfname->str, fixfname->len, tmp_path, + tmp_path_len, LIBSSH2_SFTP_SYMLINK); + if (res >= 0) + break; + + if (!sftpfs_waitsocket (super, res, mcerror)) + { + g_free (tmp_path); + return -1; + } + } + while (res == LIBSSH2_ERROR_EAGAIN); + g_free (tmp_path); + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/internal.h b/src/vfs/sftpfs/internal.h new file mode 100644 index 0000000..15c547d --- /dev/null +++ b/src/vfs/sftpfs/internal.h @@ -0,0 +1,114 @@ +/** + * \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 + +/* LIBSSH2_INVALID_SOCKET is defined in libssh2 >= 1.4.1 */ +#ifndef LIBSSH2_INVALID_SOCKET +#define LIBSSH2_INVALID_SOCKET -1 +#endif + +#define SFTP_SUPER(super) ((sftpfs_super_t *) (super)) + +/*** 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 +{ + struct vfs_s_super base; + + sftpfs_auth_type_t auth_type; + sftpfs_auth_type_t config_auth_type; + + LIBSSH2_KNOWNHOSTS *known_hosts; + char *known_hosts_file; + + LIBSSH2_SESSION *session; + LIBSSH2_SFTP *sftp_session; + + LIBSSH2_AGENT *agent; + + char *pubkey; + char *privkey; + + int socket_handle; + const char *ip_address; + vfs_path_element_t *original_connection_info; +} sftpfs_super_t; + +/*** global variables defined in .c file *********************************************************/ + +extern GString *sftpfs_filename_buffer; +extern struct vfs_s_subclass sftpfs_subclass; +extern struct vfs_class *vfs_sftpfs_ops; + +/*** declarations of public functions ************************************************************/ + +void sftpfs_init_config_variables_patterns (void); +void sftpfs_deinit_config_variables_patterns (void); + +gboolean sftpfs_is_sftp_error (LIBSSH2_SFTP * sftp_session, int sftp_res, int sftp_error); +void sftpfs_ssherror_to_gliberror (sftpfs_super_t * super, int libssh_errno, GError ** mcerror); +gboolean sftpfs_waitsocket (sftpfs_super_t * super, int sftp_res, GError ** mcerror); + +const GString *sftpfs_fix_filename (const char *file_name); + +gboolean sftpfs_op_init (sftpfs_super_t ** super, const vfs_path_element_t ** path_element, + const vfs_path_t * vpath, GError ** mcerror); + +void sftpfs_attr_to_stat (const LIBSSH2_SFTP_ATTRIBUTES * attrs, struct stat *s); +int sftpfs_lstat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror); +int sftpfs_stat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror); +int sftpfs_readlink (const vfs_path_t * vpath, char *buf, size_t size, GError ** mcerror); +int sftpfs_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror); +int sftpfs_utime (const vfs_path_t * vpath, time_t atime, time_t mtime, GError ** mcerror); +int sftpfs_chmod (const vfs_path_t * vpath, mode_t mode, GError ** mcerror); +int sftpfs_unlink (const vfs_path_t * vpath, GError ** mcerror); +int sftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2, GError ** mcerror); + +void sftpfs_fill_connection_data_from_config (struct vfs_s_super *super, GError ** mcerror); +int sftpfs_open_connection (struct vfs_s_super *super, GError ** mcerror); +void sftpfs_close_connection (struct vfs_s_super *super, const char *shutdown_message, + GError ** mcerror); + +vfs_file_handler_t *sftpfs_fh_new (struct vfs_s_inode *ino, gboolean changed); + +void *sftpfs_opendir (const vfs_path_t * vpath, GError ** mcerror); +struct vfs_dirent *sftpfs_readdir (void *data, GError ** mcerror); +int sftpfs_closedir (void *data, GError ** mcerror); +int sftpfs_mkdir (const vfs_path_t * vpath, mode_t mode, GError ** mcerror); +int sftpfs_rmdir (const vfs_path_t * vpath, GError ** mcerror); + +gboolean sftpfs_open_file (vfs_file_handler_t * fh, int flags, mode_t mode, GError ** mcerror); +ssize_t sftpfs_read_file (vfs_file_handler_t * fh, char *buffer, size_t count, GError ** mcerror); +ssize_t sftpfs_write_file (vfs_file_handler_t * fh, const char *buffer, size_t count, + GError ** mcerror); +int sftpfs_close_file (vfs_file_handler_t * fh, GError ** mcerror); +int sftpfs_fstat (void *data, struct stat *buf, GError ** mcerror); +off_t sftpfs_lseek (vfs_file_handler_t * fh, off_t offset, int whence, GError ** mcerror); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__VFS_SFTPFS_INTERNAL_H */ diff --git a/src/vfs/sftpfs/sftpfs.c b/src/vfs/sftpfs/sftpfs.c new file mode 100644 index 0000000..f2cc592 --- /dev/null +++ b/src/vfs/sftpfs/sftpfs.c @@ -0,0 +1,866 @@ +/* Virtual File System: SFTP file system. + The interface function + + Copyright (C) 2011-2023 + Free Software Foundation, Inc. + + Written by: + Ilia Maslakov <il.smind@gmail.com>, 2011 + Slava Zanko <slavazanko@gmail.com>, 2011-2013 + Andrew Borodin <aborodin@vmail.ru>, 2021-2022 + + 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 <stdlib.h> +#include <string.h> /* memset() */ + +#include "lib/global.h" +#include "lib/vfs/netutil.h" +#include "lib/vfs/utilvfs.h" +#include "lib/vfs/gc.h" +#include "lib/widget.h" +#include "lib/tty/tty.h" /* tty_enable_interrupt_key () */ + +#include "internal.h" + +#include "sftpfs.h" + +/*** global variables ****************************************************************************/ + +struct vfs_s_subclass sftpfs_subclass; +struct vfs_class *vfs_sftpfs_ops = VFS_CLASS (&sftpfs_subclass); /* used in file.c */ + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** 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 *fh; + struct vfs_class *me; + struct vfs_s_super *super; + const char *path_super; + struct vfs_s_inode *path_inode; + GError *mcerror = NULL; + gboolean is_changed = FALSE; + + path_super = vfs_s_get_path (vpath, &super, 0); + if (path_super == NULL) + return NULL; + + me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); + + path_inode = vfs_s_find_inode (me, super, path_super, LINK_FOLLOW, FL_NONE); + if (path_inode != NULL && ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))) + { + me->verrno = EEXIST; + return NULL; + } + + if (path_inode == NULL) + { + char *name; + struct vfs_s_entry *ent; + struct vfs_s_inode *dir; + + name = g_path_get_dirname (path_super); + dir = vfs_s_find_inode (me, super, name, LINK_FOLLOW, FL_DIR); + g_free (name); + if (dir == NULL) + return NULL; + + name = g_path_get_basename (path_super); + ent = vfs_s_generate_entry (me, name, dir, 0755); + g_free (name); + path_inode = ent->ino; + vfs_s_insert_entry (me, dir, ent); + is_changed = TRUE; + } + + if (S_ISDIR (path_inode->st.st_mode)) + { + me->verrno = EISDIR; + return NULL; + } + + fh = sftpfs_fh_new (path_inode, is_changed); + + if (!sftpfs_open_file (fh, flags, mode, &mcerror)) + { + mc_error_message (&mcerror, NULL); + g_free (fh); + return NULL; + } + + vfs_rmstamp (me, (vfsid) super); + super->fd_usage++; + fh->ino->st.st_nlink++; + return fh; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * 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 *mcerror = NULL; + void *ret_value; + + /* reset interrupt flag */ + tty_got_interrupt (); + + ret_value = sftpfs_opendir (vpath, &mcerror); + mc_error_message (&mcerror, NULL); + return ret_value; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for reading directory entry. + * + * @param data directory data handler + * @return information about direntry if success, NULL otherwise + */ + +static struct vfs_dirent * +sftpfs_cb_readdir (void *data) +{ + GError *mcerror = NULL; + struct vfs_dirent *sftpfs_dirent; + + if (tty_got_interrupt ()) + { + tty_disable_interrupt_key (); + return NULL; + } + + sftpfs_dirent = sftpfs_readdir (data, &mcerror); + if (!mc_error_message (&mcerror, NULL)) + { + if (sftpfs_dirent != NULL) + vfs_print_message (_("sftp: (Ctrl-G break) Listing... %s"), sftpfs_dirent->d_name); + else + vfs_print_message ("%s", _("sftp: Listing done.")); + } + + return sftpfs_dirent; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for closing directory. + * + * @param data directory data handler + * @return 0 if success, negative value otherwise + */ + +static int +sftpfs_cb_closedir (void *data) +{ + int rc; + GError *mcerror = NULL; + + rc = sftpfs_closedir (data, &mcerror); + mc_error_message (&mcerror, NULL); + return rc; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for lstat VFS-function. + * + * @param vpath path to file or directory + * @param buf buffer for store stat-info + * @return 0 if success, negative value otherwise + */ + +static int +sftpfs_cb_lstat (const vfs_path_t * vpath, struct stat *buf) +{ + int rc; + GError *mcerror = NULL; + + rc = sftpfs_lstat (vpath, buf, &mcerror); + mc_error_message (&mcerror, NULL); + return rc; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for stat VFS-function. + * + * @param vpath path to file or directory + * @param buf buffer for store stat-info + * @return 0 if success, negative value otherwise + */ + +static int +sftpfs_cb_stat (const vfs_path_t * vpath, struct stat *buf) +{ + int rc; + GError *mcerror = NULL; + + rc = sftpfs_stat (vpath, buf, &mcerror); + mc_error_message (&mcerror, NULL); + return rc; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for fstat VFS-function. + * + * @param data file data handler + * @param buf buffer for store stat-info + * @return 0 if success, negative value otherwise + */ + +static int +sftpfs_cb_fstat (void *data, struct stat *buf) +{ + int rc; + GError *mcerror = NULL; + + rc = sftpfs_fstat (data, buf, &mcerror); + mc_error_message (&mcerror, NULL); + 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 success, negative value otherwise + */ + +static int +sftpfs_cb_readlink (const vfs_path_t * vpath, char *buf, size_t size) +{ + int rc; + GError *mcerror = NULL; + + rc = sftpfs_readlink (vpath, buf, size, &mcerror); + mc_error_message (&mcerror, NULL); + return rc; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for utime VFS-function. + * + * @param vpath path to file or directory + * @param times access and modification time to set + * @return 0 if success, negative value otherwise + */ + +static int +sftpfs_cb_utime (const vfs_path_t * vpath, mc_timesbuf_t * times) +{ + int rc; + GError *mcerror = NULL; + +#ifdef HAVE_UTIMENSAT + time_t atime = (*times)[0].tv_sec; + time_t mtime = (*times)[1].tv_sec; +#else + time_t atime = times->actime; + time_t mtime = times->modtime; +#endif + + rc = sftpfs_utime (vpath, atime, mtime, &mcerror); + mc_error_message (&mcerror, NULL); + return rc; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for symlink VFS-function. + * + * @param vpath1 path to file or directory + * @param vpath2 path to symlink + * @return 0 if success, negative value otherwise + */ + +static int +sftpfs_cb_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2) +{ + int rc; + GError *mcerror = NULL; + + rc = sftpfs_symlink (vpath1, vpath2, &mcerror); + mc_error_message (&mcerror, NULL); + 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 success, negative value otherwise + */ + +static ssize_t +sftpfs_cb_read (void *data, char *buffer, size_t count) +{ + int rc; + GError *mcerror = NULL; + vfs_file_handler_t *fh = VFS_FILE_HANDLER (data); + + if (tty_got_interrupt ()) + { + tty_disable_interrupt_key (); + return 0; + } + + rc = sftpfs_read_file (fh, buffer, count, &mcerror); + mc_error_message (&mcerror, NULL); + return rc; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for writing file content. + * + * @param data file data handler + * @param buf buffer for data + * @param count data size + * @return 0 if success, negative value otherwise + */ + +static ssize_t +sftpfs_cb_write (void *data, const char *buf, size_t nbyte) +{ + int rc; + GError *mcerror = NULL; + vfs_file_handler_t *fh = VFS_FILE_HANDLER (data); + + rc = sftpfs_write_file (fh, buf, nbyte, &mcerror); + mc_error_message (&mcerror, NULL); + return rc; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for close file. + * + * @param data file data handler + * @return 0 if success, negative value otherwise + */ + +static int +sftpfs_cb_close (void *data) +{ + int rc; + GError *mcerror = NULL; + struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (data); + vfs_file_handler_t *fh = VFS_FILE_HANDLER (data); + + super->fd_usage--; + if (super->fd_usage == 0) + vfs_stamp_create (vfs_sftpfs_ops, super); + + rc = sftpfs_close_file (fh, &mcerror); + mc_error_message (&mcerror, NULL); + + if (fh->handle != -1) + close (fh->handle); + + vfs_s_free_inode (vfs_sftpfs_ops, fh->ino); + + return rc; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for chmod VFS-function. + * + * @param vpath path to file or directory + * @param mode mode (see man 2 open) + * @return 0 if success, negative value otherwise + */ + +static int +sftpfs_cb_chmod (const vfs_path_t * vpath, mode_t mode) +{ + int rc; + GError *mcerror = NULL; + + rc = sftpfs_chmod (vpath, mode, &mcerror); + mc_error_message (&mcerror, NULL); + return rc; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for mkdir VFS-function. + * + * @param vpath path directory + * @param mode mode (see man 2 open) + * @return 0 if success, negative value otherwise + */ + +static int +sftpfs_cb_mkdir (const vfs_path_t * vpath, mode_t mode) +{ + int rc; + GError *mcerror = NULL; + + rc = sftpfs_mkdir (vpath, mode, &mcerror); + mc_error_message (&mcerror, NULL); + return rc; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for rmdir VFS-function. + * + * @param vpath path directory + * @return 0 if success, negative value otherwise + */ + +static int +sftpfs_cb_rmdir (const vfs_path_t * vpath) +{ + int rc; + GError *mcerror = NULL; + + rc = sftpfs_rmdir (vpath, &mcerror); + mc_error_message (&mcerror, NULL); + 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 success, negative value otherwise + */ + +static off_t +sftpfs_cb_lseek (void *data, off_t offset, int whence) +{ + off_t ret_offset; + vfs_file_handler_t *fh = VFS_FILE_HANDLER (data); + GError *mcerror = NULL; + + ret_offset = sftpfs_lseek (fh, offset, whence, &mcerror); + mc_error_message (&mcerror, NULL); + return ret_offset; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for unlink VFS-function. + * + * @param vpath path to file or directory + * @return 0 if success, negative value otherwise + */ + +static int +sftpfs_cb_unlink (const vfs_path_t * vpath) +{ + int rc; + GError *mcerror = NULL; + + rc = sftpfs_unlink (vpath, &mcerror); + mc_error_message (&mcerror, NULL); + 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 success, negative value otherwise + */ + +static int +sftpfs_cb_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2) +{ + int rc; + GError *mcerror = NULL; + + rc = sftpfs_rename (vpath1, vpath2, &mcerror); + mc_error_message (&mcerror, NULL); + 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; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for fill_names VFS function. + * Add SFTP connections to the 'Active VFS connections' list + * + * @param me unused + * @param func callback function for adding SFTP-connection to list of active connections + */ + +static void +sftpfs_cb_fill_names (struct vfs_class *me, fill_names_f func) +{ + GList *iter; + + (void) me; + + for (iter = sftpfs_subclass.supers; iter != NULL; iter = g_list_next (iter)) + { + const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data; + GString *name; + + name = vfs_path_element_build_pretty_path_str (super->path_element); + + func (name->str); + g_string_free (name, TRUE); + } +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for checking if connection is equal to existing connection. + * + * @param vpath_element path element with connection data + * @param super data with exists connection + * @param vpath unused + * @param cookie unused + * @return TRUE if connections is equal, FALSE otherwise + */ + +static gboolean +sftpfs_archive_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *super, + const vfs_path_t * vpath, void *cookie) +{ + int result; + vfs_path_element_t *orig_connect_info; + + (void) vpath; + (void) cookie; + + orig_connect_info = SFTP_SUPER (super)->original_connection_info; + + result = ((g_strcmp0 (vpath_element->host, orig_connect_info->host) == 0) + && (g_strcmp0 (vpath_element->user, orig_connect_info->user) == 0) + && (vpath_element->port == orig_connect_info->port)); + + return result; +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct vfs_s_super * +sftpfs_new_archive (struct vfs_class *me) +{ + sftpfs_super_t *arch; + + arch = g_new0 (sftpfs_super_t, 1); + arch->base.me = me; + arch->base.name = g_strdup (PATH_SEP_STR); + arch->auth_type = NONE; + arch->config_auth_type = NONE; + arch->socket_handle = LIBSSH2_INVALID_SOCKET; + + return VFS_SUPER (arch); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for opening new connection. + * + * @param super connection data + * @param vpath unused + * @param vpath_element path element with connection data + * @return 0 if success, -1 otherwise + */ + +static int +sftpfs_open_archive (struct vfs_s_super *super, const vfs_path_t * vpath, + const vfs_path_element_t * vpath_element) +{ + GError *mcerror = NULL; + sftpfs_super_t *sftpfs_super = SFTP_SUPER (super); + int ret_value; + + (void) vpath; + + if (vpath_element->host == NULL || *vpath_element->host == '\0') + { + vfs_print_message ("%s", _("sftp: Invalid host name.")); + vpath_element->class->verrno = EPERM; + return -1; + } + + sftpfs_super->original_connection_info = vfs_path_element_clone (vpath_element); + super->path_element = vfs_path_element_clone (vpath_element); + + sftpfs_fill_connection_data_from_config (super, &mcerror); + if (mc_error_message (&mcerror, &ret_value)) + { + vpath_element->class->verrno = ret_value; + return -1; + } + + 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, &mcerror); + mc_error_message (&mcerror, NULL); + return ret_value; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Callback for closing connection. + * + * @param me unused + * @param super connection data + */ + +static void +sftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super) +{ + GError *mcerror = NULL; + + (void) me; + + sftpfs_close_connection (super, "Normal Shutdown", &mcerror); + + vfs_path_element_free (SFTP_SUPER (super)->original_connection_info); + + mc_error_message (&mcerror, NULL); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * 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, const char *remote_path) +{ + (void) me; + (void) dir; + (void) remote_path; + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ +/** + * Initialization of SFTP Virtual File Sysytem. + */ + +void +vfs_init_sftpfs (void) +{ + tcp_init (); + + vfs_init_subclass (&sftpfs_subclass, "sftpfs", VFSF_NOLINKS | VFSF_REMOTE, "sftp"); + + vfs_sftpfs_ops->init = sftpfs_cb_init; + vfs_sftpfs_ops->done = sftpfs_cb_done; + + vfs_sftpfs_ops->fill_names = sftpfs_cb_fill_names; + + vfs_sftpfs_ops->opendir = sftpfs_cb_opendir; + vfs_sftpfs_ops->readdir = sftpfs_cb_readdir; + vfs_sftpfs_ops->closedir = sftpfs_cb_closedir; + vfs_sftpfs_ops->mkdir = sftpfs_cb_mkdir; + vfs_sftpfs_ops->rmdir = sftpfs_cb_rmdir; + + vfs_sftpfs_ops->stat = sftpfs_cb_stat; + vfs_sftpfs_ops->lstat = sftpfs_cb_lstat; + vfs_sftpfs_ops->fstat = sftpfs_cb_fstat; + vfs_sftpfs_ops->readlink = sftpfs_cb_readlink; + vfs_sftpfs_ops->symlink = sftpfs_cb_symlink; + vfs_sftpfs_ops->link = sftpfs_cb_link; + vfs_sftpfs_ops->utime = sftpfs_cb_utime; + vfs_sftpfs_ops->mknod = sftpfs_cb_mknod; + vfs_sftpfs_ops->chown = sftpfs_cb_chown; + vfs_sftpfs_ops->chmod = sftpfs_cb_chmod; + + vfs_sftpfs_ops->open = sftpfs_cb_open; + vfs_sftpfs_ops->read = sftpfs_cb_read; + vfs_sftpfs_ops->write = sftpfs_cb_write; + vfs_sftpfs_ops->close = sftpfs_cb_close; + vfs_sftpfs_ops->lseek = sftpfs_cb_lseek; + vfs_sftpfs_ops->unlink = sftpfs_cb_unlink; + vfs_sftpfs_ops->rename = sftpfs_cb_rename; + vfs_sftpfs_ops->ferrno = sftpfs_cb_errno; + + sftpfs_subclass.archive_same = sftpfs_archive_same; + sftpfs_subclass.new_archive = sftpfs_new_archive; + sftpfs_subclass.open_archive = sftpfs_open_archive; + sftpfs_subclass.free_archive = sftpfs_free_archive; + sftpfs_subclass.fh_new = sftpfs_fh_new; + sftpfs_subclass.dir_load = sftpfs_cb_dir_load; + + vfs_register_class (vfs_sftpfs_ops); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/sftpfs.h b/src/vfs/sftpfs/sftpfs.h new file mode 100644 index 0000000..d3a1935 --- /dev/null +++ b/src/vfs/sftpfs/sftpfs.h @@ -0,0 +1,23 @@ +/** + * \file + * \brief Header: SFTP FS + */ + +#ifndef MC__VFS_SFTPFS_H +#define MC__VFS_SFTPFS_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 vfs_init_sftpfs (void); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__VFS_SFTPFS_H */ |