diff options
Diffstat (limited to 'src/vfs')
111 files changed, 35693 insertions, 0 deletions
diff --git a/src/vfs/Makefile.am b/src/vfs/Makefile.am new file mode 100644 index 0000000..1441953 --- /dev/null +++ b/src/vfs/Makefile.am @@ -0,0 +1,47 @@ +noinst_LTLIBRARIES = libmc-vfs.la + +AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) +libmc_vfs_la_SOURCES = plugins_init.c plugins_init.h + +SUBDIRS = local +libmc_vfs_la_LIBADD = local/libvfs-local.la + +if ENABLE_VFS_CPIO +SUBDIRS += cpio +libmc_vfs_la_LIBADD += cpio/libvfs-cpio.la +endif + +if ENABLE_VFS_EXTFS +SUBDIRS += extfs +libmc_vfs_la_LIBADD += extfs/libvfs-extfs.la +endif + +if ENABLE_VFS_FISH +SUBDIRS += fish +libmc_vfs_la_LIBADD += fish/libvfs-fish.la +endif + +if ENABLE_VFS_FTP +SUBDIRS += ftpfs +libmc_vfs_la_LIBADD += ftpfs/libvfs-ftpfs.la +endif + +if ENABLE_VFS_SFTP +SUBDIRS += sftpfs +libmc_vfs_la_LIBADD += sftpfs/libvfs-sftpfs.la +endif + +if ENABLE_VFS_SFS +SUBDIRS += sfs +libmc_vfs_la_LIBADD += sfs/libvfs-sfs.la +endif + +if ENABLE_VFS_TAR +SUBDIRS += tar +libmc_vfs_la_LIBADD += tar/libvfs-tar.la +endif + +if ENABLE_VFS_UNDELFS +SUBDIRS += undelfs +libmc_vfs_la_LIBADD += undelfs/libvfs-undelfs.la +endif diff --git a/src/vfs/Makefile.in b/src/vfs/Makefile.in new file mode 100644 index 0000000..a245efe --- /dev/null +++ b/src/vfs/Makefile.in @@ -0,0 +1,875 @@ +# 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@ +@ENABLE_VFS_CPIO_TRUE@am__append_1 = cpio +@ENABLE_VFS_CPIO_TRUE@am__append_2 = cpio/libvfs-cpio.la +@ENABLE_VFS_EXTFS_TRUE@am__append_3 = extfs +@ENABLE_VFS_EXTFS_TRUE@am__append_4 = extfs/libvfs-extfs.la +@ENABLE_VFS_FISH_TRUE@am__append_5 = fish +@ENABLE_VFS_FISH_TRUE@am__append_6 = fish/libvfs-fish.la +@ENABLE_VFS_FTP_TRUE@am__append_7 = ftpfs +@ENABLE_VFS_FTP_TRUE@am__append_8 = ftpfs/libvfs-ftpfs.la +@ENABLE_VFS_SFTP_TRUE@am__append_9 = sftpfs +@ENABLE_VFS_SFTP_TRUE@am__append_10 = sftpfs/libvfs-sftpfs.la +@ENABLE_VFS_SFS_TRUE@am__append_11 = sfs +@ENABLE_VFS_SFS_TRUE@am__append_12 = sfs/libvfs-sfs.la +@ENABLE_VFS_TAR_TRUE@am__append_13 = tar +@ENABLE_VFS_TAR_TRUE@am__append_14 = tar/libvfs-tar.la +@ENABLE_VFS_UNDELFS_TRUE@am__append_15 = undelfs +@ENABLE_VFS_UNDELFS_TRUE@am__append_16 = undelfs/libvfs-undelfs.la +subdir = src/vfs +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) +libmc_vfs_la_DEPENDENCIES = local/libvfs-local.la $(am__append_2) \ + $(am__append_4) $(am__append_6) $(am__append_8) \ + $(am__append_10) $(am__append_12) $(am__append_14) \ + $(am__append_16) +am_libmc_vfs_la_OBJECTS = plugins_init.lo +libmc_vfs_la_OBJECTS = $(am_libmc_vfs_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)/plugins_init.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 = $(libmc_vfs_la_SOURCES) +DIST_SOURCES = $(libmc_vfs_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +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)` +DIST_SUBDIRS = local cpio extfs fish ftpfs sftpfs sfs tar undelfs +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/config/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +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@ +noinst_LTLIBRARIES = libmc-vfs.la +AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) +libmc_vfs_la_SOURCES = plugins_init.c plugins_init.h +SUBDIRS = local $(am__append_1) $(am__append_3) $(am__append_5) \ + $(am__append_7) $(am__append_9) $(am__append_11) \ + $(am__append_13) $(am__append_15) +libmc_vfs_la_LIBADD = local/libvfs-local.la $(am__append_2) \ + $(am__append_4) $(am__append_6) $(am__append_8) \ + $(am__append_10) $(am__append_12) $(am__append_14) \ + $(am__append_16) +all: all-recursive + +.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/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/vfs/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}; \ + } + +libmc-vfs.la: $(libmc_vfs_la_OBJECTS) $(libmc_vfs_la_DEPENDENCIES) $(EXTRA_libmc_vfs_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libmc_vfs_la_OBJECTS) $(libmc_vfs_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugins_init.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 + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(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-recursive + +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-recursive + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +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-recursive + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/plugins_init.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/plugins_init.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) 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 \ + installdirs-am 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/cpio/Makefile.am b/src/vfs/cpio/Makefile.am new file mode 100644 index 0000000..a7806f8 --- /dev/null +++ b/src/vfs/cpio/Makefile.am @@ -0,0 +1,7 @@ + +AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) + +noinst_LTLIBRARIES = libvfs-cpio.la + +libvfs_cpio_la_SOURCES = \ + cpio.c cpio.h diff --git a/src/vfs/cpio/Makefile.in b/src/vfs/cpio/Makefile.in new file mode 100644 index 0000000..8534a52 --- /dev/null +++ b/src/vfs/cpio/Makefile.in @@ -0,0 +1,735 @@ +# 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/cpio +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_cpio_la_LIBADD = +am_libvfs_cpio_la_OBJECTS = cpio.lo +libvfs_cpio_la_OBJECTS = $(am_libvfs_cpio_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)/cpio.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_cpio_la_SOURCES) +DIST_SOURCES = $(libvfs_cpio_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) +noinst_LTLIBRARIES = libvfs-cpio.la +libvfs_cpio_la_SOURCES = \ + cpio.c cpio.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/cpio/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/vfs/cpio/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-cpio.la: $(libvfs_cpio_la_OBJECTS) $(libvfs_cpio_la_DEPENDENCIES) $(EXTRA_libvfs_cpio_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libvfs_cpio_la_OBJECTS) $(libvfs_cpio_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpio.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)/cpio.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)/cpio.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/cpio/cpio.c b/src/vfs/cpio/cpio.c new file mode 100644 index 0000000..447d1f6 --- /dev/null +++ b/src/vfs/cpio/cpio.c @@ -0,0 +1,905 @@ +/* + Virtual File System: GNU Tar file system. + + Copyright (C) 2000-2023 + Free Software Foundation, Inc. + + Written by: + Jan Hudec, 2000 + Slava Zanko <slavazanko@gmail.com>, 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/>. + */ + +/** \file + * \brief Source: Virtual File System: GNU Tar file system. + * \author Jan Hudec + * \date 2000 + */ + +#include <config.h> + +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "lib/global.h" +#include "lib/unixcompat.h" +#include "lib/util.h" +#include "lib/widget.h" /* message() */ + +#include "lib/vfs/vfs.h" +#include "lib/vfs/utilvfs.h" +#include "lib/vfs/xdirentry.h" +#include "lib/vfs/gc.h" /* vfs_rmstamp */ + +#include "cpio.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#define CPIO_SUPER(super) ((cpio_super_t *) (super)) + +#define CPIO_POS(super) cpio_position +/* If some time reentrancy should be needed change it to */ +/* #define CPIO_POS(super) (super)->u.arch.fd */ + +#define CPIO_SEEK_SET(super, where) mc_lseek (CPIO_SUPER(super)->fd, CPIO_POS(super) = (where), SEEK_SET) +#define CPIO_SEEK_CUR(super, where) mc_lseek (CPIO_SUPER(super)->fd, CPIO_POS(super) += (where), SEEK_SET) + +#define MAGIC_LENGTH (6) /* How many bytes we have to read ahead */ +#define SEEKBACK CPIO_SEEK_CUR(super, ptr - top) +#define RETURN(x) return (CPIO_SUPER(super)->type = (x)) +#define TYPEIS(x) ((CPIO_SUPER(super)->type == CPIO_UNKNOWN) || (CPIO_SUPER(super)->type == (x))) + +#define HEAD_LENGTH (26) + +/*** file scope type declarations ****************************************************************/ + +enum +{ + STATUS_START, + STATUS_OK, + STATUS_TRAIL, + STATUS_FAIL, + STATUS_EOF +}; + +enum +{ + CPIO_UNKNOWN = 0, /* Not determined yet */ + CPIO_BIN, /* Binary format */ + CPIO_BINRE, /* Binary format, reverse endianness */ + CPIO_OLDC, /* Old ASCII format */ + CPIO_NEWC, /* New ASCII format */ + CPIO_CRC /* New ASCII format + CRC */ +}; + +struct old_cpio_header +{ + unsigned short c_magic; + short c_dev; + unsigned short c_ino; + unsigned short c_mode; + unsigned short c_uid; + unsigned short c_gid; + unsigned short c_nlink; + short c_rdev; + unsigned short c_mtimes[2]; + unsigned short c_namesize; + unsigned short c_filesizes[2]; +}; + +struct new_cpio_header +{ + unsigned short c_magic; + unsigned long c_ino; + unsigned long c_mode; + unsigned long c_uid; + unsigned long c_gid; + unsigned long c_nlink; + unsigned long c_mtime; + unsigned long c_filesize; + long c_dev; + long c_devmin; + long c_rdev; + long c_rdevmin; + unsigned long c_namesize; + unsigned long c_chksum; +}; + +typedef struct +{ + unsigned long inumber; + dev_t device; + struct vfs_s_inode *inode; +} defer_inode; + +typedef struct +{ + struct vfs_s_super base; /* base class */ + + int fd; + struct stat st; + int type; /* Type of the archive */ + GSList *deferred; /* List of inodes for which another entries may appear */ +} cpio_super_t; + +/*** forward declarations (file scope functions) *************************************************/ + +static ssize_t cpio_find_head (struct vfs_class *me, struct vfs_s_super *super); +static ssize_t cpio_read_bin_head (struct vfs_class *me, struct vfs_s_super *super); +static ssize_t cpio_read_oldc_head (struct vfs_class *me, struct vfs_s_super *super); +static ssize_t cpio_read_crc_head (struct vfs_class *me, struct vfs_s_super *super); + +/*** file scope variables ************************************************************************/ + +static struct vfs_s_subclass cpio_subclass; +static struct vfs_class *vfs_cpiofs_ops = VFS_CLASS (&cpio_subclass); + +static off_t cpio_position; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static int +cpio_defer_find (const void *a, const void *b) +{ + const defer_inode *a1 = (const defer_inode *) a; + const defer_inode *b1 = (const defer_inode *) b; + + return (a1->inumber == b1->inumber && a1->device == b1->device) ? 0 : 1; +} + +/* --------------------------------------------------------------------------------------------- */ + +static ssize_t +cpio_skip_padding (struct vfs_s_super *super) +{ + switch (CPIO_SUPER (super)->type) + { + case CPIO_BIN: + case CPIO_BINRE: + return CPIO_SEEK_CUR (super, (2 - (CPIO_POS (super) % 2)) % 2); + case CPIO_NEWC: + case CPIO_CRC: + return CPIO_SEEK_CUR (super, (4 - (CPIO_POS (super) % 4)) % 4); + case CPIO_OLDC: + return CPIO_POS (super); + default: + g_assert_not_reached (); + return 42; /* & the compiler is happy :-) */ + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct vfs_s_super * +cpio_new_archive (struct vfs_class *me) +{ + cpio_super_t *arch; + + arch = g_new0 (cpio_super_t, 1); + arch->base.me = me; + arch->fd = -1; /* for now */ + arch->type = CPIO_UNKNOWN; + + return VFS_SUPER (arch); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +cpio_free_archive (struct vfs_class *me, struct vfs_s_super *super) +{ + cpio_super_t *arch = CPIO_SUPER (super); + + (void) me; + + if (arch->fd != -1) + { + mc_close (arch->fd); + arch->fd = -1; + } + + g_clear_slist (&arch->deferred, g_free); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +cpio_open_cpio_file (struct vfs_class *me, struct vfs_s_super *super, const vfs_path_t * vpath) +{ + int fd, type; + cpio_super_t *arch; + mode_t mode; + struct vfs_s_inode *root; + + fd = mc_open (vpath, O_RDONLY); + if (fd == -1) + { + message (D_ERROR, MSG_ERROR, _("Cannot open cpio archive\n%s"), vfs_path_as_str (vpath)); + return -1; + } + + super->name = g_strdup (vfs_path_as_str (vpath)); + arch = CPIO_SUPER (super); + mc_stat (vpath, &arch->st); + + type = get_compression_type (fd, super->name); + if (type == COMPRESSION_NONE) + mc_lseek (fd, 0, SEEK_SET); + else + { + char *s; + vfs_path_t *tmp_vpath; + + mc_close (fd); + s = g_strconcat (super->name, decompress_extension (type), (char *) NULL); + tmp_vpath = vfs_path_from_str_flags (s, VPF_NO_CANON); + fd = mc_open (tmp_vpath, O_RDONLY); + vfs_path_free (tmp_vpath, TRUE); + if (fd == -1) + { + message (D_ERROR, MSG_ERROR, _("Cannot open cpio archive\n%s"), s); + g_free (s); + MC_PTR_FREE (super->name); + return -1; + } + g_free (s); + } + + arch->fd = fd; + mode = arch->st.st_mode & 07777; + mode |= (mode & 0444) >> 2; /* set eXec where Read is */ + mode |= S_IFDIR; + + root = vfs_s_new_inode (me, super, &arch->st); + root->st.st_mode = mode; + root->data_offset = -1; + root->st.st_nlink++; + root->st.st_dev = VFS_SUBCLASS (me)->rdev++; + + super->root = root; + + CPIO_SEEK_SET (super, 0); + + return fd; +} + +/* --------------------------------------------------------------------------------------------- */ + +static ssize_t +cpio_read_head (struct vfs_class *me, struct vfs_s_super *super) +{ + switch (cpio_find_head (me, super)) + { + case CPIO_UNKNOWN: + return -1; + case CPIO_BIN: + case CPIO_BINRE: + return cpio_read_bin_head (me, super); + case CPIO_OLDC: + return cpio_read_oldc_head (me, super); + case CPIO_NEWC: + case CPIO_CRC: + return cpio_read_crc_head (me, super); + default: + g_assert_not_reached (); + return 42; /* & the compiler is happy :-) */ + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static ssize_t +cpio_find_head (struct vfs_class *me, struct vfs_s_super *super) +{ + cpio_super_t *arch = CPIO_SUPER (super); + char buf[BUF_SMALL * 2]; + ssize_t ptr = 0; + ssize_t top; + ssize_t tmp; + + top = mc_read (arch->fd, buf, sizeof (buf)); + if (top > 0) + CPIO_POS (super) += top; + + while (TRUE) + { + if (ptr + MAGIC_LENGTH >= top) + { + if (top > (ssize_t) (sizeof (buf) / 2)) + { + memmove (buf, buf + top - sizeof (buf) / 2, sizeof (buf) / 2); + ptr -= top - sizeof (buf) / 2; + top = sizeof (buf) / 2; + } + tmp = mc_read (arch->fd, buf, top); + if (tmp == 0 || tmp == -1) + { + message (D_ERROR, MSG_ERROR, _("Premature end of cpio archive\n%s"), super->name); + cpio_free_archive (me, super); + return CPIO_UNKNOWN; + } + top += tmp; + } + if (TYPEIS (CPIO_BIN) && ((*(unsigned short *) (buf + ptr)) == 070707)) + { + SEEKBACK; + RETURN (CPIO_BIN); + } + else if (TYPEIS (CPIO_BINRE) + && ((*(unsigned short *) (buf + ptr)) == GUINT16_SWAP_LE_BE_CONSTANT (070707))) + { + SEEKBACK; + RETURN (CPIO_BINRE); + } + else if (TYPEIS (CPIO_OLDC) && (strncmp (buf + ptr, "070707", 6) == 0)) + { + SEEKBACK; + RETURN (CPIO_OLDC); + } + else if (TYPEIS (CPIO_NEWC) && (strncmp (buf + ptr, "070701", 6) == 0)) + { + SEEKBACK; + RETURN (CPIO_NEWC); + } + else if (TYPEIS (CPIO_CRC) && (strncmp (buf + ptr, "070702", 6) == 0)) + { + SEEKBACK; + RETURN (CPIO_CRC); + }; + ptr++; + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +cpio_create_entry (struct vfs_class *me, struct vfs_s_super *super, struct stat *st, char *name) +{ + cpio_super_t *arch = CPIO_SUPER (super); + struct vfs_s_inode *inode = NULL; + struct vfs_s_inode *root = super->root; + struct vfs_s_entry *entry = NULL; + char *tn; + + switch (st->st_mode & S_IFMT) + { /* For case of HP/UX archives */ + case S_IFCHR: + case S_IFBLK: +#ifdef S_IFSOCK + /* cppcheck-suppress syntaxError */ + case S_IFSOCK: +#endif +#ifdef S_IFIFO + /* cppcheck-suppress syntaxError */ + case S_IFIFO: +#endif +#ifdef S_IFNAM + /* cppcheck-suppress syntaxError */ + case S_IFNAM: +#endif +#ifdef HAVE_STRUCT_STAT_ST_RDEV + if ((st->st_size != 0) && (st->st_rdev == 0x0001)) + { + /* FIXME: representation of major/minor differs between */ + /* different operating systems. */ + st->st_rdev = (unsigned) st->st_size; + st->st_size = 0; + } +#endif + break; + default: + break; + } + + if ((st->st_nlink > 1) && ((arch->type == CPIO_NEWC) || (arch->type == CPIO_CRC))) + { /* For case of hardlinked files */ + defer_inode i = { st->st_ino, st->st_dev, NULL }; + GSList *l; + + l = g_slist_find_custom (arch->deferred, &i, cpio_defer_find); + if (l != NULL) + { + inode = ((defer_inode *) l->data)->inode; + if (inode->st.st_size != 0 && st->st_size != 0 && (inode->st.st_size != st->st_size)) + { + message (D_ERROR, MSG_ERROR, + _("Inconsistent hardlinks of\n%s\nin cpio archive\n%s"), + name, super->name); + inode = NULL; + } + else if (inode->st.st_size == 0) + inode->st.st_size = st->st_size; + } + } + + /* remove trailing slashes */ + for (tn = name + strlen (name) - 1; tn >= name && IS_PATH_SEP (*tn); tn--) + *tn = '\0'; + + tn = strrchr (name, PATH_SEP); + if (tn == NULL) + tn = name; + else if (tn == name + 1) + { + /* started with "./" -- directory in the root of archive */ + tn++; + } + else + { + *tn = '\0'; + root = vfs_s_find_inode (me, super, name, LINK_FOLLOW, FL_MKDIR); + *tn = PATH_SEP; + tn++; + } + + entry = VFS_SUBCLASS (me)->find_entry (me, root, tn, LINK_FOLLOW, FL_NONE); /* In case entry is already there */ + + if (entry != NULL) + { + /* This shouldn't happen! (well, it can happen if there is a record for a + file and than a record for a directory it is in; cpio would die with + 'No such file or directory' is such case) */ + + if (!S_ISDIR (entry->ino->st.st_mode)) + { + /* This can be considered archive inconsistency */ + message (D_ERROR, MSG_ERROR, + _("%s contains duplicate entries! Skipping!"), super->name); + } + else + { + entry->ino->st.st_mode = st->st_mode; + entry->ino->st.st_uid = st->st_uid; + entry->ino->st.st_gid = st->st_gid; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + entry->ino->st.st_atim = st->st_atim; + entry->ino->st.st_mtim = st->st_mtim; + entry->ino->st.st_ctim = st->st_ctim; +#else + entry->ino->st.st_atime = st->st_atime; + entry->ino->st.st_mtime = st->st_mtime; + entry->ino->st.st_ctime = st->st_ctime; +#endif + } + + g_free (name); + } + else + { /* !entry */ + /* root == NULL can be in the following case: + * a/b/c -> d + * where 'a/b' is the stale link and therefore root of 'c' cannot be found in the archive + */ + if (root != NULL) + { + if (inode == NULL) + { + inode = vfs_s_new_inode (me, super, st); + if ((st->st_nlink > 0) && ((arch->type == CPIO_NEWC) || (arch->type == CPIO_CRC))) + { + /* For case of hardlinked files */ + defer_inode *i; + + i = g_new (defer_inode, 1); + i->inumber = st->st_ino; + i->device = st->st_dev; + i->inode = inode; + + arch->deferred = g_slist_prepend (arch->deferred, i); + } + } + + if (st->st_size != 0) + inode->data_offset = CPIO_POS (super); + + entry = vfs_s_new_entry (me, tn, inode); + vfs_s_insert_entry (me, root, entry); + } + + g_free (name); + + if (!S_ISLNK (st->st_mode)) + CPIO_SEEK_CUR (super, st->st_size); + else + { + if (inode != NULL) + { + /* FIXME: do we must read from arch->fd in case of inode != NULL only or in any case? */ + + inode->linkname = g_malloc (st->st_size + 1); + + if (mc_read (arch->fd, inode->linkname, st->st_size) < st->st_size) + { + inode->linkname[0] = '\0'; + return STATUS_EOF; + } + + inode->linkname[st->st_size] = '\0'; /* Linkname stored without terminating \0 !!! */ + } + + CPIO_POS (super) += st->st_size; + cpio_skip_padding (super); + } + } /* !entry */ + + return STATUS_OK; +} + +/* --------------------------------------------------------------------------------------------- */ + +static ssize_t +cpio_read_bin_head (struct vfs_class *me, struct vfs_s_super *super) +{ + union + { + struct old_cpio_header buf; + short shorts[HEAD_LENGTH >> 1]; + } u; + + cpio_super_t *arch = CPIO_SUPER (super); + ssize_t len; + char *name; + struct stat st; + + len = mc_read (arch->fd, (char *) &u.buf, HEAD_LENGTH); + if (len < HEAD_LENGTH) + return STATUS_EOF; + CPIO_POS (super) += len; + if (arch->type == CPIO_BINRE) + { + int i; + for (i = 0; i < (HEAD_LENGTH >> 1); i++) + u.shorts[i] = GUINT16_SWAP_LE_BE_CONSTANT (u.shorts[i]); + } + + if (u.buf.c_magic != 070707 || u.buf.c_namesize == 0 || u.buf.c_namesize > MC_MAXPATHLEN) + { + message (D_ERROR, MSG_ERROR, _("Corrupted cpio header encountered in\n%s"), super->name); + return STATUS_FAIL; + } + name = g_malloc (u.buf.c_namesize); + len = mc_read (arch->fd, name, u.buf.c_namesize); + if (len < u.buf.c_namesize) + { + g_free (name); + return STATUS_EOF; + } + name[u.buf.c_namesize - 1] = '\0'; + CPIO_POS (super) += len; + cpio_skip_padding (super); + + if (!strcmp ("TRAILER!!!", name)) + { /* We got to the last record */ + g_free (name); + return STATUS_TRAIL; + } + + st.st_dev = u.buf.c_dev; + st.st_ino = u.buf.c_ino; + st.st_mode = u.buf.c_mode; + st.st_nlink = u.buf.c_nlink; + st.st_uid = u.buf.c_uid; + st.st_gid = u.buf.c_gid; +#ifdef HAVE_STRUCT_STAT_ST_RDEV + st.st_rdev = u.buf.c_rdev; +#endif + st.st_size = ((off_t) u.buf.c_filesizes[0] << 16) | u.buf.c_filesizes[1]; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + st.st_atim.tv_nsec = st.st_mtim.tv_nsec = st.st_ctim.tv_nsec = 0; +#endif + st.st_atime = st.st_mtime = st.st_ctime = + ((time_t) u.buf.c_mtimes[0] << 16) | u.buf.c_mtimes[1]; + + return cpio_create_entry (me, super, &st, name); +} + +/* --------------------------------------------------------------------------------------------- */ + +#undef HEAD_LENGTH +#define HEAD_LENGTH (76) + +static ssize_t +cpio_read_oldc_head (struct vfs_class *me, struct vfs_s_super *super) +{ + cpio_super_t *arch = CPIO_SUPER (super); + struct new_cpio_header hd; + union + { + struct stat st; + char buf[HEAD_LENGTH + 1]; + } u; + ssize_t len; + char *name; + + if (mc_read (arch->fd, u.buf, HEAD_LENGTH) != HEAD_LENGTH) + return STATUS_EOF; + CPIO_POS (super) += HEAD_LENGTH; + u.buf[HEAD_LENGTH] = 0; + + if (sscanf (u.buf, "070707%6lo%6lo%6lo%6lo%6lo%6lo%6lo%11lo%6lo%11lo", + (unsigned long *) &hd.c_dev, &hd.c_ino, &hd.c_mode, &hd.c_uid, &hd.c_gid, + &hd.c_nlink, (unsigned long *) &hd.c_rdev, &hd.c_mtime, + &hd.c_namesize, &hd.c_filesize) < 10) + { + message (D_ERROR, MSG_ERROR, _("Corrupted cpio header encountered in\n%s"), super->name); + return STATUS_FAIL; + } + + if (hd.c_namesize == 0 || hd.c_namesize > MC_MAXPATHLEN) + { + message (D_ERROR, MSG_ERROR, _("Corrupted cpio header encountered in\n%s"), super->name); + return STATUS_FAIL; + } + name = g_malloc (hd.c_namesize); + len = mc_read (arch->fd, name, hd.c_namesize); + if ((len == -1) || ((unsigned long) len < hd.c_namesize)) + { + g_free (name); + return STATUS_EOF; + } + name[hd.c_namesize - 1] = '\0'; + CPIO_POS (super) += len; + cpio_skip_padding (super); + + if (!strcmp ("TRAILER!!!", name)) + { /* We got to the last record */ + g_free (name); + return STATUS_TRAIL; + } + + u.st.st_dev = hd.c_dev; + u.st.st_ino = hd.c_ino; + u.st.st_mode = hd.c_mode; + u.st.st_nlink = hd.c_nlink; + u.st.st_uid = hd.c_uid; + u.st.st_gid = hd.c_gid; +#ifdef HAVE_STRUCT_STAT_ST_RDEV + u.st.st_rdev = hd.c_rdev; +#endif + u.st.st_size = hd.c_filesize; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + u.st.st_atim.tv_nsec = u.st.st_mtim.tv_nsec = u.st.st_ctim.tv_nsec = 0; +#endif + u.st.st_atime = u.st.st_mtime = u.st.st_ctime = hd.c_mtime; + + return cpio_create_entry (me, super, &u.st, name); +} + +/* --------------------------------------------------------------------------------------------- */ + +#undef HEAD_LENGTH +#define HEAD_LENGTH (110) + +static ssize_t +cpio_read_crc_head (struct vfs_class *me, struct vfs_s_super *super) +{ + cpio_super_t *arch = CPIO_SUPER (super); + struct new_cpio_header hd; + union + { + struct stat st; + char buf[HEAD_LENGTH + 1]; + } u; + ssize_t len; + char *name; + + if (mc_read (arch->fd, u.buf, HEAD_LENGTH) != HEAD_LENGTH) + return STATUS_EOF; + + CPIO_POS (super) += HEAD_LENGTH; + u.buf[HEAD_LENGTH] = '\0'; + + if (sscanf (u.buf, "%6ho%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx", + &hd.c_magic, &hd.c_ino, &hd.c_mode, &hd.c_uid, &hd.c_gid, + &hd.c_nlink, &hd.c_mtime, &hd.c_filesize, + (unsigned long *) &hd.c_dev, (unsigned long *) &hd.c_devmin, + (unsigned long *) &hd.c_rdev, (unsigned long *) &hd.c_rdevmin, + &hd.c_namesize, &hd.c_chksum) < 14) + { + message (D_ERROR, MSG_ERROR, _("Corrupted cpio header encountered in\n%s"), super->name); + return STATUS_FAIL; + } + + if ((arch->type == CPIO_NEWC && hd.c_magic != 070701) || + (arch->type == CPIO_CRC && hd.c_magic != 070702)) + return STATUS_FAIL; + + if (hd.c_namesize == 0 || hd.c_namesize > MC_MAXPATHLEN) + { + message (D_ERROR, MSG_ERROR, _("Corrupted cpio header encountered in\n%s"), super->name); + return STATUS_FAIL; + } + + name = g_malloc (hd.c_namesize); + len = mc_read (arch->fd, name, hd.c_namesize); + + if ((len == -1) || ((unsigned long) len < hd.c_namesize)) + { + g_free (name); + return STATUS_EOF; + } + name[hd.c_namesize - 1] = '\0'; + CPIO_POS (super) += len; + cpio_skip_padding (super); + + if (strcmp ("TRAILER!!!", name) == 0) + { /* We got to the last record */ + g_free (name); + return STATUS_TRAIL; + } + + u.st.st_dev = makedev (hd.c_dev, hd.c_devmin); + u.st.st_ino = hd.c_ino; + u.st.st_mode = hd.c_mode; + u.st.st_nlink = hd.c_nlink; + u.st.st_uid = hd.c_uid; + u.st.st_gid = hd.c_gid; +#ifdef HAVE_STRUCT_STAT_ST_RDEV + u.st.st_rdev = makedev (hd.c_rdev, hd.c_rdevmin); +#endif + u.st.st_size = hd.c_filesize; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + u.st.st_atim.tv_nsec = u.st.st_mtim.tv_nsec = u.st.st_ctim.tv_nsec = 0; +#endif + u.st.st_atime = u.st.st_mtime = u.st.st_ctime = hd.c_mtime; + + return cpio_create_entry (me, super, &u.st, name); +} + +/* --------------------------------------------------------------------------------------------- */ +/** Need to CPIO_SEEK_CUR to skip the file at the end of add entry!!!! */ + +static int +cpio_open_archive (struct vfs_s_super *super, const vfs_path_t * vpath, + const vfs_path_element_t * vpath_element) +{ + (void) vpath_element; + + if (cpio_open_cpio_file (vpath_element->class, super, vpath) == -1) + return -1; + + while (TRUE) + { + ssize_t status; + + status = cpio_read_head (vpath_element->class, super); + if (status < 0) + return (-1); + + switch (status) + { + case STATUS_EOF: + { + message (D_ERROR, MSG_ERROR, _("Unexpected end of file\n%s"), + vfs_path_as_str (vpath)); + return 0; + } + case STATUS_OK: + continue; + case STATUS_TRAIL: + break; + default: + break; + } + break; + } + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/** Remaining functions are exactly same as for tarfs (and were in fact just copied) */ + +static void * +cpio_super_check (const vfs_path_t * vpath) +{ + static struct stat sb; + int stat_result; + + stat_result = mc_stat (vpath, &sb); + return (stat_result == 0 ? &sb : NULL); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +cpio_super_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *parc, + const vfs_path_t * vpath, void *cookie) +{ + struct stat *archive_stat = cookie; /* stat of main archive */ + + (void) vpath_element; + + if (strcmp (parc->name, vfs_path_as_str (vpath))) + return 0; + + /* Has the cached archive been changed on the disk? */ + if (parc != NULL && CPIO_SUPER (parc)->st.st_mtime < archive_stat->st_mtime) + { + /* Yes, reload! */ + vfs_cpiofs_ops->free ((vfsid) parc); + vfs_rmstamp (vfs_cpiofs_ops, (vfsid) parc); + return 2; + } + /* Hasn't been modified, give it a new timeout */ + vfs_stamp (vfs_cpiofs_ops, (vfsid) parc); + return 1; +} + +/* --------------------------------------------------------------------------------------------- */ + +static ssize_t +cpio_read (void *fh, char *buffer, size_t count) +{ + vfs_file_handler_t *file = VFS_FILE_HANDLER (fh); + struct vfs_class *me = VFS_FILE_HANDLER_SUPER (fh)->me; + int fd = CPIO_SUPER (VFS_FILE_HANDLER_SUPER (fh))->fd; + off_t begin = file->ino->data_offset; + ssize_t res; + + if (mc_lseek (fd, begin + file->pos, SEEK_SET) != begin + file->pos) + ERRNOR (EIO, -1); + + count = MIN (count, (size_t) (file->ino->st.st_size - file->pos)); + + res = mc_read (fd, buffer, count); + if (res == -1) + ERRNOR (errno, -1); + + file->pos += res; + return res; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +cpio_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode) +{ + (void) fh; + (void) mode; + + if ((flags & O_ACCMODE) != O_RDONLY) + ERRNOR (EROFS, -1); + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +vfs_init_cpiofs (void) +{ + /* FIXME: cpiofs used own temp files */ + vfs_init_subclass (&cpio_subclass, "cpiofs", VFSF_READONLY, "ucpio"); + vfs_cpiofs_ops->read = cpio_read; + vfs_cpiofs_ops->setctl = NULL; + cpio_subclass.archive_check = cpio_super_check; + cpio_subclass.archive_same = cpio_super_same; + cpio_subclass.new_archive = cpio_new_archive; + cpio_subclass.open_archive = cpio_open_archive; + cpio_subclass.free_archive = cpio_free_archive; + cpio_subclass.fh_open = cpio_fh_open; + vfs_register_class (vfs_cpiofs_ops); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/cpio/cpio.h b/src/vfs/cpio/cpio.h new file mode 100644 index 0000000..a00756a --- /dev/null +++ b/src/vfs/cpio/cpio.h @@ -0,0 +1,18 @@ +#ifndef MC__VFS_CPIO_H +#define MC__VFS_CPIO_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_cpiofs (void); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__VFS_CPIO_H */ diff --git a/src/vfs/extfs/Makefile.am b/src/vfs/extfs/Makefile.am new file mode 100644 index 0000000..39762a3 --- /dev/null +++ b/src/vfs/extfs/Makefile.am @@ -0,0 +1,12 @@ +SUBDIRS = helpers +DIST_SUBDIRS = helpers + +AM_CPPFLAGS = \ + -DLIBEXECDIR=\""$(libexecdir)/@PACKAGE@/"\" \ + $(GLIB_CFLAGS) \ + -I$(top_srcdir) + +noinst_LTLIBRARIES = libvfs-extfs.la + +libvfs_extfs_la_SOURCES = \ + extfs.c extfs.h diff --git a/src/vfs/extfs/Makefile.in b/src/vfs/extfs/Makefile.in new file mode 100644 index 0000000..317af30 --- /dev/null +++ b/src/vfs/extfs/Makefile.in @@ -0,0 +1,856 @@ +# 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/extfs +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_extfs_la_LIBADD = +am_libvfs_extfs_la_OBJECTS = extfs.lo +libvfs_extfs_la_OBJECTS = $(am_libvfs_extfs_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)/extfs.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_extfs_la_SOURCES) +DIST_SOURCES = $(libvfs_extfs_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +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) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +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@ +SUBDIRS = helpers +DIST_SUBDIRS = helpers +AM_CPPFLAGS = \ + -DLIBEXECDIR=\""$(libexecdir)/@PACKAGE@/"\" \ + $(GLIB_CFLAGS) \ + -I$(top_srcdir) + +noinst_LTLIBRARIES = libvfs-extfs.la +libvfs_extfs_la_SOURCES = \ + extfs.c extfs.h + +all: all-recursive + +.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/extfs/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/vfs/extfs/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-extfs.la: $(libvfs_extfs_la_OBJECTS) $(libvfs_extfs_la_DEPENDENCIES) $(EXTRA_libvfs_extfs_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libvfs_extfs_la_OBJECTS) $(libvfs_extfs_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/extfs.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 + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(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-recursive + +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-recursive + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +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-recursive + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/extfs.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/extfs.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) 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 \ + installdirs-am 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/extfs/extfs.c b/src/vfs/extfs/extfs.c new file mode 100644 index 0000000..d6ef7af --- /dev/null +++ b/src/vfs/extfs/extfs.c @@ -0,0 +1,1722 @@ +/* + Virtual File System: External file system. + + Copyright (C) 1995-2023 + Free Software Foundation, Inc. + + Written by: + Jakub Jelinek, 1995 + Pavel Machek, 1998 + Andrew T. Veliath, 1999 + Slava Zanko <slavazanko@gmail.com>, 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/>. + */ + +/** + * \file + * \brief Source: Virtual File System: External file system + * \author Jakub Jelinek + * \author Pavel Machek + * \author Andrew T. Veliath + * \date 1995, 1998, 1999 + */ + +/* Namespace: init_extfs */ + +#include <config.h> + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <sys/wait.h> + +#include "lib/global.h" +#include "lib/fileloc.h" +#include "lib/mcconfig.h" +#include "lib/util.h" +#include "lib/widget.h" /* message() */ + +#include "src/execute.h" /* For shell_execute */ + +#include "lib/vfs/vfs.h" +#include "lib/vfs/utilvfs.h" +#include "lib/vfs/xdirentry.h" +#include "lib/vfs/gc.h" /* vfs_rmstamp */ + +#include "extfs.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#undef ERRNOR +#define ERRNOR(x,y) do { my_errno = x; return y; } while(0) + +#define RECORDSIZE 512 + +#define EXTFS_SUPER(a) ((struct extfs_super_t *) (a)) + +/*** file scope type declarations ****************************************************************/ + +struct extfs_super_t +{ + struct vfs_s_super base; /* base class */ + + int fstype; + char *local_name; + struct stat local_stat; + dev_t rdev; +}; + +typedef struct +{ + char *path; + char *prefix; + gboolean need_archive; +} extfs_plugin_info_t; + +/*** forward declarations (file scope functions) *************************************************/ + +static struct vfs_s_entry *extfs_resolve_symlinks_int (struct vfs_s_entry *entry, GSList * list); + +/*** file scope variables ************************************************************************/ + +static GArray *extfs_plugins = NULL; + +static gboolean errloop; +static gboolean notadir; + +static struct vfs_s_subclass extfs_subclass; +static struct vfs_class *vfs_extfs_ops = VFS_CLASS (&extfs_subclass); + +static int my_errno = 0; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static struct extfs_super_t * +extfs_super_new (struct vfs_class *me, const char *name, const vfs_path_t * local_name_vpath, + int fstype) +{ + struct extfs_super_t *super; + struct vfs_s_super *vsuper; + + super = g_new0 (struct extfs_super_t, 1); + vsuper = VFS_SUPER (super); + + vsuper->me = me; + vsuper->name = g_strdup (name); + + super->fstype = fstype; + + if (local_name_vpath != NULL) + { + super->local_name = g_strdup (vfs_path_get_last_path_str (local_name_vpath)); + mc_stat (local_name_vpath, &super->local_stat); + } + + VFS_SUBCLASS (me)->supers = g_list_prepend (VFS_SUBCLASS (me)->supers, super); + + return super; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* unlike vfs_s_new_entry(), inode->ent is kept */ +static struct vfs_s_entry * +extfs_entry_new (struct vfs_class *me, const char *name, struct vfs_s_inode *inode) +{ + struct vfs_s_entry *entry; + + (void) me; + + entry = g_new0 (struct vfs_s_entry, 1); + + entry->name = g_strdup (name); + entry->ino = inode; + + return entry; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +extfs_fill_name (void *data, void *user_data) +{ + struct vfs_s_super *a = VFS_SUPER (data); + fill_names_f func = (fill_names_f) user_data; + extfs_plugin_info_t *info; + char *name; + + info = &g_array_index (extfs_plugins, extfs_plugin_info_t, EXTFS_SUPER (a)->fstype); + name = + g_strconcat (a->name != NULL ? a->name : "", PATH_SEP_STR, info->prefix, + VFS_PATH_URL_DELIMITER, (char *) NULL); + func (name); + g_free (name); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gint +extfs_cmp_archive (const void *a, const void *b) +{ + const struct vfs_s_super *ar = (const struct vfs_s_super *) a; + const char *archive_name = (const char *) b; + + return (ar->name != NULL && strcmp (ar->name, archive_name) == 0) ? 0 : 1; +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct vfs_s_entry * +extfs_generate_entry (struct extfs_super_t *archive, const char *name, struct vfs_s_inode *parent, + mode_t mode) +{ + struct vfs_class *me = VFS_SUPER (archive)->me; + struct stat st; + mode_t myumask; + struct vfs_s_inode *inode; + struct vfs_s_entry *entry; + + memset (&st, 0, sizeof (st)); + st.st_ino = VFS_SUPER (archive)->ino_usage++; + st.st_dev = archive->rdev; + myumask = umask (022); + umask (myumask); + st.st_mode = mode & ~myumask; + st.st_uid = getuid (); + st.st_gid = getgid (); + st.st_mtime = time (NULL); + st.st_atime = st.st_mtime; + st.st_ctime = st.st_mtime; + st.st_nlink = 1; + + inode = vfs_s_new_inode (me, VFS_SUPER (archive), &st); + entry = vfs_s_new_entry (me, name, inode); + if (parent != NULL) + vfs_s_insert_entry (me, parent, entry); + + return entry; +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct vfs_s_entry * +extfs_find_entry_int (struct vfs_s_inode *dir, const char *name, GSList * list, int flags) +{ + struct vfs_s_entry *pent, *pdir; + const char *p, *name_end; + char *q; + char c = PATH_SEP; + struct extfs_super_t *super; + + if (g_path_is_absolute (name)) + { + /* Handle absolute paths */ + name = g_path_skip_root (name); + dir = dir->super->root; + } + + super = EXTFS_SUPER (dir->super); + pent = dir->ent; + p = name; + name_end = name + strlen (name); + + while ((pent != NULL) && (c != '\0') && (*p != '\0')) + { + q = strchr (p, PATH_SEP); + if (q == NULL) + q = (char *) name_end; + + c = *q; + *q = '\0'; + + if (DIR_IS_DOTDOT (p)) + pent = pent->dir != NULL ? pent->dir->ent : NULL; + else + { + GList *pl; + + pent = extfs_resolve_symlinks_int (pent, list); + if (pent == NULL) + { + *q = c; + return NULL; + } + + if (!S_ISDIR (pent->ino->st.st_mode)) + { + *q = c; + notadir = TRUE; + return NULL; + } + + pdir = pent; + pl = g_queue_find_custom (pent->ino->subdir, p, vfs_s_entry_compare); + pent = pl != NULL ? VFS_ENTRY (pl->data) : NULL; + if (pent != NULL && q + 1 > name_end) + { + /* Hack: I keep the original semanthic unless q+1 would break in the strchr */ + *q = c; + notadir = !S_ISDIR (pent->ino->st.st_mode); + return pent; + } + + /* When we load archive, we create automagically non-existent directories */ + if (pent == NULL && (flags & FL_MKDIR) != 0) + pent = extfs_generate_entry (super, p, pdir->ino, S_IFDIR | 0777); + if (pent == NULL && (flags & FL_MKFILE) != 0) + pent = extfs_generate_entry (super, p, pdir->ino, S_IFREG | 0666); + } + + /* Next iteration */ + *q = c; + if (c != '\0') + p = q + 1; + } + if (pent == NULL) + my_errno = ENOENT; + return pent; +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct vfs_s_entry * +extfs_find_entry (struct vfs_s_inode *dir, const char *name, int flags) +{ + struct vfs_s_entry *res; + + errloop = FALSE; + notadir = FALSE; + + res = extfs_find_entry_int (dir, name, NULL, flags); + if (res == NULL) + { + if (errloop) + my_errno = ELOOP; + else if (notadir) + my_errno = ENOTDIR; + } + return res; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +extfs_fill_names (struct vfs_class *me, fill_names_f func) +{ + g_list_foreach (VFS_SUBCLASS (me)->supers, extfs_fill_name, func); +} + +/* --------------------------------------------------------------------------------------------- */ + +/* Create this function because VFSF_USETMP flag is not used in extfs */ +static void +extfs_free_inode (struct vfs_class *me, struct vfs_s_inode *ino) +{ + (void) me; + + if (ino->localname != NULL) + { + unlink (ino->localname); + MC_PTR_FREE (ino->localname); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +extfs_free_archive (struct vfs_class *me, struct vfs_s_super *psup) +{ + struct extfs_super_t *archive = EXTFS_SUPER (psup); + + (void) me; + + if (archive->local_name != NULL) + { + struct stat my; + vfs_path_t *local_name_vpath, *name_vpath; + + local_name_vpath = vfs_path_from_str (archive->local_name); + name_vpath = vfs_path_from_str (psup->name); + mc_stat (local_name_vpath, &my); + mc_ungetlocalcopy (name_vpath, local_name_vpath, + archive->local_stat.st_mtime != my.st_mtime); + vfs_path_free (local_name_vpath, TRUE); + vfs_path_free (name_vpath, TRUE); + g_free (archive->local_name); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline char * +extfs_skip_leading_dotslash (char *s) +{ + /* Skip leading "./" (if present). + * Some programs don't understand it: + * + * $ zip file.zip ./-file2.txt file1.txt + * adding: -file2.txt (stored 0%) + * adding: file1.txt (stored 0%) + * $ /usr/lib/mc/extfs.d/uzip copyout file.zip ./-file2.txt ./tmp-file2.txt + * caution: filename not matched: ./-file2.txt + */ + if (s[0] == '.' && s[1] == PATH_SEP) + s += 2; + + return s; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_add_file (struct extfs_super_t *archive, const char *file_name) +{ + struct vfs_s_super *super = VFS_SUPER (archive); + struct stat hstat; + char *current_file_name = NULL, *current_link_name = NULL; + int ret = 0; + + if (vfs_parse_ls_lga (file_name, &hstat, ¤t_file_name, ¤t_link_name, NULL)) + { + char *cfn = current_file_name; + + if (*cfn != '\0') + { + struct vfs_s_entry *entry; + struct vfs_s_entry *pent = NULL; + struct vfs_s_inode *inode; + char *p, *q; + + cfn = extfs_skip_leading_dotslash (cfn); + if (IS_PATH_SEP (*cfn)) + cfn++; + p = strchr (cfn, '\0'); + if (p != cfn && IS_PATH_SEP (p[-1])) + p[-1] = '\0'; + p = strrchr (cfn, PATH_SEP); + if (p == NULL) + { + p = cfn; + q = strchr (cfn, '\0'); + } + else + { + *(p++) = '\0'; + q = cfn; + } + + if (*q != '\0') + { + pent = extfs_find_entry (super->root, q, FL_MKDIR); + if (pent == NULL) + { + ret = -1; + goto done; + } + } + + if (pent != NULL) + { + entry = extfs_entry_new (super->me, p, pent->ino); + entry->dir = pent->ino; + g_queue_push_tail (pent->ino->subdir, entry); + } + else + { + entry = extfs_entry_new (super->me, p, super->root); + entry->dir = super->root; + g_queue_push_tail (super->root->subdir, entry); + } + + if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL)) + { + pent = extfs_find_entry (super->root, current_link_name, FL_NONE); + if (pent == NULL) + { + ret = -1; + goto done; + } + + pent->ino->st.st_nlink++; + entry->ino = pent->ino; + } + else + { + struct stat st; + + memset (&st, 0, sizeof (st)); + st.st_ino = super->ino_usage++; + st.st_nlink = 1; + st.st_dev = archive->rdev; + st.st_mode = hstat.st_mode; +#ifdef HAVE_STRUCT_STAT_ST_RDEV + st.st_rdev = hstat.st_rdev; +#endif + st.st_uid = hstat.st_uid; + st.st_gid = hstat.st_gid; + st.st_size = hstat.st_size; + st.st_mtime = hstat.st_mtime; + st.st_atime = hstat.st_atime; + st.st_ctime = hstat.st_ctime; + + if (current_link_name == NULL && S_ISLNK (hstat.st_mode)) + st.st_mode &= ~S_IFLNK; /* You *DON'T* want to do this always */ + + inode = vfs_s_new_inode (super->me, super, &st); + inode->ent = entry; + entry->ino = inode; + + if (current_link_name != NULL && S_ISLNK (hstat.st_mode)) + { + inode->linkname = current_link_name; + current_link_name = NULL; + } + } + } + + done: + g_free (current_file_name); + g_free (current_link_name); + } + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static mc_pipe_t * +extfs_open_archive (int fstype, const char *name, struct extfs_super_t **pparc, GError ** error) +{ + const extfs_plugin_info_t *info; + static dev_t archive_counter = 0; + mc_pipe_t *result = NULL; + mode_t mode; + char *cmd; + struct stat mystat; + struct extfs_super_t *current_archive; + struct vfs_s_entry *root_entry; + char *tmp = NULL; + vfs_path_t *local_name_vpath = NULL; + vfs_path_t *name_vpath; + + memset (&mystat, 0, sizeof (mystat)); + + name_vpath = vfs_path_from_str (name); + info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype); + + if (info->need_archive) + { + if (mc_stat (name_vpath, &mystat) == -1) + goto ret; + + if (!vfs_file_is_local (name_vpath)) + { + local_name_vpath = mc_getlocalcopy (name_vpath); + if (local_name_vpath == NULL) + goto ret; + } + + tmp = name_quote (vfs_path_get_last_path_str (name_vpath), FALSE); + } + + cmd = g_strconcat (info->path, info->prefix, " list ", + vfs_path_get_last_path_str (local_name_vpath) != NULL ? + vfs_path_get_last_path_str (local_name_vpath) : tmp, (char *) NULL); + g_free (tmp); + + result = mc_popen (cmd, TRUE, TRUE, error); + g_free (cmd); + + if (result == NULL) + { + if (local_name_vpath != NULL) + { + mc_ungetlocalcopy (name_vpath, local_name_vpath, FALSE); + vfs_path_free (local_name_vpath, TRUE); + } + goto ret; + } + + current_archive = extfs_super_new (vfs_extfs_ops, name, local_name_vpath, fstype); + current_archive->rdev = archive_counter++; + vfs_path_free (local_name_vpath, TRUE); + + mode = mystat.st_mode & 07777; + if (mode & 0400) + mode |= 0100; + if (mode & 0040) + mode |= 0010; + if (mode & 0004) + mode |= 0001; + mode |= S_IFDIR; + + root_entry = extfs_generate_entry (current_archive, PATH_SEP_STR, NULL, mode); + root_entry->ino->st.st_uid = mystat.st_uid; + root_entry->ino->st.st_gid = mystat.st_gid; + root_entry->ino->st.st_atime = mystat.st_atime; + root_entry->ino->st.st_ctime = mystat.st_ctime; + root_entry->ino->st.st_mtime = mystat.st_mtime; + root_entry->ino->ent = root_entry; + VFS_SUPER (current_archive)->root = root_entry->ino; + + *pparc = current_archive; + + ret: + vfs_path_free (name_vpath, TRUE); + return result; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Main loop for reading an archive. + * Return 0 on success, -1 on error. + */ + +static int +extfs_read_archive (mc_pipe_t * pip, struct extfs_super_t *archive, GError ** error) +{ + int ret = 0; + GString *buffer; + GString *err_msg = NULL; + GString *remain_file_name = NULL; + + while (ret != -1) + { + /* init buffers before call of mc_pread() */ + pip->out.len = MC_PIPE_BUFSIZE; + pip->err.len = MC_PIPE_BUFSIZE; + + mc_pread (pip, error); + + if (*error != NULL) + return (-1); + + if (pip->err.len > 0) + { + /* join errors/warnings */ + if (err_msg == NULL) + err_msg = g_string_new_len (pip->err.buf, pip->err.len); + else + { + if (err_msg->str[err_msg->len - 1] != '\n') + g_string_append_c (err_msg, '\n'); + g_string_append_len (err_msg, pip->err.buf, pip->err.len); + } + } + + if (pip->out.len == MC_PIPE_STREAM_EOF) + break; + + if (pip->out.len == 0) + continue; + + if (pip->out.len == MC_PIPE_ERROR_READ) + return (-1); + + while (ret != -1 && (buffer = mc_pstream_get_string (&pip->out)) != NULL) + { + /* handle a \n-separated file list */ + + if (buffer->str[buffer->len - 1] == '\n') + { + /* entire file name or last chunk */ + + g_string_truncate (buffer, buffer->len - 1); + + /* join filename chunks */ + if (remain_file_name != NULL) + { + g_string_append_len (remain_file_name, buffer->str, buffer->len); + g_string_free (buffer, TRUE); + buffer = remain_file_name; + remain_file_name = NULL; + } + } + else + { + /* first or middle chunk of file name */ + + if (remain_file_name == NULL) + remain_file_name = buffer; + else + { + g_string_append_len (remain_file_name, buffer->str, buffer->len); + g_string_free (buffer, TRUE); + } + + continue; + } + + ret = extfs_add_file (archive, buffer->str); + + g_string_free (buffer, TRUE); + } + } + + if (remain_file_name != NULL) + g_string_free (remain_file_name, TRUE); + + if (err_msg != NULL) + { + if (*error == NULL) + mc_propagate_error (error, 0, "%s", err_msg->str); + + g_string_free (err_msg, TRUE); + } + else if (ret == -1) + mc_propagate_error (error, 0, "%s", _("Inconsistent archive")); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_which (struct vfs_class *me, const char *path) +{ + size_t path_len; + size_t i; + + (void) me; + + path_len = strlen (path); + + for (i = 0; i < extfs_plugins->len; i++) + { + extfs_plugin_info_t *info; + + info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i); + + if ((strncmp (path, info->prefix, path_len) == 0) + && ((info->prefix[path_len] == '\0') || (info->prefix[path_len] == '+'))) + return i; + } + return -1; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_open_and_read_archive (int fstype, const char *name, struct extfs_super_t **archive) +{ + int result = -1; + struct extfs_super_t *a; + mc_pipe_t *pip; + GError *error = NULL; + + pip = extfs_open_archive (fstype, name, archive, &error); + + a = *archive; + + if (pip == NULL) + { + const extfs_plugin_info_t *info; + + info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype); + message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s:\n%s"), info->prefix, name, + error->message); + g_error_free (error); + } + else + { + result = extfs_read_archive (pip, a, &error); + + if (result != 0) + VFS_SUPER (a)->me->free (VFS_SUPER (a)); + + if (error != NULL) + { + message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), error->message); + g_error_free (error); + } + + mc_pclose (pip, NULL); + } + + return result; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Dissect the path and create corresponding superblock. + */ +static const char * +extfs_get_path (const vfs_path_t * vpath, struct extfs_super_t **archive, int flags) +{ + char *archive_name; + int result = -1; + GList *parc; + int fstype; + const vfs_path_element_t *path_element; + struct extfs_super_t *a = NULL; + + path_element = vfs_path_get_by_index (vpath, -1); + + fstype = extfs_which (path_element->class, path_element->vfs_prefix); + if (fstype == -1) + return NULL; + + archive_name = vfs_path_to_str_elements_count (vpath, -1); + + /* All filesystems should have some local archive, at least it can be PATH_SEP ('/'). */ + parc = g_list_find_custom (extfs_subclass.supers, archive_name, extfs_cmp_archive); + if (parc != NULL) + { + a = EXTFS_SUPER (parc->data); + vfs_stamp (vfs_extfs_ops, (vfsid) a); + g_free (archive_name); + } + else + { + if ((flags & FL_NO_OPEN) == 0) + result = extfs_open_and_read_archive (fstype, archive_name, &a); + + g_free (archive_name); + + if (result == -1) + { + path_element->class->verrno = EIO; + return NULL; + } + } + + *archive = a; + return path_element->path; +} + +/* --------------------------------------------------------------------------------------------- */ +/* Return allocated path (without leading slash) inside the archive */ + +static char * +extfs_get_path_from_entry (const struct vfs_s_entry *entry) +{ + const struct vfs_s_entry *e; + GString *localpath; + + localpath = g_string_new (""); + + for (e = entry; e->dir != NULL; e = e->dir->ent) + { + g_string_prepend (localpath, e->name); + if (e->dir->ent->dir != NULL) + g_string_prepend_c (localpath, PATH_SEP); + } + + return g_string_free (localpath, FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct vfs_s_entry * +extfs_resolve_symlinks_int (struct vfs_s_entry *entry, GSList * list) +{ + struct vfs_s_entry *pent = NULL; + + if (!S_ISLNK (entry->ino->st.st_mode)) + return entry; + + if (g_slist_find (list, entry) != NULL) + { + /* Here we protect us against symlink looping */ + errloop = TRUE; + } + else + { + GSList *looping; + + looping = g_slist_prepend (list, entry); + pent = extfs_find_entry_int (entry->dir, entry->ino->linkname, looping, FL_NONE); + looping = g_slist_delete_link (looping, looping); + + if (pent == NULL) + my_errno = ENOENT; + } + + return pent; +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct vfs_s_entry * +extfs_resolve_symlinks (struct vfs_s_entry *entry) +{ + struct vfs_s_entry *res; + + errloop = FALSE; + notadir = FALSE; + res = extfs_resolve_symlinks_int (entry, NULL); + if (res == NULL) + { + if (errloop) + my_errno = ELOOP; + else if (notadir) + my_errno = ENOTDIR; + } + return res; +} + +/* --------------------------------------------------------------------------------------------- */ + +static char * +extfs_get_archive_name (const struct extfs_super_t *archive) +{ + const char *archive_name; + + if (archive->local_name != NULL) + archive_name = archive->local_name; + else + archive_name = CONST_VFS_SUPER (archive)->name; + + if (archive_name == NULL || *archive_name == '\0') + return g_strdup ("no_archive_name"); + else + { + char *ret_str; + vfs_path_t *vpath; + const char *path; + + vpath = vfs_path_from_str (archive_name); + path = vfs_path_get_last_path_str (vpath); + ret_str = g_strdup (path); + vfs_path_free (vpath, TRUE); + return ret_str; + } +} + +/* --------------------------------------------------------------------------------------------- */ +/** Don't pass localname as NULL */ + +static int +extfs_cmd (const char *str_extfs_cmd, const struct extfs_super_t *archive, + const struct vfs_s_entry *entry, const char *localname) +{ + char *file; + char *quoted_file; + char *quoted_localname; + char *archive_name, *quoted_archive_name; + const extfs_plugin_info_t *info; + char *cmd; + int retval = 0; + GError *error = NULL; + mc_pipe_t *pip; + + file = extfs_get_path_from_entry (entry); + quoted_file = name_quote (file, FALSE); + g_free (file); + + /* Skip leading "./" (if present) added in name_quote() */ + file = extfs_skip_leading_dotslash (quoted_file); + + archive_name = extfs_get_archive_name (archive); + quoted_archive_name = name_quote (archive_name, FALSE); + g_free (archive_name); + quoted_localname = name_quote (localname, FALSE); + info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype); + cmd = g_strconcat (info->path, info->prefix, str_extfs_cmd, + quoted_archive_name, " ", file, " ", quoted_localname, (char *) NULL); + g_free (quoted_file); + g_free (quoted_localname); + g_free (quoted_archive_name); + + /* don't read stdout */ + pip = mc_popen (cmd, FALSE, TRUE, &error); + g_free (cmd); + + if (pip == NULL) + { + message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), error->message); + g_error_free (error); + return (-1); + } + + pip->err.null_term = TRUE; + + mc_pread (pip, &error); + if (error != NULL) + { + message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), error->message); + g_error_free (error); + retval = -1; + } + else if (pip->err.len > 0) + message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), pip->err.buf); + + mc_pclose (pip, NULL); + + return retval; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +extfs_run (const vfs_path_t * vpath) +{ + struct extfs_super_t *archive = NULL; + const char *p; + char *q, *archive_name, *quoted_archive_name; + char *cmd; + const extfs_plugin_info_t *info; + + p = extfs_get_path (vpath, &archive, FL_NONE); + if (p == NULL) + return; + q = name_quote (p, FALSE); + + archive_name = extfs_get_archive_name (archive); + quoted_archive_name = name_quote (archive_name, FALSE); + g_free (archive_name); + info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype); + cmd = + g_strconcat (info->path, info->prefix, " run ", quoted_archive_name, " ", q, (char *) NULL); + g_free (quoted_archive_name); + g_free (q); + shell_execute (cmd, 0); + g_free (cmd); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void * +extfs_open (const vfs_path_t * vpath, int flags, mode_t mode) +{ + vfs_file_handler_t *extfs_info; + struct extfs_super_t *archive = NULL; + const char *q; + struct vfs_s_entry *entry; + int local_handle; + gboolean created = FALSE; + + q = extfs_get_path (vpath, &archive, FL_NONE); + if (q == NULL) + return NULL; + entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE); + if ((entry == NULL) && ((flags & O_CREAT) != 0)) + { + /* Create new entry */ + entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_MKFILE); + created = (entry != NULL); + } + + if (entry == NULL) + return NULL; + entry = extfs_resolve_symlinks (entry); + if (entry == NULL) + return NULL; + + if (S_ISDIR (entry->ino->st.st_mode)) + ERRNOR (EISDIR, NULL); + + if (entry->ino->localname == NULL) + { + vfs_path_t *local_filename_vpath; + const char *local_filename; + + local_handle = vfs_mkstemps (&local_filename_vpath, "extfs", entry->name); + + if (local_handle == -1) + return NULL; + close (local_handle); + local_filename = vfs_path_get_last_path_str (local_filename_vpath); + + if (!created && ((flags & O_TRUNC) == 0) + && extfs_cmd (" copyout ", archive, entry, local_filename)) + { + unlink (local_filename); + vfs_path_free (local_filename_vpath, TRUE); + my_errno = EIO; + return NULL; + } + entry->ino->localname = g_strdup (local_filename); + vfs_path_free (local_filename_vpath, TRUE); + } + + local_handle = open (entry->ino->localname, NO_LINEAR (flags), mode); + + if (local_handle == -1) + { + /* file exists(may be). Need to drop O_CREAT flag and truncate file content */ + flags = ~O_CREAT & (NO_LINEAR (flags) | O_TRUNC); + local_handle = open (entry->ino->localname, flags, mode); + } + + if (local_handle == -1) + ERRNOR (EIO, NULL); + + extfs_info = g_new (vfs_file_handler_t, 1); + vfs_s_init_fh (extfs_info, entry->ino, created); + extfs_info->handle = local_handle; + + /* i.e. we had no open files and now we have one */ + vfs_rmstamp (vfs_extfs_ops, (vfsid) archive); + VFS_SUPER (archive)->fd_usage++; + return extfs_info; +} + +/* --------------------------------------------------------------------------------------------- */ + +static ssize_t +extfs_read (void *fh, char *buffer, size_t count) +{ + vfs_file_handler_t *file = VFS_FILE_HANDLER (fh); + + return read (file->handle, buffer, count); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_close (void *fh) +{ + vfs_file_handler_t *file = VFS_FILE_HANDLER (fh); + int errno_code = 0; + + close (file->handle); + file->handle = -1; + + /* Commit the file if it has changed */ + if (file->changed) + { + struct stat file_status; + + if (extfs_cmd + (" copyin ", EXTFS_SUPER (VFS_FILE_HANDLER_SUPER (fh)), file->ino->ent, + file->ino->localname)) + errno_code = EIO; + + if (stat (file->ino->localname, &file_status) != 0) + errno_code = EIO; + else + file->ino->st.st_size = file_status.st_size; + + file->ino->st.st_mtime = time (NULL); + } + + if (--VFS_FILE_HANDLER_SUPER (fh)->fd_usage == 0) + vfs_stamp_create (vfs_extfs_ops, VFS_FILE_HANDLER_SUPER (fh)); + + g_free (fh); + if (errno_code != 0) + ERRNOR (EIO, -1); + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_errno (struct vfs_class *me) +{ + (void) me; + return my_errno; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void * +extfs_opendir (const vfs_path_t * vpath) +{ + struct extfs_super_t *archive = NULL; + const char *q; + struct vfs_s_entry *entry; + GList **info; + + q = extfs_get_path (vpath, &archive, FL_NONE); + if (q == NULL) + return NULL; + entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE); + if (entry == NULL) + return NULL; + entry = extfs_resolve_symlinks (entry); + if (entry == NULL) + return NULL; + if (!S_ISDIR (entry->ino->st.st_mode)) + ERRNOR (ENOTDIR, NULL); + + info = g_new (GList *, 1); + *info = g_queue_peek_head_link (entry->ino->subdir); + + return info; +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct vfs_dirent * +extfs_readdir (void *data) +{ + struct vfs_dirent *dir; + GList **info = (GList **) data; + + if (*info == NULL) + return NULL; + + dir = vfs_dirent_init (NULL, VFS_ENTRY ((*info)->data)->name, 0); /* FIXME: inode */ + + *info = g_list_next (*info); + + return dir; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_closedir (void *data) +{ + g_free (data); + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + + +static void +extfs_stat_move (struct stat *buf, const struct vfs_s_inode *inode) +{ + *buf = inode->st; + +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + buf->st_blksize = RECORDSIZE; +#endif + vfs_adjust_stat (buf); +#ifdef HAVE_STRUCT_STAT_ST_MTIM + buf->st_atim.tv_nsec = buf->st_mtim.tv_nsec = buf->st_ctim.tv_nsec = 0; +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_internal_stat (const vfs_path_t * vpath, struct stat *buf, gboolean resolve) +{ + struct extfs_super_t *archive; + const char *q; + struct vfs_s_entry *entry; + int result = -1; + + q = extfs_get_path (vpath, &archive, FL_NONE); + if (q == NULL) + goto cleanup; + entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE); + if (entry == NULL) + goto cleanup; + if (resolve) + { + entry = extfs_resolve_symlinks (entry); + if (entry == NULL) + goto cleanup; + } + extfs_stat_move (buf, entry->ino); + result = 0; + cleanup: + return result; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_stat (const vfs_path_t * vpath, struct stat *buf) +{ + return extfs_internal_stat (vpath, buf, TRUE); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_lstat (const vfs_path_t * vpath, struct stat *buf) +{ + return extfs_internal_stat (vpath, buf, FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_fstat (void *fh, struct stat *buf) +{ + vfs_file_handler_t *file = VFS_FILE_HANDLER (fh); + + extfs_stat_move (buf, file->ino); + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_readlink (const vfs_path_t * vpath, char *buf, size_t size) +{ + struct extfs_super_t *archive; + const char *q; + size_t len; + struct vfs_s_entry *entry; + int result = -1; + + q = extfs_get_path (vpath, &archive, FL_NONE); + if (q == NULL) + goto cleanup; + entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE); + if (entry == NULL) + goto cleanup; + if (!S_ISLNK (entry->ino->st.st_mode)) + { + VFS_CLASS (vfs_path_get_last_path_vfs (vpath))->verrno = EINVAL; + goto cleanup; + } + len = strlen (entry->ino->linkname); + if (size < len) + len = size; + /* readlink() does not append a NUL character to buf */ + result = len; + memcpy (buf, entry->ino->linkname, result); + cleanup: + return result; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group) +{ + (void) vpath; + (void) owner; + (void) group; + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_chmod (const vfs_path_t * vpath, mode_t mode) +{ + (void) vpath; + (void) mode; + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static ssize_t +extfs_write (void *fh, const char *buf, size_t nbyte) +{ + vfs_file_handler_t *file = VFS_FILE_HANDLER (fh); + + file->changed = TRUE; + return write (file->handle, buf, nbyte); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_unlink (const vfs_path_t * vpath) +{ + struct extfs_super_t *archive; + const char *q; + struct vfs_s_entry *entry; + int result = -1; + + q = extfs_get_path (vpath, &archive, FL_NONE); + if (q == NULL) + goto cleanup; + entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE); + if (entry == NULL) + goto cleanup; + entry = extfs_resolve_symlinks (entry); + if (entry == NULL) + goto cleanup; + if (S_ISDIR (entry->ino->st.st_mode)) + { + VFS_CLASS (vfs_path_get_last_path_vfs (vpath))->verrno = EISDIR; + goto cleanup; + } + if (extfs_cmd (" rm ", archive, entry, "")) + { + my_errno = EIO; + goto cleanup; + } + vfs_s_free_entry (VFS_SUPER (archive)->me, entry); + result = 0; + cleanup: + return result; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_mkdir (const vfs_path_t * vpath, mode_t mode) +{ + struct extfs_super_t *archive; + const char *q; + struct vfs_s_entry *entry; + int result = -1; + struct vfs_class *me; + + (void) mode; + + me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); + q = extfs_get_path (vpath, &archive, FL_NONE); + if (q == NULL) + goto cleanup; + entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE); + if (entry != NULL) + { + me->verrno = EEXIST; + goto cleanup; + } + entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_MKDIR); + if (entry == NULL) + goto cleanup; + entry = extfs_resolve_symlinks (entry); + if (entry == NULL) + goto cleanup; + if (!S_ISDIR (entry->ino->st.st_mode)) + { + me->verrno = ENOTDIR; + goto cleanup; + } + + if (extfs_cmd (" mkdir ", archive, entry, "")) + { + my_errno = EIO; + vfs_s_free_entry (VFS_SUPER (archive)->me, entry); + goto cleanup; + } + result = 0; + cleanup: + return result; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_rmdir (const vfs_path_t * vpath) +{ + struct extfs_super_t *archive; + const char *q; + struct vfs_s_entry *entry; + int result = -1; + + q = extfs_get_path (vpath, &archive, FL_NONE); + if (q == NULL) + goto cleanup; + entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE); + if (entry == NULL) + goto cleanup; + entry = extfs_resolve_symlinks (entry); + if (entry == NULL) + goto cleanup; + if (!S_ISDIR (entry->ino->st.st_mode)) + { + VFS_CLASS (vfs_path_get_last_path_vfs (vpath))->verrno = ENOTDIR; + goto cleanup; + } + + if (extfs_cmd (" rmdir ", archive, entry, "")) + { + my_errno = EIO; + goto cleanup; + } + vfs_s_free_entry (VFS_SUPER (archive)->me, entry); + result = 0; + cleanup: + return result; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_chdir (const vfs_path_t * vpath) +{ + void *data; + + my_errno = ENOTDIR; + data = extfs_opendir (vpath); + if (data == NULL) + return (-1); + extfs_closedir (data); + my_errno = 0; + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static off_t +extfs_lseek (void *fh, off_t offset, int whence) +{ + vfs_file_handler_t *file = VFS_FILE_HANDLER (fh); + + return lseek (file->handle, offset, whence); +} + +/* --------------------------------------------------------------------------------------------- */ + +static vfsid +extfs_getid (const vfs_path_t * vpath) +{ + struct extfs_super_t *archive = NULL; + const char *p; + + p = extfs_get_path (vpath, &archive, FL_NO_OPEN); + return (p == NULL ? NULL : (vfsid) archive); +} + +/* --------------------------------------------------------------------------------------------- */ + +static vfs_path_t * +extfs_getlocalcopy (const vfs_path_t * vpath) +{ + vfs_file_handler_t *fh; + vfs_path_t *p; + + fh = VFS_FILE_HANDLER (extfs_open (vpath, O_RDONLY, 0)); + if (fh == NULL) + return NULL; + if (fh->ino->localname == NULL) + { + extfs_close ((void *) fh); + return NULL; + } + p = vfs_path_from_str (fh->ino->localname); + VFS_FILE_HANDLER_SUPER (fh)->fd_usage++; + extfs_close ((void *) fh); + return p; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed) +{ + vfs_file_handler_t *fh; + + fh = VFS_FILE_HANDLER (extfs_open (vpath, O_RDONLY, 0)); + if (fh == NULL) + return 0; + + if (strcmp (fh->ino->localname, vfs_path_get_last_path_str (local)) == 0) + { + VFS_FILE_HANDLER_SUPER (fh)->fd_usage--; + if (has_changed) + fh->changed = TRUE; + extfs_close ((void *) fh); + return 0; + } + else + { + /* Should not happen */ + extfs_close ((void *) fh); + return 0; + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +extfs_get_plugins (const char *where, gboolean silent) +{ + char *dirname; + GDir *dir; + const char *filename; + + dirname = g_build_path (PATH_SEP_STR, where, MC_EXTFS_DIR, (char *) NULL); + dir = g_dir_open (dirname, 0, NULL); + + /* We may not use vfs_die() message or message or similar, + * UI is not initialized at this time and message would not + * appear on screen. */ + if (dir == NULL) + { + if (!silent) + fprintf (stderr, _("Warning: cannot open %s directory\n"), dirname); + g_free (dirname); + return FALSE; + } + + if (extfs_plugins == NULL) + extfs_plugins = g_array_sized_new (FALSE, TRUE, sizeof (extfs_plugin_info_t), 32); + + while ((filename = g_dir_read_name (dir)) != NULL) + { + char fullname[MC_MAXPATHLEN]; + struct stat s; + + g_snprintf (fullname, sizeof (fullname), "%s" PATH_SEP_STR "%s", dirname, filename); + + if ((stat (fullname, &s) == 0) && S_ISREG (s.st_mode) && !S_ISDIR (s.st_mode) + && is_exe (s.st_mode)) + { + int f; + + f = open (fullname, O_RDONLY); + + if (f >= 0) + { + size_t len, i; + extfs_plugin_info_t info; + gboolean found = FALSE; + + close (f); + + /* Handle those with a trailing '+', those flag that the + * file system does not require an archive to work + */ + len = strlen (filename); + info.need_archive = (filename[len - 1] != '+'); + info.path = g_strconcat (dirname, PATH_SEP_STR, (char *) NULL); + info.prefix = g_strndup (filename, len); + + /* prepare to compare file names without trailing '+' */ + if (!info.need_archive) + info.prefix[len - 1] = '\0'; + + /* don't overload already found plugin */ + for (i = 0; i < extfs_plugins->len && !found; i++) + { + extfs_plugin_info_t *p; + + p = &g_array_index (extfs_plugins, extfs_plugin_info_t, i); + + /* 2 files with same names cannot be in a directory */ + found = strcmp (info.path, p->path) != 0 + && strcmp (info.prefix, p->prefix) == 0; + } + + if (found) + { + g_free (info.path); + g_free (info.prefix); + } + else + { + /* restore file name */ + if (!info.need_archive) + info.prefix[len - 1] = '+'; + g_array_append_val (extfs_plugins, info); + } + } + } + } + + g_dir_close (dir); + g_free (dirname); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_init (struct vfs_class *me) +{ + gboolean d1, d2; + + (void) me; + + /* 1st: scan user directory */ + d1 = extfs_get_plugins (mc_config_get_data_path (), TRUE); /* silent about user dir */ + /* 2nd: scan system dir */ + d2 = extfs_get_plugins (LIBEXECDIR, d1); + + return (d1 || d2 ? 1 : 0); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +extfs_done (struct vfs_class *me) +{ + size_t i; + + while (VFS_SUBCLASS (me)->supers != NULL) + me->free ((vfsid) VFS_SUBCLASS (me)->supers->data); + + if (extfs_plugins == NULL) + return; + + for (i = 0; i < extfs_plugins->len; i++) + { + extfs_plugin_info_t *info; + + info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i); + g_free (info->path); + g_free (info->prefix); + } + + g_array_free (extfs_plugins, TRUE); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +extfs_setctl (const vfs_path_t * vpath, int ctlop, void *arg) +{ + (void) arg; + + if (ctlop == VFS_SETCTL_RUN) + { + extfs_run (vpath); + return 1; + } + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +vfs_init_extfs (void) +{ + vfs_init_subclass (&extfs_subclass, "extfs", VFSF_UNKNOWN, NULL); + vfs_extfs_ops->init = extfs_init; + vfs_extfs_ops->done = extfs_done; + vfs_extfs_ops->fill_names = extfs_fill_names; + vfs_extfs_ops->which = extfs_which; + vfs_extfs_ops->open = extfs_open; + vfs_extfs_ops->close = extfs_close; + vfs_extfs_ops->read = extfs_read; + vfs_extfs_ops->write = extfs_write; + vfs_extfs_ops->opendir = extfs_opendir; + vfs_extfs_ops->readdir = extfs_readdir; + vfs_extfs_ops->closedir = extfs_closedir; + vfs_extfs_ops->stat = extfs_stat; + vfs_extfs_ops->lstat = extfs_lstat; + vfs_extfs_ops->fstat = extfs_fstat; + vfs_extfs_ops->chmod = extfs_chmod; + vfs_extfs_ops->chown = extfs_chown; + vfs_extfs_ops->readlink = extfs_readlink; + vfs_extfs_ops->unlink = extfs_unlink; + vfs_extfs_ops->chdir = extfs_chdir; + vfs_extfs_ops->ferrno = extfs_errno; + vfs_extfs_ops->lseek = extfs_lseek; + vfs_extfs_ops->getid = extfs_getid; + vfs_extfs_ops->getlocalcopy = extfs_getlocalcopy; + vfs_extfs_ops->ungetlocalcopy = extfs_ungetlocalcopy; + vfs_extfs_ops->mkdir = extfs_mkdir; + vfs_extfs_ops->rmdir = extfs_rmdir; + vfs_extfs_ops->setctl = extfs_setctl; + extfs_subclass.free_inode = extfs_free_inode; + extfs_subclass.free_archive = extfs_free_archive; + vfs_register_class (vfs_extfs_ops); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/extfs/extfs.h b/src/vfs/extfs/extfs.h new file mode 100644 index 0000000..c576dc0 --- /dev/null +++ b/src/vfs/extfs/extfs.h @@ -0,0 +1,18 @@ +#ifndef MC__VFS_EXTFS_H +#define MC__VFS_EXTFS_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_extfs (void); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__VFS_CPIO_H */ diff --git a/src/vfs/extfs/helpers/Makefile.am b/src/vfs/extfs/helpers/Makefile.am new file mode 100644 index 0000000..f1ea0ac --- /dev/null +++ b/src/vfs/extfs/helpers/Makefile.am @@ -0,0 +1,76 @@ +extfsdir = $(libexecdir)/@PACKAGE@/extfs.d + +# Files to install and distribute other than extfs scripts +EXTFS_MISC = README README.extfs + +# Scripts hat don't need adaptation to the local system +EXTFS_CONST = bpp changesetfs gitfs+ patchsetfs rpm trpm u7z uc1541 + +# Scripts that need adaptation to the local system - source files +EXTFS_IN = \ + a+.in \ + apt+.in \ + audio.in \ + deb.in \ + deba.in \ + debd.in \ + dpkg+.in \ + iso9660.in \ + hp48+.in \ + lslR.in \ + mailfs.in \ + patchfs.in \ + rpms+.in \ + s3+.in \ + uace.in \ + ualz.in \ + uar.in \ + uarc.in \ + uarj.in \ + ucab.in \ + uha.in \ + ulha.in \ + ulib.in \ + unar.in \ + urar.in \ + uwim.in \ + uzip.in \ + uzoo.in + +# Scripts that need adaptation to the local system - files to install +EXTFS_OUT = \ + a+ \ + apt+ \ + audio \ + deb \ + deba \ + debd \ + dpkg+ \ + iso9660 \ + hp48+ \ + lslR \ + mailfs \ + patchfs \ + rpms+ \ + s3+ \ + uace \ + ualz \ + uar \ + uarc \ + uarj \ + ucab \ + uha \ + ulha \ + ulib \ + unar \ + urar \ + uwim \ + uzip \ + uzoo + +if ENABLE_VFS_EXTFS +extfs_DATA = $(EXTFS_MISC) +extfs_SCRIPTS = $(EXTFS_CONST) $(EXTFS_OUT) +endif + +EXTRA_DIST = $(EXTFS_MISC) $(EXTFS_CONST) diff --git a/src/vfs/extfs/helpers/Makefile.in b/src/vfs/extfs/helpers/Makefile.in new file mode 100644 index 0000000..0a240fb --- /dev/null +++ b/src/vfs/extfs/helpers/Makefile.in @@ -0,0 +1,812 @@ +# 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/extfs/helpers +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 = a+ apt+ audio deb deba debd dpkg+ iso9660 hp48+ \ + lslR mailfs patchfs rpms+ s3+ uace ualz uar uarc uarj ucab uha \ + ulha ulib unar urar uwim uzip uzoo +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(extfsdir)" "$(DESTDIR)$(extfsdir)" +SCRIPTS = $(extfs_SCRIPTS) +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 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(extfs_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/a+.in \ + $(srcdir)/apt+.in $(srcdir)/audio.in $(srcdir)/deb.in \ + $(srcdir)/deba.in $(srcdir)/debd.in $(srcdir)/dpkg+.in \ + $(srcdir)/hp48+.in $(srcdir)/iso9660.in $(srcdir)/lslR.in \ + $(srcdir)/mailfs.in $(srcdir)/patchfs.in $(srcdir)/rpms+.in \ + $(srcdir)/s3+.in $(srcdir)/uace.in $(srcdir)/ualz.in \ + $(srcdir)/uar.in $(srcdir)/uarc.in $(srcdir)/uarj.in \ + $(srcdir)/ucab.in $(srcdir)/uha.in $(srcdir)/ulha.in \ + $(srcdir)/ulib.in $(srcdir)/unar.in $(srcdir)/urar.in \ + $(srcdir)/uwim.in $(srcdir)/uzip.in $(srcdir)/uzoo.in README +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@ +extfsdir = $(libexecdir)/@PACKAGE@/extfs.d + +# Files to install and distribute other than extfs scripts +EXTFS_MISC = README README.extfs + +# Scripts hat don't need adaptation to the local system +EXTFS_CONST = bpp changesetfs gitfs+ patchsetfs rpm trpm u7z uc1541 + +# Scripts that need adaptation to the local system - source files +EXTFS_IN = \ + a+.in \ + apt+.in \ + audio.in \ + deb.in \ + deba.in \ + debd.in \ + dpkg+.in \ + iso9660.in \ + hp48+.in \ + lslR.in \ + mailfs.in \ + patchfs.in \ + rpms+.in \ + s3+.in \ + uace.in \ + ualz.in \ + uar.in \ + uarc.in \ + uarj.in \ + ucab.in \ + uha.in \ + ulha.in \ + ulib.in \ + unar.in \ + urar.in \ + uwim.in \ + uzip.in \ + uzoo.in + + +# Scripts that need adaptation to the local system - files to install +EXTFS_OUT = \ + a+ \ + apt+ \ + audio \ + deb \ + deba \ + debd \ + dpkg+ \ + iso9660 \ + hp48+ \ + lslR \ + mailfs \ + patchfs \ + rpms+ \ + s3+ \ + uace \ + ualz \ + uar \ + uarc \ + uarj \ + ucab \ + uha \ + ulha \ + ulib \ + unar \ + urar \ + uwim \ + uzip \ + uzoo + +@ENABLE_VFS_EXTFS_TRUE@extfs_DATA = $(EXTFS_MISC) +@ENABLE_VFS_EXTFS_TRUE@extfs_SCRIPTS = $(EXTFS_CONST) $(EXTFS_OUT) +EXTRA_DIST = $(EXTFS_MISC) $(EXTFS_CONST) +all: all-am + +.SUFFIXES: +$(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/extfs/helpers/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/vfs/extfs/helpers/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): +a+: $(top_builddir)/config.status $(srcdir)/a+.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +apt+: $(top_builddir)/config.status $(srcdir)/apt+.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +audio: $(top_builddir)/config.status $(srcdir)/audio.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +deb: $(top_builddir)/config.status $(srcdir)/deb.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +deba: $(top_builddir)/config.status $(srcdir)/deba.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +debd: $(top_builddir)/config.status $(srcdir)/debd.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +dpkg+: $(top_builddir)/config.status $(srcdir)/dpkg+.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +iso9660: $(top_builddir)/config.status $(srcdir)/iso9660.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +hp48+: $(top_builddir)/config.status $(srcdir)/hp48+.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +lslR: $(top_builddir)/config.status $(srcdir)/lslR.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +mailfs: $(top_builddir)/config.status $(srcdir)/mailfs.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +patchfs: $(top_builddir)/config.status $(srcdir)/patchfs.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +rpms+: $(top_builddir)/config.status $(srcdir)/rpms+.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +s3+: $(top_builddir)/config.status $(srcdir)/s3+.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +uace: $(top_builddir)/config.status $(srcdir)/uace.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ualz: $(top_builddir)/config.status $(srcdir)/ualz.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +uar: $(top_builddir)/config.status $(srcdir)/uar.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +uarc: $(top_builddir)/config.status $(srcdir)/uarc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +uarj: $(top_builddir)/config.status $(srcdir)/uarj.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ucab: $(top_builddir)/config.status $(srcdir)/ucab.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +uha: $(top_builddir)/config.status $(srcdir)/uha.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ulha: $(top_builddir)/config.status $(srcdir)/ulha.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +ulib: $(top_builddir)/config.status $(srcdir)/ulib.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +unar: $(top_builddir)/config.status $(srcdir)/unar.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +urar: $(top_builddir)/config.status $(srcdir)/urar.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +uwim: $(top_builddir)/config.status $(srcdir)/uwim.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +uzip: $(top_builddir)/config.status $(srcdir)/uzip.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +uzoo: $(top_builddir)/config.status $(srcdir)/uzoo.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-extfsSCRIPTS: $(extfs_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(extfs_SCRIPTS)'; test -n "$(extfsdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(extfsdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(extfsdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(extfsdir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(extfsdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-extfsSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(extfs_SCRIPTS)'; test -n "$(extfsdir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(extfsdir)'; $(am__uninstall_files_from_dir) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-extfsDATA: $(extfs_DATA) + @$(NORMAL_INSTALL) + @list='$(extfs_DATA)'; test -n "$(extfsdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(extfsdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(extfsdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(extfsdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(extfsdir)" || exit $$?; \ + done + +uninstall-extfsDATA: + @$(NORMAL_UNINSTALL) + @list='$(extfs_DATA)'; test -n "$(extfsdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(extfsdir)'; $(am__uninstall_files_from_dir) +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + +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 $(SCRIPTS) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(extfsdir)" "$(DESTDIR)$(extfsdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +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 mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-extfsDATA install-extfsSCRIPTS + +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 Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-extfsDATA uninstall-extfsSCRIPTS + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool 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-extfsDATA \ + install-extfsSCRIPTS 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-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ + uninstall-am uninstall-extfsDATA uninstall-extfsSCRIPTS + +.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/extfs/helpers/README b/src/vfs/extfs/helpers/README new file mode 100644 index 0000000..64da3d6 --- /dev/null +++ b/src/vfs/extfs/helpers/README @@ -0,0 +1,200 @@ + Writing scripts for Midnight Commander's external vfs + +IMPORTANT NOTE: There may be some bugs left in extfs. Enjoy. + +Starting with version 3.1, the Midnight Commander comes with so called +extfs, which is one of the virtual filesystems. This system makes it +possible to create new virtual filesystems for the GNU MC very easily. + +To handle requests, create a shell/perl/python/etc script/program +(with executable permissions) in $(libexecdir)/mc/extfs.d +or in ~/.local/share/mc/extfs.d/. + +(Note: $(libexecdir) should be substituted for actual libexecdir path +stored when configured or compiled, like /usr/local/libexec or /usr/libexec). + +Assign a vfs suffix. For example, if you have .zip file, and would like +to see what's inside it, path will be + +/anypath/my.zip/uzip://some_path/... + +In this example, .zip is suffix, but I call vfs 'uzip'. Why? Well, +what this vfs essentially does is UNzip. UN is too long, so I chose +U. Note that sometime in future filesystem like zip may exist: It will +take whole tree and create .zip file from it. So /usr/zip:// will be +zipfile containing whole /usr tree. + +If your vfs does not require file to work on, add '+' to the end of name. +Note, that trailing '+' in file name is not a part of vfs name, it is +just an vfs attribute. So you have not use it in vfs commands: + +cd rpms:// + +is correct command, and + +cd rpms+:// + +is incorrect command. + + +* Commands that should be implemented by your shell script +---------------------------------------------------------- + +Return zero from your script upon completion of the command, otherwise +nonzero for failure or in case of an unsupported command. + +$libdir/extfs/prefix command [arguments] + +* Command: list archivename + +This command should list the complete archive content in the following format +(a little modified ls -l listing): + +AAAAAAA NNN OOOOOOOO GGGGGGGG SSSSSSSS DATETIME [PATH/]FILENAME [-> [PATH/]FILENAME[/]]] + +where (things in [] are optional): + +AAAAAAA is the permission string like in ls -l +NNN is the number of links +OOOOOOOO is the owner (either UID or name) +GGGGGGGG is the group (either GID or name) +SSSSSSSS is the file size +FILENAME is the filename +PATH is the path from the archive's root without the leading slash (/) +DATETIME has one of the following formats: + Mon DD hh:mm[:ss], Mon DD YYYY, MM-DD-YYYY hh:mm[:ss] + + where Mon is a three letter English month name, DD is day + 01-31 (can be 1-31, if following Mon), MM is month 01-12, + YYYY is four digit year, hh is hours, mm is minutes, + and ss is optional seconds. + +If the -> [PATH/]FILENAME part is present, it means: + +If permissions start with an l (ell), then it is the name that symlink +points to. (If this PATH starts with a MC vfs prefix, then it is a symlink +somewhere to the other virtual filesystem (if you want to specify path from +the local root, use local:/path_name instead of /path_name, since /path_name +means from root of the archive listed). + +If permissions do not start with l, but number of links is greater than one, +then it says that this file should be a hardlinked with the other file. + +The result of list command must not contain "." and ".." items. + +* Command: copyout archivename storedfilename extractto + +This should extract from archive archivename the file called +storedfilename (possibly with path if not located in archive's root +[this is wrong. current extfs strips paths! -- pavel@ucw.cz]) +to file extractto. + +* Command: copyin archivename storedfilename sourcefile + +This should add to the archivename the sourcefile with the name +storedfilename inside the archive. + +Important note: archivename in the above examples may not have the +extension you are expecting to have, like it may happen that +archivename will be something like /tmp/f43513254 or just +anything. Some archivers do not like it, so you'll have to find some +workaround. + +* Command: rm archivename storedfilename + +This should remove storedfilename from archivename. + +* Command: mkdir archivename dirname + +This should create a new directory called dirname inside archivename. + +* Command: rmdir archivename dirname + +This should remove an existing directory dirname. If the directory is +not empty, mc will recursively delete it (possibly prompting). + +* Command: run + +Undocumented :-) + +--------------------------------------------------------- + +Don't forget to mark this file executable (chmod 755 ThisFile, for example) + +For skeleton structure of executable, look at some of filesystems +similar to yours. + +--------------------------------------------------------- + +In constructing these routines, errors will be made, and mc will not display +a malformed printing line. That can lead the programmer down many false +trails in search of the bug. Since this routine is an executable shell script +it can be run from the command line independently of mc, and its output will +show on the console or can be redirected to a file. + +* Putting it to use +---------------------------------------------------------- +The file .mc.ext in a home directory, and in mc's user directory (commonly +/etc/mc), contains instructions for operations on files depending +on filename extensions. It is well documented in other files in this +distribution, so here are just a few notes specifically on use of the +Virtual File System you just built. + +There are entries in .mc.ext defining a few operations that can be done on a +file from an mc panel. Typically they are annotated with a hash mark and a +file extension like this: + +# zip + +There must be a way to find the file by extension, so the next line does +that. In essence it says "identify the string ".zip" or (|) ".ZIP" at the +end ($) of a filename": + +regex/\.(zip|ZIP)$ + +The operations themselves follow that. They must be indented by at least a +space, and a tab works as well. In particular, the Open operation will +now use your new virtual file system by cd'ing to it like this: + + Open=%cd zip:%d/%p + +This is the line used when a file is highlighted in a panel and the user +presses <Enter> or <Return>. The contents of the archive should show just +as if they were in a real directory, and can be manipulated as such. +The rest of the entry pertains to use of the F3 View key: + + View=%view{ascii} unzip -v %f + +And perhaps an optional icon for X: + + Icon=zip.xpm + +And perhaps an operation to extract the contents of the file, called from +a menu selection: + + Extract=unzip %f '*' + +This is just an example. The current entry for .zip files has a menu selection +of 'Unzip' which could be used in place of 'Extract'. What goes here depends +on what items you have in, or add to, the menu system, and that's another +subject. The sum of this is the .mc.ext entry: + +# zip +regex/\.(zip|ZIP)$ + Open=%cd %p/uzip:// + View=%view{ascii} unzip -v %f + Icon=zip.xpm + Extract=unzip %f '*' + +Add an entry like this to the .mc.ext file in a user's home directory, If you +want others to have it, add it to the mc.ext file in the mc system directory, +often /etc/mc/mc.ext. Notice this file is not prepended with a dot. + +Once all this is done, and things are in their proper places, exit mc if you +were using it, and restart it so it picks up the new information. + +That's all there is to it. The hardest part is making a listing function +that sorts the output of a system listing command and turns it into a form +that mc can use. Currently awk (or gawk) is used because nearly all systems +have it. If another scripting language is available, like perl, that could +also be used. diff --git a/src/vfs/extfs/helpers/README.extfs b/src/vfs/extfs/helpers/README.extfs new file mode 100644 index 0000000..ce7d086 --- /dev/null +++ b/src/vfs/extfs/helpers/README.extfs @@ -0,0 +1,78 @@ +# Each external VFS type must be registered in extfs.d directory if you want to use it. +# Trailing plus means that the filesystem is not tied to a certain file. + +# Popular PC archivers +uzip +uzoo +ulha +urar +uha +u7z +ualz +# FIXME: for arj usage you need a special patch to unarj (see unarj.diff) +uarj +uarc +uace + +# For cab files +ucab + +# ar is used for static libraries +uar + +# Packages from popular Linux distributions +rpm +deb + +# a+ - mtools filesystem +a+ + +# For browsing lslR listings (found on many ftp sites) +lslR + +# Hewlett Packard calculator +hp48+ + +# Commodore 64/128 d64/D64 files +uc1541 + +# Break patches into chunks +patchfs + +# Represents a mailbox as a directory +mailfs + +# List all installed RPM packages on the system +rpms+ +trpm + +# dpkg frontend +dpkg+ +debd + +# apt frontend +apt+ +deba + +# Simple filesystem for audio cdroms. Use /dev/cdrom#audio (or /#audio) +audio + +# Package of Bad Penguin (an Italian GNU/Linux distribution) +bpp + +# ISO image +iso9660 + +# Amazon S3 +s3+ + +# git frontend +gitfs - browse the git repo +changesetfs - list of versions of current file +patchsetfs - list of patches of current file + +# Gputils lib archives. +ulib + +# PAK Archive +unar diff --git a/src/vfs/extfs/helpers/a+.in b/src/vfs/extfs/helpers/a+.in new file mode 100644 index 0000000..fe446f4 --- /dev/null +++ b/src/vfs/extfs/helpers/a+.in @@ -0,0 +1,126 @@ +#! @PERL@ +# +# External filesystem for mc, using mtools +# Written Ludek Brukner <lubr@barco.cz>, 1997 +# Much improved by Tom Perkins <968794022@noid.net>, 2000 +# +# WARNING - This software is ALPHA - Absolutely NO WARRANTY +# + +# These mtools components must be in PATH for this to work + +use warnings; + +sub quote { + $_ = shift(@_); + s/([^\w\/.+-])/\\$1/g; + return($_); +} + +$mmd = "mmd"; +$mrd = "mrd"; +$mdel = "mdel"; +$mdir = "mdir -a"; +$mcopy = "mcopy -noQ"; + +$0 =~ s|.*/||; +$qdisk = quote($0); + +$ENV{MTOOLS_DATE_STRING} = "mm-dd-yyyy"; +$ENV{MTOOLS_TWENTY_FOUR_HOUR_CLOCK} = "1"; + +SWITCH: for ( $ARGV[0] ) { + /list/ && do { + @dirs = get_dirs(""); + while ($dir = shift(@dirs)) { + push @dirs, get_dirs("$dir/"); + } exit 0; }; + /mkdir/ && do { + shift; shift; + exit 1 if scalar(@ARGV) != 1; + $qname = quote($ARGV[0]); + system("$mmd $qdisk:/$qname >/dev/null"); + exit 0; }; + /rmdir/ && do { + shift; shift; + exit 1 if scalar(@ARGV) != 1; + $qname = quote($ARGV[0]); + system("$mrd $qdisk:/$qname >/dev/null"); + exit 0; }; + /rm/ && do { + shift; shift; + exit 1 if scalar(@ARGV) != 1; + $qname = quote($ARGV[0]); + system("$mdel $qdisk:/$qname >/dev/null"); + exit 0; }; + /copyout/ && do { + shift; shift; + exit 1 if scalar(@ARGV) != 2; + ( $qsrc, $qdest ) = @ARGV; + $qsrc = quote($qsrc); + $qdest = quote($qdest); + system("$mcopy $qdisk:/$qsrc $qdest >/dev/null"); + exit 0; }; + /copyin/ && do { + shift; shift; + exit 1 if scalar(@ARGV) != 2; + ( $qdest, $qsrc ) = @ARGV; + $qsrc = quote($qsrc); + $qdest = quote($qdest); + system("$mcopy $qsrc $qdisk:/$qdest >/dev/null"); + exit 0; }; + /.*/ && do { # an unfamiliar command + exit 1; }; +} + +sub get_dirs { + my ($path, $name, $size, $date, $time, $longname, @lst, @rv); + $path = shift(@_); + my $qpath = quote($path); + @rv = (); + + open(FILE,"$mdir $qdisk:/$qpath |"); + while ( <FILE> ) { + chomp(); + /^ / && next; # ignore `non-file' lines + m{^Directory for $0:/}i && next; # ignore `non-file' lines + /^$/ && next; # ignore empty lines + /^\.\.?/ && next; # ignore `.' and `..' + + $name = substr($_,0,12); + $name =~ s/^([^ ]*) +([^ ]+)[ \t]*$/$1.$2/; + $name =~ s/[ .]+$//; + + $_ = substr($_,12); + s/^[ ]+//; + + ($size,$date,$time,$longname) = split(/[ \t]+/, $_, 4); + + defined $time || next; + + # process "am" and "pm". Should not be needed if + # MTOOLS_TWENTY_FOUR_HOUR_CLOCK is respected. + @lst = split(/([:ap])/, $time); + $lst[0] += 12 if (defined $lst[3] && $lst[3] eq "p"); + + $time = sprintf("%02d:%02d", $lst[0], $lst[2]); + @lst = split(/-/, $date); + $lst[2] %= 100 if ($lst[2] > 100); + $date = sprintf ("%02d-%02d-%02d", @lst); + + $name = $path . lc(($longname) ? $longname : $name); + + if ($size =~ /DIR/) { + printf("drwxr-xr-x 1 %-8d %-8d %8d %s %s %s\n", + 0, 0, 0, $date, $time, $name); + push @rv, $name; + } else { + printf("-rw-r--r-- 1 %-8d %-8d %8d %s %s %s\n", + 0, 0, $size, $date, $time, $name); + } + } + close(FILE); + return @rv; +} + +1; diff --git a/src/vfs/extfs/helpers/apt+.in b/src/vfs/extfs/helpers/apt+.in new file mode 100644 index 0000000..60011e6 --- /dev/null +++ b/src/vfs/extfs/helpers/apt+.in @@ -0,0 +1,359 @@ +#! @PERL@ +# +# 1999 (c) Piotr Roszatycki <dexter@debian.org> +# This software is under GNU license +# last modification: 1999-12-08 +# +# apt + +sub quote { + $_ = shift(@_); + s/([^\w\/.+-])/\\$1/g; + return($_); +} + +sub bt +{ + my ($dt) = @_; + my (@time); + @time = localtime($dt); + $bt = sprintf "%02d-%02d-%d %02d:%02d", $time[4] + 1, $time[3], + $time[5] + 1900, $time[2], $time[1]; + return $bt; +} + + +sub ft +{ + my ($f) = @_; + return "d" if -d $f; + return "l" if -l $f; + return "p" if -p $f; + return "S" if -S $f; + return "b" if -b $f; + return "c" if -c $f; + return "-"; +} + +sub fm +{ + my ($n) = @_; + my ($m); + + if( $n & 0400 ) { + $m .= "r"; + } else { + $m .= "-"; + } + if( $n & 0200 ) { + $m .= "w"; + } else { + $m .= "-"; + } + if( $n & 04000 ) { + $m .= "s"; + } elsif( $n & 0100 ) { + $m .= "x"; + } else { + $m .= "-"; + } + + if( $n & 0040 ) { + $m .= "r"; + } else { + $m .= "-"; + } + if( $n & 0020 ) { + $m .= "w"; + } else { + $m .= "-"; + } + if( $n & 02000 ) { + $m .= "s"; + } elsif( $n & 0010 ) { + $m .= "x"; + } else { + $m .= "-"; + } + + if( $n & 0004 ) { + $m .= "r"; + } else { + $m .= "-"; + } + if( $n & 0002 ) { + $m .= "w"; + } else { + $m .= "-"; + } + if( $n & 01000 ) { + $m .= "t"; + } elsif( $n & 0001 ) { + $m .= "x"; + } else { + $m .= "-"; + } + + return $m; +} + +sub ls { + my ($file,$path,$mode) = @_; + + if (-f $file) { + my @stat = stat(_); + # mode, nlink, uid, gid, size, mtime, filename + printf "%s %d %d %d %d %s %s\n", $mode || ft($file).fm($stat[2] & 07777), + $stat[3], $stat[4], $stat[5], $stat[7], bt($stat[9]), $path; + } +} + +$DATE=bt(time()); + +sub list +{ + my ($pkg, $fn, $dn, $sz, $bt); + + my($check,$stats,$config); + chop($check = `apt-get -q check 2>/dev/null`); + chop($available = `apt-cache dumpavail 2>/dev/null`); + chop($stats = `apt-cache stats 2>/dev/null`); + chop($config = `apt-config dump 2>&1`); + $sz = length($check); + print "-r--r--r-- 1 root root $sz $DATE CHECK\n"; + $sz = length($available); + print "-r--r--r-- 1 root root $sz $DATE AVAILABLE\n"; + $sz = length($stats); + print "-r--r--r-- 1 root root $sz $DATE STATS\n"; + $sz = length($config); + print "-r--r--r-- 1 root root $sz $DATE CONFIG\n"; + $sz = length($pressupdate); + print "-r-xr--r-- 1 root root $sz $DATE UPDATE\n"; + $sz = length($pressupgrade); + print "-r-xr--r-- 1 root root $sz $DATE UPGRADE\n"; + print "-r-xr--r-- 1 root root $sz $DATE DIST-UPGRADE\n"; + + ls("/etc/apt/sources.list","sources.list"); + ls('/etc/apt/apt.conf','apt.conf') if (-f '/etc/apt/apt.conf'); + + print "drwxr-xr-x 1 root root 0 $DATE all\n"; + + if ( open(PIPEIN, "find /var/cache/apt/archives -type f |") ) { + while(<PIPEIN>) { + chop; + next if /\/lock$/; + my $file = $_; + s%/var/cache/apt/archives/%CACHE/%; + ls($file, $_); + } + close PIPEIN; + } + + my %sects = (); + my %debd = (); + my %deba = (); + + open STAT, "/var/lib/dpkg/status" + or exit 1; + while( <STAT> ) { + chop; + if( /^([\w-]*): (.*)/ ) { + $pkg = $2 if( lc($1) eq 'package' ); + $debd{$pkg}{lc($1)} = $2; + } + } + close STAT; + + foreach $pkg (sort keys %debd) { + next if $debd{$pkg}{status} =~ /not-installed/; + $fn = $debd{$pkg}{package}. "_". $debd{$pkg}{version}; + $dn = $debd{$pkg}{section}; + if( ! $dn ) { + $dn = "unknown"; + } elsif( $dn =~ /^(non-us)$/i ) { + $dn .= "/main"; + } elsif( $dn !~ /\// ) { + $dn = "main/". $dn; + } + unless( $sects{$dn} ) { + my $sub = $dn; + while( $sub =~ s!^(.*)/[^/]*$!$1! ) { + unless( $sects{$sub} ) { + print "drwxr-xr-x 1 root root 0 $DATE $sub/\n"; + $sects{$sub} = 1; + } + } + print "drwxr-xr-x 1 root root 0 $DATE $dn/\n"; + $sects{$dn} = 1; + } + $sz = $debd{$pkg}{'status'} =~ /config-files/ ? 0 : $debd{$pkg}{'installed-size'} * 1024; + @stat = stat("/var/lib/dpkg/info/".$debd{$pkg}{package}.".list"); + $bt = bt($stat[9]); + print "-rw-r--r-- 1 root root $sz $bt $dn/$fn.debd\n"; + print "lrwxrwxrwx 1 root root $sz $bt all/$fn.debd -> ../$dn/$fn.debd\n"; + } + + open STAT, "apt-cache dumpavail |" + or exit 1; + while( <STAT> ) { + chop; + if( /^([\w-]*): (.*)/ ) { + $pkg = $2 if( lc($1) eq 'package' ); + $deba{$pkg}{lc($1)} = $2; + } + } + close STAT; + + foreach $pkg (sort keys %deba) { + next if $deba{$pkg}{version} eq $debd{$pkg}{version}; + $fn = $deba{$pkg}{package}. "_". $deba{$pkg}{version}; + $dn = $deba{$pkg}{section}; + if( ! $dn ) { + $dn = "unknown"; + } elsif( $dn =~ /^(non-us)$/i ) { + $dn .= "/main"; + } elsif( $dn !~ /\// ) { + $dn = "main/". $dn; + } + unless( $sects{$dn} ) { + my $sub = $dn; + while( $sub =~ s!^(.*)/[^/]*$!$1! ) { + unless( $sects{$sub} ) { + print "drwxr-xr-x 1 root root 0 $DATE $sub/\n"; + $sects{$sub} = 1; + } + } + print "drwxr-xr-x 1 root root 0 $DATE $dn/\n"; + $sects{$dn} = 1; + } + $sz = $deba{$pkg}{'status'} =~ /config-files/ ? 0 : $deba{$pkg}{'installed-size'} * 1024; + print "-rw-r--r-- 1 root root $sz $DATE $dn/$fn.deba\n"; + print "lrwxrwxrwx 1 root root $sz $DATE all/$fn.deba -> ../$dn/$fn.deba\n"; + } +} + +sub copyout +{ + my($archive,$filename) = @_; + my $qarchive = quote($archive); + my $qfilename = quote($filename); + if( $archive eq 'CHECK' ) { + system("apt-get -q check > $qfilename"); + } elsif( $archive eq 'AVAILABLE' ) { + system("apt-cache dumpavail > $qfilename"); + } elsif( $archive eq 'STATS' ) { + system("apt-cache stats > $qfilename"); + } elsif( $archive eq 'CONFIG' ) { + system("(apt-config dump 2>&1) > $qfilename"); + } elsif( $archive eq 'UPDATE' ) { + open O, ">$filename"; + print O $pressupdate; + close O; + } elsif( $archive eq 'UPGRADE' || $archive eq 'DIST-UPGRADE' ) { + open O, ">$filename"; + print O $pressupgrade; + close O; + } elsif( $archive eq 'apt.conf' ) { + system("cp /etc/apt/apt.conf $qfilename"); + } elsif( $archive eq 'sources.list' ) { + system("cp /etc/apt/sources.list $qfilename"); + } elsif( $archive =~ /^CACHE\// ) { + $archive =~ s%^CACHE/%/var/cache/apt/archives/%; + system("cp $qarchive $qfilename"); + } else { + open O, ">$filename"; + print O $archive, "\n"; + close O; + } +} + +sub copyin +{ + my($archive,$filename) = @_; + my $qarchive = quote($archive); + my $qfilename = quote($filename); + if( $archive =~ /\.deb$/ ) { + system("dpkg -i $qfilename>/dev/null"); + } elsif( $archive eq 'apt.conf' ) { + system("cp $qfilename /etc/apt/apt.conf"); + } elsif( $archive eq 'sources.list' ) { + system("cp $qfilename /etc/apt/sources.list"); + } elsif( $archive =~ /^CACHE\// ) { + $qarchive =~ s%^CACHE/%/var/cache/apt/archives/%; + system("cp $qfilename $qarchive"); + } else { + die "extfs: cannot create regular file \`$archive\': Permission denied\n"; + } +} + +sub run +{ + my($archive,$filename) = @_; + if( $archive eq 'UPDATE' ) { + system("apt-get update"); + } elsif( $archive eq 'UPGRADE' ) { + system("apt-get upgrade -u"); + } elsif( $archive eq 'DIST-UPGRADE' ) { + system("apt-get dist-upgrade -u"); + } else { + die "extfs: $archive: command not found\n"; + } +} + +sub rm +{ + my($archive) = @_; + my $qarchive = quote($archive); + if( $archive =~ /^CACHE\// ) { + $qarchive =~ s%^CACHE/%/var/cache/apt/archives/%; + system("rm -f $qarchive"); + } elsif( $archive eq 'apt.conf' ) { + system("rm -f /etc/apt/apt.conf"); + } elsif( $archive eq 'sources.list' ) { + system("rm -f /etc/apt/sources.list"); + } elsif( $archive =~ /\.debd?$/ ) { + # uncommented and changed to use dpkg - alpha + my $qname = $qarchive; + $qname =~ s%.*/%%g; + $qname =~ s%_.*%%g; + system("dpkg --remove $qname >/dev/null"); + die("extfs: $archive: Operation not permitted\n") if $? != 0; + } else { + die "extfs: $archive: Operation not permitted\n"; + } +} + + +$pressupdate=<<EOInstall; + + WARNING + Don\'t use this method if you don't want to retrieve new lists of packages. + ========================================================================== + +This is not a real file. It is a way to retrieve new lists of packages. +To update this information go back to the panel and press Enter on this file. + +EOInstall + +$pressupgrade=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to perform an upgrade. + =================================================================== + +This is not a real file. It is a way to perform an upgrade. +To upgrade this information go back to the panel and press Enter on this file. + +EOInstall + + +# override any locale for dates +$ENV{"LC_ALL"}="C"; + +if ($ARGV[0] eq "list") { list(); exit(0); } +elsif ($ARGV[0] eq "copyout") { copyout($ARGV[2], $ARGV[3]); exit(0); } +elsif ($ARGV[0] eq "copyin") { copyin($ARGV[2], $ARGV[3]); exit(0); } +elsif ($ARGV[0] eq "run") { run($ARGV[2]); exit(0); } +elsif ($ARGV[0] eq "rm") { rm($ARGV[2]); exit(0); } +exit(1); + diff --git a/src/vfs/extfs/helpers/audio.in b/src/vfs/extfs/helpers/audio.in new file mode 100755 index 0000000..05c8c65 --- /dev/null +++ b/src/vfs/extfs/helpers/audio.in @@ -0,0 +1,53 @@ +#! /bin/sh +# +# Written by Pavel Machek +# CDDB support by Adam Byrtek +# +# (C) 2000 The Free Software Foundation. +# + +set -e + +CDDB_SERVER="http://freedb.freedb.org" +CDDB_HANDSHAKE="hello=user+localhost+mc+1.0&proto=1" +CDDB_TIMEOUT=20 # in seconds + +audiofs_list() +{ + DATE=`date +"%b %d %H:%M"` + echo "-r--r--r-- 1 0 0 0 $DATE CDDB" + cdparanoia -Q -d "$1" 2>&1 | grep '^[ 0-9][ 0-9][ 0-9]\.' | while read A B C + do + A=`echo "$A" | sed -e 's/\.//' -e 's/^\(.\)$/0\1/'` + SIZE=`expr 44 + $B \* 2352` + echo "-r--r--r-- 1 0 0 $SIZE $DATE track-${A}.wav" + done +} + +audiofs_copyout() +{ + if [ x"$2" = x"CDDB" ]; then + DISCID=`cd-discid "$1" | tr " " "+"` + if [ -z "$DISCID" ]; then + exit 1 + fi + RESPONSE=`wget -q -T $CDDB_TIMEOUT -O - "$CDDB_SERVER/~cddb/cddb.cgi?cmd=cddb+query+$DISCID&$CDDB_HANDSHAKE" | tee "$3" | @AWK@ '/^200/ { print $2,$3; }'` + wget -q -T $CDDB_TIMEOUT -O - "$CDDB_SERVER/~cddb/cddb.cgi?cmd=cddb+read+$RESPONSE&$CDDB_HANDSHAKE" | grep -v "^#" >> "$3" + else + TRACK=`echo "$2" | sed 's/track-0*//' | sed 's/\.wav//'` + cdparanoia -q -d "$1" "$TRACK" "$3" >/dev/null + fi +} + +if [ ! -b "$2" ] +then + BASE="/dev/cdrom" +else + BASE="$2" +fi + +case "$1" in + list) audiofs_list "$BASE"; exit 0;; + copyout) audiofs_copyout "$BASE" "$3" "$4"; exit 0;; +esac +exit 1 diff --git a/src/vfs/extfs/helpers/bpp b/src/vfs/extfs/helpers/bpp new file mode 100755 index 0000000..f71fe7e --- /dev/null +++ b/src/vfs/extfs/helpers/bpp @@ -0,0 +1,50 @@ +#! /bin/sh +# +# Written by Marco Ciampa 2000 +# (a simple cut & paste from rpm vfs) +# (C) 1996 The Free Software Foundation. +# +# Package of a new italian distribution: Bad Penguin +# http://www.badpenguin.org/ + +# override any locale for dates +unset LC_ALL +LC_TIME=C +export LC_TIME + +mcbppfs_list () +{ + FILEPREF="-r--r--r-- 1 root root " + FIEXPREF="-r-xr-xr-x 1 root root " + DATE=`date +"%b %d %H:%M"` + set x `ls -l "$1"` + size=$6 + echo "$FILEPREF $size $DATE CONTENTS.tar.gz" + echo "$FIEXPREF 35 $DATE INSTALL" + echo "$FIEXPREF 35 $DATE UPGRADE" +} + +mcbppfs_copyout () +{ + case "$2" in + CONTENTS.tar.gz) cat "$1" > "$3"; exit 0;; + INSTALL) echo "# Run this to install this package" > "$3"; exit 0;; + UPGRADE) echo "# Run this to upgrade this package" > "$3"; exit 0;; + esac +} + +mcbppfs_run () +{ + case "$2" in + INSTALL) echo "Installing \"$1\""; package-setup --install "$1"; exit 0;; + UPGRADE) echo "Upgrading \"$1\""; package-setup --update "$1"; exit 0;; + esac +} + +umask 077 +case "$1" in + list) mcbppfs_list "$2"; exit 0;; + copyout) mcbppfs_copyout "$2" "$3" "$4"; exit 0;; + run) mcbppfs_run "$2" "$3"; exit 1;; +esac +exit 1 diff --git a/src/vfs/extfs/helpers/changesetfs b/src/vfs/extfs/helpers/changesetfs new file mode 100755 index 0000000..eebdf8c --- /dev/null +++ b/src/vfs/extfs/helpers/changesetfs @@ -0,0 +1,109 @@ +#!/bin/sh + +LANG=C +export LANG +LC_TIME=C +export LC_TIME + +# --- GIT ----------------------------------------------------------------------- + +found_git_dir() +{ + work_dir=$1 + while [ -n "$work_dir" -a "$work_dir" != "/" ]; do + [ -d "${work_dir}/.git" ] && { + echo "${work_dir}/.git/" + return + } + work_dir=`dirname "$work_dir"` + done + echo '' +} + +changesetfs_list_git() +{ + WORK_DIR=$1; shift + fname=$1; shift + USER=$1; shift + DATE=$1; shift + + GIT_DIR=`found_git_dir "$WORK_DIR"` + [ -z "$GIT_DIR" ] && GIT_DIR=$WORK_DIR + curr_year=`date +"%Y"` + + git --git-dir="$GIT_DIR" log --abbrev=7 --pretty="format:%at %h %an" -- "$fname" | while read TIMESTAMP chset author + do + year=`date -d @"$TIMESTAMP" +"%Y"` + [ "$year" = "$curr_year" ] && { + DATE=`date -d @"$TIMESTAMP" +"%b %d %H:%M"` + } || { + DATE=`date -d @"$TIMESTAMP" +"%b %d %Y"` + } + NAME="$chset $author" + echo "-rw-rw-rw- 1 $USER 0 0 $DATE $NAME `basename $fname`" + done +} + +changesetfs_copyout_git() +{ + WORK_DIR=$1; shift + fname=$1; shift + orig_fname=$1;shift + output_fname=$1;shift + + chset=`echo "$orig_fname"| cut -f 1 -d " "` + + GIT_DIR=`found_git_dir "$WORK_DIR"` + [ -z "$GIT_DIR" ] && GIT_DIR=$WORK_DIR + + filecommit=`git --git-dir="$GIT_DIR" show --raw --pretty=tformat:%h "$chset" -- "$fname"| \ + tail -n1 | \ + sed 's@^::[0-9]*\s*[0-9]*\s*[0-9]*\s*@@' | \ + sed 's@^:[0-9]*\s*[0-9]*\s*@@' | \ + cut -d'.' -f 1` + git --git-dir="$GIT_DIR" show "$filecommit" > "$output_fname" +} + +# --- COMMON -------------------------------------------------------------------- + +changesetfs_list() +{ + VCS_type=$1; shift + WORK_DIR=$1; shift + fname=$1; shift + + DATE=`date +"%b %d %H:%M"` + USER=`whoami` + + case "$VCS_type" in + git) changesetfs_list_git "$WORK_DIR" "$fname" "$USER" "$DATE" ;; + esac +} + +changesetfs_copyout() +{ + VCS_type=$1; shift + WORK_DIR=$1; shift + fname=$1; shift + + case "$VCS_type" in + git) changesetfs_copyout_git "$WORK_DIR" "$fname" "$@" ;; + esac + +} + +# --- MAIN ---------------------------------------------------------------------- + +command=$1; shift +tmp_file=$1; shift + +WORK_DIR=`head -n1 $tmp_file` +fname=`tail -n2 $tmp_file | head -n1` +VCS_type=`tail -n1 $tmp_file` + +case "$command" in + list) changesetfs_list "$VCS_type" "$WORK_DIR" "$fname" ;; + copyout) changesetfs_copyout "$VCS_type" "$WORK_DIR" "$fname" "$@" ;; + *) exit 1 ;; +esac +exit 0 diff --git a/src/vfs/extfs/helpers/deb.in b/src/vfs/extfs/helpers/deb.in new file mode 100644 index 0000000..abc98aa --- /dev/null +++ b/src/vfs/extfs/helpers/deb.in @@ -0,0 +1,203 @@ +#! @PERL@ +# +# Written by Fernando Alegre <alegre@debian.org> 1996 +# +# Applied patch by Dimitri Maziuk <emaziuk@curtin.edu.au> 1997 +# (to handle new tar format) +# +# Modified by Fernando Alegre <alegre@debian.org> 1997 +# (to handle both new and old tar formats) +# +# Modified by Patrik Rak <prak@post.cz> 1998 +# (add by Michael Bramer Debian-mc-maintainer <grisu@debian.org>) +# (to allow access to package control files) +# +# Modified by Martin Bialasinski <martinb@debian.org> 1999 +# (deal with change in tar format) +# +# +# Copyright (C) 1997 Free Software Foundation +# + +sub quote { + $_ = shift(@_); + s/([^\w\/.+-])/\\$1/g; + return($_); +} + +sub mcdebfs_list +{ +# +# CAVEAT: Hard links are listed as if they were symlinks +# Empty directories do not appear at all +# + local($archivename)=@_; + local $qarchivename = quote($archivename); + chop($date=`LC_ALL=C date "+%b %d %H:%M"`); + chop($info_size=`dpkg -I $qarchivename | wc -c`); + $install_size=length($pressinstall); + + print "dr-xr-xr-x 1 root root 0 $date CONTENTS\n"; + print "dr-xr-xr-x 1 root root 0 $date DEBIAN\n"; + print "-r--r--r-- 1 root root $info_size $date INFO\n"; + print "-r-xr--r-- 1 root root $install_size $date INSTALL\n"; + + if ( open(PIPEIN, "LC_ALL=C dpkg-deb -c $qarchivename |") ) + { + while(<PIPEIN>) + { + @_ = split; + + $perm=$_[0]; $owgr=$_[1]; $size=$_[2]; + if($_[3] =~ /^\d\d\d\d\-/) { # New tar format + + ($year,$mon,$day) = split(/-/,$_[3]); + $month = ("Gee","Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec")[$mon] || "Gee"; + $time=$_[4]; + $pathindex=5; + } + else { + $mstring='GeeJanFebMarAprMayJunJulAugSepOctNovDec'; + $month=$_[3]; + $mon=index($mstring,$month) / 3; + $day=$_[4]; + $time=$_[5]; + $year=$_[6]; + $pathindex=7; + } + + $path=$_[$pathindex++]; + # remove leading ./ + $path=~s/^\.\///; + next if ($path eq ''); + $arrow=$_[$pathindex++]; + $link=$_[$pathindex++]; + $link2=$_[$pathindex++]; + + $owgr=~s!/! !; + if($arrow eq 'link') + { +# report hard links as soft links + $arrow='->'; $link="/$link2"; + substr($perm, 0, 1) = "l"; + } + if($arrow ne '') + { + $arrow=' ' . $arrow; + $link= ' ' . $link; + } + $now=`date "+%Y %m"`; + ($thisyear, $thismon) = split(/ /, $now); + # show time for files younger than 6 months + # but not for files with dates in the future + if ($year * 12 + $mon > $thisyear * 12 + $thismon - 6 && + $year * 12 + $mon <= $thisyear * 12 + $thismon) { + print "$perm 1 $owgr $size $month $day $time CONTENTS/$path$arrow$link\n"; + } else { + print "$perm 1 $owgr $size $month $day $year CONTENTS/$path$arrow$link\n"; + } + } + } + if ( open(PIPEIN, "LC_ALL=C dpkg-deb -I $qarchivename |") ) + { + while(<PIPEIN>) + { + @_ = split; + $size=$_[0]; + last if $size =~ /:/; + next if $size !~ /\d+/; + if($_[4] eq '*') + { + $perm='-r-xr-xr-x'; + $name=$_[5]; + } + else + { + $perm='-r--r--r--'; + $name=$_[4]; + } + print "$perm 1 root root $size $date DEBIAN/$name\n"; + } + } +} + +sub mcdebfs_copyout +{ + local($archive,$filename,$destfile)=@_; + local $qarchive = quote($archive); + local $qfilename = quote($filename); + local $qdestfile = quote($destfile); + + if($filename eq "INFO") + { + system("dpkg-deb -I $qarchive > $qdestfile"); + } + elsif($filename =~ /^DEBIAN/) + { + $qfilename=~s!^DEBIAN/!!; + system("dpkg-deb -I $qarchive $qfilename > $qdestfile"); + } + elsif($filename eq "INSTALL") + { + if ( open(FILEOUT,">$destfile") ) + { + print FILEOUT $pressinstall; + close FILEOUT; + system("chmod a+x $qdestfile"); + } + } + else + { + # files can be prepended with ./ or not, depending on the version of tar + $qfilename=~s!^CONTENTS/!!; + system("dpkg-deb --fsys-tarfile $qarchive | tar xOf - $qfilename ./$qfilename > $qdestfile 2>/dev/null"); + } +} + +sub mcdebfs_run +{ + local($archive,$filename)=@_; + local $qarchive = quote($archive); + if($filename eq "INSTALL") + { + print "Installing $archive\n"; + system("dpkg -i $qarchive"); + } + else + { + use File::Temp qw(mkdtemp); + my $template = "/tmp/mcdebfs.run.XXXXXX"; + $template="$ENV{MC_TMPDIR}/mcdebfs.XXXXXX" if ($ENV{MC_TMPDIR}); + $tmpdir = mkdtemp($template); + $tmpcmd="$tmpdir/run"; + &mcdebfs_copyout($archive, $filename, $tmpcmd); + system("chmod u+x $tmpcmd"); + system($tmpcmd); + unlink($tmpcmd); + rmdir($tmpdir); + } +} + +$pressinstall=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to reinstall everything... + +This is not a real file. It is a way to install the package you are browsing. + +To install this package go back to the panel and press Enter on this file. + +In Debian systems, a package is automatically upgraded when you install a new +version of it. There is no special upgrade option. Install always works. + +EOInstall + +umask 077; + +if($ARGV[0] eq "list") { shift; &mcdebfs_list(@ARGV); exit 0; } +elsif($ARGV[0] eq "copyout") { shift; &mcdebfs_copyout(@ARGV); exit 0; } +elsif($ARGV[0] eq "run") { shift; &mcdebfs_run(@ARGV); exit 0; } + +exit 1; + diff --git a/src/vfs/extfs/helpers/deba.in b/src/vfs/extfs/helpers/deba.in new file mode 100644 index 0000000..3d1a552 --- /dev/null +++ b/src/vfs/extfs/helpers/deba.in @@ -0,0 +1,107 @@ +#! @PERL@ +# +# 1999 (c) Piotr Roszatycki <dexter@debian.org> +# This software is under GNU license +# last modification: 1999-12-08 +# +# deba + +sub quote { + $_ = shift(@_); + s/([^\w\/.+-])/\\$1/g; + return($_); +} + +sub list +{ + my($qarchive)=@_; + $qarchive = quote($qarchive); + chop($date=`LC_ALL=C date "+%m-%d-%Y %H:%M"`); + chop($info_size=`apt-cache show $qarchive | wc -c`); + $install_size=length($pressinstall); + $upgrade_size=length($pressupgrade); + + print "-r--r--r-- 1 root root $info_size $date INFO\n"; + + chop($debd = `dpkg -s $qarchive | grep -i ^Version | sed 's/^version: //i'`); + chop($deba = `apt-cache show $qarchive | grep -i ^Version | sed 's/^version: //i'`); + if( ! $debd ) { + print "-r-xr--r-- 1 root root $install_size $date INSTALL\n"; + } elsif( $debd ne $deba ) { + print "-r-xr--r-- 1 root root $upgrade_size $date UPGRADE\n"; + } +} + +sub copyout +{ + my($archive,$filename,$destfile)=@_; + my $qarchive = quote($archive); + my $qdestfile = quote($destfile); + if($filename eq "INFO") { + system("apt-cache show $qarchive > $qdestfile"); + } elsif($filename eq "INSTALL") { + if ( open(FILEOUT, "> $destfile") ) { + print FILEOUT $pressinstall; + close FILEOUT; + system("chmod a+x $qdestfile"); + } + } elsif($filename eq "UPGRADE") { + if ( open(FILEOUT, ">, $destfile") ) { + print FILEOUT $pressupgrade; + close FILEOUT; + system("chmod a+x $qdestfile"); + } + } else { + die "extfs: $filename: No such file or directory\n"; + } +} + +sub run +{ + my($archive,$filename)=@_; + my $qarchive = quote($archive); + if($filename eq "INSTALL") { + system("apt-get install $qarchive"); + } elsif($filename eq "UPGRADE") { + system("apt-get install $qarchive"); + } else { + die "extfs: $filename: Permission denied\n"; + } +} + +$pressinstall=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to install this package... + +This is not a real file. It is a way to install the package you are browsing. + +To install this package go back to the panel and press Enter on this file. + +EOInstall + +$pressupgrade=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to upgrade this package... + +This is not a real file. It is a way to upgrade the package you are browsing. + +To upgrade this package go back to the panel and press Enter on this file. + +EOInstall + + +umask 077; + +chop($name = `if [ -f "$ARGV[1]" ]; then cat $ARGV[1]; else echo $ARGV[1]; fi`); +$name =~ s%.*/([0-9a-z.-]*)_.*%$1%; + +exit 1 unless $name; + +if($ARGV[0] eq "list") { &list($name); exit 0; } +elsif($ARGV[0] eq "copyout") { ©out($name,$ARGV[2],$ARGV[3]); exit 0; } +elsif($ARGV[0] eq "run") { &run($name,$ARGV[2]); exit 0; } + +exit 1; + diff --git a/src/vfs/extfs/helpers/debd.in b/src/vfs/extfs/helpers/debd.in new file mode 100644 index 0000000..858dadd --- /dev/null +++ b/src/vfs/extfs/helpers/debd.in @@ -0,0 +1,362 @@ +#! @PERL@ +# +# 1999 (c) Piotr Roszatycki <dexter@debian.org> +# This software is under GNU license +# last modification: 1999-12-08 +# +# debd + +sub quote { + $_ = shift(@_); + s/([^\w\/.+-])/\\$1/g; + return($_); +} + +sub bt +{ + my ($dt) = @_; + my (@time); + @time = localtime($dt); + $bt = sprintf "%02d-%02d-%d %02d:%02d", $time[4] + 1, $time[3], + $time[5] + 1900, $time[2], $time[1]; + return $bt; +} + + +sub ft +{ + my ($f) = @_; + return "d" if -d $f; + return "l" if -l $f; + return "p" if -p $f; + return "S" if -S $f; + return "b" if -b $f; + return "c" if -c $f; + return "-"; +} + +sub fm +{ + my ($n) = @_; + my ($m); + + if( $n & 0400 ) { + $m .= "r"; + } else { + $m .= "-"; + } + if( $n & 0200 ) { + $m .= "w"; + } else { + $m .= "-"; + } + if( $n & 04000 ) { + $m .= "s"; + } elsif( $n & 0100 ) { + $m .= "x"; + } else { + $m .= "-"; + } + + if( $n & 0040 ) { + $m .= "r"; + } else { + $m .= "-"; + } + if( $n & 0020 ) { + $m .= "w"; + } else { + $m .= "-"; + } + if( $n & 02000 ) { + $m .= "s"; + } elsif( $n & 0010 ) { + $m .= "x"; + } else { + $m .= "-"; + } + + if( $n & 0004 ) { + $m .= "r"; + } else { + $m .= "-"; + } + if( $n & 0002 ) { + $m .= "w"; + } else { + $m .= "-"; + } + if( $n & 01000 ) { + $m .= "t"; + } elsif( $n & 0001 ) { + $m .= "x"; + } else { + $m .= "-"; + } + + return $m; +} + +sub ls { + my ($file) = @_; + my @stat = stat($file); + # mode, nlink, uid, gid, size, mtime, filename + printf "%s%s %d %d %d %d %s CONTENTS%s\n", ft($file), fm($stat[2] & 07777), + $stat[3], $stat[4], $stat[5], $stat[7], bt($stat[9]), $file; +} + +sub list +{ + my($archive)=@_; + my $qarchive = quote($archive); + chop($date=`LC_ALL=C date "+%m-%d-%Y %H:%M"`); + chop($info_size=`dpkg -s $qarchive | wc -c`); + $repack_size=length($pressrepack); + $reinstall_size=length($pressreinstall); + $remove_size=length($pressremove); + $purge_size=length($presspurge); + $reconfigure_size=length($pressreconfigure); + $reinstall_size=length($pressreinstall); + $select_size=length($pressselect); + $unselect_size=length($pressunselect); + + print "dr-xr-xr-x 1 root root 0 $date CONTENTS\n"; + print "dr-xr-xr-x 1 root root 0 $date DEBIAN\n"; + print "-r--r--r-- 1 root root $info_size $date INFO\n"; + print "-r-xr--r-- 1 root root $purge_size $date DPKG-PURGE\n"; + + chop($status = `dpkg -s $qarchive | grep ^Status`); + if( $status =~ /deinstall/ ) { + print "-r-xr--r-- 1 root root $select_size $date DPKG-SELECT\n"; + } elsif( $status =~ /install/ ) { + print "-r-xr--r-- 1 root root $unselect_size $date DPKG-UNSELECT\n"; + } + if( $status !~ /config-files/ ) { + if ( -x "/usr/bin/dpkg-repack" ) { + print "-r-xr--r-- 1 root root $repack_size $date DPKG-REPACK\n"; + } + print "-r-xr--r-- 1 root root $remove_size $date DPKG-REMOVE\n"; + if ( -x "/usr/bin/apt-get" ) { + print "-r-xr--r-- 1 root root $remove_size $date APT-REMOVE\n"; + print "-r-xr--r-- 1 root root $reinstall_size $date APT-REINSTALL\n"; + print "-r-xr--r-- 1 root root $purge_size $date APT-PURGE\n"; + } + } + if( -x "/usr/bin/dpkg-reconfigure" && -x "/var/lib/dpkg/info/$archive.config" ) { + print "-r-xr--r-- 1 root root $reconfigure_size $date DPKG-RECONFIGURE\n"; + } + + + + if ( open(PIPEIN, "LC_TIME=C LANG=C ls -l /var/lib/dpkg/info/$qarchive.* |") ) { + while(<PIPEIN>) { + chop; + next if /\.list$/; + s%/var/lib/dpkg/info/$archive.%DEBIAN/%; + print $_, "\n"; + } + close PIPEIN; + } + + if ( open(LIST, "/var/lib/dpkg/info/$archive.list") ) { + while(<LIST>) { + chop; + ls($_); + } + close LIST; + } +} + +sub copyout +{ + my($archive,$filename,$destfile)=@_; + my $qarchive = quote($archive); + my $qfilename = quote($filename); + my $qdestfile = quote($destfile); + + if($filename eq "INFO") { + system("dpkg -s $qarchive > $qdestfile"); + } elsif($filename eq "DPKG-REPACK") { + if ( open(FILEOUT,">$destfile") ) { + print FILEOUT $pressrepack; + close FILEOUT; + system("chmod a+x $qdestfile"); + } + } elsif($filename =~ /^DEBIAN/) { + $qfilename=~s!^DEBIAN/!!; + system("cat /var/lib/dpkg/info/$qarchive.$qfilename > $qdestfile"); + } elsif($filename eq "DPKG-REMOVE" || $filename eq "APT-REMOVE") { + if ( open(FILEOUT,">$destfile") ) { + print FILEOUT $pressremove; + close FILEOUT; + system("chmod a+x $qdestfile"); + } + } elsif($filename eq "DPKG-PURGE" || $filename eq "APT-PURGE") { + if ( open(FILEOUT,">$destfile") ) { + print FILEOUT $presspurge; + close FILEOUT; + system("chmod a+x $qdestfile"); + } + } elsif($filename eq "DPKG-RECONFIGURE") { + if ( open(FILEOUT,">$destfile") ) { + print FILEOUT $pressreconfigure; + close FILEOUT; + system("chmod a+x $qdestfile"); + } + } elsif($filename eq "APT-REINSTALL") { + if ( open(FILEOUT,">$destfile") ) { + print FILEOUT $pressreinstall; + close FILEOUT; + system("chmod a+x $destfile"); + } + } elsif($filename eq "DPKG-SELECT") { + if ( open(FILEOUT,">$destfile") ) { + print FILEOUT $pressselect; + close FILEOUT; + system("chmod a+x $destfile"); + } + } elsif($filename eq "DPKG-UNSELECT") { + if ( open(FILEOUT,">$destfile") ) { + print FILEOUT $pressunselect; + close FILEOUT; + system("chmod a+x $qdestfile"); + } + } else { + $qfilename=~s!^CONTENTS!!; + system("cat $qfilename > $qdestfile"); + } +} + +sub run +{ + my($archive,$filename)=@_; + my $qarchive = quote($archive); + my $qfilename = quote($filename); + if($filename eq "DPKG-REMOVE") { + system("dpkg --remove $qarchive"); + } elsif($filename eq "APT-REMOVE") { + system("apt-get remove $qarchive"); + } elsif($filename eq "DPKG-PURGE") { + system("dpkg --purge $qarchive"); + } elsif($filename eq "APT-PURGE") { + system("apt-get --purge remove $qarchive"); + } elsif($filename eq "DPKG-REPACK") { + system("dpkg-repack $qarchive"); + } elsif($filename eq "DPKG-SELECT") { + system("echo $aqrchive install | dpkg --set-selections"); + } elsif($filename eq "DPKG-UNSELECT") { + system("echo $qarchive deinstall | dpkg --set-selections"); + } elsif($filename eq "APT-REINSTALL") { + system("apt-get -u --reinstall install $qarchive"); + } elsif($filename eq "DPKG-RECONFIGURE") { + system("dpkg-reconfigure $qarchive"); + } elsif($filename=~/^DEBIAN/) { + $qfilename=~s!^DEBIAN!!; + system("/var/lib/dpkg/info/$qarchive.$qfilename"); + } else { + $qfilename=~s!^CONTENTS!!; + system($qfilename); + } +} + +$pressrepack=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to repack this package... + +This is not a real file. It is a way to repack the package you are browsing. + +To repack this package go back to the panel and press Enter on this file. + +EOInstall + +$pressreinstall=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to reinstall this package... + +This is not a real file. It is a way to reinstall the package you are browsing. + +To reinstall this package go back to the panel and press Enter on this file. + +EOInstall + +$pressremove=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to remove this package... + +This is not a real file. It is a way to remove the package you are browsing. + +To remove this package go back to the panel and press Enter on this file. + +EOInstall + +$presspurge=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to purge this package... + +This is not a real file. It is a way to purge the package you are browsing. + +To purge this package go back to the panel and press Enter on this file. + +EOInstall + +$pressreconfigure=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to reconfigure this package... + +This is not a real file. It is a way to reconfigure the package you are browsing. + +To reconfigure this package go back to the panel and press Enter on this file. + +EOInstall + +$pressreinstall=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to reinstall this package... + +This is not a real file. It is a way to reinstall the package you are browsing. + +To reinstall this package go back to the panel and press Enter on this file. + +EOInstall + +$pressselect=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to select this package... + +This is not a real file. It is a way to select the package you are browsing. + +To select this package go back to the panel and press Enter on this file. + +EOInstall + +$pressunselect=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to unselect this package... + +This is not a real file. It is a way to unselect the package you are browsing. + +To unselect this package go back to the panel and press Enter on this file. + +EOInstall + +umask 077; + +chop($name = `if [ -f "$ARGV[1]" ]; then cat $ARGV[1]; else echo $ARGV[1]; fi`); +$name =~ s%.*/([0-9a-z.-]*)_.*%$1%; + +exit 1 unless $name; + +if($ARGV[0] eq "list") { &list($name); exit 0; } +elsif($ARGV[0] eq "copyout") { ©out($name,$ARGV[2],$ARGV[3]); exit 0; } +elsif($ARGV[0] eq "run") { &run($name,$ARGV[2]); exit 0; } + +exit 1; + diff --git a/src/vfs/extfs/helpers/dpkg+.in b/src/vfs/extfs/helpers/dpkg+.in new file mode 100644 index 0000000..048862e --- /dev/null +++ b/src/vfs/extfs/helpers/dpkg+.in @@ -0,0 +1,337 @@ +#! @PERL@ +# +# 1999 (c) Piotr Roszatycki <dexter@debian.org> +# This software is under GNU license +# last modification: 1999-12-08 +# +# dpkg + +sub quote { + $_ = shift(@_); + s/([^\w\/.+-])/\\$1/g; + return($_); +} + +sub bt +{ + my ($dt) = @_; + my (@time); + @time = localtime($dt); + $bt = sprintf "%02d-%02d-%d %02d:%02d", $time[4] + 1, $time[3], + $time[5] + 1900, $time[2], $time[1]; + return $bt; +} + + +sub ft +{ + my ($f) = @_; + return "d" if -d $f; + return "l" if -l $f; + return "p" if -p $f; + return "S" if -S $f; + return "b" if -b $f; + return "c" if -c $f; + return "-"; +} + +sub fm +{ + my ($n) = @_; + my ($m); + + if( $n & 0400 ) { + $m .= "r"; + } else { + $m .= "-"; + } + if( $n & 0200 ) { + $m .= "w"; + } else { + $m .= "-"; + } + if( $n & 04000 ) { + $m .= "s"; + } elsif( $n & 0100 ) { + $m .= "x"; + } else { + $m .= "-"; + } + + if( $n & 0040 ) { + $m .= "r"; + } else { + $m .= "-"; + } + if( $n & 0020 ) { + $m .= "w"; + } else { + $m .= "-"; + } + if( $n & 02000 ) { + $m .= "s"; + } elsif( $n & 0010 ) { + $m .= "x"; + } else { + $m .= "-"; + } + + if( $n & 0004 ) { + $m .= "r"; + } else { + $m .= "-"; + } + if( $n & 0002 ) { + $m .= "w"; + } else { + $m .= "-"; + } + if( $n & 01000 ) { + $m .= "t"; + } elsif( $n & 0001 ) { + $m .= "x"; + } else { + $m .= "-"; + } + + return $m; +} + +sub ls { + my ($file,$path,$mode) = @_; + + if (-f $file) { + my @stat = stat(_); + # mode, nlink, uid, gid, size, mtime, filename + printf "%s %d %d %d %d %s %s\n", $mode || ft($file).fm($stat[2] & 07777), + $stat[3], $stat[4], $stat[5], $stat[7], bt($stat[9]), $path; + } +} + +$DATE=bt(time()); + +sub list +{ + my ($pkg, $fn, $dn, $sz, $bt); + my %debs = (); + my %sects = (); + + my($diversions,$architecture); + chop($diversions = `dpkg-divert --list 2>/dev/null`); + chop($architecture = `dpkg-architecture 2>/dev/null`); + chop($list = `dpkg -l '*' 2>/dev/null`); + chop($getselections = `dpkg --get-selections 2>/dev/null`); + chop($audit = `dpkg --audit 2>/dev/null`); + $sz = length($diversions); + print "-r--r--r-- 1 root root $sz $DATE DIVERSIONS\n"; + $sz = length($architecture); + print "-r--r--r-- 1 root root $sz $DATE ARCHITECTURE\n"; + $sz = length($list); + print "-r--r--r-- 1 root root $sz $DATE LIST\n"; + $sz = length($getselections); + print "-r--r--r-- 1 root root $sz $DATE GET-SELECTIONS\n"; + $sz = length($audit); + print "-r--r--r-- 1 root root $sz $DATE AUDIT\n"; + $sz = length($pressconfigure); + print "-r-xr--r-- 1 root root $sz $DATE CONFIGURE\n"; + $sz = length($pressremove); + print "-r-xr--r-- 1 root root $sz $DATE REMOVE\n"; + $sz = length($pressclearavail); + print "-r-xr--r-- 1 root root $sz $DATE CLEAR-AVAIL\n"; + $sz = length($pressforgetoldunavail); + print "-r-xr--r-- 1 root root $sz $DATE FORGET-OLD-UNAVAIL\n"; + ls("/var/lib/dpkg/status","STATUS","-r--r--r--"); + # ls("/var/lib/dpkg/available","AVAILABLE","-r--r--r--"); + + print "drwxr-xr-x 1 root root 0 $DATE all\n"; + + open STAT, "/var/lib/dpkg/status" + or exit 1; + while( <STAT> ) { + chop; + if( /^([\w-]*): (.*)/ ) { + $pkg = $2 if( lc($1) eq 'package' ); + $debs{$pkg}{lc($1)} = $2; + } + } + close STAT; + + foreach $pkg (sort keys %debs) { + next if $debs{$pkg}{status} =~ /not-installed/; + $fn = $debs{$pkg}{package}. "_". $debs{$pkg}{version}; + $dn = $debs{$pkg}{section}; + if( ! $dn ) { + $dn = "unknown"; + } elsif( $dn =~ /^(non-us)$/i ) { + $dn .= "/main"; + } elsif( $dn !~ /\// ) { + $dn = "main/". $dn; + } + unless( $sects{$dn} ) { + my $sub = $dn; + while( $sub =~ s!^(.*)/[^/]*$!$1! ) { + unless( $sects{$sub} ) { + print "drwxr-xr-x 1 root root 0 $DATE $sub/\n"; + $sects{$sub} = 1; + } + } + print "drwxr-xr-x 1 root root 0 $DATE $dn/\n"; + $sects{$dn} = 1; + } + $sz = $debs{$pkg}{'status'} =~ /config-files/ ? 0 : $debs{$pkg}{'installed-size'} * 1024; + @stat = stat("/var/lib/dpkg/info/".$debs{$pkg}{package}.".list"); + $bt = bt($stat[9]); + print "-rw-r--r-- 1 root root $sz $bt $dn/$fn.debd\n"; + print "lrwxrwxrwx 1 root root $sz $bt all/$fn.debd -> ../$dn/$fn.debd\n"; + } +} + +sub copyout +{ + my($archive,$filename) = @_; + my $qfilename = quote($filename); + if( $archive eq 'DIVERSIONS' ) { + system("dpkg-divert --list > $qfilename 2>/dev/null"); + } elsif( $archive eq 'ARCHITECTURE' ) { + system("dpkg-architecture > $qfilename 2>/dev/null"); + } elsif( $archive eq 'LIST' ) { + system("dpkg -l '*' > $qfilename 2>/dev/null"); + } elsif( $archive eq 'AUDIT' ) { + system("dpkg --audit > $qfilename 2>/dev/null"); + } elsif( $archive eq 'GET-SELECTIONS' ) { + system("dpkg --get-selections > $qfilename 2>/dev/null"); + } elsif( $archive eq 'STATUS' ) { + system("cp /var/lib/dpkg/status $qfilename"); + } elsif( $archive eq 'AVAILABLE' ) { + system("cp /var/lib/dpkg/available $qfilename"); + } elsif( $archive eq 'CONFIGURE' ) { + open O, ">$filename"; + print O $pressconfigure; + close O; + } elsif( $archive eq 'REMOVE' ) { + open O, ">$filename"; + print O $pressremove; + close O; + } elsif( $archive eq 'CLEAR-AVAIL' ) { + open O, ">$filename"; + print O $pressclearavail; + close O; + } elsif( $archive eq 'FORGET-OLD-UNAVAIL' ) { + open O, ">$filename"; + print O $pressforgetoldunavail; + close O; + } else { + open O, ">$filename"; + print O $archive, "\n"; + close O; + } +} + +# too noisy but less dangerouse +sub copyin +{ + my($archive,$filename) = @_; + my $qfilename = quote($filename); + if( $archive =~ /\.deb$/ ) { + system("dpkg -i $qfilename>/dev/null"); + } else { + die "extfs: cannot create regular file \`$archive\': Permission denied\n"; + } +} + +sub run +{ + my($archive,$filename) = @_; + if( $archive eq 'CONFIGURE' ) { + system("dpkg --pending --configure"); + } elsif( $archive eq 'REMOVE' ) { + system("dpkg --pending --remove"); + } elsif( $archive eq 'CLEAR-AVAIL' ) { + system("dpkg --clear-avail"); + } elsif( $archive eq 'FORGET-OLD-UNAVAIL' ) { + system("dpkg --forget-old-unavail"); + } else { + die "extfs: $filename: command not found\n"; + } +} + +# Disabled - too dangerous and too noisy +sub rm_disabled +{ + my($archive) = @_; + if( $archive =~ /\.debd?$/ ) { + my $qname = quote($archive); + $qname =~ s%.*/%%g; + $qname =~ s%_.*%%g; + system("if dpkg -s $qname | grep ^Status | grep -qs config-files; \ + then dpkg --purge $qname>/dev/null; \ + else dpkg --remove $qname>/dev/null; fi"); + die("extfs: $archive: Operation not permitted\n") if $? != 0; + } else { + die "extfs: $archive: Operation not permitted\n"; + } +} + + +$pressconfigure=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to configure all + non configured packages. + +This is not a real file. It is a way to configure all non configured packages. + +To configure packages go back to the panel and press Enter on this file. + +EOInstall + +$pressremove=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to remove all + unselected packages. + +This is not a real file. It is a way to remove all unselected packages. + +To remove packages go back to the panel and press Enter on this file. + +EOInstall + +$pressforgetoldunavail=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to forget about + uninstalled unavailable packages. + +This is not a real file. It is a way to forget about uninstalled +unavailable packages. + +To forget this information go back to the panel and press Enter on this file. + +EOInstall + +$pressclearavail=<<EOInstall; + + WARNING + Don\'t use this method if you are not willing to erase the existing + information about what packages are available. + +This is not a real file. It is a way to erase the existing information +about what packages are available. + +To clear this information go back to the panel and press Enter on this file. + +EOInstall + + + +# override any locale for dates +$ENV{"LC_ALL"}="C"; + +if ($ARGV[0] eq "list") { list(); exit(0); } +elsif ($ARGV[0] eq "copyout") { copyout($ARGV[2], $ARGV[3]); exit(0); } +elsif ($ARGV[0] eq "copyin") { copyin($ARGV[2], $ARGV[3]); exit(0); } +elsif ($ARGV[0] eq "run") { run($ARGV[2],$ARGV[3]); exit(0); } +#elsif ($ARGV[0] eq "rm") { rm($ARGV[2]); exit(0); } +exit(1); + diff --git a/src/vfs/extfs/helpers/gitfs+ b/src/vfs/extfs/helpers/gitfs+ new file mode 100755 index 0000000..66861fb --- /dev/null +++ b/src/vfs/extfs/helpers/gitfs+ @@ -0,0 +1,39 @@ +#!/bin/sh + +LANG=C +export LANG +LC_TIME=C +export LC_TIME + +umask 077 +prefix='[git]' + +gitfs_list() +{ + DATE=`date +"%b %d %H:%M"` + GIT_DIR="$2/.git" + user=`whoami` + git ls-files -v -c -m -d | sort -k 2 | uniq -f 1 | while read status fname + do + [ "$status" = "H" ] && status=" " + [ "$status" = "C" ] && status="*" + echo "-r--r--r-- 1 $user 0 0 $DATE `dirname $fname`/$prefix$status`basename $fname`" + done +} + +gitfs_copyout() +{ + printf "%s\n" "$2" > "$4" + b=`echo "$prefix"| wc -c` + b=`expr "$b" + 1` + # remove prefix from file name + echo "`dirname "$3"`/`basename "$3" | tail -c+"$b"`" >> "$4" + echo "git" >> "$4" +} + +case "$1" in + list) gitfs_list "$@" ;; + copyout) gitfs_copyout "$@" ;; + *) exit 1 ;; +esac +exit 0 diff --git a/src/vfs/extfs/helpers/hp48+.in b/src/vfs/extfs/helpers/hp48+.in new file mode 100644 index 0000000..17c03ab --- /dev/null +++ b/src/vfs/extfs/helpers/hp48+.in @@ -0,0 +1,132 @@ +#!/bin/sh +# +# Written by Christofer Edvardsen <ce@earthling.net>, Feb 1998 +# +# This script makes it possible to view and copy files to/from a hp48 +# (tested with a HP48G and the emulator x48) +# +# To use the hp48 external filesystem: +# - read the relevant parts of your HP48 manual +# - install kermit +# - connect the HP48 to your computer or start x48 +# - below change the line which reflects the serial device you use +# - configure your HP48 (<left shift> - i/o - iopar): +# port: wire +# baud: 9600 +# transfer format: binary (fast transfers) or +# ascii (editable on the pc) +# - start the server on the HP48: <left shift> - i/o - srvr - serve +# or the shortcut <right shift> - <right arrow> +# - on MC's commandline enter "cd hp48://" +# +# Make sure you have kermit installed and that it's using the right serial +# device by changing /dev/ttyXX on the next line +AWK=@AWK@ +KERMIT=${MC_TEST_EXTFS_LIST_CMD:-"kermit -l /dev/ttyS1 -b 9600"} + +NOW=`date +"%m-%d-%Y %H:%M"` + +hp48_cmd() +{ +$KERMIT -C "SET EXIT WARNING OFF,REMOTE $1,QUIT" +} + +hp48_cd() +{ +(echo SET EXIT WARNING OFF;echo REMOTE HOST HOME +for HP48_DIR in `echo "$1" | tr '/' ' '`;do + if [ "x$HP48_DIR" != "x." ];then echo REMOTE HOST "$HP48_DIR"; fi +done +echo QUIT)| $KERMIT -B >/dev/null +} + +# +# Parses the reply to the DIRECTORY command. +# +# Here's an example reply (taken from [1][2]): +# +# { HOME } 105617 +# STRAY 185.5 Directory 29225 +# YEN 30.5 Program 53391 +# JYTLIGHT 21848.5 String 62692 +# IOPAR 37.5 List 61074 +# +# The meaning of the fields (according to [3][4]): +# +# { Current_directory } Free_space +# Object_name Object_size_bytes Object_type Object_CRC +# ... +# +# [1] http://newarea48.tripod.com/kermit.html +# [2] http://www.hpmuseum.org/forum/thread-4684.html +# [3] https://groups.google.com/d/msg/comp.sys.hp48/bYTCu9K3k20/YWQfF--W3EEJ +# [4] http://www.columbia.edu/kermit/hp48.html (also has a link to the HP's user manual). +# +hp48_parser() +{ +HP48_DIRS= + +read -r INPUT +while [ "x$INPUT" != "xEOF" ] +do + set -- $INPUT + + obj_name=$1 + obj_size=$2 + obj_type=$3 + + obj_size=`echo $obj_size | $AWK '{ print int($0) }'` # Truncates floats to ints; anything else to "0". + + if [ "$obj_size" != "0" ]; then # Skips the 1st reply line (purportedly there aren't zero-size files b/c, according to resource [4], the size is "including name"). + case "$obj_type" in + Directory) + HP48_DIRS="$HP48_DIRS $obj_name" + printf "%crwxr-xr-x 1 %-8d %-8d %8d %s %s\n" 'd' \ + 0 0 $obj_size "$NOW" "$HP48_CDIR/$obj_name" + ;; + *) + printf "%crw-r--r-- 1 %-8d %-8d %8d %s %s\n" '-' \ + 0 0 $obj_size "$NOW" "$HP48_CDIR/$obj_name" + ;; + esac + fi + + read -r INPUT +done + +for HP48_DIR in $HP48_DIRS; +do + HP48_PDIR="$HP48_CDIR" + HP48_CDIR="$HP48_CDIR/$HP48_DIR"; hp48_cmd "HOST $HP48_DIR" >/dev/null + hp48_list + HP48_CDIR="$HP48_PDIR"; hp48_cmd "HOST UPDIR" >/dev/null +done +} + +hp48_list() +{ +# It's hard to see why this "EOF" thing is needed. The loop above can be changed to "while read -r obj_name ...". @TODO. +{ hp48_cmd "DIRECTORY"; echo; echo EOF; } | hp48_parser +} + +# override any locale for dates +LC_ALL=C +export LC_ALL + +case "$1" in +list) HP48_CDIR= + hp48_cmd "HOST HOME" >/dev/null + hp48_list + exit 0;; +copyout) + cd "`dirname "$4"`" + hp48_cd "`dirname "$3"`" + $KERMIT -B -g "`basename "$3"`" -a "$4" >/dev/null + exit 0;; +copyin) + cd "`dirname "$4"`" + hp48_cd "`dirname "$3"`" + $KERMIT -B -s "$4" -a "`basename "$3"`" >/dev/null + exit 0;; +esac +exit 1 diff --git a/src/vfs/extfs/helpers/iso9660.in b/src/vfs/extfs/helpers/iso9660.in new file mode 100644 index 0000000..5a6f1d5 --- /dev/null +++ b/src/vfs/extfs/helpers/iso9660.in @@ -0,0 +1,235 @@ +#! /bin/sh +# Midnight Commander - ISO9660 VFS for MC +# based on lslR by Tomas Novak <tnovak@ipex.cz> April 2000 +# +# Copyright (C) 2000, 2003 +# The Free Software Foundation, Inc. +# +# Written by: +# Michael Shigorin <mike@altlinux.org>, +# Grigory Milev <week@altlinux.org>, +# Kachalov Anton <mouse@linux.ru.net>, 2003 +# Victor Ananjevsky <ananasik@gmail.com>, 2013 +# slava zanko <slavazanko@gmail.com>, 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 section (source functions, for example) ******************* + +#*** file scope functions ********************************************** + +XORRISO=$(which xorriso 2>/dev/null) + +xorriso_list() { + if test -z "$XORRISO"; then + return 1 + fi + local dir attr ln usr gr sz dt1 dt2 dt3 nm len name lsl r + dir="${2:-/}" + lsl=$( $XORRISO -abort_on FATAL -dev stdio:"$1" -cd "$dir" -lsl 2> /dev/null ) + r=$? + test $r -gt 0 && return $r + + echo "$lsl" | grep "^[-d]" | \ + while read attr ln usr gr sz dt1 dt2 dt3 nm ; do + len=$((${#nm} - 1)) + name=$(printf -- "$nm" | cut -c2-$len) # remove quotes + + if test $(printf -- "$attr" | cut -c1-1) != "d"; then + printf -- "%s %s %s %s %s %s %s %s %s/%s\n" "$attr" "$ln" "$usr" "$gr" "$sz" "$dt1" "$dt2" "$dt3" "$dir" "$name" + else + xorriso_list "$1" "$dir/$name" + fi + done +} + +xorriso_copyout() { + if test -z "$XORRISO"; then + return 1 + fi + $XORRISO -dev stdio:"$1" -osirrox on -extract "$2" "$3" >/dev/null 2>&1 +} + +xorriso_copyin() { + if test -z "$XORRISO"; then + return 1 + fi + $XORRISO -dev stdio:"$1" -cpr "$3" "$2" >/dev/null 2>&1 +} + +xorriso_mkdir() { + if test -z "$XORRISO"; then + return 1 + fi + $XORRISO -dev stdio:"$1" -mkdir "$2" >/dev/null 2>&1 +} + +xorriso_rmdir() { + if test -z "$XORRISO"; then + return 1 + fi + $XORRISO -dev stdio:"$1" -rmdir "$2" >/dev/null 2>&1 +} + +xorriso_rm() { + if test -z "$XORRISO"; then + return 1 + fi + $XORRISO -dev stdio:"$1" -rm "$2" >/dev/null 2>&1 +} + +# tested to comply with isoinfo 2.0's output +test_iso () { + ISOINFO=$(which isoinfo 2>/dev/null) + if test -z "$ISOINFO"; then + echo "isoinfo not found" >&2 + return 1 + fi + + CHARSET=$(locale charmap 2>/dev/null) + if test -z "$CHARSET"; then + CHARSET=$(locale 2>/dev/null | grep LC_CTYPE | sed -n -e 's/.*\.\(.*\)"$/\1/p') + fi + if test -n "$CHARSET"; then + CHARSET=$(echo "$CHARSET" | tr '[A-Z]' '[a-z]' | sed -e 's/^iso-/iso/') + $ISOINFO -j $CHARSET -i /dev/null 2>&1 | grep "Iconv not yet supported\|Unknown charset" >/dev/null && CHARSET= + fi + if test -n "$CHARSET"; then + JOLIET_OPT="-j $CHARSET -J" + else + JOLIET_OPT="-J" + fi + + ISOINFO_D_I="$($ISOINFO -d -i "$1" 2>/dev/null)" + ISOINFO="$ISOINFO -R" + + echo "$ISOINFO_D_I" | grep "UCS level 1\|NO Joliet" > /dev/null || ISOINFO="$ISOINFO $JOLIET_OPT" + + if [ $(echo "$ISOINFO_D_I" | grep "Joliet with UCS level 3 found" | wc -l) = 1 \ + -a $(echo "$ISOINFO_D_I" | grep "NO Rock Ridge" | wc -l) = 1 ] ; then + SEMICOLON="YES" + fi +} + +mcisofs_list () { + local lsl r + + # left as a reminder to implement compressed image support =) + case "$1" in + *.lz) MYCAT="lzip -dc";; + *.lz4) MYCAT="lz4 -dc";; + *.lzma) MYCAT="lzma -dc";; + *.xz) MYCAT="xz -dc";; + *.zst) MYCAT="zstd -dc";; + *.bz2) MYCAT="bzip2 -dc";; + *.gz) MYCAT="gzip -dc";; + *.z) MYCAT="gzip -dc";; + *.Z) MYCAT="gzip -dc";; + *) MYCAT="cat";; + esac + + lsl=$($ISOINFO -l -i "$1" 2>/dev/null) + r=$? + test $r -gt 0 && return $r + + echo "$lsl" | @AWK@ -v SEMICOLON=$SEMICOLON ' +BEGIN { + dir=""; + # Pattern to match 8 first fields. + rx = "[^ ]+[ ]+"; + rx = "^" rx rx rx rx rx rx rx rx; + irx = "^\\[ *-?[0-9]* *[0-9]+\\] +"; +} +/^$/ { next } +/^d---------/ { next } +/^Directory listing of [^ ].*$/ { + dir=substr($0, 23); + next; +} +{ $11 != "" } { + name=$0 + sub(rx, "", name) + attr=substr($0, 1, length($0)-length(name)) + # strip inodes and extra dir entries; fix perms + sub(irx, "", name) + sub("^---------- 0 0 0", "-r--r--r-- 1 root root", attr) + sub(" $", "", name) + # for Joliet UCS level 3 + if (SEMICOLON == "YES") sub(";1$", "", name); + ## sub(";[0-9]+$", "", name) ## would break copyout + # skip . and .. + if (name == ".") next; + if (name == "..") next; + printf "%s%s%s\n", attr, dir, name +}' +} + +mcisofs_copyout () { + if [ "x$SEMICOLON" = "xYES" ]; then + $ISOINFO -i "$1" -x "/$2;1" 2>/dev/null > "$3" + else + $ISOINFO -i "$1" -x "/$2" 2>/dev/null > "$3" + fi +} + +#*** main code ********************************************************* + +LC_ALL=C + +cmd="$1" +shift + +case "$cmd" in + list) + xorriso_list "$@" || { + test_iso "$@" || exit 1 + mcisofs_list "$@" || exit 1 + } + exit 0 + ;; + rm) + xorriso_rm "$@" || { + exit 1 + } + exit 0 + ;; + rmdir) + xorriso_rmdir "$@" || { + exit 1 + } + exit 0 + ;; + mkdir) + xorriso_mkdir "$@" || { + exit 1 + } + exit 0 + ;; + copyin) + xorriso_copyin "$@" || { + exit 1 + } + exit 0 + ;; + copyout) + xorriso_copyout "$@" || { + test_iso "$@" || exit 1 + mcisofs_copyout "$@" || exit 1 + } + exit 0 + ;; +esac +exit 1 diff --git a/src/vfs/extfs/helpers/lslR.in b/src/vfs/extfs/helpers/lslR.in new file mode 100644 index 0000000..69b663b --- /dev/null +++ b/src/vfs/extfs/helpers/lslR.in @@ -0,0 +1,74 @@ +#! /bin/sh + +# Based on previous version of lslR +# Modified by Tomas Novak <tnovak@ipex.cz> April 2000 +# (to allow spaces in filenames) +# +# It's assumed that lslR was generated in C locale. +LC_ALL=C +export LC_ALL=C + +AWK=@AWK@ + +mclslRfs_list () { +case "$1" in + *.lz) MYCAT="lzip -dc";; + *.lz4) MYCAT="lz4 -dc";; + *.lzma) MYCAT="lzma -dc";; + *.xz) MYCAT="xz -dc";; + *.zst) MYCAT="zstd -dc";; + *.bz2) MYCAT="bzip2 -dc";; + *.gz) MYCAT="gzip -dc";; + *.z) MYCAT="gzip -dc";; + *.Z) MYCAT="gzip -dc";; + *) MYCAT="cat";; +esac + +MYCAT=${MC_TEST_EXTFS_LIST_CMD:-$MYCAT} # Let the test framework hook in. + +$MYCAT "$1" | $AWK ' +BEGIN { + dir=""; + empty=1; + rx = "[^ ]+[ ]+"; + # Pattern to match 7 first fields. + rx7 = "^" rx rx rx rx rx rx "[^ ]+[ ]"; + # Pattern to match 8 first fields. + rx8 = "^" rx rx rx rx rx rx rx "[^ ]+[ ]"; +} +/^total\ [0-9]*$/ { next } +/^$/ { empty=1; next } +empty==1 && /:$/ { + empty=0 + if ($0 ~ /^\//) dir=substr($0, 2); + else dir=$0; + if (dir ~ /\/:$/) sub(/:$/, "", dir); + else sub(/:$/, "/", dir); + if (dir ~ /^[ ]/) dir="./"dir; + next; +} +( NF > 7 ) { + empty=0 + # gensub() is not portable. + name=$0 + i=index($6, "-") + if (i) { + sub(rx7, "", name) + NF = 7 + $6=substr($6,i+1)"-"substr($6,1,i-1) + } + else { + sub(rx8, "", name) + NF = 8 + } + printf "%s %s%s\n", $0, dir, name +} + { + empty=0 +}' +} + +case "$1" in + list) mclslRfs_list "$2"; exit 0;; +esac +exit 1 diff --git a/src/vfs/extfs/helpers/mailfs.in b/src/vfs/extfs/helpers/mailfs.in new file mode 100644 index 0000000..5bb373b --- /dev/null +++ b/src/vfs/extfs/helpers/mailfs.in @@ -0,0 +1,219 @@ +#! @PERL@ + +use bytes; +use warnings; + +# MC extfs for (possibly compressed) Berkeley style mailbox files +# Peter Daum <gator@cs.tu-berlin.de> (Jan 1998, mc-4.1.24) + +$zcat="zcat"; # gunzip to stdout +$bzcat="bzip2 -dc"; # bunzip2 to stdout +$lzipcat="lzip -dc"; # unlzip to stdout +$lz4cat="lz4 -dc"; # unlz4 to stdout +$lzcat="lzma -dc"; # unlzma to stdout +$xzcat="xz -dc"; # unxz to stdout +$zstdcat="zstd -dc"; # unzstd to stdout +$file="file"; # "file" command +$TZ='GMT'; # default timezone (for Date module) + +if (eval "require Date::Parse") { + import Date::Parse; + $parse_date= + sub { + local $ftime = str2time($_[0],$TZ); + $_ = localtime($ftime); + /^(...) (...) ([ \d]\d) (\d\d:\d\d):\d\d (\d\d\d\d)$/; + if ($ftime + 6 * 30 * 24 * 60 * 60 < $now || + $ftime + 60 * 60 > $now) { + return "$2 $3 $5"; + } else { + return "$2 $3 $4"; + } + } +} elsif (eval "require Date::Manip") { + import Date::Manip; + $parse_date= + sub { + return UnixDate($_[0], "%l"); # "ls -l" format + } +} else { # use "light" version + $parse_date= sub { + local $mstring='GeeJanFebMarAprMayJunJulAugSepOctNovDec'; + # assumes something like: Mon, 5 Jan 1998 16:08:19 +0200 (GMT+0200) + # if you have mails with another date format, add it here + if (/(\d\d?) ([A-Z][a-z][a-z]) (\d\d\d\d) (\d\d?):(\d\d)/) { + $day = $1; + $month = $2; + $mon = index($mstring,$month) / 3; + $year = $3; + $hour = $4; + $min = $5; + # pass time not year for files younger than roughly 6 months + # but not for files with dates more than 1-2 hours in the future + if ($year * 12 + $mon > $thisyear * 12 + $thismon - 7 && + $year * 12 + $mon <= $thisyear * 12 + $thismon && + ! (($year * 12 + $mon) * 31 + $day == + ($thisyear * 12 + $thismon) * 31 + $thisday && + $hour > $thishour + 2)) { + return "$month $day $hour:$min"; + } else { + return "$month $day $year"; + } + } + # Y2K bug. + # Date: Mon, 27 Mar 100 16:30:47 +0000 (GMT) + if (/(\d\d?) ([A-Z][a-z][a-z]) (1?\d\d) (\d\d?):(\d\d)/) { + $day = $1; + $month = $2; + $mon = index($mstring,$month) / 3; + $year = 1900 + $3; + $hour = $4; + $min = $5; + if ($year < 1970) { + $year += 100; + } + if ($year * 12 + $mon > $thisyear * 12 + $thismon - 7 && + $year * 12 + $mon <= $thisyear * 12 + $thismon && + ! (($year * 12 + $mon) * 31 + $day == + ($thisyear * 12 + $thismon) * 31 + $thisday && + $hour > $thishour + 2)) { + return "$month $day $hour:$min"; + } else { + return "$month $day $year"; + } + } + # AOLMail(SM). + # Date: Sat Jul 01 10:06:06 2000 + if (/([A-Z][a-z][a-z]) (\d\d?) (\d\d?):(\d\d)(:\d\d)? (\d\d\d\d)/) { + $month = $1; + $mon = index($mstring,$month) / 3; + $day = $2; + $hour = $3; + $min = $4; + $year = $6; + if ($year * 12 + $mon > $thisyear * 12 + $thismon - 7 && + $year * 12 + $mon <= $thisyear * 12 + $thismon && + ! (($year * 12 + $mon) * 31 + $day == + ($thisyear * 12 + $thismon) * 31 + $thisday && + $hour > $thishour + 2)) { + return "$month $day $hour:$min"; + } else { + return "$month $day $year"; + } + } + # Fallback + return $fallback; + } +} + +sub process_header { + while (<IN>) { + $size+=length; + s/\r$//; + last if /^$/; + die "unexpected EOF\n" if eof; + if (/^date:\s(.*)$/i) { + $date=&$parse_date($1); + } elsif (/^subject:\s(.*)$/i) { + $subj=lc($1); + $subj=~ s/^(re:\s?)+//gi; # no leading Re: + $subj=~ tr/a-zA-Z0-9//cd; # strip all "special" characters + } elsif (/^from:\s.*?(\w+)\@/i) { + $from=$1; + } elsif (/^to:\s.*?(\w+)\@/i) { + $to=lc($1); + } + } +} + +sub print_dir_line { + $from=$to if ($from eq $user); # otherwise, it would look pretty boring + $date=localtime(time) if (!defined $date); + printf "-r-------- 1 $< $< %d %s %3.3d_%.25s\n", + $size, $date, $msg_nr, "${from}_${subj}"; + +} + +sub mailfs_list { + my $blank = 1; + $user=$ENV{USER}||getlogin||getpwuid($<) || "nobody"; + + while(<IN>) { + s/\r$//; + if($blank && /^from\s+\w+(\.\w+)*@/i) { # Start of header + print_dir_line unless (!$msg_nr); + $size=length; + $msg_nr++; + ($from,$to,$subj,$date)=("none","none","none", "01-01-80"); + process_header; + $line=$blank=0; + } else { + $size+=length; + $line++; + $blank= /^$/; + } + } + print_dir_line unless (!$msg_nr); + exit 0; +} + +sub mailfs_copyout { + my($source,$dest)=@_; + exit 1 unless (open STDOUT, ">$dest"); + ($nr)= ($source =~ /^(\d+)/); # extract message number from "filename" + + my $blank = 1; + while(<IN>) { + s/\r$//; + if($blank && /^from\s+\w+(\.\w+)*@/i) { + $msg_nr++; + exit(0) if ($msg_nr > $nr); + $blank= 0; + } else { + $blank= /^$/; + } + print if ($msg_nr == $nr); + } +} + +# main { +exit 1 unless ($#ARGV >= 1); +$msg_nr=0; +$cmd=shift; +$mbox_name=shift; +my $mbox_qname = quotemeta ($mbox_name); +$_=`$file $mbox_qname`; + +if (/gzip/) { + exit 1 unless (open IN, "$zcat $mbox_qname|"); +} elsif (/bzip/) { + exit 1 unless (open IN, "$bzcat $mbox_qname|"); +} elsif (/lzip/) { + exit 1 unless (open IN, "$lzipcat $mbox_qname|"); +} elsif (/lz4/) { + exit 1 unless (open IN, "$lz4cat $mbox_qname|"); +} elsif (/lzma/) { + exit 1 unless (open IN, "$lzcat $mbox_qname|"); +} elsif (/xz/) { + exit 1 unless (open IN, "$xzcat $mbox_qname|"); +} elsif (/zst/) { + exit 1 unless (open IN, "$zstdcat $mbox_qname|"); +} else { + exit 1 unless (open IN, "<$mbox_name"); +} + +umask 077; + +if($cmd eq "list") { + $now = time; + $_ = localtime($now); + /^... (... [ \d]\d \d\d:\d\d):\d\d \d\d\d\d$/; + $fallback = $1; + $nowstring=`date "+%Y %m %d %H"`; + ($thisyear, $thismon, $thisday, $thishour) = split(/ /, $nowstring); + &mailfs_list; + exit 0; +} +elsif($cmd eq "copyout") { &mailfs_copyout(@ARGV); exit 0; } + +exit 1; diff --git a/src/vfs/extfs/helpers/patchfs.in b/src/vfs/extfs/helpers/patchfs.in new file mode 100644 index 0000000..ee1e651 --- /dev/null +++ b/src/vfs/extfs/helpers/patchfs.in @@ -0,0 +1,427 @@ +#! @PERL@ +# +# Written by Adam Byrtek <alpha@debian.org>, 2002 +# Rewritten by David Sterba <dave@jikos.cz>, 2009 +# +# Extfs to handle patches in context and unified diff format. +# Known issues: When name of file to patch is modified during editing, +# hunk is duplicated on copyin. It is unavoidable. + +use bytes; +use strict; +use warnings; +use POSIX; +use File::Temp 'tempfile'; + +# standard binaries +my $lzip = 'lzip'; +my $lz4 = 'lz4'; +my $lzma = 'lzma'; +my $xz = 'xz'; +my $zstd = 'zstd'; +my $bzip = 'bzip2'; +my $gzip = 'gzip'; +my $fileutil = 'file -b'; + +# date parsing requires Date::Parse from TimeDate module +my $parsedates = eval 'require Date::Parse'; + +# regular expressions +my $unified_header=qr/^--- .*\t.*\n\+\+\+ .*\t.*\n$/; +my $unified_extract=qr/^--- ([^\t]+).*\n\+\+\+ ([^\t]+)\s*(.*)\n/; +my $unified_header2=qr/^--- .*\n\+\+\+ .*\n$/; +my $unified_extract2=qr/^--- ([^\s]+).*\n\+\+\+ ([^\s]+)\s*(.*)\n/; +my $unified_contents=qr/^([+\-\\ \n]|@@ .* @@)/; +my $unified_hunk=qr/@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@.*\n/; + +my $context_header=qr/^\*\*\* .*\t.*\n--- .*\t.*\n$/; +my $context_extract=qr/^\*\*\* ([^\t]+).*\n--- ([^\t]+)\s*(.*)\n/; +my $context_header2=qr/^\*\*\* .*\n--- .*\n$/; +my $context_extract2=qr/^\*\*\* ([^\s]+).*\n--- ([^\s]+)\s*(.*)\n/; +my $context_contents=qr/^([!+\-\\ \n]|-{3} .* -{4}|\*{3} .* \*{4}|\*{15})/; + +my $ls_extract_id=qr/^[^\s]+\s+[^\s]+\s+([^\s]+)\s+([^\s]+)/; +my $basename=qr|^(.*/)*([^/]+)$|; + +sub patchfs_canonicalize_path ($) { + my ($fname) = @_; + $fname =~ s,/+,/,g; + $fname =~ s,(^|/)(?:\.?\./)+,$1,; + return $fname; +} + +# output unix date in a mc-readable format +sub timef +{ + my @time=localtime($_[0]); + return sprintf '%02d-%02d-%02d %02d:%02d', $time[4]+1, $time[3], + $time[5]+1900, $time[2], $time[1]; +} + +# parse given string as a date and return unix time +sub datetime +{ + # in case of problems fall back to 0 in unix time + # note: str2time interprets some wrong values (eg. " ") as 'today' + if ($parsedates && defined (my $t=str2time($_[0]))) { + return timef($t); + } + return timef(time); +} + +# print message on stderr and exit +sub error +{ + print STDERR $_[0], "\n"; + exit 1; +} + +# (compressed) input +sub myin +{ + my ($qfname)=(quotemeta $_[0]); + + $_=`$fileutil $qfname`; + if (/^'*lz4/) { + return "$lz4 -dc $qfname"; + } elsif (/^'*lzip/) { + return "$lzip -dc $qfname"; + } elsif (/^'*lzma/) { + return "$lzma -dc $qfname"; + } elsif (/^'*xz/) { + return "$xz -dc $qfname"; + } elsif (/^'*zst/) { + return "$zstd -dc $qfname"; + } elsif (/^'*bzip/) { + return "$bzip -dc $qfname"; + } elsif (/^'*gzip/) { + return "$gzip -dc $qfname"; + } else { + return "cat $qfname"; + } +} + +# (compressed) output +sub myout +{ + my ($qfname,$append)=(quotemeta $_[0],$_[1]); + my ($sep) = $append ? '>>' : '>'; + + $_=`$fileutil $qfname`; + if (/^'*lz4/) { + return "$lz4 -c $sep $qfname"; + } elsif (/^'*lzip/) { + return "$lzip -c $sep $qfname"; + } elsif (/^'*lzma/) { + return "$lzma -c $sep $qfname"; + } elsif (/^'*xz/) { + return "$xz -c $sep $qfname"; + } elsif (/^'*zst/) { + return "$zstd -c $sep $qfname"; + } elsif (/^'*bzip/) { + return "$bzip -c $sep $qfname"; + } elsif (/^'*gzip/) { + return "$gzip -c $sep $qfname"; + } else { + return "cat $sep $qfname"; + } +} + +# select diff filename conforming with rules found in diff.info +sub diff_filename +{ + my ($fsrc,$fdst)= @_; + # TODO: can remove these two calls later + $fsrc = patchfs_canonicalize_path ($fsrc); + $fdst = patchfs_canonicalize_path ($fdst); + if (!$fdst && !$fsrc) { + error 'Index: not yet implemented'; + } elsif (!$fsrc || $fsrc eq '/dev/null') { + return ($fdst,'PATCH-CREATE/'); + } elsif (!$fdst || $fdst eq '/dev/null') { + return ($fsrc,'PATCH-REMOVE/'); + } elsif (($fdst eq '/dev/null') && ($fsrc eq '/dev/null')) { + error 'Malformed diff, missing a sane filename'; + } else { + # fewest path name components + if ($fdst=~s|/|/|g < $fsrc=~s|/|/|g) { + return ($fdst,''); + } elsif ($fdst=~s|/|/|g > $fsrc=~s|/|/|g) { + return ($fsrc,''); + } else { + # shorter base name + if (($fdst=~/$basename/o,length $2) < ($fsrc=~/$basename/o,length $2)) { + return ($fdst,''); + } elsif (($fdst=~/$basename/o,length $2) > ($fsrc=~/$basename/o,length $2)) { + return ($fsrc,''); + } else { + # shortest names + if (length $fdst < length $fsrc) { + return ($fdst,''); + } else { + return ($fsrc,''); + } + } + } + } +} + +# IN: diff "archive" name +# IN: file handle for output; STDIN for list, tempfile else +# IN: filename to watch (for: copyout, rm), '' for: list +# IN: remove the file? +# true - ... and print out the rest +# false - ie. copyout mode, print just the file +sub parse($$$$) +{ + my $archive=shift; + my $fh=shift; + my $file=shift; + my $rmmod=shift; + my ($state,$fsize,$time); + my ($f,$fsrc,$fdst,$prefix); + my ($unified,$context); + my ($skipread, $filetoprint, $filefound); + my ($h_add,$h_del,$h_ctx); # hunk line counts + my ($h_r1,$h_r2); # hunk ranges + my @outsrc; # if desired ... + my @outdst; + my $line; + my %fmap_size=(); + my %fmap_time=(); + + import Date::Parse if ($parsedates && $file eq ''); + + $line=1; + $state=0; $fsize=0; $f=''; + $filefound=0; + while ($skipread || ($line++,$_=<I>)) { + $skipread=0; + if($state == 0) { # expecting comments + $unified=$context=0; + $unified=1 if (/^--- /); + $context=1 if (/^\*\*\* /); + if (!$unified && !$context) { + $filefound=0 if($file ne '' && $filetoprint); + # shortcut for rmmod xor filefound + # - in rmmod we print if not found + # - in copyout (!rmmod) we print if found + print $fh $_ if($rmmod != $filefound); + next; + } + + if($file eq '' && $filetoprint) { + $fmap_size{"$prefix$f"}+=$fsize; + $fmap_time{"$prefix$f"}=$time; + } + + # start of new file + $_ .=<I>; # steal next line, both formats + $line++; + if($unified) { + if(/$unified_header/o) { + ($fsrc,$fdst,$time) = /$unified_extract/o; + } elsif(/$unified_header2/o) { + ($fsrc,$fdst,$time) = /$unified_extract2/o; + } else { + error "Can't parse unified diff header"; + } + } elsif($context) { + if(/$context_header/o) { + ($fsrc,$fdst,$time) = /$context_extract/o; + } elsif(/$context_header2/o) { + ($fsrc,$fdst,$time) = /$context_extract2/o; + } else { + error "Can't parse context diff header"; + } + } else { + error "Unrecognized diff header"; + } + $fsrc=patchfs_canonicalize_path($fsrc); + $fdst=patchfs_canonicalize_path($fdst); + if(wantarray) { + push @outsrc,$fsrc; + push @outdst,$fdst; + } + ($f,$prefix)=diff_filename($fsrc,$fdst); + $filefound=($f eq $file); + + $f="$f.diff"; + $filetoprint=1; + $fsize=length; + print $fh $_ if($rmmod != $filefound); + + $state=1; + } elsif($state == 1) { # expecting diff hunk headers, end of file or comments + if($unified) { + my ($a,$b,$c,$d); + ($a,$b,$h_r1,$c,$d,$h_r2)=/$unified_hunk/o; + if(!defined($a) || !defined($c)) { + # hunk header does not come, a comment inside + # or maybe a new file, state 0 will decide + $skipread=1; + $state=0; + next; + } + $fsize+=length; + print $fh $_ if($rmmod != $filefound); + $h_r1=1 if(!defined($b)); + $h_r2=1 if(!defined($d)); + $h_add=$h_del=$h_ctx=0; + $state=2; + } elsif($context) { + if(!/$context_contents/o) { + $skipread=1; + $state=0; + next; + } + print $fh $_ if($rmmod != $filefound); + $fsize+=length; + } + } elsif($state == 2) { # expecting hunk contents + if($h_del + $h_ctx == $h_r1 && $h_add + $h_ctx == $h_r2) { + # hooray, end of hunk + # we optimistically ended with a hunk before but + # the line has been read already + $skipread=1; + $state=1; + next; + } + print $fh $_ if($rmmod != $filefound); + $fsize+=length; + my ($first)= /^(.)/; + if(ord($first) == ord('+')) { $h_add++; } + elsif(ord($first) == ord('-')) { $h_del++; } + elsif(ord($first) == ord(' ')) { $h_ctx++; } + elsif(ord($first) == ord('\\')) { 0; } + elsif(ord($first) == ord('@')) { error "Malformed hunk, header came too early"; } + else { error "$archive:$line: Unrecognized character '$first' in hunk"; } + } + } + if($file eq '' && $filetoprint) { + $fmap_size{"$prefix$f"}+=$fsize; + $fmap_time{"$prefix$f"}=$time; + } + + # use uid and gid from file + my $qarchive = quotemeta $archive; + my ($uid,$gid)=(`ls -l $qarchive`=~/$ls_extract_id/o); + + # flush all file names with cumulative file size + while(my ($fn, $fs) = each %fmap_size) { + printf $fh "-rw-r--r-- 1 %s %s %d %s %s\n", $uid, $gid, $fs, datetime($fmap_time{$fn}), $fn; + } + + close($fh) if($file ne ''); + return \(@outsrc, @outdst) if wantarray; +} + +# list files affected by patch +sub list($) { + parse($_[0], *STDOUT, '', 0); + close(I); +} + +# extract diff from patch +# IN: diff file to find +# IN: output file name +sub copyout($$) { + my ($file,$out)=@_; + + $file=~s/^(PATCH-(CREATE|REMOVE)\/)?(.*)\.diff$/$3/; + $file = patchfs_canonicalize_path ($file); + + open(FH, ">$out") or error("Cannot open output file"); + parse('', *FH, $file, 0); +} + +# remove diff(s) from patch +# IN: archive +# IN: file to delete +sub rm($$) { + my $archive=shift; + my ($tmp,$tmpname)=tempfile(); + + @_=map {scalar(s/^(PATCH-(CREATE|REMOVE)\/)?(.*)\.diff$/$3/,$_)} @_; + + # just the first file for now + parse($archive, $tmp, $_[0], 1); + close I; + + # replace archive + system("cat \Q$tmpname\E | " . myout($archive,0))==0 + or error "Can't write to archive"; + system("rm -f -- \Q$tmpname\E"); +} + +# append diff to archive +# IN: diff archive name +# IN: newly created file name in archive +# IN: the real source file +sub copyin($$$) { + # TODO: seems to be tricky. what to do? + # copyin of file which is already there may: + # * delete the original and copy only the new + # * just append the new hunks to the same file + # problems: may not be a valid diff, unmerged hunks + # * try to merge the two together + # ... but we do not want write patchutils again, right? + error "Copying files into diff not supported"; + return; + + my ($archive,$name,$src)=@_; + + # in case we are appending another diff, we have + # to delete/merge all the files + open(DEVNULL, ">/dev/null"); + open I, myin($src).'|'; + my ($srclist,$dstlist)=parse($archive, *DEVNULL, '', 0); + close(I); + close(DEVNULL); + foreach(@$srclist) { + print("SRC: del $_\n"); + } + foreach(@$dstlist) { + print("DST: del $_\n"); + } + return; + + # remove overwritten file + open I, myin($archive).'|'; + rm ($archive, $name); + close I; + + my $cmd1=myin("$src.diff"); + my $cmd2=myout($archive,1); + system("$cmd1 | $cmd2")==0 + or error "Can't write to archive"; +} + +my $fin = $ARGV[1]; + +# resolve symlink +while (-l $fin) { + $fin = readlink $fin; +} + +if ($ARGV[0] eq 'list') { + open I, myin($fin).'|'; + list ($fin); + exit 0; +} elsif ($ARGV[0] eq 'copyout') { + open I, myin($fin)."|"; + copyout ($ARGV[2], $ARGV[3]); + exit 0; +} elsif ($ARGV[0] eq 'rm') { + open I, myin($fin)."|"; + rm ($fin, $ARGV[2]); + exit 0; +} elsif ($ARGV[0] eq 'rmdir') { + exit 0; +} elsif ($ARGV[0] eq 'mkdir') { + exit 0; +} elsif ($ARGV[0] eq 'copyin') { + copyin ($fin, $ARGV[2], $ARGV[3]); + exit 0; +} +exit 1; diff --git a/src/vfs/extfs/helpers/patchsetfs b/src/vfs/extfs/helpers/patchsetfs new file mode 100755 index 0000000..9bbe9f9 --- /dev/null +++ b/src/vfs/extfs/helpers/patchsetfs @@ -0,0 +1,104 @@ +#!/bin/sh + +LANG=C +export LANG +LC_TIME=C +export LC_TIME + +# --- GIT ----------------------------------------------------------------------- + +found_git_dir() +{ + work_dir=$1 + while [ -n "$work_dir" -a "$work_dir" != "/" ]; do + [ -d "${work_dir}/.git" ] && { + echo "${work_dir}/.git/" + return + } + work_dir=`dirname "$work_dir"` + done + echo '' +} + +patchsetfs_list_git() +{ + WORK_DIR=$1; shift + fname=$1; shift + USER=$1; shift + DATE=$1; shift + + GIT_DIR=`found_git_dir "$WORK_DIR"` + [ -z "$GIT_DIR" ] && GIT_DIR=$WORK_DIR + curr_year=`date +"%Y"` + + git --git-dir="$GIT_DIR" log --abbrev=7 --pretty="format:%at %h %an" -- "$fname" | while read TIMESTAMP chset author + do + year=`date -d @"$TIMESTAMP" +"%Y"` + [ "$year" = "$curr_year" ] && { + DATE=`date -d @"$TIMESTAMP" +"%b %d %H:%M"` + } || { + DATE=`date -d @"$TIMESTAMP" +"%b %d %Y"` + } + NAME="$chset $author" + echo "-rw-rw-rw- 1 $USER 0 0 $DATE $NAME.diff" + done +} + +patchsetfs_copyout_git() +{ + WORK_DIR=$1; shift + fname=$1; shift + orig_fname=$1;shift + output_fname=$1;shift + + chset=`echo "$orig_fname"| cut -f 1 -d " "` + + GIT_DIR=`found_git_dir "$WORK_DIR"` + [ -z "$GIT_DIR" ] && GIT_DIR=$WORK_DIR + + git --git-dir="$GIT_DIR" show "$chset" -- "$fname" > "$output_fname" +} + +# --- COMMON -------------------------------------------------------------------- + +patchsetfs_list() +{ + VCS_type=$1; shift + WORK_DIR=$1; shift + fname=$1; shift + + DATE=`date +"%b %d %H:%M"` + USER=`whoami` + + case "$VCS_type" in + git) patchsetfs_list_git "$WORK_DIR" "$fname" "$USER" "$DATE" ;; + esac +} + +patchsetfs_copyout() +{ + VCS_type=$1; shift + WORK_DIR=$1; shift + fname=$1; shift + + case "$VCS_type" in + git) patchsetfs_copyout_git "$WORK_DIR" "$fname" "$@" ;; + esac + +} + +# --- MAIN ---------------------------------------------------------------------- + +command=$1; shift +tmp_file=$1; shift + +WORK_DIR=`head -n1 $tmp_file` +fname=`tail -n2 $tmp_file | head -n1` +VCS_type=`tail -n1 $tmp_file` + +case "$command" in + list) patchsetfs_list "$VCS_type" "$WORK_DIR" "$fname" ;; + copyout) patchsetfs_copyout "$VCS_type" "$WORK_DIR" "$fname" "$@" ;; + *) exit 1 ;; +esac +exit 0 diff --git a/src/vfs/extfs/helpers/rpm b/src/vfs/extfs/helpers/rpm new file mode 100755 index 0000000..8fa9188 --- /dev/null +++ b/src/vfs/extfs/helpers/rpm @@ -0,0 +1,349 @@ +#! /bin/sh +# VFS-wrapper for RPM (and src.rpm) files +# +# Copyright (C) 1996-2004,2009 +# Free Software Foundation, Inc. +# +# Written by +# Erik Troan <ewt@redhat.com> 1996 +# Jakub Jelinek <jj@sunsite.mff.cuni.cz> 1996, 2004 +# Tomasz KÅ‚oczko <kloczek@rudy.mif.pg.gda.pl> 1997 +# Wojtek Pilorz <wpilorz@bdk.lublin.pl> +# 1997: minor changes +# Michele Marziani <marziani@fe.infn.it> +# 1997: minor changes +# Marc Merlin <marcsoft@merlins.org> 1998 +# 1998: bug files +# Michal Svec <rebel@penguin.cz> 2000 +# 2000: locale bugfix +# Andrew V. Samoilov <sav@bcs.zp.ua> +# 2004: Whitespace(s) & single quote(s) in filename workaround +# https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=64007 +# Slava Zanko <slavazanko@gmail.com> +# 2009: Totally rewritten. +# Alexander Chumachenko <ledest@gmail.com> +# 2013: add dependency version output +# Denis Silakov <denis.silakov@rosalab.ru> +# 2013: tar payload support. +# Arkadiusz MiÅ›kiewicz <arekm@maven.pl> +# 2013: improve support for EPOCH +# add support for PREINPROG/POSTINPROG/PREUNPROG/POSTUNPROG +# add support for VERIFYSCRIPTPROG +# add support for TRIGGERSCRIPTS/TRIGGERSCRIPTPROG +# Jiri Tyr <jiri.tyr@gmail.com> +# 2016: add support for PRETRANS/PRETRANSPROG/POSTTRANS/POSTTRANSPROG +# +# This file is part of the Midnight Commander. +# +# This program 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. +# +# This program 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/>. + + +# override any locale for dates +unset LC_ALL +LC_TIME=C +export LC_TIME + +if rpmbuild --version >/dev/null 2>&1; then + RPMBUILD="rpmbuild" +else + RPMBUILD="rpm" +fi + +if rpm --nosignature --version >/dev/null 2>&1; then + RPM="rpm --nosignature" + RPMBUILD="$RPMBUILD --nosignature" +else + RPM="rpm" +fi +RPM_QUERY_FMT="$RPM -qp --qf" +RPM2CPIO="rpm2cpio" + +SED="sed" + +param=$1; shift +rpm_filename=$1; shift + +FILEPREF="-r--r--r-- 1 root root " + +mcrpmfs_getDesription() +{ + $RPM -qip "${rpm_filename}" +} + +mcrpmfs_getAllNeededTags() +{ + $RPM_QUERY_FMT \ +"|NAME=%{NAME}"\ +"|VERSION=%{VERSION}"\ +"|RELEASE=%{RELEASE}"\ +"|DISTRIBUTION=%{DISTRIBUTION}"\ +"|VENDOR=%{VENDOR}"\ +"|DESCRIPTION=%{DESCRIPTION}"\ +"|SUMMARY=%{SUMMARY}"\ +"|URL=%{URL}"\ +"|EPOCH=%{EPOCH}"\ +"|LICENSE=%{LICENSE}"\ +"|REQUIRES=%{REQUIRENAME} %{REQUIREFLAGS:depflags} %{REQUIREVERSION}"\ +"|OBSOLETES=%{OBSOLETES}"\ +"|PROVIDES=%{PROVIDES} %{PROVIDEFLAGS:depflags} %{PROVIDEVERSION}"\ +"|CONFLICTS=%{CONFLICTS}"\ +"|PACKAGER=%{PACKAGER}" \ + "${rpm_filename}" \ + | tr '\n' ' ' # The newlines in DESCRIPTION mess with the sed script in mcrpmfs_getOneTag(). +} + +mcrpmfs_getRawOneTag() +{ + $RPM_QUERY_FMT "$1" "${rpm_filename}" +} + +mcrpmfs_getOneTag() +{ + # 'echo' can't be used for arbitrary data (see commit message). + printf "%s" "$AllTAGS" | $SED "s/.*|${1}=//" | cut -d '|' -f 1 +} + +mcrpmfs_printOneMetaInfo() +{ + if test "$3" = "raw"; then + metaInfo=`mcrpmfs_getRawOneTag "%{$2}"` + else + metaInfo=`mcrpmfs_getOneTag "$2"` + fi + + if test -n "${metaInfo}" -a "${metaInfo}" != "(none)"; then + echo "${FILEPREF} 0 ${DATE} ${1}" + return 0 + fi + return 1 +} + +mcrpmfs_list_fastRPM () +{ + echo "$FILEPREF 0 $DATE INFO/DISTRIBUTION" + echo "$FILEPREF 0 $DATE INFO/VENDOR" + echo "$FILEPREF 0 $DATE INFO/DESCRIPTION" + echo "$FILEPREF 0 $DATE INFO/SUMMARY" + echo "dr-xr-xr-x 1 root root 0 $DATE INFO/SCRIPTS" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/PRETRANS" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/POSTTRANS" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/PREIN" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/POSTIN" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/PREUN" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/POSTUN" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/VERIFYSCRIPT" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/TRIGGERSCRIPTS" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/ALL" + echo "$FILEPREF 0 $DATE INFO/PACKAGER" + echo "$FILEPREF 0 $DATE INFO/URL" + echo "$FILEPREF 0 $DATE INFO/EPOCH" + echo "$FILEPREF 0 $DATE INFO/LICENSE" + echo "$FILEPREF 0 $DATE INFO/REQUIRES" + echo "$FILEPREF 0 $DATE INFO/OBSOLETES" + echo "$FILEPREF 0 $DATE INFO/PROVIDES" + echo "$FILEPREF 0 $DATE INFO/ENHANCES" + echo "$FILEPREF 0 $DATE INFO/SUGGESTS" + echo "$FILEPREF 0 $DATE INFO/RECOMMENDS" + echo "$FILEPREF 0 $DATE INFO/SUPPLEMENTS" + echo "$FILEPREF 0 $DATE INFO/CONFLICTS" + echo "$FILEPREF 0 $DATE INFO/CHANGELOG" +} + +mcrpmfs_list_fullRPM () +{ + mcrpmfs_printOneMetaInfo "INFO/DISTRIBUTION" "DISTRIBUTION" + mcrpmfs_printOneMetaInfo "INFO/VENDOR" "VENDOR" + mcrpmfs_printOneMetaInfo "INFO/DESCRIPTION" "DESCRIPTION" + mcrpmfs_printOneMetaInfo "INFO/SUMMARY" "SUMMARY" + + if test "`mcrpmfs_getRawOneTag \"%{RPMTAG_PRETRANS}%{RPMTAG_POSTTRANS}%{RPMTAG_PREIN}%{RPMTAG_POSTIN}%{RPMTAG_PREUN}%{RPMTAG_POSTUN}%{VERIFYSCRIPT}%{TRIGGERSCRIPTS}\"`" != "(none)(none)(none)(none)(none)(none)(none)(none)"; then + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/PRETRANS" "RPMTAG_PRETRANS" "raw" + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/POSTTRANS" "RPMTAG_POSTTRANS" "raw" + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/PREIN" "RPMTAG_PREIN" "raw" + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/POSTIN" "RPMTAG_POSTIN" "raw" + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/PREUN" "RPMTAG_PREUN" "raw" + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/POSTUN" "RPMTAG_POSTUN" "raw" + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/VERIFYSCRIPT" "VERIFYSCRIPT" "raw" + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/TRIGGERSCRIPTS" "TRIGGERSCRIPTS" "raw" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/ALL" + fi + + if test "`mcrpmfs_getRawOneTag \"%{RPMTAG_PRETRANSPROG}%{RPMTAG_POSTTRANSPROG}%{RPMTAG_PREINPROG}%{RPMTAG_POSTINPROG}%{RPMTAG_PREUNPROG}%{RPMTAG_POSTUNPROG}%{VERIFYSCRIPTPROG}%{TRIGGERSCRIPTPROG}\"`" != "(none)(none)(none)(none)(none)(none)(none)(none)"; then + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/PRETRANSPROG" "RPMTAG_PRETRANSPROG" "raw" + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/POSTTRANSPROG" "RPMTAG_POSTTRANSPROG" "raw" + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/PREINPROG" "RPMTAG_PREINPROG" "raw" + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/POSTINPROG" "RPMTAG_POSTINPROG" "raw" + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/PREUNPROG" "RPMTAG_PREUNPROG" "raw" + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/POSTUNPROG" "RPMTAG_POSTUNPROG" "raw" + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/VERIFYSCRIPTPROG" "VERIFYSCRIPTPROG" "raw" + mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/TRIGGERSCRIPTPROG" "TRIGGERSCRIPTPROG" "raw" + fi + + mcrpmfs_printOneMetaInfo "INFO/PACKAGER" "PACKAGER" + mcrpmfs_printOneMetaInfo "INFO/URL" "URL" + mcrpmfs_printOneMetaInfo "INFO/EPOCH" "EPOCH" + mcrpmfs_printOneMetaInfo "INFO/LICENSE" "LICENSE" + + mcrpmfs_printOneMetaInfo "INFO/REQUIRES" "REQUIRES" + mcrpmfs_printOneMetaInfo "INFO/OBSOLETES" "OBSOLETES" + mcrpmfs_printOneMetaInfo "INFO/PROVIDES" "PROVIDES" + mcrpmfs_printOneMetaInfo "INFO/CONFLICTS" "CONFLICTS" + mcrpmfs_printOneMetaInfo "INFO/CHANGELOG" "CHANGELOGTEXT" "raw" +} + +mcrpmfs_list () +{ + # set MCFASTRPM_DFLT to 1 for faster rpm files handling by default, to 0 for + # slower handling + MCFASTRPM_DFLT=0 + if test -z "$MCFASTRPM"; then + MCFASTRPM=$MCFASTRPM_DFLT + fi + + DESC=`mcrpmfs_getDesription 2>/dev/null` || { + echo "$FILEPREF 0 "`date +"%b %d %H:%M"`" ERROR" + exit 1 + } + DATE=`mcrpmfs_getRawOneTag "%{BUILDTIME:date}\n" | cut -c 5-11,21-24` + PAYLOAD=`mcrpmfs_getRawOneTag "%{PAYLOADFORMAT}\n" | sed s/ustar/tar/` + + HEADERSIZE=`printf '%s\n' "$DESC" | wc -c` # 'echo' can't be used for arbitrary data (see commit message). + printf '%s %s %s HEADER\n' "${FILEPREF}" "${HEADERSIZE}" "${DATE}" + echo "-r-xr-xr-x 1 root root 0 $DATE INSTALL" + case "${rpm_filename}" in + *.src.rpm) + echo "-r-xr-xr-x 1 root root 0 $DATE REBUILD" + ;; + *) + echo "-r-xr-xr-x 1 root root 0 $DATE UPGRADE" + ;; + esac + + echo "dr-xr-xr-x 3 root root 0 $DATE INFO" + if [ `mcrpmfs_getRawOneTag "%{EPOCH}"` = "(none)" ]; then + echo "$FILEPREF 0 $DATE INFO/NAME-VERSION-RELEASE" + else + echo "$FILEPREF 0 $DATE INFO/NAME-EPOCH:VERSION-RELEASE" + fi + echo "$FILEPREF 0 $DATE INFO/GROUP" + echo "$FILEPREF 0 $DATE INFO/BUILDHOST" + echo "$FILEPREF 0 $DATE INFO/SOURCERPM" + echo "$FILEPREF 0 $DATE INFO/BUILDTIME" + echo "$FILEPREF 0 $DATE INFO/RPMVERSION" + echo "$FILEPREF 0 $DATE INFO/OS" + echo "$FILEPREF 0 $DATE INFO/SIZE" + + if test "$MCFASTRPM" = 0 ; then + mcrpmfs_list_fullRPM + else + mcrpmfs_list_fastRPM + fi + + echo "$FILEPREF 0 $DATE CONTENTS.$PAYLOAD" +} + +mcrpmfs_copyout () +{ + case "$1" in + HEADER) mcrpmfs_getDesription > "$2"; exit 0;; + INSTALL) + echo "# Run this to install this RPM package" > "$2" + exit 0 + ;; + UPGRADE) + echo "# Run this to upgrade this RPM package" > "$2" + exit 0 + ;; + REBUILD) + echo "# Run this to rebuild this RPM package" > "$2" + exit 0 + ;; + ERROR) mcrpmfs_getDesription > /dev/null 2> "$2"; exit 0;; + INFO/NAME-VERSION-RELEASE) + echo `mcrpmfs_getOneTag "NAME"`-`mcrpmfs_getOneTag "VERSION"`-`mcrpmfs_getOneTag "RELEASE"` > "$2" + exit 0 + ;; + INFO/NAME-EPOCH:VERSION-RELEASE) + echo `mcrpmfs_getOneTag "NAME"`-`mcrpmfs_getOneTag "EPOCH"`:`mcrpmfs_getOneTag "VERSION"`-`mcrpmfs_getOneTag "RELEASE"` > "$2" + exit 0 + ;; + INFO/RELEASE) mcrpmfs_getOneTag "RELEASE" > "$2"; exit 0;; + INFO/GROUP) mcrpmfs_getRawOneTag "%{GROUP}\n" > "$2"; exit 0;; + INFO/DISTRIBUTION) mcrpmfs_getOneTag "DISTRIBUTION" > "$2"; exit 0;; + INFO/VENDOR) mcrpmfs_getOneTag "VENDOR" > "$2"; exit 0;; + INFO/BUILDHOST) mcrpmfs_getRawOneTag "%{BUILDHOST}\n" > "$2"; exit 0;; + INFO/SOURCERPM) mcrpmfs_getRawOneTag "%{SOURCERPM}\n" > "$2"; exit 0;; + INFO/DESCRIPTION) mcrpmfs_getRawOneTag "%{DESCRIPTION}\n" > "$2"; exit 0;; + INFO/PACKAGER) mcrpmfs_getOneTag "PACKAGER" > "$2"; exit 0;; + INFO/URL) mcrpmfs_getOneTag "URL" >"$2"; exit 0;; + INFO/BUILDTIME) mcrpmfs_getRawOneTag "%{BUILDTIME:date}\n" >"$2"; exit 0;; + INFO/EPOCH) mcrpmfs_getOneTag "EPOCH" >"$2"; exit 0;; + INFO/LICENSE) mcrpmfs_getOneTag "LICENSE" >"$2"; exit 0;; + INFO/RPMVERSION) mcrpmfs_getRawOneTag "%{RPMVERSION}\n" >"$2"; exit 0;; + INFO/REQUIRES) mcrpmfs_getRawOneTag "[%{REQUIRENAME} %{REQUIREFLAGS:depflags} %{REQUIREVERSION}\n]" >"$2"; exit 0;; + INFO/ENHANCES) mcrpmfs_getRawOneTag "[%|ENHANCESFLAGS:depflag_strong?{}:{%{ENHANCESNAME} %{ENHANCESFLAGS:depflags} %{ENHANCESVERSION}\n}|]" "$f" >"$3"; exit 0;; + INFO/SUGGESTS) mcrpmfs_getRawOneTag "[%|SUGGESTSFLAGS:depflag_strong?{}:{%{SUGGESTSNAME} %{SUGGESTSFLAGS:depflags} %{SUGGESTSVERSION}\n}|]" "$f" >"$3"; exit 0;; + INFO/RECOMMENDS) mcrpmfs_getRawOneTag "[%|SUGGESTSFLAGS:depflag_strong?{%{SUGGESTSNAME} %{SUGGESTSFLAGS:depflags} %{SUGGESTSVERSION}\n}|]" "$f" >"$3"; exit 0;; + INFO/SUPPLEMENTS) mcrpmfs_getRawOneTag "[%|ENHANCESFLAGS:depflag_strong?{%{ENHANCESNAME} %{ENHANCESFLAGS:depflags} %{ENHANCESVERSION}\n}|]" "$f" >"$3"; exit 0;; + INFO/PROVIDES) mcrpmfs_getRawOneTag "[%{PROVIDES} %{PROVIDEFLAGS:depflags} %{PROVIDEVERSION}\n]" >"$2"; exit 0;; + INFO/SCRIPTS/PRETRANS) mcrpmfs_getRawOneTag "%{RPMTAG_PRETRANS}\n" >"$2"; exit 0;; + INFO/SCRIPTS/PRETRANSPROG) mcrpmfs_getRawOneTag "%{RPMTAG_PRETRANSPROG}\n" >"$2"; exit 0;; + INFO/SCRIPTS/POSTTRANS) mcrpmfs_getRawOneTag "%{RPMTAG_POSTTRANS}\n" >"$2"; exit 0;; + INFO/SCRIPTS/POSTTRANSPROG) mcrpmfs_getRawOneTag "%{RPMTAG_POSTTRANSPROG}\n" >"$2"; exit 0;; + INFO/SCRIPTS/PREIN) mcrpmfs_getRawOneTag "%{RPMTAG_PREIN}\n" >"$2"; exit 0;; + INFO/SCRIPTS/PREINPROG) mcrpmfs_getRawOneTag "%{RPMTAG_PREINPROG}\n" >"$2"; exit 0;; + INFO/SCRIPTS/POSTIN) mcrpmfs_getRawOneTag "%{RPMTAG_POSTIN}\n" >"$2"; exit 0;; + INFO/SCRIPTS/POSTINPROG) mcrpmfs_getRawOneTag "%{RPMTAG_POSTINPROG}\n" >"$2"; exit 0;; + INFO/SCRIPTS/PREUN) mcrpmfs_getRawOneTag "%{RPMTAG_PREUN}\n" >"$2"; exit 0;; + INFO/SCRIPTS/PREUNPROG) mcrpmfs_getRawOneTag "%{RPMTAG_PREUNPROG}\n" >"$2"; exit 0;; + INFO/SCRIPTS/POSTUN) mcrpmfs_getRawOneTag "%{RPMTAG_POSTUN}\n" >"$2"; exit 0;; + INFO/SCRIPTS/POSTUNPROG) mcrpmfs_getRawOneTag "%{RPMTAG_POSTUNPROG}\n" >"$2"; exit 0;; + INFO/SCRIPTS/VERIFYSCRIPT) mcrpmfs_getRawOneTag "%{VERIFYSCRIPT}\n" > "$2"; exit 0;; + INFO/SCRIPTS/VERIFYSCRIPTPROG) mcrpmfs_getRawOneTag "%{VERIFYSCRIPTPROG}\n" > "$2"; exit 0;; + INFO/SCRIPTS/TRIGGERSCRIPTS) $RPM -qp --triggers "${rpm_filename}" > "$2"; exit 0;; + INFO/SCRIPTS/TRIGGERSCRIPTPROG) mcrpmfs_getRawOneTag "%{TRIGGERSCRIPTPROG}\n" > "$2"; exit 0;; + INFO/SCRIPTS/ALL) $RPM -qp --scripts "${rpm_filename}" > "$2"; exit 0;; + INFO/SUMMARY) mcrpmfs_getRawOneTag "%{SUMMARY}\n" > "$2"; exit 0;; + INFO/OS) mcrpmfs_getRawOneTag "%{OS}\n" > "$2"; exit 0;; + INFO/CHANGELOG) mcrpmfs_getRawOneTag "[* %{CHANGELOGTIME:date} %{CHANGELOGNAME}\n%{CHANGELOGTEXT}\n\n]\n" > "$2"; exit 0;; + INFO/SIZE) mcrpmfs_getRawOneTag "%{SIZE} bytes\n" > "$2"; exit 0;; + INFO/OBSOLETES) mcrpmfs_getRawOneTag "[%{OBSOLETENAME} %|OBSOLETEFLAGS?{%{OBSOLETEFLAGS:depflags} %{OBSOLETEVERSION}}:{}|\n]" > "$2"; exit 0;; + INFO/CONFLICTS) mcrpmfs_getRawOneTag "[%{CONFLICTNAME} %{CONFLICTFLAGS:depflags} %{CONFLICTVERSION}\n]" >"$2"; exit 0;; + CONTENTS.*) $RPM2CPIO "${rpm_filename}" > "$2"; exit 0;; + *) + ;; + esac +} + +mcrpmfs_run () +{ + case "$1" in + INSTALL) echo "Installing \"${rpm_filename}\""; $RPM -ivh "${rpm_filename}"; exit 0;; + UPGRADE) echo "Upgrading \"${rpm_filename}\""; $RPM -Uvh "${rpm_filename}"; exit 0;; + REBUILD) echo "Rebuilding \"${rpm_filename}\""; $RPMBUILD --rebuild "${rpm_filename}"; exit 0;; + esac +} + +# Let the test framework override functions and variables. +[ -n "$MC_TEST_RPM_REWRITE" ] && . "$MC_TEST_RPM_REWRITE" + +AllTAGS=`mcrpmfs_getAllNeededTags "$1"` + +umask 077 +case "${param}" in + list) mcrpmfs_list; exit 0;; + copyout) mcrpmfs_copyout "$1" "$2"; exit 0;; + run) mcrpmfs_run "$1"; exit 1;; +esac +exit 1 diff --git a/src/vfs/extfs/helpers/rpms+.in b/src/vfs/extfs/helpers/rpms+.in new file mode 100644 index 0000000..9a8e7de --- /dev/null +++ b/src/vfs/extfs/helpers/rpms+.in @@ -0,0 +1,66 @@ +#! @PERL@ +# +# Written by Balazs Nagy (julian7@kva.hu) 1998 +# locale bugfix by Michal Svec (rebel@penguin.cz) 2000 +# (C) 1998 The Free Software Foundation. +# +# + +# override any locale for dates +delete $ENV{"LC_ALL"}; +$ENV{"LC_TIME"}="C"; + +#print $ENV{"LC_ALL"}; +#exit 0; + +sub gd +{ + my ($dt) = @_; + $dt =~ tr/ //s; + $dt =~ s/^\w+ (\w+) (\d+) (\d+:\d+):\d+ .+\n?$/$1 $2 $3/; + return $dt; +} + +$DATE=gd(`date`); + +sub list +{ + my (@rpms, %files, $i, $fn, $dn, $sz, $bt); +# @rpms = `rpm -qa --qf "\%{NAME}-\%{VERSION}-\%{RELEASE}:\%{GROUP}:\%{SIZE}:\%{BUILDTIME:date}\n"`; + @rpms = `rpm -qa --qf "\%{NAME}-\%{VERSION}:\%{GROUP}:\%{SIZE}:\%{BUILDTIME:date}\n"`; + print @trpms; + %files = (); + %sizes = (); + %dates = (); + for $i (@rpms) { + if ($i =~ /^([^:]+):([^:]+):([^:]+):(.+)$/) { + ($fn, $dn, $sz, $bt) = ($1, $2, $3, $4); + $dn =~ s/ /_/g; + if (defined $files{$dn}) { + push(@{$files{$dn}}, $fn); + } else { + @{$files{$dn}} = ($fn); + } + $sizes{$fn} = $sz; + $dates{$fn} = gd($bt); + } + } + for $i (sort keys %files) { + print "dr-xr-xr-x 1 root root 0 $DATE $i/\n"; + for $fn (sort @{$files{$i}}) { + print "-r--r--r-- 1 root root $sizes{$fn} $dates{$fn} $i/$fn.trpm\n"; + } + } +} + +#open O, ">>/tmp/tt"; +#print O "RPMS: "; +#for $i (@ARGV) { +# print O "$i "; +#} +#print O "\n"; +#close O; + +if ($ARGV[0] eq "list") { list(); exit(0); } +elsif ($ARGV[0] eq "copyout") { open O, ">$ARGV[3]"; print O $ARGV[2], "\n"; close O; exit(0); } +exit(1); diff --git a/src/vfs/extfs/helpers/s3+.in b/src/vfs/extfs/helpers/s3+.in new file mode 100644 index 0000000..f5e4b90 --- /dev/null +++ b/src/vfs/extfs/helpers/s3+.in @@ -0,0 +1,490 @@ +#! @PYTHON@ +# -*- coding: utf-8 -*- + +# +# Midnight Commander compatible EXTFS for accessing Amazon Web Services S3. +# Written by Jakob Kemi <jakob.kemi@gmail.com> 2009 +# +# This program 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. +# +# This program 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/>. +# +# +# Notes: +# This EXTFS exposes buckets as directories and keys as files +# Due to EXTFS limitations all buckets & keys have to be read initially which might +# take quite some time. +# Tested on Debian with Python 2.4-2.6 and boto 1.4c and 1.6b +# (Python 2.6 might need -W ignore::DeprecationWarning due to boto using +# deprecated module Popen2) +# +# +# Installation: +# Make sure that boto <http://code.google.com/p/boto> (python-boto in Debian) is installed. +# Preferably pytz (package python-tz in Debian) should be installed as well. +# +# Save as executable file /usr/libexec/mc/extfs/s3 (or wherever your mc expects to find extfs modules) +# +# Settings: (should be set via environment) +# Required: +# AWS_ACCESS_KEY_ID : Amazon AWS access key (required) +# AWS_SECRET_ACCESS_KEY : Amazon AWS secret access key (required) +# Optional: +# MCVFS_EXTFS_S3_LOCATION : where to create new buckets: "EU" - default, "USWest", "APNortheast" etc. +# MCVFS_EXTFS_S3_DEBUGFILE : write debug info to this file (no info by default) +# MCVFS_EXTFS_S3_DEBUGLEVEL : debug messages level ("WARNING" - default, "DEBUG" - verbose) +# +# +# Usage: +# Open dialog "Quick cd" (<alt-c>) and type: s3:// <enter> (or simply type `cd s3://' in shell line) +# +# +# History: +# +# 2015-07-22 Dmitry Koterov <dmitry.koterov@gmail.com> +# - Support for non-ASCII characters in filenames (system encoding detection). +# +# 2015-05-21 Dmitry Koterov <dmitry.koterov@gmail.com> +# - Resolve "Please use AWS4-HMAC-SHA256" error: enforce the new V4 authentication method. +# It is required in many (if not all) locations nowadays. +# - Now s3+ works with buckets in different regions: locations are auto-detected. +# - Debug level specification support (MCVFS_EXTFS_S3_DEBUGLEVEL). +# +# 2009-02-07 Jakob Kemi <jakob.kemi@gmail.com> +# - Updated instructions. +# - Improved error reporting. +# +# 2009-02-06 Jakob Kemi <jakob.kemi@gmail.com> +# - Threaded list command. +# - Handle rm of empty "subdirectories" (as seen in mc). +# - List most recent datetime and total size of keys as directory properties. +# - List modification time in local time. +# +# 2009-02-05 Jakob Kemi <jakob.kemi@gmail.com> +# - Initial version. +# + +import sys +import os +import time +import re +import datetime + + +import boto +from boto.s3.connection import S3Connection +from boto.exception import BotoServerError + + +# Get settings from environment +USER=os.getenv('USER','0') +AWS_ACCESS_KEY_ID=os.getenv('AWS_ACCESS_KEY_ID') +AWS_SECRET_ACCESS_KEY=os.getenv('AWS_SECRET_ACCESS_KEY') +S3LOCATION=os.getenv('MCVFS_EXTFS_S3_LOCATION', 'EU') +DEBUGFILE=os.getenv('MCVFS_EXTFS_S3_DEBUGFILE') +DEBUGLEVEL=os.getenv('MCVFS_EXTFS_S3_DEBUGLEVEL', 'WARNING') + +if not AWS_ACCESS_KEY_ID or not AWS_SECRET_ACCESS_KEY: + sys.stderr.write('Missing AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY environment variables.\n') + sys.exit(1) + +# Setup logging +if DEBUGFILE: + import logging + logging.basicConfig( + filename=DEBUGFILE, + level=logging.DEBUG, + format='%(asctime)s %(levelname)s %(message)s') + logging.getLogger('boto').setLevel(getattr(logging, DEBUGLEVEL)) +else: + class Void(object): + def __getattr__(self, attr): + return self + def __call__(self, *args, **kw): + return self + logging = Void() + +logger = logging.getLogger('s3extfs') + + +def __fix_io_encoding(last_resort_default='UTF-8'): + """ + The following code is needed to work with non-ASCII characters in filenames. + We're trying hard to detect the system encoding. + """ + import codecs + import locale + for var in ('stdin', 'stdout', 'stderr'): + if getattr(sys, var).encoding is None: + enc = None + if enc is None: + try: + enc = locale.getpreferredencoding() + except: + pass + if enc is None: + try: + enc = sys.getfilesystemencoding() + except: + pass + if enc is None: + try: + enc = sys.stdout.encoding + except: + pass + if enc is None: + enc = last_resort_default + setattr(sys, var, codecs.getwriter(enc)(getattr(sys, var), 'strict')) +__fix_io_encoding() + + +def threadmap(fun, iterable, maxthreads=16): + """ + Quick and dirty threaded version of builtin method map. + Propagates exception safely. + """ + from threading import Thread + import Queue + + items = list(iterable) + nitems = len(items) + if nitems < 2: + return map(fun, items) + + # Create and fill input queue + input = Queue.Queue() + output = Queue.Queue() + + for i,item in enumerate(items): + input.put( (i,item) ) + + class WorkThread(Thread): + """ + Takes one item from input queue (thread terminates when input queue is empty), + performs fun, puts result in output queue + """ + def run(self): + while True: + try: + (i,item) = input.get_nowait() + try: + result = fun(item) + output.put( (i,result) ) + except: + output.put( (None,sys.exc_info()) ) + except Queue.Empty: + return + + # Start threads + for i in range( min(len(items), maxthreads) ): + t = WorkThread() + t.setDaemon(True) + t.start() + + # Wait for all threads to finish & collate results + ret = [] + for i in range(nitems): + try: + i,res = output.get() + if i == None: + raise res[0],res[1],res[2] + except Queue.Empty: + break + ret.append(res) + + return ret + +logger.debug('started') + +if S3LOCATION.upper() == "EU": + S3LOCATION = "eu-central-1" +if S3LOCATION.upper() == "US": + S3LOCATION = "us-east-1" +for att in dir(boto.s3.connection.Location): + v = getattr(boto.s3.connection.Location, att) + if type(v) is str and att.lower() == S3LOCATION.lower(): + S3LOCATION = v + break +logger.debug('Using location %s for new buckets', S3LOCATION) + + +def get_connection(location): + """ + Creates a connection to the specified region. + """ + os.environ['S3_USE_SIGV4'] = 'True' # only V4 method is supported in all locations. + return boto.s3.connect_to_region( + location, + aws_access_key_id=AWS_ACCESS_KEY_ID, + aws_secret_access_key=AWS_SECRET_ACCESS_KEY + ) + + +# Global S3 default connection. +s3 = get_connection('us-east-1') + + +def get_bucket(name): + """ + Returns a bucket by its name, no matter what region is it in. + """ + try: + b = s3.get_bucket(name, validate=False) + b.get_location() # just to raise an exception on error + return b + except boto.exception.S3ResponseError, e: + # Seems this is the only proper way to switch to the bucket's region. + # Requesting of the default region for "?location" does not work unfortunately. + m = re.search(r'<Region>(.*?)</Region>', e.body) + if m: + return get_connection(m.group(1)).get_bucket(name) + raise + + +logger.debug('argv: ' + str(sys.argv)) +try: + cmd = sys.argv[1] + args = sys.argv[2:] +except: + sys.stderr.write('This program should be called from within MC\n') + sys.exit(1) + + +def handleServerError(msg): + e = sys.exc_info() + msg += ', reason: ' + e[1].reason + logger.error(msg, exc_info=e) + sys.stderr.write(msg+'\n') + sys.exit(1) + +# +# Lists all S3 contents +# +if cmd == 'list': + if len(args) > 0: + path = args[0] + else: + path = '' + + logger.info('list') + + rs = s3.get_all_buckets() + + # Import python timezones (pytz) + try: + import pytz + except: + logger.warning('Missing pytz module, timestamps will be off') + # A fallback UTC tz stub + class pytzutc(datetime.tzinfo): + def __init__(self): + datetime.tzinfo.__init__(self) + self.utc = self + self.zone = 'UTC' + def utcoffset(self, dt): + return datetime.timedelta(0) + def tzname(self, dt): + return "UTC" + def dst(self, dt): + return datetime.timedelta(0) + pytz = pytzutc() + + + # Find timezone + # (yes, timeZONE as in _geographic zone_ not EST/CEST or whatever crap we get from time.tzname) + # http://regebro.wordpress.com/2008/05/10/python-and-time-zones-part-2-the-beast-returns/ + def getGuessedTimezone(): + # 1. check TZ env. var + try: + tz = os.getenv('TZ', '') + return pytz.timezone(tz) + except: + pass + # 2. check if /etc/timezone exists (Debian at least) + try: + if os.path.isfile('/etc/timezone'): + tz = open('/etc/timezone', 'r').readline().strip() + return pytz.timezone(tz) + except: + pass + # 3. check if /etc/localtime is a _link_ to something useful + try: + if os.path.islink('/etc/localtime'): + link = os.readlink('/etc/localtime') + tz = '/'.join(link.split(os.path.sep)[-2:]) + return pytz.timezone(tz) + except: + pass + # 4. use time.tzname which will probably be wrong by an hour 50% of the time. + try: + return pytz.timezone(time.tzname[0]) + except: + pass + # 5. use plain UTC ... + return pytz.utc + + tz=getGuessedTimezone() + logger.debug('Using timezone: ' + tz.zone) + + # AWS time is on format: 2009-01-07T16:43:39.000Z + # we "want" MM-DD-YYYY hh:mm (in localtime) + expr = re.compile(r'^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.\d{3}Z$') + def convDate(awsdatetime): + m = expr.match(awsdatetime) + ye,mo,da,ho,mi,se = map(int,m.groups()) + + dt = datetime.datetime(ye,mo,da,ho,mi,se, tzinfo=pytz.utc) + return dt.astimezone(tz).strftime('%m-%d-%Y %H:%M') + + + def bucketList(b): + b = get_bucket(b.name) # get the bucket at its own region + totsz = 0 + mostrecent = '1970-01-01T00:00:00.000Z' + ret = [] + for k in b.list(): + if k.name.endswith('/'): + # Sometimes someone create S3 keys which are ended with "/". + # Extfs cannot work with them as with files, and such keys may + # hide same-name directories, so we skip them. + continue + mostrecent = max(mostrecent, k.last_modified) + datetime = convDate(k.last_modified) + ret.append('%10s %3d %-8s %-8s %d %s %s\n' % ( + '-rw-r--r--', 1, USER, USER, k.size, datetime, b.name+'/'+k.name) + ) + totsz += k.size + + datetime=convDate(mostrecent) + sys.stdout.write('%10s %3d %-8s %-8s %d %s %s\n' % ( + 'drwxr-xr-x', 1, USER, USER, totsz, datetime, b.name) + ) + for line in ret: + sys.stdout.write(line) + + threadmap(bucketList, rs) + +# +# Fetch file from S3 +# +elif cmd == 'copyout': + archivename = args[0] + storedfilename = args[1] + extractto = args[2] + + bucket,key = storedfilename.split('/', 1) + logger.info('copyout bucket: %s, key: %s'%(bucket, key)) + + try: + b = get_bucket(bucket) + k = b.get_key(key) + + out = open(extractto, 'w') + + k.open(mode='r') + for buf in k: + out.write(buf) + k.close() + out.close() + except BotoServerError: + handleServerError('Unable to fetch key "%s"'%(key)) + +# +# Upload file to S3 +# +elif cmd == 'copyin': + archivename = args[0] + storedfilename = args[1] + sourcefile = args[2] + + bucket,key = storedfilename.split('/', 1) + logger.info('copyin bucket: %s, key: %s'%(bucket, key)) + + try: + b = get_bucket(bucket) + k = b.new_key(key) + k.set_contents_from_file(fp=open(sourcefile,'r')) + except BotoServerError: + handleServerError('Unable to upload key "%s"' % (key)) + +# +# Remove file from S3 +# +elif cmd == 'rm': + archivename = args[0] + storedfilename = args[1] + + bucket,key = storedfilename.split('/', 1) + logger.info('rm bucket: %s, key: %s'%(bucket, key)) + + try: + b = get_bucket(bucket) + b.delete_key(key) + except BotoServerError: + handleServerError('Unable to remove key "%s"' % (key)) + +# +# Create directory +# +elif cmd == 'mkdir': + archivename = args[0] + dirname = args[1] + + logger.info('mkdir dir: %s' %(dirname)) + if '/' in dirname: + logger.warning('skipping mkdir') + pass + else: + bucket = dirname + try: + get_connection(S3LOCATION).create_bucket(bucket, location=S3LOCATION) + except BotoServerError: + handleServerError('Unable to create bucket "%s"' % (bucket)) + +# +# Remove directory +# +elif cmd == 'rmdir': + archivename = args[0] + dirname = args[1] + + logger.info('rmdir dir: %s' %(dirname)) + if '/' in dirname: + logger.warning('skipping rmdir') + pass + else: + bucket = dirname + try: + b = get_bucket(bucket) + b.connection.delete_bucket(b) + except BotoServerError: + handleServerError('Unable to delete bucket "%s"' % (bucket)) + +# +# Run from S3 +# +elif cmd == 'run': + archivename = args[0] + storedfilename = args[1] + arguments = args[2:] + + bucket,key = storedfilename.split('/', 1) + logger.info('run bucket: %s, key: %s'%(bucket, key)) + + os.execv(storedfilename, arguments) +else: + logger.error('unhandled, bye') + sys.exit(1) + +logger.debug('command handled') +sys.exit(0) + diff --git a/src/vfs/extfs/helpers/trpm b/src/vfs/extfs/helpers/trpm new file mode 100755 index 0000000..d9a7930 --- /dev/null +++ b/src/vfs/extfs/helpers/trpm @@ -0,0 +1,176 @@ +#! /bin/sh +# +# Browse contents of an installed RPM package. +# This filesystem works on the entries of the "rpms" filesystem. +# +# Written by Erik Troan (ewt@redhat.com) 1996 +# Jakub Jelinek (jj@sunsite.mff.cuni.cz) 1996 +# Tomasz K³oczko (kloczek@rudy.mif.pg.gda.pl) 1997 +# minor changes by Wojtek Pilorz (wpilorz@bdk.lublin.pl) 1997 +# minor changes by Michele Marziani (marziani@fe.infn.it) 1997 +# slight changes to put rpm to Trpm by Balazs Nagy (julian7@kva.hu) 1998 +# locale bugfix by Michal Svec (rebel@penguin.cz) 2000 +# (C) 1996 The Free Software Foundation. +# +# + +# override any locale for dates +unset LC_ALL +LC_TIME=C +export LC_TIME + +if rpm --nosignature --version >/dev/null 2>&1; then + RPM="rpm --nosignature" +else + RPM="rpm" +fi + +mcrpmfs_list () +{ + # set MCFASTRPM_DFLT to 1 for faster rpm files handling by default, to 0 for + # slower handling + MCFASTRPM_DFLT=0 + if test -z "$MCFASTRPM"; then + MCFASTRPM=$MCFASTRPM_DFLT + fi + FILEPREF="-r--r--r-- 1 root root " + DESC=`$RPM -qi -- "$1"` + DATE=`$RPM -q --qf "%{BUILDTIME:date}" -- "$1" | cut -c 5-11,21-24` + HEADERSIZE=`echo "$DESC" | wc -c` + echo "-r--r--r-- 1 root root $HEADERSIZE $DATE HEADER" + echo "-r-xr-xr-x 1 root root 40 $DATE UNINSTALL" + echo "dr-xr-xr-x 3 root root 0 $DATE INFO" + echo "$FILEPREF 0 $DATE INFO/NAME-VERSION-RELEASE" + echo "$FILEPREF 0 $DATE INFO/GROUP" + echo "$FILEPREF 0 $DATE INFO/BUILDHOST" + echo "$FILEPREF 0 $DATE INFO/SOURCERPM" + if test "$MCFASTRPM" = 0 ; then + test "`$RPM -q --qf \"%{DISTRIBUTION}\" -- "$1"`" = "(none)" || + echo "$FILEPREF 0 $DATE INFO/DISTRIBUTION" + test "`$RPM -q --qf \"%{VENDOR}\" -- "$1"`" = "(none)" || + echo "$FILEPREF 0 $DATE INFO/VENDOR" + test "`$RPM -q --qf \"%{DESCRIPTION}\" -- "$1"`" = "(none)" || + echo "$FILEPREF 0 $DATE INFO/DESCRIPTION" + test "`$RPM -q --qf \"%{SUMMARY}\" -- "$1"`" = "(none)" || + echo "$FILEPREF 0 $DATE INFO/SUMMARY" + if test "`$RPM -q --qf \"%{RPMTAG_PREIN}%{RPMTAG_POSTIN}%{RPMTAG_PREUN}%{RPMTAG_POSTUN}%{VERIFYSCRIPT}\" -- "$1"`" != "(none)(none)(none)(none)(none)"; then + echo "dr-xr-xr-x 1 root root 0 $DATE INFO/SCRIPTS" + test "`$RPM -q --qf \"%{RPMTAG_PREIN}\" -- "$1"`" = '(none)' || + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/PREIN" + test "`$RPM -q --qf \"%{RPMTAG_POSTIN}\" -- "$1"`" = '(none)' || + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/POSTIN" + test "`$RPM -q --qf \"%{RPMTAG_PREUN}\" -- "$1"`" = '(none)' || + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/PREUN" + test "`$RPM -q --qf \"%{RPMTAG_POSTUN}\" -- "$1"`" = '(none)' || + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/POSTUN" + test "`$RPM -q --qf \"%{VERIFYSCRIPT}\" -- "$1"`" = '(none)' || + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/VERIFYSCRIPT" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/ALL" + fi + else + echo "$FILEPREF 0 $DATE INFO/DISTRIBUTION" + echo "$FILEPREF 0 $DATE INFO/VENDOR" + echo "$FILEPREF 0 $DATE INFO/DESCRIPTION" + echo "$FILEPREF 0 $DATE INFO/SUMMARY" + echo "dr-xr-xr-x 1 root root 0 $DATE INFO/SCRIPTS" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/PREIN" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/POSTIN" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/PREUN" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/POSTUN" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/VERIFYSCRIPT" + echo "$FILEPREF 0 $DATE INFO/SCRIPTS/ALL" + fi + if test "$MCFASTRPM" = 0 ; then + test "`$RPM -q --qf \"%{PACKAGER}\" -- "$1"`" = "(none)" || + echo "$FILEPREF 0 $DATE INFO/PACKAGER" + test "`$RPM -q --qf \"%{URL}\" -- "$1"`" = "(none)" || + echo "$FILEPREF 0 $DATE INFO/URL" + test "`$RPM -q --qf \"%{EPOCH}\" -- "$1"`" = "(none)" || + echo "$FILEPREF 0 $DATE INFO/EPOCH" + test "`$RPM -q --qf \"%{LICENSE}\" -- "$1"`" = "(none)" || + echo "$FILEPREF 0 $DATE INFO/LICENSE" + else + echo "$FILEPREF 0 $DATE INFO/PACKAGER" + echo "$FILEPREF 0 $DATE INFO/URL" + echo "$FILEPREF 0 $DATE INFO/EPOCH" + echo "$FILEPREF 0 $DATE INFO/LICENSE" + fi + echo "$FILEPREF 0 $DATE INFO/BUILDTIME" + echo "$FILEPREF 0 $DATE INFO/RPMVERSION" + echo "$FILEPREF 0 $DATE INFO/OS" + echo "$FILEPREF 0 $DATE INFO/SIZE" + if test "$MCFASTRPM" != 0 ; then + $RPM -q --qf "[%{REQUIRENAME}\n]" -- "$1" | grep "(none)" > /dev/null || + echo "$FILEPREF 0 $DATE INFO/REQUIRENAME" + $RPM -q --qf "[%{OBSOLETES}\n]" -- "$1" | grep "(none)" > /dev/null || + echo "$FILEPREF 0 $DATE INFO/OBSOLETES" + $RPM -q --qf "[%{PROVIDES}\n]" -- "$1" | grep "(none)" > /dev/null || + echo "$FILEPREF 0 $DATE INFO/PROVIDES" + $RPM -q --qf "[%{CONFLICTS}\n]" -- "$1" | grep "(none)" > /dev/null || + echo "$FILEPREF 0 $DATE INFO/CONFLICTS" + test "`$RPM -q --qf \"%{CHANGELOGTEXT}\" -- "$1"`" = "(none)" || + echo "$FILEPREF 0 $DATE INFO/CHANGELOG" + else + echo "$FILEPREF 0 $DATE INFO/REQUIRENAME" + echo "$FILEPREF 0 $DATE INFO/OBSOLETES" + echo "$FILEPREF 0 $DATE INFO/PROVIDES" + echo "$FILEPREF 0 $DATE INFO/CONFLICTS" + echo "$FILEPREF 0 $DATE INFO/CHANGELOG" + fi + + $RPM -qlv -- "$1" | grep '^[A-Za-z0-9-]' +} + +mcrpmfs_copyout () +{ + case "$2" in + HEADER) $RPM -qi -- "$1" > "$3"; exit 0;; + UNINSTALL) echo "# Run this to uninstall this RPM package" > "$3"; exit 0;; + INFO/NAME-VERSION-RELEASE) $RPM -q --qf "%{NAME}-%{VERSION}-%{RELEASE}\n" -- "$1" > "$3"; exit 0;; + INFO/RELEASE) $RPM -q --qf "%{RELEASE}\n" -- "$1" > "$3"; exit 0;; + INFO/GROUP) $RPM -q --qf "%{GROUP}\n" -- "$1" > "$3"; exit 0;; + INFO/DISTRIBUTION) $RPM -q --qf "%{DISTRIBUTION}\n" -- "$1" > "$3"; exit 0;; + INFO/VENDOR) $RPM -q --qf "%{VENDOR}\n" -- "$1" > "$3"; exit 0;; + INFO/BUILDHOST) $RPM -q --qf "%{BUILDHOST}\n" -- "$1" > "$3"; exit 0;; + INFO/SOURCERPM) $RPM -q --qf "%{SOURCERPM}\n" -- "$1" > "$3"; exit 0;; + INFO/DESCRIPTION) $RPM -q --qf "%{DESCRIPTION}\n" -- "$1" > "$3"; exit 0;; + INFO/PACKAGER) $RPM -q --qf "%{PACKAGER}\n" -- "$1" > "$3"; exit 0;; + INFO/URL) $RPM -q --qf "%{URL}\n" -- "$1" > "$3"; exit 0;; + INFO/BUILDTIME) $RPM -q --qf "%{BUILDTIME:date}\n" -- "$1" > "$3"; exit 0;; + INFO/EPOCH) $RPM -q --qf "%{EPOCH}\n" -- "$1" > "$3"; exit 0;; + INFO/LICENSE) $RPM -q --qf "%{LICENSE}\n" -- "$1" > "$3"; exit 0;; + INFO/RPMVERSION) $RPM -q --qf "%{RPMVERSION}\n" -- "$1" > "$3"; exit 0;; + INFO/REQUIRENAME) $RPM -q --qf "[%{REQUIRENAME} %{REQUIREFLAGS:depflags} %{REQUIREVERSION}\n]" -- "$1" > "$3"; exit 0;; + INFO/OBSOLETES) $RPM -q --qf "[%{OBSOLETENAME} %|OBSOLETEFLAGS?{%{OBSOLETEFLAGS:depflags} %{OBSOLETEVERSION}}:{}|\n]" -- "$1" > "$3"; exit 0;; + INFO/PROVIDES) $RPM -q --qf "[%{PROVIDES}\n]" -- "$1" > "$3"; exit 0;; + INFO/CONFLICTS) $RPM -q --qf "[%{CONFLICTS}\n]" -- "$1" > "$3"; exit 0;; + INFO/SCRIPTS/PREIN) $RPM -q --qf "%{RPMTAG_PREIN}\n" -- "$1" > "$3"; exit 0;; + INFO/SCRIPTS/POSTIN) $RPM -q --qf "%{RPMTAG_POSTIN}\n" -- "$1" > "$3"; exit 0;; + INFO/SCRIPTS/PREUN) $RPM -q --qf "%{RPMTAG_PREUN}\n" -- "$1" > "$3"; exit 0;; + INFO/SCRIPTS/POSTUN) $RPM -q --qf "%{RPMTAG_POSTUN}\n" -- "$1" > "$3"; exit 0;; + INFO/SCRIPTS/VERIFYSCRIPT) $RPM -q --qf "%{VERIFYSCRIPT}\n" -- "$1" > "$3"; exit 0;; + INFO/SCRIPTS/ALL) $RPM -q --scripts -- "$1" > "$3"; exit 0;; + INFO/SUMMARY) $RPM -q --qf "%{SUMMARY}\n" -- "$1" > "$3"; exit 0;; + INFO/OS) $RPM -q --qf "%{OS}\n" -- "$1" > "$3"; exit 0;; + INFO/CHANGELOG) $RPM -q --qf "[* %{CHANGELOGTIME:date} %{CHANGELOGNAME}\n%{CHANGELOGTEXT}\n\n]\n" -- "$1" > "$3"; exit 0;; + INFO/SIZE) $RPM -q --qf "%{SIZE} bytes\n" -- "$1" > "$3"; exit 0;; + *) + cp "/$2" "$3" + esac +} + +mcrpmfs_run () +{ + case "$2" in + UNINSTALL) echo "Uninstalling $1"; rpm -e -- "$1"; exit 0;; + esac +} + +name=`sed 's/.*\///;s/\.trpm$//' "$2"` + +case "$1" in + list) mcrpmfs_list "$name"; exit 0;; + copyout) mcrpmfs_copyout "$name" "$3" "$4"; exit 0;; + run) mcrpmfs_run "$name" "$3"; exit 1;; +esac +exit 1 diff --git a/src/vfs/extfs/helpers/u7z b/src/vfs/extfs/helpers/u7z new file mode 100755 index 0000000..91301c3 --- /dev/null +++ b/src/vfs/extfs/helpers/u7z @@ -0,0 +1,135 @@ +#! /bin/sh +# +# extfs support for p7zip +# Written by Pavel Roskin <proski@gnu.org> +# Some Bugfixes/workarounds by Sergiy Niskorodov <sgh@mail.zp.ua> +# +# This program 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. +# +# This program 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/>. + +P7ZIP=`which 7z 2>/dev/null` \ + || P7ZIP=`which 7zz 2>/dev/null` \ + || P7ZIP=`which 7za 2>/dev/null` \ + || P7ZIP=`which 7zr 2>/dev/null` \ + || P7ZIP="" + +# Let the test framework hook in: +P7ZIP=${MC_TEST_EXTFS_LIST_CMD:-$P7ZIP} +STAT=${MC_TEST_EXTFS_U7Z_STAT:-stat} + +mcu7zip_list () +{ + # Symlinks are not shown - no idea how to distinguish them + # Read-only files are not shown as such - it's rarely useful + + ugid="`id -nu` `id -ng`" + + date_re='^\(....\)-\(..\)-\(..\) \(..:..:..\)' # 19 chars. + date_mc='\2-\3-\1 \4' + empty_date_re='^ \{19\}' + + size_re='............' # 12 chars. + empty_size_re=' \{12\}' + zero_size=' 0' + + # archive entries can have no datetime info, 7z will use archive file datetime + date_archive=`$STAT -c %y "$1" 2>/dev/null | sed -n "s/${date_re}.*/${date_mc}/p" 2>/dev/null` + [ "${date_archive}"x = x ] && date_archive=`ls -lan "$1" 2>/dev/null | awk '{print $6, $7, $8}' 2>/dev/null` + [ "${date_archive}"x = x ] && date_archive="01-01-1970 00:00:00" + + $P7ZIP l "$1" | sed -n " + + # If the uncompressed size is missing, we copy the compressed size onto it. + # + # But first, if the compressed size is missing too, set it to zero: + s/^\(.\{19\} [D.]....\) $empty_size_re $empty_size_re/\1 $zero_size $zero_size/ + # Next, do the copy: + s/^\(.\{19\} [D.]....\) $empty_size_re \($size_re\)/\1 \2 \2/ + # + # (We use '.\{19\}' as the date may be missing. It may give false positives + # but we don't mind: the printing commands that follow use strict patterns.). + + # Handle directories. + s/$date_re D.... $size_re $size_re\(.*\)/drwxr-xr-x 1 $ugid 0 $date_mc \5/p + s/$empty_date_re D.... $size_re $size_re\(.*\)/drwxr-xr-x 1 $ugid 0 $date_archive \1/p + + # Handle normal files. + s/$date_re \..... \($size_re\) $size_re\(.*\)/-rw-r--r-- 1 $ugid \5 $date_mc \6/p + s/$empty_date_re \..... \($size_re\) $size_re\(.*\)/-rw-r--r-- 1 $ugid \1 $date_archive \2/p + " +} + +mcu7zip_copyout () +{ + #first we check if we have old p7zip archive with prefix ./ in filename + $P7ZIP l "$1" "$2" | grep -q "0 files, 0 folders" && \ + EXFNAME='*./'"$2" || EXFNAME="$2" + $P7ZIP e -so "$1" "$EXFNAME" > "$3" 2>/dev/null +} + +mcu7zip_copyin () +{ + $P7ZIP a -si"$2" "$1" <"$3" >/dev/null 2>&1 +} + +mcu7zip_mkdir () +{ + dir=`mktemp -d "${MC_TMPDIR:-/tmp}/mctmpdir-u7z.XXXXXX"` || exit 1 + mkdir -p "$dir"/"$2" + $P7ZIP a -w"$dir" "$1" "$dir"/"$2" >/dev/null 2>&1 + rm -rf "$dir" +} + +mcu7zip_rm () +{ + # NOTE: Version 4.20 fails to delete files in subdirectories + #first we check if we have old p7zip archive with prefix ./ in filename + $P7ZIP l "$1" "$2" | grep -q "0 files, 0 folders" && \ + EXFNAME='*./'"$2" || EXFNAME="$2" + $P7ZIP d "$1" "$EXFNAME" 2>&1 | grep -q E_NOTIMPL > /dev/null 2>&1 && \ + { printf "Function not implemented...\n7z cannot delete from solid archive." >&2 ; exit 1 ; } +} + +mcu7zip_rmdir () +{ + #first we check if we have old p7zip archive with prefix ./ in filename + $P7ZIP l "$1" "$2" | grep -q "0 files, 0 folders" && \ + EXFNAME='*./'"$2" || EXFNAME="$2" + $P7ZIP d "$1" "$EXFNAME"/ 2>&1 | grep -q E_NOTIMPL > /dev/null 2>&1 && \ + { printf "Function not implemented...\n7z cannot delete from solid archive." >&2 ; exit 1 ; } +} + +# override any locale for dates +LC_DATE=C +export LC_DATE + +umask 077 + +if [ -z "$P7ZIP" ]; then + echo "Error: could not find p7zip (looked for 7z, 7za and 7zr)" >&2 + exit 1 +fi + +cmd="$1" +shift + +case "$cmd" in + list) mcu7zip_list "$@" | sort -k 8 ;; + copyout) mcu7zip_copyout "$@" ;; + copyin) mcu7zip_copyin "$@" ;; + mkdir) mcu7zip_mkdir "$@" ;; + rm) mcu7zip_rm "$@" ;; + rmdir) mcu7zip_rmdir "$@" ;; + *) exit 1 ;; +esac +exit 0 diff --git a/src/vfs/extfs/helpers/uace.in b/src/vfs/extfs/helpers/uace.in new file mode 100644 index 0000000..22eae30 --- /dev/null +++ b/src/vfs/extfs/helpers/uace.in @@ -0,0 +1,67 @@ +#! /bin/sh + +# +# ACE Virtual filesystem executive v0.1 +# Works with unace v2.5 + +# Note: There are two packages for Debian: 'unace' (v1.2b) and +# 'unace-nonfree' (v2.x). This script supports 'unace-nonfree' only. +# 'unace', which supports only old versions of ACE archives (and is +# therefore of little use), uses the pipe character to separate columns +# in its listing format. + +# Copyright (C) 2008 Jacques Pelletier +# May be distributed under the terms of the GNU Public License +# <jpelletier@ieee.org> +# + +# Define which archiver you are using with appropriate options +ACE_LIST=${MC_TEST_EXTFS_LIST_CMD:-"unace l"} +ACE_GET="unace x" +# ACE_PUT="unace ?" not available + +# The 'list' command executive + +# Unace: DD.MM.YY HH:MM packed size ratio file +# ls: +mc_ace_fs_list() +{ + if [ "x$UID" = "x" ]; then + UID=`id -ru 2>/dev/null` + if [ "x$UID" = "x" ]; then + UID=0 + fi + fi + $ACE_LIST "$1" | @AWK@ -v uid=$UID ' +/%/ { + split($1,date,".") + + if (date[3] > 50) + date[3]=date[3] + 1900 + else + date[3]=date[3] + 2000 + + printf "-rw-r--r-- 1 %-8d %-8d %8d %02d-%02d-%04d %s %s\n", uid, 0, $4, date[2], date[1], date[3], $2, $6 +}' 2>/dev/null + exit 0 +} + +# Command: copyout archivename storedfilename extractto +mc_ace_fs_copyout() +{ + $ACE_GET "$1" "$2" > /dev/null 2>&1 + mv "$2" "$3" +} + +# The main routine +umask 077 + +cmd="$1" +shift + +case "$cmd" in + list) mc_ace_fs_list "$@" ;; + copyout) mc_ace_fs_copyout "$@" ;; + *) exit 1 ;; +esac +exit 0 diff --git a/src/vfs/extfs/helpers/ualz.in b/src/vfs/extfs/helpers/ualz.in new file mode 100644 index 0000000..d054553 --- /dev/null +++ b/src/vfs/extfs/helpers/ualz.in @@ -0,0 +1,68 @@ +#!/bin/sh +# +# Written by Pavel Roskin <proski@gnu.org> +# (C) 2005 The Free Software Foundation. +# +# + +UNALZ=unalz + +mcualz_list () +{ + $UNALZ -l "$1" | @AWK@ -v uid=`id -nu` -v gid=`id -ng` ' +{ + if ($1 ~ /[0-9][0-9][:/][0-9][0-9][:/][0-9][0-9]$/) + { + # Kludge for non-POSIX date format in unalz 0.50 + split($1, date, "[/:]") + if (length(date[1]) == 4) { + pdate = date[2] "/" date[3] "/" date[1] + } else { + pdate = date[1] "/" date[2] "/" date[3] + } + + time=$2 + perm=$3 + size=$4 + sub(/^ *[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* */, "") + file=$0 + gsub(/\\/, "/", file) + if (perm ~ /.D../) + perm = "drwxr-xr-x" + else + perm = "-rw-r--r--" + printf "%s 1 %s %s %d %s %s %s\n", perm, uid, gid, size, pdate, time, file + } +} +' +} + +mcualz_copyout () +{ + TMPDIR=`mktemp -d ${MC_TMPDIR:-/tmp}/mctmpdir-ualz.XXXXXX` || exit 1 + + # This is a workaround for a bug in unalz 0.50 - it crashes if the + # output directory is an absolute path. + dir=`dirname "$TMPDIR/$2"` + mkdir -p "$dir" + + $UNALZ -d "$TMPDIR" "$1" "$2" >/dev/null + cat "$TMPDIR/$2" > "$3" + rm -rf "$TMPDIR" +} + +# override any locale for dates +LC_ALL=C +export LC_ALL +umask 077 + +cmd="$1" +shift + +case "$cmd" in + list) mcualz_list "$@" ;; + copyout) mcualz_copyout "$@" ;; + *) exit 1 ;; +esac + +exit 0 diff --git a/src/vfs/extfs/helpers/uar.in b/src/vfs/extfs/helpers/uar.in new file mode 100644 index 0000000..269bdb6 --- /dev/null +++ b/src/vfs/extfs/helpers/uar.in @@ -0,0 +1,60 @@ +#!/bin/sh +# +# Written by Alex Kuchma <ask@bcs.zp.ua> +# Alex Tkachenko <alex@bcs.zp.ua> +# Updated by Vitezslav Samel <xsamel00@dcse.fee.vutbr.cz> +# +# (C) 1997, 1998 The Free Software Foundation. +# +# + +XAR=ar + +mcarfs_list () +{ + # If $temp_replace string is part of the filename that part might get lost + temp_replace='Unique Separator String' + thisyear="`date +%Y`" + $XAR tv "$1" | sed 's,^,-,;s, , 1 ,;s,/, ,' | + sed -e "s/\( [0-2][0-9]\:[0-5][0-9]\)\( $thisyear \)\(.*\)/\1$temp_replace\3/" | + sed -e "s/\( [0-2][0-9]\:[0-5][0-9] \)\([12][0-9][0-9][0-9] \)\(.*\)/ \2\3/" | + sed -e "s/$temp_replace/ /" +} + +mcarfs_copyout () +{ + $XAR p "$1" "$2" > "$3" +} + +mcarfs_copyin () +{ + TMPDIR=`mktemp -d "${MC_TMPDIR:-/tmp}/mctmpdir-uar.XXXXXX"` || exit 1 + name=`basename "$2"` + (cd "$TMPDIR" && cp -fp "$3" "$name" && $XAR r "$1" "$name") + rm -rf "$TMPDIR" +} + +mcarfs_rm () +{ + $XAR d "$1" "$2" +} + +# override any locale for dates +LC_ALL=C +export LC_ALL + +umask 077 +case "$1" in + list) mcarfs_list "$2" ;; + copyout) shift; mcarfs_copyout "$@" ;; + copyin) shift; mcarfs_copyin "$@" ;; + rm) shift; mcarfs_rm "$@" ;; + mkdir|rmdir) + echo "mcarfs: ar archives cannot contain directories." 1>&2 + exit 1;; + *) + echo "mcarfs: unknown command: \"$1\"." 1>&2 + exit 1;; +esac + +exit 0 diff --git a/src/vfs/extfs/helpers/uarc.in b/src/vfs/extfs/helpers/uarc.in new file mode 100644 index 0000000..a81839a --- /dev/null +++ b/src/vfs/extfs/helpers/uarc.in @@ -0,0 +1,92 @@ +#! /bin/sh + +# +# ARC Virtual filesystem executive +# Copyright (C) 2008 Jacques Pelletier +# May be distributed under the terms of the GNU Public License +# <jpelletier@ieee.org> +# + +# Define which archiver you are using with appropriate options +ARC_LIST=${MC_TEST_EXTFS_LIST_CMD:-"arc v"} +ARC_GET="arc x" +ARC_PUT="arc a" +ARC_DEL="arc d" + +# The 'list' command executive + +mc_arc_fs_list() +{ + if [ "x$UID" = "x" ]; then + UID=`id -ru 2>/dev/null` + if [ "x$UID" = "x" ]; then + UID=0 + fi + fi + $ARC_LIST "$1" | @AWK@ -v uid=$UID ' +BEGIN { + # Copied from uzoo.in. + split("Jan:Feb:Mar:Apr:May:Jun:Jul:Aug:Sep:Oct:Nov:Dec", month_list, ":") + for (i=1; i<=12; i++) { + month[month_list[i]] = i + } +} +/^Name/ { next } +/===/ { next } +/^Total/ { next } +{ + if ($8 > 50) + $8=$8 + 1900 + else + $8=$8 + 2000 + + split($9, a, ":") + + # convert AM/PM to 00-23 + if (a[2] ~ /a$|p$/) + { + if (a[2] ~ /p$/) + a[1] = a[1]+12 + + a[2]=substr(a[2],1,2) + } + + printf "-rw-r--r-- 1 %-8d %-8d %8d %02d-%02d-%04d %02d:%02d %s\n", uid, 0, $2, month[$7], $6, $8, a[1], a[2], $1 +}' 2>/dev/null + exit 0 +} + +# Command: copyout archivename storedfilename extractto +mc_arc_fs_copyout() +{ + $ARC_GET "$1" "$2" 2> /dev/null + mv "$2" "$3" +} + +# Command: copyin archivename storedfilename sourcefile +mc_arc_fs_copyin() +{ + mv "$3" "$2" + $ARC_PUT "$1" "$2" 2> /dev/null +} + +# Command: rm archivename storedfilename +mc_arc_fs_rm() +{ + $ARC_DEL "$1" "$2" 2> /dev/null +} + +# The main routine +umask 077 + +cmd="$1" +shift + +case "$cmd" in + list) mc_arc_fs_list "$@" ;; + copyout) mc_arc_fs_copyout "$@" ;; + copyin) mc_arc_fs_copyin "$@" ;; + rm) mc_arc_fs_rm "$@" ;; + *) exit 1 ;; +esac +exit 0 diff --git a/src/vfs/extfs/helpers/uarj.in b/src/vfs/extfs/helpers/uarj.in new file mode 100644 index 0000000..15549a0 --- /dev/null +++ b/src/vfs/extfs/helpers/uarj.in @@ -0,0 +1,75 @@ +#! /bin/sh +# +# Written by Viatcheslav Odintsov (2:5020/181) +# (C) 2002 ARJ Software Russia. +# +# This is an updated parser for ARJ archives in Midnight Commander. You need +# full ARJ rather than UNARJ. Open-source ARJ v 3.10 for Unix platforms can +# be obtained here: +# +# - http://www.sourceforge.net/projects/arj/ +# - http://arj.sourceforge.net/ + + +ARJ="arj -+ -ja1" + + +mcarjfs_list () +{ + $ARJ v "$1" | @AWK@ -v uuid=$(id -ru) ' + { + if (($0 ~ /^[0-9]+\) .*/)||($0 ~ /^------------ ---------- ---------- -----/)){ + if (filestr ~ /^[0-9]+\) .*/) { + printf "%s 1 %-8d %-8d %8d %02d-%02d-%02d %02d:%02d %s%s\n", perm, uid, gid, size, date[2], date[3], date[1], time[1], time[2], file, symfile + perm="" + file="" + symfile="" + filestr="" + } + } + + if ($0 ~ /^[0-9]+\) .*/) { + filestr=$0 + sub(/^[0-9]*\) /, "") + file=$0 + uid=uuid + gid=0 + } + + if ($0 ~ /^.* [0-9]+[\t ]+[0-9]+ [0-9]\.[0-9][0-9][0-9] [0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9].*/) { + size=$3 + split($6, date, "-") + split($7, time, ":") + if ($8 ~ /^[rwx-]/) {perm=$8;} + else {perm="-rw-r--r--"} + } + + if ($0 ~ /^[\t ]+SymLink -> .*/) { + symfile = " -> "$3 + perm="l"substr(perm, 2) + } + + if ($0 ~ /^[\t ]+Owner: UID [0-9]+\, GID [0-9]+/) { + uid=$3 + gid=$5 + owner=1 + } + }' +} + + +mcarjfs_copyout () +{ + $ARJ e -y "$1" "$2" -jw"$3" >/dev/null 2>/dev/null +} + + +umask 077 +cmd="$1" +shift +case "$cmd" in + list) mcarjfs_list "$@" ;; + copyout) mcarjfs_copyout "$@" ;; + *) exit 1 ;; +esac +exit 0 diff --git a/src/vfs/extfs/helpers/uc1541 b/src/vfs/extfs/helpers/uc1541 new file mode 100755 index 0000000..dc15b42 --- /dev/null +++ b/src/vfs/extfs/helpers/uc1541 @@ -0,0 +1,702 @@ +#!/usr/bin/env python +""" +UC1541 Virtual filesystem + +Author: Roman 'gryf' Dobosz <gryf73@gmail.com> +Date: 2019-09-20 +Version: 3.3 +Licence: BSD +source: https://bitbucket.org/gryf/uc1541 +mirror: https://github.com/gryf/uc1541 +""" + +import sys +import re +import os +import gzip +from subprocess import Popen, PIPE + +if os.getenv('UC1541_DEBUG'): + import logging + LOG = logging.getLogger('UC1541') + LOG.setLevel(logging.DEBUG) + FILE_HANDLER = logging.FileHandler("/tmp/uc1541.log") + FILE_FORMATTER = logging.Formatter("%(asctime)s %(levelname)-8s " + "%(lineno)s %(funcName)s - %(message)s") + FILE_HANDLER.setFormatter(FILE_FORMATTER) + FILE_HANDLER.setLevel(logging.DEBUG) + LOG.addHandler(FILE_HANDLER) +else: + class LOG(object): + """ + Dummy logger object. Does nothing. + """ + @classmethod + def debug(*args, **kwargs): + pass + + @classmethod + def info(*args, **kwargs): + pass + + @classmethod + def warning(*args, **kwargs): + pass + + @classmethod + def error(*args, **kwargs): + pass + + @classmethod + def critical(*args, **kwargs): + pass + + +SECLEN = 256 + + +def _ord(string_or_int): + """ + Return an int value for the (possible) string passed in argument. This + function is for compatibility between python2 and python3, where single + element in byte string array is a string or an int respectively. + """ + try: + return ord(string_or_int) + except TypeError: + return string_or_int + + +def _get_raw(dimage): + """ + Try to get contents of the D64 image either it's gzip compressed or not. + """ + raw = None + with gzip.open(dimage, 'rb') as fobj: + # Although the common approach with gzipped files is to check the + # magic number, in this case there is no guarantee that first track + # does not contain exactly the same byte sequence as the magic number. + # So the only way left is to actually try to uncompress the file. + try: + raw = fobj.read() + except (IOError, OSError): + pass + if not raw: + with open(dimage, 'rb') as fobj: + raw = fobj.read() + + return raw + + +def _get_implementation(disk): + """ + Check the file under fname and return right class for creating an object + corresponding for the file + """ + len_map = {822400: D81, # 80 tracks + 819200: D81, # 80 tracks, 3200 error bytes + 349696: D71, # 70 tracks + 351062: D71, # 70 tracks, 1366 error bytes + 174848: D64, # usual d64 disc image, 35 tracks, no errors + 175531: D64, # 35 track, 683 error bytes + 196608: D64, # 40 track, no errors + 197376: D64} # 40 track, 768 error bytes + + if disk[:32].startswith(b'C64'): + return # T64 + + return len_map.get(len(disk))(disk) + + +class Disk(object): + """ + Represent common disk interface + """ + CHAR_MAP = {32: ' ', 33: '!', 34: '"', 35: '#', 37: '%', 38: '&', 39: "'", + 40: '(', 41: ')', 42: '*', 43: '+', 44: ',', 45: '-', 46: '.', + 47: '/', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', + 54: '6', 55: '7', 56: '8', 57: '9', 59: ';', 60: '<', 61: '=', + 62: '>', 63: '?', 64: '@', 65: 'a', 66: 'b', 67: 'c', 68: 'd', + 69: 'e', 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j', 75: 'k', + 76: 'l', 77: 'm', 78: 'n', 79: 'o', 80: 'p', 81: 'q', 82: 'r', + 83: 's', 84: 't', 85: 'u', 86: 'v', 87: 'w', 88: 'x', 89: 'y', + 90: 'z', 91: '[', 93: ']', 97: 'A', 98: 'B', 99: 'C', + 100: 'D', 101: 'E', 102: 'F', 103: 'G', 104: 'H', 105: 'I', + 106: 'J', 107: 'K', 108: 'L', 109: 'M', 110: 'N', 111: 'O', + 112: 'P', 113: 'Q', 114: 'R', 115: 'S', 116: 'T', 117: 'U', + 118: 'V', 119: 'W', 120: 'X', 121: 'Y', 122: 'Z', 193: 'A', + 194: 'B', 195: 'C', 196: 'D', 197: 'E', 198: 'F', 199: 'G', + 200: 'H', 201: 'I', 202: 'J', 203: 'K', 204: 'L', 205: 'M', + 206: 'N', 207: 'O', 208: 'P', 209: 'Q', 210: 'R', 211: 'S', + 212: 'T', 213: 'U', 214: 'V', 215: 'W', 216: 'X', 217: 'Y', + 218: 'Z'} + + FILE_TYPES = {0b000: 'del', + 0b001: 'seq', + 0b010: 'prg', + 0b011: 'usr', + 0b100: 'rel'} + + DIR_TRACK = 18 + DIR_SECTOR = 1 + + def __init__(self, raw): + """ + Init + """ + self.raw = raw + self.current_sector_data = None + self.next_sector = 0 + self.next_track = None + self._dir_contents = [] + self._already_done = [] + + def _map_filename(self, string): + """ + Transcode filename to ASCII compatible. Replace not supported + characters with jokers. + """ + + filename = list() + + for chr_ in string: + if _ord(chr_) == 160: # shift+space character; $a0 + break + + character = D64.CHAR_MAP.get(_ord(chr_), '?') + filename.append(character) + + # special cases + if filename[0] == "-": + filename[0] = "?" + + LOG.debug("string: ``%s'' mapped to: ``%s''", string, + "".join(filename)) + return "".join(filename) + + def _go_to_next_sector(self): + """ + Fetch (if exist) next sector from a directory chain + Return False if the chain ends, True otherwise + """ + + # Well, self.next_sector _should_ have value $FF, but apparently there + # are the cases where it is not, therefore checking for that will not + # be performed and value of $00 on the next track will end the + # directory + if self.next_track == 0: + LOG.debug("End of directory") + return False + + if self.next_track is None: + LOG.debug("Going to the track: %s, %s", self.DIR_TRACK, + self.DIR_SECTOR) + offset = self._get_offset(self.DIR_TRACK, self.DIR_SECTOR) + else: + offset = self._get_offset(self.next_track, self.next_sector) + LOG.debug("Going to the track: %s,%s", self.next_track, + self.next_sector) + + self.current_sector_data = self.raw[offset:offset + SECLEN] + + # Guard for reading data out of bound - that happened for discs which + # store only raw data, even on directory track + if not self.current_sector_data: + return False + + self.next_track = _ord(self.current_sector_data[0]) + self.next_sector = _ord(self.current_sector_data[1]) + + if (self.next_track, self.next_sector) in self._already_done: + # Just a failsafe. Endless loop is not what is expected. + LOG.debug("Loop in track/sector pointer at %d,%d", + self.next_track, self.next_sector) + self._already_done = [] + return False + + self._already_done.append((self.next_track, self.next_sector)) + LOG.debug("Next track: %s,%s", self.next_track, self.next_sector) + return True + + def _get_ftype(self, num): + """ + Get filetype as a string + """ + return D64.FILE_TYPES.get(int("%d%d%d" % (num & 4 and 1, + num & 2 and 1, + num & 1), 2), '???') + + def _get_offset(self, track, sector): + """ + Return offset (in bytes) for specified track and sector. + """ + return 0 + + def _harvest_entries(self): + """ + Traverse through sectors and store entries in _dir_contents + """ + sector = self.current_sector_data + for dummy in range(8): + entry = sector[:32] + ftype = _ord(entry[2]) + + if ftype == 0: # deleted + sector = sector[32:] + continue + + type_verbose = self._get_ftype(ftype) + + protect = _ord(entry[2]) & 64 and "<" or " " + fname = entry[5:21] + if ftype == 'rel': + size = _ord(entry[23]) + else: + size = _ord(entry[30]) + _ord(entry[31]) * 226 + + self._dir_contents.append({'fname': self._map_filename(fname), + 'ftype': type_verbose, + 'size': size, + 'protect': protect}) + sector = sector[32:] + + def list_dir(self): + """ + Return directory list as list of dict with keys: + fname, ftype, protect and size + """ + while self._go_to_next_sector(): + self._harvest_entries() + + return self._dir_contents + + +class D64(Disk): + """ + Implement d64 directory reader + """ + + def _get_offset(self, track, sector): + """ + Return offset (in bytes) for specified track and sector. + + Track Sectors/track # Tracks + ----- ------------- --------- + 1-17 21 17 + 18-24 19 7 + 25-30 18 6 + 31-40 17 10 + """ + offset = 0 + truncate_track = 0 + + if track > 17: + offset = 17 * 21 * SECLEN + truncate_track = 17 + + if track > 24: + offset += 7 * 19 * SECLEN + truncate_track = 24 + + if track > 30: + offset += 6 * 18 * SECLEN + truncate_track = 30 + + track = track - truncate_track + offset += track * sector * SECLEN + + return offset + + +class D71(Disk): + """ + Implement d71 directory reader + """ + + def _get_offset(self, track, sector): + """ + Return offset (in bytes) for specified track and sector. + + Track Sec/trk # Tracks + -------------- ------- --------- + 1-17 (side 0) 21 17 + 18-24 (side 0) 19 7 + 25-30 (side 0) 18 6 + 31-35 (side 0) 17 5 + 36-52 (side 1) 21 17 + 53-59 (side 1) 19 7 + 60-65 (side 1) 18 6 + 66-70 (side 1) 17 5 + """ + offset = 0 + truncate_track = 0 + + if track > 17: + offset = 17 * 21 * SECLEN + truncate_track = 17 + + if track > 24: + offset += 7 * 19 * SECLEN + truncate_track = 24 + + if track > 30: + offset += 6 * 18 * SECLEN + truncate_track = 30 + + if track > 35: + offset += 5 * 17 * SECLEN + truncate_track = 35 + + if track > 52: + offset = 17 * 21 * SECLEN + truncate_track = 17 + + if track > 59: + offset += 7 * 19 * SECLEN + truncate_track = 24 + + if track > 65: + offset += 6 * 18 * SECLEN + truncate_track = 30 + + track = track - truncate_track + offset += track * sector * SECLEN + + return offset + + +class D81(Disk): + """ + Implement d81 directory reader + """ + DIR_TRACK = 40 + DIR_SECTOR = 3 + FILE_TYPES = {0b000: 'del', + 0b001: 'seq', + 0b010: 'prg', + 0b011: 'usr', + 0b100: 'rel', + 0b101: 'cbm'} + + def _get_offset(self, track, sector): + """ + Return offset (in bytes) for specified track and sector. In d81 is + easy, since we have 80 tracks with 40 sectors for 256 bytes each. + """ + # we wan to go to the beginning (first sector) of the track, not it's + # max, so that we need to extract its amount. + return (track * 40 - 40) * SECLEN + sector * SECLEN + + +class Uc1541(object): + """ + Class for interact with c1541 program and MC + """ + PRG = re.compile(r'(\d+)\s+"([^"]*)".+?\s(del|prg|rel|seq|usr)([\s<])') + + def __init__(self, archname): + self.arch = archname + self.out = '' + self.err = '' + self._verbose = os.getenv("UC1541_VERBOSE", False) + self._hide_del = os.getenv("UC1541_HIDE_DEL", False) + + self.dirlist = _get_implementation(_get_raw(archname)).list_dir() + self.file_map = {} + self.directory = [] + + def list(self): + """ + Output list contents of D64 image. + Convert filenames to be Unix filesystem friendly + Add suffix to show user what kind of file do he dealing with. + """ + LOG.info("List contents of %s", self.arch) + directory = self._get_dir() + + # If there is an error reading directory, show the reason to the user + if self.out.startswith("Error"): + sys.stderr.write(self.out.split("\n")[0] + "\n") + return 2 + + for entry in directory: + sys.stdout.write("%(perms)s 1 %(uid)-8d %(gid)-8d %(size)8d " + "Jan 01 1980 %(display_name)s\n" % entry) + return 0 + + def rm(self, dst): + """ + Remove file from D64 image + """ + LOG.info("Removing file %s", dst) + dst = self._get_masked_fname(dst) + + if not self._call_command('delete', dst=dst): + return self._show_error() + + return 0 + + def copyin(self, dst, src): + """ + Copy file to the D64 image. Destination filename has to be corrected. + """ + LOG.info("Copy into D64 %s as %s", src, dst) + dst = self._correct_fname(dst) + + if not self._call_command('write', src=src, dst=dst): + return self._show_error() + + return 0 + + def copyout(self, src, dst): + """ + Copy file form the D64 image. Source filename has to be corrected, + since it's representation differ from the real one inside D64 image. + """ + LOG.info("Copy form D64 %s as %s", src, dst) + if not src.endswith(".prg"): + return "cannot read" + + src = self._get_masked_fname(src) + + if not self._call_command('read', src=src, dst=dst): + return self._show_error() + + return 0 + + def mkdir(self, dirname): + """Not supported""" + self.err = "D64 format doesn't support directories" + return self._show_error() + + def run(self, fname): + """Not supported""" + self.err = "Not supported, unless you are using MC on real C64 ;)" + return self._show_error() + + def _correct_fname(self, fname): + """ + Return filename with mapped characters, without .prg extension. + Characters like $, *, + in filenames are perfectly legal, but c1541 + program seem to have issues with it while writing, so it will also be + replaced. + """ + char_map = {'|': "/", + "\\": "/", + "~": " ", + "$": "?", + "*": "?"} + + if fname.lower().endswith(".prg"): + fname = fname[:-4] + + new_fname = [] + for char in fname: + trans = char_map.get(char) + new_fname.append(trans if trans else char) + + return "".join(new_fname) + + def _get_masked_fname(self, fname): + """ + Return masked filename with '?' jokers instead of non ASCII + characters, useful for copying or deleting files with c1541. In case + of several files with same name exists in directory, only first one + will be operative (first as appeared in directory). + + Warning! If there are two different names but the only difference is in + non-ASCII characters (some PET ASCII or control characters) there is + a risk that one can remove both files. + """ + directory = self._get_dir() + + for entry in directory: + if entry['display_name'] == fname: + return entry['pattern_name'] + + def _get_dir(self): + """ + Retrieve directory via c1541 program + """ + directory = [] + + uid = os.getuid() + gid = os.getgid() + + if not self._call_command('list'): + return self._show_error() + + idx = 0 + for line in self.out.split("\n"): + if Uc1541.PRG.match(line): + blocks, fname, ext, rw = Uc1541.PRG.match(line).groups() + + if ext == 'del' and self._hide_del: + continue + + display_name = ".".join([fname, ext]) + pattern_name = self.dirlist[idx]['fname'] + + if '/' in display_name: + display_name = display_name.replace('/', '|') + + # workaround for space and dash at the beginning of the + # filename + char_map = {' ': '~', + '-': '_'} + display_name = "".join([char_map.get(display_name[0], + display_name[0]), + display_name[1:]]) + + if ext == 'del': + perms = "----------" + else: + perms = "-r%s-r--r--" % (rw.strip() and "-" or "w") + + directory.append({'pattern_name': pattern_name, + 'display_name': display_name, + 'uid': uid, + 'gid': gid, + 'size': int(blocks) * SECLEN, + 'perms': perms}) + idx += 1 + return directory + + def _show_error(self): + """ + Pass out error output from c1541 execution + """ + if self._verbose: + return self.err + else: + return 1 + + def _call_command(self, cmd, src=None, dst=None): + """ + Return status of the provided command, which can be one of: + write + read + delete + dir/list + """ + command = ['c1541', '-attach', self.arch, '-%s' % cmd] + if src: + command.append(src) + if dst: + command.append(dst) + + LOG.debug('executing command: %s', ' '.join(command)) + # For some reason using write and delete commands and reading output + # confuses Python3 beneath MC and as a consequence MC report an + # error...therefore for those commands let's not use + # universal_newlines... + universal_newlines = True + if cmd in ['delete', 'write']: + universal_newlines = False + self.out, self.err = Popen(command, + universal_newlines=universal_newlines, + stdout=PIPE, stderr=PIPE).communicate() + + if self.err: + LOG.debug('an err: %s', self.err) + return not self.err + + +CALL_MAP = {'list': lambda a: Uc1541(a.arch).list(), + 'copyin': lambda a: Uc1541(a.arch).copyin(a.src, a.dst), + 'copyout': lambda a: Uc1541(a.arch).copyout(a.src, a.dst), + 'mkdir': lambda a: Uc1541(a.arch).mkdir(a.dst), + 'rm': lambda a: Uc1541(a.arch).rm(a.dst), + 'run': lambda a: Uc1541(a.arch).run(a.dst)} + + +def parse_args(): + """Use ArgumentParser to check for script arguments and execute.""" + parser = ArgumentParser() + subparsers = parser.add_subparsers(help='supported commands', + dest='subcommand') + subparsers.required = True + parser_list = subparsers.add_parser('list', help="List contents of D64 " + "image") + parser_copyin = subparsers.add_parser('copyin', help="Copy file into D64 " + "image") + parser_copyout = subparsers.add_parser('copyout', help="Copy file out of " + "D64 image") + parser_rm = subparsers.add_parser('rm', help="Delete file from D64 image") + parser_mkdir = subparsers.add_parser('mkdir', help="Create directory in " + "archive") + parser_run = subparsers.add_parser('run', help="Execute archived file") + + parser_list.add_argument('arch', help="D64 Image filename") + parser_list.set_defaults(func=CALL_MAP['list']) + + parser_copyin.add_argument('arch', help="D64 Image filename") + parser_copyin.add_argument('src', help="Source filename") + parser_copyin.add_argument('dst', help="Destination filename (to be " + "written into D64 image)") + parser_copyin.set_defaults(func=CALL_MAP['copyin']) + + parser_copyout.add_argument('arch', help="D64 Image filename") + parser_copyout.add_argument('src', help="Source filename (to be read from" + " D64 image") + parser_copyout.add_argument('dst', help="Destination filename") + parser_copyout.set_defaults(func=CALL_MAP['copyout']) + + parser_rm.add_argument('arch', help="D64 Image filename") + parser_rm.add_argument('dst', help="File inside D64 image to be deleted") + parser_rm.set_defaults(func=CALL_MAP['rm']) + + parser_mkdir.add_argument('arch', help="archive filename") + parser_mkdir.add_argument('dst', help="Directory name inside archive to " + "be created") + parser_mkdir.set_defaults(func=CALL_MAP['mkdir']) + + parser_run.add_argument('arch', help="archive filename") + parser_run.add_argument('dst', help="File to be executed") + parser_run.set_defaults(func=CALL_MAP['run']) + + args = parser.parse_args() + return args.func(args) + + +def no_parse(): + """Failsafe argument "parsing". Note, that it blindly takes positional + arguments without checking them. In case of wrong arguments it will + silently exit""" + try: + if sys.argv[1] not in ('list', 'copyin', 'copyout', 'rm', 'mkdir', + "run"): + sys.exit(2) + except IndexError: + sys.exit(2) + + class Arg(object): + """Mimic argparse object""" + dst = None + src = None + arch = None + + arg = Arg() + + try: + arg.arch = sys.argv[2] + if sys.argv[1] in ('copyin', 'copyout'): + arg.src = sys.argv[3] + arg.dst = sys.argv[4] + elif sys.argv[1] in ('rm', 'run', 'mkdir'): + arg.dst = sys.argv[3] + except IndexError: + sys.exit(2) + + return CALL_MAP[sys.argv[1]](arg) + + +if __name__ == "__main__": + LOG.debug("Script params: %s", str(sys.argv)) + try: + from argparse import ArgumentParser + PARSE_FUNC = parse_args + except ImportError: + PARSE_FUNC = no_parse + + sys.exit(PARSE_FUNC()) diff --git a/src/vfs/extfs/helpers/ucab.in b/src/vfs/extfs/helpers/ucab.in new file mode 100644 index 0000000..252c8ca --- /dev/null +++ b/src/vfs/extfs/helpers/ucab.in @@ -0,0 +1,40 @@ +#! /bin/sh + +CAB=cabextract + +mccabfs_list () +{ + $CAB -l "$1" | @AWK@ -v uid=`id -un` -v gid=`id -gn` ' +BEGIN { flag=0 } +/^-------/ { flag++; if (flag > 1) exit 0; next } +{ +if (flag == 0) next +if (length($6) == 0) next +pr="-rw-r--r--" +split($3, a, ".") +split($4, b, ":") +printf "%s 1 %s %s %d %02d/%02d/%02d %02d:%02d %s\n", pr, uid, gid, $1, a[2], a[1], a[3], b[1], b[2], $6 +}' + +} + +mccabfs_copyout () +{ + $CAB -F "$2" -p "$1" > "$3" +} + +LC_ALL=C +export LC_ALL + +umask 077 + +cmd="$1" + +case "$cmd" in + # Workaround for a bug in mc - directories must precede files to + # avoid duplicate entries, so we sort output by filenames + list) mccabfs_list "$2" ;; + copyout) mccabfs_copyout "$2" "$3" "$4" ;; + *) exit 1 ;; +esac +exit 0 diff --git a/src/vfs/extfs/helpers/uha.in b/src/vfs/extfs/helpers/uha.in new file mode 100644 index 0000000..9dd0016 --- /dev/null +++ b/src/vfs/extfs/helpers/uha.in @@ -0,0 +1,52 @@ +#!/bin/sh +# +# It is the uhafs Valery Kornienkov vlk@st.simbirsk.su 2:5051/30@fidonet +# ver 0.1 Thu Apr 6 12:05:08 2000 +# +# Tested with HA 0.999. Source of ha can be found at +# ftp://ftp.ibiblio.org/pub/Linux/utils/compress/ + +HA=ha + +mchafs_list () +{ + $HA lf "$1" 2>/dev/null | @AWK@ -v uid=$(id -ru) ' +/^===========/ {next} +{ + if ($5="%" && $8~/DIR|ASC|HSC|CPY/) { + split($6, a, "-") + split($7, t, ":") + filename=$1 + filesize=$2 + getline + if ($2=="(none)") $2="" + path=$2 + getline + if ($1~/^d.*/) next + printf "%s %s %-8d %-8d %8d %s-%s-%s %s:%s %s%s\n",\ + $1,1,0,0,filesize,a[3],a[2],a[1],t[1],t[2],path,filename + } +}' +} + +mchafs_copyout () +{ + TMPDIR=`mktemp -d "${MC_TMPDIR:-/tmp}/mctmpdir-uha.XXXXXX"` || exit 1 + cd "$TMPDIR" + + $HA xyq "$1" "$2" >/dev/null + cat "$2" > "$3" + + cd / + rm -rf "$TMPDIR" +} + +cmd="$1" +shift + +case "$cmd" in + list) mchafs_list "$@" ;; + copyout) mchafs_copyout "$@" ;; + *) exit 1 ;; +esac +exit 0 diff --git a/src/vfs/extfs/helpers/ulha.in b/src/vfs/extfs/helpers/ulha.in new file mode 100644 index 0000000..0b5735c --- /dev/null +++ b/src/vfs/extfs/helpers/ulha.in @@ -0,0 +1,142 @@ +#! /bin/sh + +# +# LHa Virtual filesystem executive v0.1 +# Copyright (C) 1996, 1997 Joseph M. Hinkle +# May be distributed under the terms of the GNU Public License +# <jhinkle@rockisland.com> +# + +# Code for mc_lha_fs_run() suggested by: +# Jan 97 Zdenek Kabelac <kabi@informatics.muni.cz> + +# Tested with mc 3.5.18 and gawk 3.0.0 on Linux 2.0.0 +# Tested with lha v1.01 and lharc v1.02 +# Information and sources for other forms of lha/lzh appreciated + +# Nota bene: +# There are several compression utilities which produce *.lha files. +# LHArc and LHa in exist several versions, and their listing output varies. +# Another variable is the architecture on which the compressed file was made. +# This program attempts to sort out the variables known to me, but it is likely +# to display an empty panel if it encounters a mystery. +# In that case it will be useful to execute this file from the command line: +# ./lha list Mystery.lha +# to examine the output directly on the console. The output string must be +# precisely in the format described in the README in this directory. +# Caveat emptor. +# Learn Latin. + +# Define your awk +AWK=@AWK@ + +# Define which archiver you are using with appropriate options +LHA_LIST="lha lq" +LHA_GET="lha pq" +LHA_PUT="lha aq" + +# The 'list' command executive + +mc_lha_fs_list() +{ + # List the contents of the archive and sort it out + $LHA_LIST "$1" | $AWK -v uid=`id -nu` -v gid=`id -ng` ' + # Strip a leading '/' if present in a filepath + $(NF) ~ /^\// { $(NF) = substr($NF,2) } + # Print the line this way if there is no permission string + $1 ~ /^\[.*\]/ { + # Invent a generic permission + $1 = ($NF ~ /\/$/) ? "drwxr-xr-x":"-rwxr--r--"; + # Print it + printf "%s 1 %-8s %-8s %-8d %s %s %s %s\n", + $1, uid, gid, $2, $4, $5, $6, $7; + # Get the next line of the list + next; + } + # Do it this way for a defined permission + $1 !~ /^\[.*\]/ { + # If the permissions and UID run together + if ($1 ~ /\//) { + $8 = $7; + $7 = $6; + $6 = $5; + $5 = $4; + $3 = $2; + $2 = substr($1,10); + $1 = substr($1,1,9); + } + # If the permission string is missing a type + if (length($1) == 9) { + if ($NF ~ /\/$/) + $1 = ("d" $1); + else + $1 = ("-" $1); + } + # UID:GID might not be the same as on your system so print numbers + # Well, that is the intent. At the moment mc is translating them. + split($2, id, "/"); + printf "%s 1 %-8d %-8d %-8d %s %s %s %s\n", + $1, id[1], id[2], $3, $5, $6, $7, $8; + # Get the next line of the list + next; + } + + ' +} + +# The 'copyout' command executive to copy displayed files to a destination + +mc_lha_fs_copyout() +{ + $LHA_GET "$1" "$2" > "$3" +} + +# The 'copyin' command executive to add something to the archive + +mc_lha_fs_copyin () +{ + NAME2=`basename "$2"`; DIR2=${2%$NAME2} + NAME3=`basename "$3"`; DIR3=${3%$NAME3} + + cd "${DIR3}" + + ONE2=${2%%/*} + [ -n "${ONE2}" ] || exit 1 + [ -e "${ONE2}" ] && exit 1 + + [ -e "${DIR2}" ] || mkdir -p "${DIR2}" + ln "$3" "$2" || exit 1 + + $LHA_PUT "$1" "$2" + rm -r "${ONE2}" +} + +# The 'run' command executive to run a command from within an archive + +mc_lha_fs_run() +{ + TMPDIR=`mktemp -d "${MC_TMPDIR:-/tmp}/mctmpdir-ulha.XXXXXX"` || exit 1 + trap "rm -rf \"$TMPDIR\"; exit 0" 1 2 3 4 15 + TMPCMD=$TMPDIR/run + $LHA_GET "$1" "$2" > $TMPCMD + chmod a+x "$TMPCMD" + "$TMPCMD" + rm -rf "$TMPDIR" +} + + +# The main routine +umask 077 + +cmd="$1" +shift + +case "$cmd" in + list) mc_lha_fs_list "$@" ;; + copyout) mc_lha_fs_copyout "$@" ;; + copyin) mc_lha_fs_copyin "$@" ;; + run) mc_lha_fs_run "$@" ;; + *) exit 1 ;; +esac + +exit 0 diff --git a/src/vfs/extfs/helpers/ulib.in b/src/vfs/extfs/helpers/ulib.in new file mode 100644 index 0000000..82c7ccf --- /dev/null +++ b/src/vfs/extfs/helpers/ulib.in @@ -0,0 +1,146 @@ +#! @PERL@ +# +# VFS to manage the gputils archives. +# Written by Molnár Károly (proton7@freemail.hu) 2012 +# + +use warnings; + +my %month = ('jan' => '01', 'feb' => '02', 'mar' => '03', + 'apr' => '04', 'may' => '05', 'jun' => '06', + 'jul' => '07', 'aug' => '08', 'sep' => '09', + 'oct' => '10', 'nov' => '11', 'dec' => '12'); + +my @PATHS = ('/usr/bin/gplib', '/usr/local/bin/gplib'); + +my $gplib = ''; + +foreach my $i (@PATHS) +{ + if (-x $i) + { + $gplib = $i; + last; + } +} + +if ($gplib eq '') +{ + print STDERR "\a\t$0 : Gplib not found!\n"; + exit(1); +} + +my $cmd = shift; +my $archive = shift; + +#------------------------------------------------------------------------------- + +sub mc_ulib_fs_list +{ + open(PIPE, "$gplib -tq $archive |") || die("Error in $gplib -tq"); + + my($dev, $inode, $mode, $nlink, $uid, $gid) = stat($archive); + + while (<PIPE>) + { + chomp; + my @w = split(/\s+/o); + my $fname = $w[0]; + + $fname =~ s|\\|/|g; + + printf("-rw-r--r-- 1 %s %s %d %s-%02u-%s %s %s\n", + $uid, $gid, int($w[1]), $month{lc($w[4])}, $w[5], $w[7], substr($w[6], 0, 5), $fname); + } + close (PIPE); +} + +#------------------------------------------------------------------------------- + +sub mc_ulib_fs_copyin +{ + system("$gplib -r $archive $_[0]"); + my $ret = $?; + + if ($ret) + { + die("Error in: $gplib -r"); + } +} + +#------------------------------------------------------------------------------- + +sub mc_ulib_fs_copyout +{ + my($module, $fname) = @_; + my $tmpdir = $ENV{'TMPDIR'}; + + $tmpdir = '/tmp' if (! defined $tmpdir or $tmpdir eq ''); + + open(PIPE, "$gplib -tq $archive |") || die("Error in: $gplib -tq"); + + while (<PIPE>) + { + chomp; + my @w = split(/\s+/o); + my $module_orig = $w[0]; + my $count = () = ($module_orig =~ /(\\)/g); + my $md = $module_orig; + + $md =~ s|\\|/|g; + + if ($module eq $md) + { + return if ($count); + } + } + + close (PIPE); + + chdir($tmpdir); + system("$gplib -x $archive $module"); + my $ret = $?; + + if ($ret) + { + die("Error in: $gplib -x"); + } + + rename($module, $fname) || die("Error in: rename($module, $fname)"); +} + +#------------------------------------------------------------------------------- + +sub mc_ulib_fs_rm +{ + system("$gplib -d $archive $_[0]"); + my $ret = $?; + + if ($ret) + { + die("Error in: $gplib -d"); + } +} + +################################################################################ + +if ($cmd eq 'list') +{ + mc_ulib_fs_list(@ARGV); +} +elsif ($cmd eq 'copyin') +{ + mc_ulib_fs_copyin(@ARGV); +} +elsif ($cmd eq 'copyout') +{ + mc_ulib_fs_copyout(@ARGV); +} +elsif ($cmd eq 'rm') +{ + mc_ulib_fs_rm(@ARGV); +} +else +{ + exit(1); +} diff --git a/src/vfs/extfs/helpers/unar.in b/src/vfs/extfs/helpers/unar.in new file mode 100644 index 0000000..e810307 --- /dev/null +++ b/src/vfs/extfs/helpers/unar.in @@ -0,0 +1,59 @@ +#! /bin/sh + +# Written by Ilia Maslakov <il.smind@gmail.com> +# +# (C) 2020 The Free Software Foundation. + +# Define awk +AWK=@AWK@ + +# Define which archiver you are using with appropriate options +UNAR_LIST="lsar " +UNAR_GET="unar " + +# The 'list' command executive +mc_unar_fs_list() +{ + # List the contents of the archive and sort it out + $UNAR_LIST -l "$1" | $AWK -v uid=`id -nu` -v gid=`id -ng` ' + BEGIN { flag = 0 } + /^\(Flags/ {next} + /^\(Mode/ {next} + { + flag++; + if (flag < 4) + next + pr="-r--r--r--" + if (index($2, "D") != 0) + pr="dr-xr-xr-x" + split($6, a, "-") + split($7, b, ":") + printf "%s 1 %s %s %d %02d/%02d/%02d %02d:%02d %s\n", pr, uid, gid, $3, a[3], a[2], a[1], b[1], b[2], $8 + }' +} + +# The 'copyout' command executive to copy displayed files to a destination +mc_unar_fs_copyout () +{ + TMPDIR=`mktemp -d "${MC_TMPDIR:-/tmp}/mctmpdir-uha.XXXXXX"` || exit 1 + + $UNAR_GET "$1" "$2" -o "$TMPDIR" >/dev/null + we=`basename "$1" | sed -E 's|^(.*?)\.\w+$|\1|'` + cat "$TMPDIR/$we/$2" > "$3" + cd / + rm -rf "$TMPDIR" +} + +# The main routine +umask 077 + +cmd="$1" +shift + +case "$cmd" in + list) mc_unar_fs_list "$@" ;; + copyout) mc_unar_fs_copyout "$@" ;; + *) exit 1 ;; +esac + +exit 0 diff --git a/src/vfs/extfs/helpers/urar.in b/src/vfs/extfs/helpers/urar.in new file mode 100644 index 0000000..684234c --- /dev/null +++ b/src/vfs/extfs/helpers/urar.in @@ -0,0 +1,180 @@ +#! /bin/sh +# +# Written by andrey joukov +# (C) 1996 2:5020/337.13@fidonet.org +# Updated by christian.gennerat@alcatel.fr 1999 +# Andrew V. Samoilov <sav@bcs.zp.ua> 2000 +# +# Andrew Borodin <aborodin@vmail.ru> +# David Haller <dnh@opensuse.org> +# 2013: support unrar5 +# +# beta version 2.0 +# +# rar and unrar can be found on http://www.rarlabs.com/ + +RAR=rar + +# Prefer unrar (freeware). +UNRAR=`which unrar 2>/dev/null` + +[ -z $UNRAR ] && UNRAR=$RAR +[ ! -x $UNRAR -a -x $RAR ] && UNRAR=$RAR + +# Let the test framework hook in: +UNRAR=${MC_TEST_EXTFS_LIST_CMD:-$UNRAR} + +# Determine the $UNRAR version +if [ -n "$MC_TEST_EXTFS_UNRAR_VERSION" ]; then + # Let the test framework fool us: + UNRAR_VERSION=$MC_TEST_EXTFS_UNRAR_VERSION +else + # Figure it out from rar itself: + UNRAR_VERSION=`$UNRAR -cfg- -? | grep "Copyright" | sed -e 's/.*\([0-9]\)\..*/\1/'` +fi + +mcrar4fs_list () +{ + $UNRAR v -c- -cfg- "$1" | @AWK@ -v uid=`id -u` -v gid=`id -g` ' +BEGIN { flag=0 } +/^-------/ { flag++; if (flag > 1) exit 0; next } +flag==1 { + str = substr($0, 2) + getline + split($4, a, "-") + if (index($6, "D") != 0) + $6="drwxr-xr-x" + else + if (index($6, ".") != 0) + $6="-rw-r--r--" + printf "%s 1 %s %s %d %02d/%02d/%02d %s ./%s\n", $6, uid, gid, $1, a[2], a[1], a[3], $5, str +}' +} + +mcrar5fs_list () +{ + $UNRAR vt -c- -cfg- "$1" | @AWK@ -F ':' -v uid=`id -u` -v gid=`id -g` ' + { + ### remove space after the ":" of the field name + sub ("^ ", "", $2); + } + + $1 ~ /^ *Name$/ { + ### next file + name = mtime = size = attrs = ""; + delete date; + name = $2; + ### if the name contains ":", append the rest of the fields + if (NF > 2) { + for (i = 3; i <= NF; i++) { + name = name ":" $i; + } + } + } + $1 ~ /^ *mtime$/ { + mtime = $2 ":" $3; + } + $1 ~ /^ *Size$/ { + size = $2; + } + $1 ~ /^ *Attributes$/ { + attrs = $2; + } + + $1 ~ /^ *Compression$/ { + ### file done, using /^$/ is not so good you + ### would have to skip the version stuff first + + ### get date and time + split (mtime, date, " "); + time = date[2]; + ### cut off seconds from the time + sub (",[0-9]*$", "", time); + ### split for reordering of the date in the printf below + split (date[1], date, "-"); + ### mc seems to be able to parse 4 digit years too, so remove if tested + # sub ("^..", "", date[1]); ### cut year to 2 digits only + + ### check/adjust rights + if (index (attrs, "D") != 0) { + attrs = "drwxr-xr-x"; + } else { + if (index (attrs, ".") != 0) { + attrs = "-rw-r--r--"; + } + } + + ### and finally + printf ("%s 1 %s %s %d %02d/%02d/%02d %s ./%s\n", + attrs, uid, gid, size, date[2], date[3], date[1], time, name); + } +' +} + +mcrarfs_list () +{ + [ x$UNRAR_VERSION = x6 -o x$UNRAR_VERSION = x5 ] && mcrar5fs_list "$@" || mcrar4fs_list "$@" +} + +mcrarfs_copyin () +{ +# copyin by christian.gennerat@alcatel.fr +# preserve pwd. It is clean, but is it necessary? + pwd=`pwd` +# Create a directory and copy in it the tmp file with the good name + mkdir "$3.dir" + cd "$3.dir" + di="${2%/*}" +# if file is to be written upper in the archive tree, make fake dir + if test x"$di" != x"${2##*/}" ; then + mkdir -p "$di" + fi + cp -fp "$3" "$3.dir/$2" + $RAR a "$1" "$2" >/dev/null + cd "$pwd" + rm -rf "$3.dir" +} + +mcrarfs_copyout () +{ + $UNRAR p -p- -c- -cfg- -inul "$1" "$2" > "$3" +} + +mcrarfs_mkdir () +{ +# preserve pwd. It is clean, but is it necessary? + pwd=`pwd` +# Create a directory and create in it a tmp directory with the good name + dir=`mktemp -d "${MC_TMPDIR:-/tmp}/mctmpdir-urar.XXXXXX"` || exit 1 + cd "$dir" + mkdir -p "$2" +# rar cannot create an empty directory + touch "$2"/.rarfs + $RAR a -r "$1" "$2" >/dev/null + $RAR d "$1" "$2/.rarfs" >/dev/null + cd "$pwd" + rm -rf "$dir" +} + +mcrarfs_rm () +{ + $RAR d "$1" "$2" >/dev/null +} + +umask 077 + +cmd="$1" +shift + +case "$cmd" in + # Workaround for a bug in mc - directories must precede files to + # avoid duplicate entries, so we sort output by filenames + list) mcrarfs_list "$@" | sort -k 8 ;; + rm) mcrarfs_rm "$@" ;; + rmdir) mcrarfs_rm "$@" ;; + mkdir) mcrarfs_mkdir "$@" ;; + copyin) mcrarfs_copyin "$@" ;; + copyout) mcrarfs_copyout "$@" ;; + *) exit 1 ;; +esac +exit 0 diff --git a/src/vfs/extfs/helpers/uwim.in b/src/vfs/extfs/helpers/uwim.in new file mode 100644 index 0000000..76197f7 --- /dev/null +++ b/src/vfs/extfs/helpers/uwim.in @@ -0,0 +1,208 @@ +#! /bin/sh +# Midnight Commander - WIM support +# +# Written by: +# Vadim Kalinnikov <moose@ylsoftware.com>, +# +# This file is part of the Midnight Commander. +# +# It required wimtools: https://wimlib.net/ +# On Debian/Ubuntu wimtools can be installed via: +# apt install wimtools + +which wimlib-imagex 2>/dev/null > /dev/null || exit 1 +WIM=`which wimlib-imagex` +AWK=@AWK@ + +[ -n "$2" ] || exit 1 + +ACTION=$1 +WIMFILENAME=$2 + +mcwim_list() { + # Here we can use "Image count" from output, + # but on some broken images we can get garbage, instead of number + IMAGECOUNT=`${WIM} info ${WIMFILENAME} | grep Index: | grep -v Boot | wc -l` + IMGNUM=1 + VUID=`id -nu` + VGID=`id -ng` + while [ ${IMGNUM} -le ${IMAGECOUNT} ]; do + ${WIM} dir ${WIMFILENAME} ${IMGNUM} --detailed | \ + ${AWK} -v uid=${VUID} -v gid=${VGID} -v imgnum=${IMGNUM} ' + /^----------------------------------------------------------------------------/,/^$/ { + if (match($0, /^Full Path/)) { + split($0, namesrc, "\""); + name=namesrc[2]; + } + if (match($0, /FILE_ATTRIBUTE_DIRECTORY is set/)) { + attr="drwxr-xr-x" + } + if (match($0, /^Uncompressed size/)) { + size=$4; + } + if (match($0, /^Last Write Time/)) { + months["Jan"] = "01"; + months["Feb"] = "02"; + months["Mar"] = "03"; + months["Apr"] = "04"; + months["May"] = "05"; + months["Jun"] = "06"; + months["Jul"] = "07"; + months["Aug"] = "08"; + months["Sep"] = "09"; + months["Oct"] = "10"; + months["Nov"] = "11"; + months["Dec"] = "12"; + split($0, mtimesrc, " "); + mtime=sprintf("%s/%s/%s %s", months[mtimesrc[6]], mtimesrc[7], mtimesrc[9], mtimesrc[8]); + } + + if (match($0, /^$/)) { + printf("%s 1 %s %s % 20s % 24s IMAGE%s%s\n", + attr, uid, gid, size, mtime, imgnum, name); + name = size = mtime = ""; + attr="-rw-r--r--"; + } + } + ' + + IMGNUM=$((IMGNUM+1)) + done + + # Virtual files + echo "-r-xr-xr-x 1 ${VUID} ${VGID} 1 01/01/2020 00:00:00 OPTIMIZE" + echo "-r-xr-xr-x 1 ${VUID} ${VGID} 1 01/01/2020 00:00:00 VERIFY" +} + +mcwim_copyout() { + # Virtual files + if [ "${FILENAMESRC}" = "OPTIMIZE" ]; then + echo "#/bin/sh" > ${FILENAMEDST} + echo "# Run this to optimize archive" >> ${FILENAMEDST} + echo "${WIM} optimize \"${WIMFILENAME}\"" >> ${FILENAMEDST} + chmod a+x ${FILENAMEDST} + exit 0; + elif [ "${FILENAMESRC}" = "VERIFY" ]; then + echo "#/bin/sh" > ${FILENAMEDST} + echo "# Run this to verify archive" >> ${FILENAMEDST} + echo "${WIM} verify \"${WIMFILENAME}\"" >> ${FILENAMEDST} + chmod a+x ${FILENAMEDST} + exit 0; + fi + + # Filename must contain imgnum + echo ${FILENAMESRC} | grep -E '^IMAGE[0-9]+/' || exit 1 + + IMGNUM=`echo ${FILENAMESRC} | cut -d '/' -f1 | sed "s/IMAGE//"` + REALFILENAME=`echo ${FILENAMESRC} | sed "s/IMAGE${IMGNUM}//"` + ${WIM} extract ${WIMFILENAME} ${IMGNUM} ${REALFILENAME} --to-stdout > ${FILENAMEDST} +} + +mcwim_copyin() { + # Skip virtual files + [ "${FILENAMEDST}" = "OPTIMIZE" ] && exit 1; + [ "${FILENAMEDST}" = "VERIFY" ] && exit 1; + + # Filename must contain imgnum + echo ${FILENAMEDST} | grep -E '^IMAGE[0-9]+/' || exit 1 + + IMGNUM=`echo ${FILENAMEDST} | cut -d '/' -f1 | sed "s/IMAGE//"` + REALFILENAME=`echo ${FILENAMEDST} | sed "s/IMAGE${IMGNUM}//"` + echo "add \"${FILENAMESRC}\" \"${REALFILENAME}\"" | ${WIM} update ${WIMFILENAME} ${IMGNUM} > /dev/null +} + + +mcwim_rm() { + # Skip virtual files + [ "${FILENAMESRC}" = "OPTIMIZE" ] && exit 0; + [ "${FILENAMESRC}" = "VERIFY" ] && exit 0; + + # Filename must contain imgnum + echo ${FILENAMESRC} | grep -E '^IMAGE[0-9]+/' || exit 1 + + IMGNUM=`echo ${FILENAMESRC} | cut -d '/' -f1 | sed "s/IMAGE//"` + REALFILENAME=`echo ${FILENAMESRC} | sed "s/IMAGE${IMGNUM}//"` + + if [ -z "${REALFILENAME}" ]; then + # If user want to remove image + ${WIM} delete ${WIMFILENAME} ${IMGNUM} + else + # remove regular file or directory + echo "delete \"${REALFILENAME}\"" | ${WIM} update ${WIMFILENAME} ${IMGNUM} --force --recursive > /dev/null + fi +} + +mcwim_run() { + case ${RUNFILENAME} in + OPTIMIZE) + ${WIM} optimize ${WIMFILENAME} + exit 0; + ;; + VERIFY) + ${WIM} verify ${WIMFILENAME} + exit 0; + ;; + esac + exit 1; +} + + +mcwim_mkdir() { + # New dirname must contain imgnum + echo ${NEWDIRNAME} | grep -E '^IMAGE[0-9]+/' || exit 1 + IMGNUM=`echo ${NEWDIRNAME} | cut -d '/' -f1 | sed "s/IMAGE//"` + REALDIRNAME=`echo ${NEWDIRNAME} | sed "s/IMAGE${IMGNUM}//"` + [ -z "${REALDIRNAME}" ] && exit 1 + + TMPDIR=`mktemp -d` + DSTBASENAME=`basename ${REALDIRNAME}` + SRCDIRNAME="${TMPDIR}/${DSTBASENAME}" + mkdir -p ${SRCDIRNAME} + echo "add \"${SRCDIRNAME}\" \"${REALDIRNAME}\"" | ${WIM} update ${WIMFILENAME} ${IMGNUM} > /dev/null + rm -rf ${TMPDIR} +} + +#echo "'$1' '$2' '$3' '$4' '$5'" >> /tmp/mcdebug + +case "$ACTION" in + list) + mcwim_list + ;; + + copyout) + [ -n "$4" ] || exit 1 + FILENAMESRC="$3" + FILENAMEDST="$4" + mcwim_copyout + ;; + + copyin) + [ -n "$4" ] || exit 1 + FILENAMEDST="$3" + FILENAMESRC="$4" + mcwim_copyin + ;; + + rm|rmdir) + [ -n "$3" ] || exit 1 + FILENAMESRC="$3" + mcwim_rm + ;; + + run) + [ -n "$3" ] || exit 1 + RUNFILENAME="$3" + mcwim_run + ;; + + mkdir) + [ -n "$3" ] || exit 1 + NEWDIRNAME="$3" + mcwim_mkdir + ;; + + + *) + exit 1 + ;; +esac diff --git a/src/vfs/extfs/helpers/uzip.in b/src/vfs/extfs/helpers/uzip.in new file mode 100644 index 0000000..ceffb53 --- /dev/null +++ b/src/vfs/extfs/helpers/uzip.in @@ -0,0 +1,483 @@ +#! @PERL@ +# +# zip file archive Virtual File System for Midnight Commander +# Version 1.4.0 (2001-08-07). +# +# (C) 2000-2001 Oskar Liljeblad <osk@hem.passagen.se>. +# + +use POSIX; +use File::Basename; +use strict; +use warnings; + +# +# Configuration options +# + +# Location of the zip program +my $app_zip = "@ZIP@"; +# Location of the unzip program +my $app_unzip = $ENV{MC_TEST_EXTFS_LIST_CMD} || "@UNZIP@"; +# Set this to 1 if zipinfo (unzip -Z) is to be used (recommended), otherwise 0. +my $op_has_zipinfo = exists($ENV{MC_TEST_EXTFS_HAVE_ZIPINFO}) ? $ENV{MC_TEST_EXTFS_HAVE_ZIPINFO} : @HAVE_ZIPINFO@; + +# Command used to list archives (zipinfo mode) +my $cmd_list_zi = "$app_unzip -Z -l -T"; +# Command used to list archives (non-zipinfo mode) +my $cmd_list_nzi = "$app_unzip -qq -v"; +# Command used to add a file to the archive +my $cmd_add = "$app_zip -g"; +# Command used to add a link file to the archive (unused) +my $cmd_addlink = "$app_zip -g -y"; +# Command used to delete a file from the archive +my $cmd_delete = "$app_zip -d"; +# Command used to extract a file to standard out +my $cmd_extract = "$app_unzip -p"; + +# -rw-r--r-- 2.2 unx 2891 tx 1435 defN 20000330.211927 ./edit.html +# (perm) (?) (?) (size) (?) (zippedsize) (method) (yyyy)(mm)(dd).(HH)(MM)(SS) (fname) +my $regex_zipinfo_line = qr"^(\S{7,10})\s+(\d+\.\d+)\s+(\S+)\s+(\d+)\s+(\S\S)\s+(\d+)\s+(\S{4})\s+(\d{4})(\d\d)(\d\d)\.(\d\d)(\d\d)(\d\d)\s(.*)$"; + +# 2891 Defl:N 1435 50% 03-30-00 21:19 50cbaaf8 ./edit.html +# (size) (method) (zippedsize) (zipratio) (mm)-(dd)-(yy|yyyy) (HH):(MM) (cksum) (fname) +# or: (yyyy)-(mm)-(dd) +my $regex_nonzipinfo_line = qr"^\s*(\d+)\s+(\S+)\s+(\d+)\s+(-?\d+\%)\s+(\d+)-(\d?\d)-(\d+)\s+(\d?\d):(\d\d)\s+([0-9a-f]+)\s\s(.*)$"; + +# +# Main code +# + +die "uzip: missing command and/or archive arguments\n" if ($#ARGV < 1); + +# Initialization of some global variables +my $cmd = shift; +my %known = ( './' => 1 ); +my %pending = (); +my $oldpwd = POSIX::getcwd(); +my $archive = shift; +my $aarchive = absolutize($archive, $oldpwd); +my $cmd_list = ($op_has_zipinfo ? $cmd_list_zi : $cmd_list_nzi); +my ($qarchive, $aqarchive) = map (quotemeta, $archive, $aarchive); + +# Strip all "." and ".." path components from a pathname. +sub zipfs_canonicalize_pathname($) { + my ($fname) = @_; + $fname =~ s,/+,/,g; + $fname =~ s,(^|/)(?:\.?\./)+,$1,; + return $fname; +} + +# The Midnight Commander never calls this script with archive pathnames +# starting with either "./" or "../". Some ZIP files contain such names, +# so we need to build a translation table for them. +my $zipfs_realpathname_table = undef; +sub zipfs_realpathname($) { + my ($fname) = @_; + + if (!defined($zipfs_realpathname_table)) { + $zipfs_realpathname_table = {}; + if (!open(ZIP, "$cmd_list $qarchive |")) { + return $fname; + } + foreach my $line (<ZIP>) { + $line =~ s/\r*\n*$//; + if ($op_has_zipinfo) { + if ($line =~ $regex_zipinfo_line) { + my ($fname) = ($14); + $zipfs_realpathname_table->{zipfs_canonicalize_pathname($fname)} = $fname; + } + } else { + if ($line =~ $regex_nonzipinfo_line) { + my ($fname) = ($11); + $zipfs_realpathname_table->{zipfs_canonicalize_pathname($fname)} = $fname; + } + } + } + if (!close(ZIP)) { + return $fname; + } + } + if (exists($zipfs_realpathname_table->{$fname})) { + return $zipfs_realpathname_table->{$fname}; + } + return $fname; +} + +if ($cmd eq 'list') { &mczipfs_list(@ARGV); } +if ($cmd eq 'rm') { &mczipfs_rm(@ARGV); } +if ($cmd eq 'rmdir') { &mczipfs_rmdir(@ARGV); } +if ($cmd eq 'mkdir') { &mczipfs_mkdir(@ARGV); } +if ($cmd eq 'copyin') { &mczipfs_copyin(@ARGV); } +if ($cmd eq 'copyout') { &mczipfs_copyout(@ARGV); } +if ($cmd eq 'run') { &mczipfs_run(@ARGV); } +#if ($cmd eq 'mklink') { &mczipfs_mklink(@ARGV); } # Not supported by MC extfs +#if ($cmd eq 'linkout') { &mczipfs_linkout(@ARGV); } # Not supported by MC extfs +exit 1; + +# Remove a file from the archive. +sub mczipfs_rm { + my ($qfile) = map { &zipquotemeta(zipfs_realpathname($_)) } @_; + + # "./" at the beginning of pathnames is stripped by Info-ZIP, + # so convert it to "[.]/" to prevent stripping. + $qfile =~ s/^\\\./[.]/; + + &checkargs(1, 'archive file', @_); + &safesystem("$cmd_delete $qarchive $qfile >/dev/null"); + exit; +} + +# Remove an empty directory from the archive. +# The only difference from mczipfs_rm is that we append an +# additional slash to the directory name to remove. I am not +# sure this is absolutely necessary, but it doesn't hurt. +sub mczipfs_rmdir { + my ($qfile) = map { &zipquotemeta(zipfs_realpathname($_)) } @_; + &checkargs(1, 'archive directory', @_); + &safesystem("$cmd_delete $qarchive $qfile/ >/dev/null", 12); + exit; +} + +# Extract a file from the archive. +# Note that we don't need to check if the file is a link, +# because mc apparently doesn't call copyout for symbolic links. +sub mczipfs_copyout { + my ($qafile, $qfsfile) = map { &zipquotemeta(zipfs_realpathname($_)) } @_; + &checkargs(1, 'archive file', @_); + &checkargs(2, 'local file', @_); + &safesystem("$cmd_extract $qarchive $qafile > $qfsfile", 11); + exit; +} + +# Add a file to the archive. +# This is done by making a temporary directory, in which +# we create a symlink the original file (with a new name). +# Zip is then run to include the real file in the archive, +# with the name of the symbolic link. +# Here we also doesn't need to check for symbolic links, +# because the mc extfs doesn't allow adding of symbolic +# links. +sub mczipfs_copyin { + my ($afile, $fsfile) = @_; + &checkargs(1, 'archive file', @_); + &checkargs(2, 'local file', @_); + my ($qafile) = quotemeta $afile; + $fsfile = &absolutize($fsfile, $oldpwd); + my $adir = File::Basename::dirname($afile); + + my $tmpdir = &mktmpdir(); + chdir $tmpdir || &croak("chdir $tmpdir failed"); + &mkdirs($adir, 0700); + symlink ($fsfile, $afile) || &croak("link $afile failed"); + &safesystem("$cmd_add $aqarchive $qafile >/dev/null"); + unlink $afile || &croak("unlink $afile failed"); + &rmdirs($adir); + chdir $oldpwd || &croak("chdir $oldpwd failed"); + rmdir $tmpdir || &croak("rmdir $tmpdir failed"); + exit; +} + +# Add an empty directory the the archive. +# This is similar to mczipfs_copyin, except that we don't need +# to use symlinks. +sub mczipfs_mkdir { + my ($dir) = @_; + &checkargs(1, 'directory', @_); + my ($qdir) = quotemeta $dir; + + my $tmpdir = &mktmpdir(); + chdir $tmpdir || &croak("chdir $tmpdir failed"); + &mkdirs($dir, 0700); + &safesystem("$cmd_add $aqarchive $qdir >/dev/null"); + &rmdirs($dir); + chdir $oldpwd || &croak("chdir $oldpwd failed"); + rmdir $tmpdir || &croak("rmdir $tmpdir failed"); + exit; +} + +# Add a link to the archive. This operation is not used yet, +# because it is not supported by the MC extfs. +sub mczipfs_mklink { + my ($linkdest, $afile) = @_; + &checkargs(1, 'link destination', @_); + &checkargs(2, 'archive file', @_); + my ($qafile) = quotemeta $afile; + my $adir = File::Basename::dirname($afile); + + my $tmpdir = &mktmpdir(); + chdir $tmpdir || &croak("chdir $tmpdir failed"); + &mkdirs($adir, 0700); + symlink ($linkdest, $afile) || &croak("link $afile failed"); + &safesystem("$cmd_addlink $aqarchive $qafile >/dev/null"); + unlink $afile || &croak("unlink $afile failed"); + &rmdirs($adir); + chdir $oldpwd || &croak("chdir $oldpwd failed"); + rmdir $tmpdir || &croak("rmdir $tmpdir failed"); + exit; +} + +# This operation is not used yet, because it is not +# supported by the MC extfs. +sub mczipfs_linkout { + my ($afile, $fsfile) = @_; + &checkargs(1, 'archive file', @_); + &checkargs(2, 'local file', @_); + my ($qafile) = map { &zipquotemeta($_) } $afile; + + my $linkdest = &get_link_destination($afile); + symlink ($linkdest, $fsfile) || &croak("link $fsfile failed"); + exit; +} + +# Use unzip to find the link destination of a certain file in the +# archive. +sub get_link_destination { + my ($afile) = @_; + my ($qafile) = map { &zipquotemeta($_) } $afile; + my $linkdest = safeticks("$cmd_extract $qarchive $qafile"); + &croak ("extract failed", "link destination of $afile not found") + if (!defined $linkdest || $linkdest eq ''); + return $linkdest; +} + +# List files in the archive. +# Because mc currently doesn't allow a file's parent directory +# to be listed after the file itself, we need to do some +# rearranging of the output. Most of this is done in +# checked_print_file. +sub mczipfs_list { + open (PIPE, "$cmd_list $qarchive |") || &croak("$app_unzip failed"); + if ($op_has_zipinfo) { + while (<PIPE>) { + chomp; + next if /^Archive:/; + next if /^\d+ file/; + next if /^Empty zipfile\.$/; + my @match = /$regex_zipinfo_line/; + next if ($#match != 13); + &checked_print_file(@match); + } + } else { + while (<PIPE>) { + chomp; + my @match = /$regex_nonzipinfo_line/; + next if ($#match != 10); + + # Massage the date. + my ($year, $month, $day) = $match[4] > 12 + ? ($match[4], $match[5], $match[6]) # 4,5,6 = Y,M,D + : ($match[6], $match[4], $match[5]); # 4,5,6 = M,D,Y + $year += ($year < 70 ? 2000 : 1900) if $year < 100; # Fix 2-digit year. + + my @rmatch = ('', '', 'unknown', $match[0], '', $match[2], $match[1], + $year, $month, $day, $match[7], $match[8], "00", $match[10]); + &checked_print_file(@rmatch); + } + } + if (!close (PIPE)) { + &croak("$app_unzip failed") if ($! != 0); + &croak("$app_unzip failed", 'non-zero exit status ('.($? >> 8).')') + } + + foreach my $key (sort keys %pending) { + foreach my $file (@{ $pending{$key} }) { + &print_file(@{ $file }); + } + } + + exit; +} + +# Execute a file in the archive, by first extracting it to a +# temporary directory. The name of the extracted file will be +# the same as the name of it in the archive. +sub mczipfs_run { + my ($afile) = @_; + &checkargs(1, 'archive file', @_); + my $qafile = &zipquotemeta(zipfs_realpathname($afile)); + my $tmpdir = &mktmpdir(); + my $tmpfile = File::Basename::basename($afile); + + chdir $tmpdir || &croak("chdir $tmpdir failed"); + &safesystem("$cmd_extract $aqarchive $qafile > $tmpfile"); + chmod 0700, $tmpfile; + &safesystem("./$tmpfile"); + unlink $tmpfile || &croak("rm $tmpfile failed"); + chdir $oldpwd || &croak("chdir $oldpwd failed"); + rmdir $tmpdir || &croak("rmdir $tmpdir failed"); + exit; +} + +# This is called prior to printing the listing of a file. +# A check is done to see if the parent directory of the file has already +# been printed or not. If it hasn't, we must cache it (in %pending) and +# print it later once the parent directory has been listed. When all +# files have been processed, there may still be some that haven't been +# printed because their parent directories weren't listed at all. These +# files are dealt with in mczipfs_list. +sub checked_print_file { + my @waiting = ([ @_ ]); + + while ($#waiting != -1) { + my $item = shift @waiting; + my $filename = ${$item}[13]; + my $dirname = File::Basename::dirname($filename) . '/'; + + if (exists $known{$dirname}) { + &print_file(@{$item}); + if ($filename =~ /\/$/) { + $known{$filename} = 1; + if (exists $pending{$filename}) { + push @waiting, @{ $pending{$filename} }; + delete $pending{$filename}; + } + } + } else { + push @{$pending{$dirname}}, $item; + } + } +} + +# Print the mc extfs listing of a file from a set of parsed fields. +# If the file is a link, we extract it from the zip archive and +# include the output as the link destination. Because this output +# is not newline terminated, we must execute unzip once for each +# link file encountered. +sub print_file { + my ($perms,$zipver,$platform,$realsize,$format,$cmpsize,$comp,$year,$mon,$day,$hours,$mins,$secs,$filename) = @_; + if ($platform ne 'unx') { + $perms = ($filename =~ /\/$/ ? 'drwxr-xr-x' : '-rw-r--r--'); + } + # adjust abnormal perms on directory + if ($platform eq 'unx' && $filename =~ /\/$/ && $perms =~ /^\?(.*)$/) { + $perms = 'd'.$1; + } + printf "%-10s 1 %-8d %-8d %8s %s/%s/%s %s:%s:%s ./%s", $perms, $<, + $(, $realsize, $mon, $day, $year, $hours, $mins, $secs, $filename; + if ($platform eq 'unx' && $perms =~ /^l/) { + my $linkdest = &get_link_destination($filename); + print " -> $linkdest"; + } + print "\n"; +} + +# Die with a reasonable error message. +sub croak { + my ($command, $desc) = @_; + die "uzip ($cmd): $command - $desc\n" if (defined $desc); + die "uzip ($cmd): $command - $!\n"; +} + +# Make a set of directories, like the command `mkdir -p'. +# This subroutine has been tailored for this script, and +# because of that, it ignored the directory name '.'. +sub mkdirs { + my ($dirs, $mode) = @_; + $dirs = &cleandirs($dirs); + return if ($dirs eq '.'); + + my $newpos = -1; + while (($newpos = index($dirs, '/', $newpos+1)) != -1) { + my $dir = substr($dirs, 0, $newpos); + mkdir ($dir, $mode) || &croak("mkdir $dir failed"); + } + mkdir ($dirs, $mode) || &croak("mkdir $dirs failed"); +} + +# Remove a set of directories, failing if the directories +# contain other files. +# This subroutine has been tailored for this script, and +# because of that, it ignored the directory name '.'. +sub rmdirs { + my ($dirs) = @_; + $dirs = &cleandirs($dirs); + return if ($dirs eq '.'); + + rmdir $dirs || &croak("rmdir $dirs failed"); + my $newpos = length($dirs); + while (($newpos = rindex($dirs, '/', $newpos-1)) != -1) { + my $dir = substr($dirs, 0, $newpos); + rmdir $dir || &croak("rmdir $dir failed"); + } +} + +# Return a semi-canonical directory name. +sub cleandirs { + my ($dir) = @_; + $dir =~ s:/+:/:g; + $dir =~ s:/*$::; + return $dir; +} + +# Make a temporary directory with mode 0700. +sub mktmpdir { + use File::Temp qw(mkdtemp); + my $template = "/tmp/mcuzipfs.XXXXXX"; + $template="$ENV{MC_TMPDIR}/mcuzipfs.XXXXXX" if ($ENV{MC_TMPDIR}); + return mkdtemp($template); +} + +# Make a filename absolute and return it. +sub absolutize { + my ($file, $pwd) = @_; + return "$pwd/$file" if ($file !~ /^\//); + return $file; +} + +# Like the system built-in function, but with error checking. +# The other argument is an exit status to allow. +sub safesystem { + my ($command, @allowrc) = @_; + my ($desc) = ($command =~ /^([^ ]*) */); + $desc = File::Basename::basename($desc); + system $command; + my $rc = $?; + &croak("`$desc' failed") if (($rc & 0xFF) != 0); + if ($rc != 0) { + $rc = $rc >> 8; + foreach my $arc (@allowrc) { + return if ($rc == $arc); + } + &croak("`$desc' failed", "non-zero exit status ($rc)"); + } +} + +# Like backticks built-in, but with error checking. +sub safeticks { + my ($command, @allowrc) = @_; + my ($desc) = ($command =~ /^([^ ]*) /); + $desc = File::Basename::basename($desc); + my $out = `$command`; + my $rc = $?; + &croak("`$desc' failed") if (($rc & 0xFF) != 0); + if ($rc != 0) { + $rc = $rc >> 8; + foreach my $arc (@allowrc) { + return if ($rc == $arc); + } + &croak("`$desc' failed", "non-zero exit status ($rc)"); + } + return $out; +} + +# Make sure enough arguments are supplied, or die. +sub checkargs { + my $count = shift; + my $desc = shift; + &croak('missing argument', $desc) if ($count-1 > $#_); +} + +# Quote zip wildcard metacharacters. Unfortunately Info-ZIP zip and unzip +# on unix interpret some wildcards in filenames, despite the fact that +# the shell already does this. Thus this function. +sub zipquotemeta { + my ($name) = @_; + my $out = ''; + for (my $c = 0; $c < length $name; $c++) { + my $ch = substr($name, $c, 1); + $out .= '\\' if (index('*?[]\\', $ch) != -1); + $out .= $ch; + } + return quotemeta($out); +} diff --git a/src/vfs/extfs/helpers/uzoo.in b/src/vfs/extfs/helpers/uzoo.in new file mode 100644 index 0000000..fb079a5 --- /dev/null +++ b/src/vfs/extfs/helpers/uzoo.in @@ -0,0 +1,69 @@ +#! /bin/sh +# +# Zoo file system +# +# Source of zoo can be found at +# ftp://ftp.ibiblio.org/pub/Linux/utils/compress/ + +ZOO=${MC_TEST_EXTFS_LIST_CMD:-zoo} + +# Stupid zoo won't work if the archive name has no .zoo extension, so we +# have to make a symlink with a "better" name. Also, zoo can create +# directories even if printing files to stdout, so it's safer to confine +# it to a temporary directory. +mklink () +{ + TMPDIR=`mktemp -d ${MC_TMPDIR:-/tmp}/mctmpdir-uzoo.XXXXXX` || exit 1 + trap 'cd /; rm -rf "$TMPDIR"' 0 1 2 3 5 13 15 + ARCHIVE="$TMPDIR/tmp.zoo" + ln -sf "$1" "$ARCHIVE" + cd "$TMPDIR" || exit 1 +} + +mczoofs_list () +{ + mklink "$1" + $ZOO lq "$ARCHIVE" | @AWK@ -v uid=$(id -ru) ' +/^[^\ ]/ { next } +{ +if (NF < 8) + next +if ($8 ~ /^\^/) + $8=substr($8, 2) +if ($6 > 50) + $6=$6 + 1900 +else + $6=$6 + 2000 +split($7, a, ":") +split("Jan:Feb:Mar:Apr:May:Jun:Jul:Aug:Sep:Oct:Nov:Dec", month_list, ":") +for (i=1; i<=12; i++) { + month[month_list[i]] = i +} +if ($8 ~ /\/$/) + printf "drwxr-xr-x 1 %-8d %-8d %8d %02d-%02d-%4d %02d:%02d %s\n", uid, 0, $1, month[$5], $4, $6, a[1], a[2], $8 +else + printf "-rw-r--r-- 1 %-8d %-8d %8d %02d-%02d-%4d %02d:%02d %s\n", uid, 0, $1, month[$5], $4, $6, a[1], a[2], $8 +}' 2>/dev/null + exit 0 +} + +mczoofs_copyout () +{ + mklink "$1" + # zoo only accepts name without directory as file to extract + base=`echo "$2" | sed 's,.*/,,'` + $ZOO xpq: "$ARCHIVE" "$base" > "$3" + cd / + exit 0 +} + +umask 077 + +cmd="$1" +shift +case "$cmd" in + list) mczoofs_list "$@" ;; + copyout) mczoofs_copyout "$@" ;; + *) exit 1 ;; +esac +exit 0 diff --git a/src/vfs/fish/Makefile.am b/src/vfs/fish/Makefile.am new file mode 100644 index 0000000..4f3ca87 --- /dev/null +++ b/src/vfs/fish/Makefile.am @@ -0,0 +1,13 @@ +SUBDIRS = helpers +DIST_SUBDIRS = helpers + +AM_CPPFLAGS = \ + -DLIBEXECDIR=\""$(libexecdir)/@PACKAGE@/"\" \ + $(GLIB_CFLAGS) \ + -I$(top_srcdir) + +noinst_LTLIBRARIES = libvfs-fish.la + +libvfs_fish_la_SOURCES = \ + fish.c fish.h \ + fishdef.h diff --git a/src/vfs/fish/Makefile.in b/src/vfs/fish/Makefile.in new file mode 100644 index 0000000..cd952a8 --- /dev/null +++ b/src/vfs/fish/Makefile.in @@ -0,0 +1,857 @@ +# 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/fish +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_fish_la_LIBADD = +am_libvfs_fish_la_OBJECTS = fish.lo +libvfs_fish_la_OBJECTS = $(am_libvfs_fish_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)/fish.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_fish_la_SOURCES) +DIST_SOURCES = $(libvfs_fish_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +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) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +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@ +SUBDIRS = helpers +DIST_SUBDIRS = helpers +AM_CPPFLAGS = \ + -DLIBEXECDIR=\""$(libexecdir)/@PACKAGE@/"\" \ + $(GLIB_CFLAGS) \ + -I$(top_srcdir) + +noinst_LTLIBRARIES = libvfs-fish.la +libvfs_fish_la_SOURCES = \ + fish.c fish.h \ + fishdef.h + +all: all-recursive + +.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/fish/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/vfs/fish/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-fish.la: $(libvfs_fish_la_OBJECTS) $(libvfs_fish_la_DEPENDENCIES) $(EXTRA_libvfs_fish_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libvfs_fish_la_OBJECTS) $(libvfs_fish_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fish.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 + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(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-recursive + +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-recursive + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +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-recursive + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/fish.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/fish.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) 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 \ + installdirs-am 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/fish/fish.c b/src/vfs/fish/fish.c new file mode 100644 index 0000000..ec71a41 --- /dev/null +++ b/src/vfs/fish/fish.c @@ -0,0 +1,1805 @@ +/* + Virtual File System: FISH implementation for transferring files over + shell connections. + + Copyright (C) 1998-2023 + Free Software Foundation, Inc. + + Written by: + Pavel Machek, 1998 + Michal Svec, 2000 + Andrew Borodin <aborodin@vmail.ru>, 2010-2022 + Slava Zanko <slavazanko@gmail.com>, 2010, 2013 + Ilia Maslakov <il.smind@gmail.com>, 2010 + + Derived from ftpfs.c. + + 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/>. + */ + +/** + * \file + * \brief Source: Virtual File System: FISH implementation for transferring files over + * shell connections + * \author Pavel Machek + * \author Michal Svec + * \date 1998, 2000 + * + * Derived from ftpfs.c + * Read README.fish for protocol specification. + * + * Syntax of path is: \verbatim sh://user@host[:Cr]/path \endverbatim + * where C means you want compressed connection, + * and r means you want to use rsh + * + * Namespace: fish_vfs_ops exported. + */ + +/* Define this if your ssh can take -I option */ + +#include <config.h> +#include <errno.h> +#include <pwd.h> +#include <grp.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> /* uintmax_t */ + +#include "lib/global.h" +#include "lib/tty/tty.h" /* enable/disable interrupt key */ +#include "lib/strescape.h" +#include "lib/unixcompat.h" +#include "lib/fileloc.h" +#include "lib/util.h" /* my_exit() */ +#include "lib/mcconfig.h" + +#include "src/execute.h" /* pre_exec, post_exec */ + +#include "lib/vfs/vfs.h" +#include "lib/vfs/utilvfs.h" +#include "lib/vfs/netutil.h" +#include "lib/vfs/xdirentry.h" +#include "lib/vfs/gc.h" /* vfs_stamp_create */ + +#include "fish.h" +#include "fishdef.h" + +/*** global variables ****************************************************************************/ + +int fish_directory_timeout = 900; + +/*** file scope macro definitions ****************************************************************/ + +#define DO_RESOLVE_SYMLINK 1 +#define DO_OPEN 2 +#define DO_FREE_RESOURCE 4 + +#define FISH_FLAG_COMPRESSED 1 +#define FISH_FLAG_RSH 2 + +#define OPT_FLUSH 1 +#define OPT_IGNORE_ERROR 2 + +/* + * Reply codes. + */ +#define PRELIM 1 /* positive preliminary */ +#define COMPLETE 2 /* positive completion */ +#define CONTINUE 3 /* positive intermediate */ +#define TRANSIENT 4 /* transient negative completion */ +#define ERROR 5 /* permanent negative completion */ + +/* command wait_flag: */ +#define NONE 0x00 +#define WAIT_REPLY 0x01 +#define WANT_STRING 0x02 + +/* environment flags */ +#define FISH_HAVE_HEAD 1 +#define FISH_HAVE_SED 2 +#define FISH_HAVE_AWK 4 +#define FISH_HAVE_PERL 8 +#define FISH_HAVE_LSQ 16 +#define FISH_HAVE_DATE_MDYT 32 +#define FISH_HAVE_TAIL 64 + +#define FISH_SUPER(super) ((fish_super_t *) (super)) +#define FISH_FILE_HANDLER(fh) ((fish_file_handler_t *) fh) + +/*** file scope type declarations ****************************************************************/ + +typedef struct +{ + struct vfs_s_super base; /* base class */ + + int sockr; + int sockw; + char *scr_ls; + char *scr_chmod; + char *scr_utime; + char *scr_exists; + char *scr_mkdir; + char *scr_unlink; + char *scr_chown; + char *scr_rmdir; + char *scr_ln; + char *scr_mv; + char *scr_hardlink; + char *scr_get; + char *scr_send; + char *scr_append; + char *scr_info; + int host_flags; + GString *scr_env; +} fish_super_t; + +typedef struct +{ + vfs_file_handler_t base; /* base class */ + + off_t got; + off_t total; + gboolean append; +} fish_file_handler_t; + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +static char reply_str[80]; + +static struct vfs_s_subclass fish_subclass; +static struct vfs_class *vfs_fish_ops = VFS_CLASS (&fish_subclass); + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static void +fish_set_blksize (struct stat *s) +{ +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + /* redefine block size */ + s->st_blksize = 64 * 1024; /* FIXME */ +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct stat * +fish_default_stat (struct vfs_class *me) +{ + struct stat *s; + + s = vfs_s_default_stat (me, S_IFDIR | 0755); + fish_set_blksize (s); + vfs_adjust_stat (s); + + return s; +} + +/* --------------------------------------------------------------------------------------------- */ + +static char * +fish_load_script_from_file (const char *hostname, const char *script_name, const char *def_content) +{ + char *scr_filename = NULL; + char *scr_content; + gsize scr_len = 0; + + /* 1st: scan user directory */ + scr_filename = g_build_path (PATH_SEP_STR, mc_config_get_data_path (), FISH_PREFIX, hostname, + script_name, (char *) NULL); + /* silent about user dir */ + g_file_get_contents (scr_filename, &scr_content, &scr_len, NULL); + g_free (scr_filename); + /* 2nd: scan system dir */ + if (scr_content == NULL) + { + scr_filename = + g_build_path (PATH_SEP_STR, LIBEXECDIR, FISH_PREFIX, script_name, (char *) NULL); + g_file_get_contents (scr_filename, &scr_content, &scr_len, NULL); + g_free (scr_filename); + } + + if (scr_content != NULL) + return scr_content; + + return g_strdup (def_content); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_decode_reply (char *s, gboolean was_garbage) +{ + int code; + + /* cppcheck-suppress invalidscanf */ + if (sscanf (s, "%d", &code) == 0) + { + code = 500; + return 5; + } + if (code < 100) + return was_garbage ? ERROR : (code == 0 ? COMPLETE : PRELIM); + return code / 100; +} + +/* --------------------------------------------------------------------------------------------- */ +/* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */ + +static int +fish_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len) +{ + char answer[BUF_1K]; + gboolean was_garbage = FALSE; + + while (TRUE) + { + if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n')) + { + if (string_buf != NULL) + *string_buf = '\0'; + return 4; + } + + if (strncmp (answer, "### ", 4) == 0) + return fish_decode_reply (answer + 4, was_garbage ? 1 : 0); + + was_garbage = TRUE; + if (string_buf != NULL) + g_strlcpy (string_buf, answer, string_len); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *cmd, + size_t cmd_len) +{ + ssize_t status; + FILE *logfile = me->logfile; + + if (cmd_len == (size_t) (-1)) + cmd_len = strlen (cmd); + + if (logfile != NULL) + { + size_t ret; + + ret = fwrite (cmd, cmd_len, 1, logfile); + ret = fflush (logfile); + (void) ret; + } + + tty_enable_interrupt_key (); + status = write (FISH_SUPER (super)->sockw, cmd, cmd_len); + tty_disable_interrupt_key (); + + if (status < 0) + return TRANSIENT; + + if (wait_reply) + return fish_get_reply (me, FISH_SUPER (super)->sockr, + (wait_reply & WANT_STRING) != 0 ? reply_str : + NULL, sizeof (reply_str) - 1); + return COMPLETE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +G_GNUC_PRINTF (5, 0) +fish_command_va (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *scr, + const char *vars, va_list ap) +{ + int r; + GString *command; + + command = mc_g_string_dup (FISH_SUPER (super)->scr_env); + g_string_append_vprintf (command, vars, ap); + g_string_append (command, scr); + r = fish_command (me, super, wait_reply, command->str, command->len); + g_string_free (command, TRUE); + + return r; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +G_GNUC_PRINTF (5, 6) +fish_command_v (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *scr, + const char *vars, ...) +{ + int r; + va_list ap; + + va_start (ap, vars); + r = fish_command_va (me, super, wait_reply, scr, vars, ap); + va_end (ap); + + return r; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +G_GNUC_PRINTF (5, 6) +fish_send_command (struct vfs_class *me, struct vfs_s_super *super, int flags, const char *scr, + const char *vars, ...) +{ + int r; + va_list ap; + + va_start (ap, vars); + r = fish_command_va (me, super, WAIT_REPLY, scr, vars, ap); + va_end (ap); + vfs_stamp_create (vfs_fish_ops, super); + + if (r != COMPLETE) + ERRNOR (E_REMOTE, -1); + if ((flags & OPT_FLUSH) != 0) + vfs_s_invalidate (me, super); + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct vfs_s_super * +fish_new_archive (struct vfs_class *me) +{ + fish_super_t *arch; + + arch = g_new0 (fish_super_t, 1); + arch->base.me = me; + + return VFS_SUPER (arch); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +fish_free_archive (struct vfs_class *me, struct vfs_s_super *super) +{ + fish_super_t *fish_super = FISH_SUPER (super); + + if ((fish_super->sockw != -1) || (fish_super->sockr != -1)) + vfs_print_message (_("fish: Disconnecting from %s"), super->name ? super->name : "???"); + + if (fish_super->sockw != -1) + { + fish_command (me, super, NONE, "#BYE\nexit\n", -1); + close (fish_super->sockw); + fish_super->sockw = -1; + } + + if (fish_super->sockr != -1) + { + close (fish_super->sockr); + fish_super->sockr = -1; + } + + g_free (fish_super->scr_ls); + g_free (fish_super->scr_exists); + g_free (fish_super->scr_mkdir); + g_free (fish_super->scr_unlink); + g_free (fish_super->scr_chown); + g_free (fish_super->scr_chmod); + g_free (fish_super->scr_utime); + g_free (fish_super->scr_rmdir); + g_free (fish_super->scr_ln); + g_free (fish_super->scr_mv); + g_free (fish_super->scr_hardlink); + g_free (fish_super->scr_get); + g_free (fish_super->scr_send); + g_free (fish_super->scr_append); + g_free (fish_super->scr_info); + g_string_free (fish_super->scr_env, TRUE); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +fish_pipeopen (struct vfs_s_super *super, const char *path, const char *argv[]) +{ + int fileset1[2], fileset2[2]; + int res; + + if ((pipe (fileset1) < 0) || (pipe (fileset2) < 0)) + vfs_die ("Cannot pipe(): %m."); + + res = fork (); + + if (res != 0) + { + if (res < 0) + vfs_die ("Cannot fork(): %m."); + /* We are the parent */ + close (fileset1[0]); + FISH_SUPER (super)->sockw = fileset1[1]; + close (fileset2[1]); + FISH_SUPER (super)->sockr = fileset2[0]; + } + else + { + res = dup2 (fileset1[0], STDIN_FILENO); + close (fileset1[0]); + close (fileset1[1]); + res = dup2 (fileset2[1], STDOUT_FILENO); + close (STDERR_FILENO); + /* stderr to /dev/null */ + res = open ("/dev/null", O_WRONLY); + close (fileset2[0]); + close (fileset2[1]); + execvp (path, (char **) argv); + my_exit (3); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static GString * +fish_set_env (int flags) +{ + GString *ret; + + ret = g_string_sized_new (256); + + if ((flags & FISH_HAVE_HEAD) != 0) + g_string_append (ret, "FISH_HAVE_HEAD=1 export FISH_HAVE_HEAD; "); + + if ((flags & FISH_HAVE_SED) != 0) + g_string_append (ret, "FISH_HAVE_SED=1 export FISH_HAVE_SED; "); + + if ((flags & FISH_HAVE_AWK) != 0) + g_string_append (ret, "FISH_HAVE_AWK=1 export FISH_HAVE_AWK; "); + + if ((flags & FISH_HAVE_PERL) != 0) + g_string_append (ret, "FISH_HAVE_PERL=1 export FISH_HAVE_PERL; "); + + if ((flags & FISH_HAVE_LSQ) != 0) + g_string_append (ret, "FISH_HAVE_LSQ=1 export FISH_HAVE_LSQ; "); + + if ((flags & FISH_HAVE_DATE_MDYT) != 0) + g_string_append (ret, "FISH_HAVE_DATE_MDYT=1 export FISH_HAVE_DATE_MDYT; "); + + if ((flags & FISH_HAVE_TAIL) != 0) + g_string_append (ret, "FISH_HAVE_TAIL=1 export FISH_HAVE_TAIL; "); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +fish_info (struct vfs_class *me, struct vfs_s_super *super) +{ + fish_super_t *fish_super = FISH_SUPER (super); + + if (fish_command (me, super, NONE, fish_super->scr_info, -1) == COMPLETE) + { + while (TRUE) + { + int res; + char buffer[BUF_8K] = ""; + + res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), fish_super->sockr); + if ((res == 0) || (res == EINTR)) + ERRNOR (ECONNRESET, FALSE); + if (strncmp (buffer, "### ", 4) == 0) + break; + fish_super->host_flags = atol (buffer); + } + return TRUE; + } + ERRNOR (E_PROTO, FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +fish_open_archive_pipeopen (struct vfs_s_super *super) +{ + char gbuf[10]; + const char *argv[10]; /* All of 10 is used now */ + const char *xsh = (super->path_element->port == FISH_FLAG_RSH ? "rsh" : "ssh"); + int i = 0; + + argv[i++] = xsh; + if (super->path_element->port == FISH_FLAG_COMPRESSED) + argv[i++] = "-C"; + + if (super->path_element->port > FISH_FLAG_RSH) + { + argv[i++] = "-p"; + g_snprintf (gbuf, sizeof (gbuf), "%d", super->path_element->port); + argv[i++] = gbuf; + } + + /* + * Add the user name to the ssh command line only if it was explicitly + * set in vfs URL. rsh/ssh will get current user by default + * plus we can set convenient overrides in ~/.ssh/config (explicit -l + * option breaks it for some) + */ + + if (super->path_element->user != NULL) + { + argv[i++] = "-l"; + argv[i++] = super->path_element->user; + } + else + { + /* The rest of the code assumes it to be a valid username */ + super->path_element->user = vfs_get_local_username (); + } + + argv[i++] = super->path_element->host; + argv[i++] = "echo FISH:; /bin/sh"; + argv[i++] = NULL; + + fish_pipeopen (super, xsh, argv); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +fish_open_archive_talk (struct vfs_class *me, struct vfs_s_super *super) +{ + fish_super_t *fish_super = FISH_SUPER (super); + char answer[2048]; + + printf ("\n%s\n", _("fish: Waiting for initial line...")); + + if (vfs_s_get_line (me, fish_super->sockr, answer, sizeof (answer), ':') == 0) + return FALSE; + + if (strstr (answer, "assword") != NULL) + { + /* Currently, this does not work. ssh reads passwords from + /dev/tty, not from stdin :-(. */ + + printf ("\n%s\n", _("Sorry, we cannot do password authenticated connections for now.")); + + return FALSE; +#if 0 + if (super->path_element->password == NULL) + { + char *p, *op; + + p = g_strdup_printf (_("fish: Password is required for %s"), super->path_element->user); + op = vfs_get_password (p); + g_free (p); + if (op == NULL) + return FALSE; + super->path_element->password = op; + } + + printf ("\n%s\n", _("fish: Sending password...")); + + { + size_t str_len; + + str_len = strlen (super->path_element->password); + if ((write (fish_super.sockw, super->path_element->password, str_len) != + (ssize_t) str_len) || (write (fish_super->sockw, "\n", 1) != 1)) + return FALSE; + } +#endif + } + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_open_archive_int (struct vfs_class *me, struct vfs_s_super *super) +{ + gboolean ftalk; + + /* hide panels */ + pre_exec (); + + /* open pipe */ + fish_open_archive_pipeopen (super); + + /* Start talk with ssh-server (password prompt, etc ) */ + ftalk = fish_open_archive_talk (me, super); + + /* show panels */ + post_exec (); + + if (!ftalk) + ERRNOR (E_PROTO, -1); + + vfs_print_message ("%s", _("fish: Sending initial line...")); + /* + * Run 'start_fish_server'. If it doesn't exist - no problem, + * we'll talk directly to the shell. + */ + + if (fish_command + (me, super, WAIT_REPLY, "#FISH\necho; start_fish_server 2>&1; echo '### 200'\n", + -1) != COMPLETE) + ERRNOR (E_PROTO, -1); + + vfs_print_message ("%s", _("fish: Handshaking version...")); + if (fish_command (me, super, WAIT_REPLY, "#VER 0.0.3\necho '### 000'\n", -1) != COMPLETE) + ERRNOR (E_PROTO, -1); + + /* Set up remote locale to C, otherwise dates cannot be recognized */ + if (fish_command + (me, super, WAIT_REPLY, + "LANG=C LC_ALL=C LC_TIME=C; export LANG LC_ALL LC_TIME;\n" "echo '### 200'\n", + -1) != COMPLETE) + ERRNOR (E_PROTO, -1); + + vfs_print_message ("%s", _("fish: Getting host info...")); + if (fish_info (me, super)) + FISH_SUPER (super)->scr_env = fish_set_env (FISH_SUPER (super)->host_flags); + +#if 0 + super->name = + g_strconcat ("sh://", super->path_element->user, "@", super->path_element->host, + PATH_SEP_STR, (char *) NULL); +#else + super->name = g_strdup (PATH_SEP_STR); +#endif + + super->root = vfs_s_new_inode (me, super, fish_default_stat (me)); + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_open_archive (struct vfs_s_super *super, + const vfs_path_t * vpath, const vfs_path_element_t * vpath_element) +{ + fish_super_t *fish_super = FISH_SUPER (super); + + (void) vpath; + + super->path_element = vfs_path_element_clone (vpath_element); + + if (strncmp (vpath_element->vfs_prefix, "rsh", 3) == 0) + super->path_element->port = FISH_FLAG_RSH; + + fish_super->scr_ls = + fish_load_script_from_file (super->path_element->host, FISH_LS_FILE, FISH_LS_DEF_CONTENT); + fish_super->scr_exists = + fish_load_script_from_file (super->path_element->host, FISH_EXISTS_FILE, + FISH_EXISTS_DEF_CONTENT); + fish_super->scr_mkdir = + fish_load_script_from_file (super->path_element->host, FISH_MKDIR_FILE, + FISH_MKDIR_DEF_CONTENT); + fish_super->scr_unlink = + fish_load_script_from_file (super->path_element->host, FISH_UNLINK_FILE, + FISH_UNLINK_DEF_CONTENT); + fish_super->scr_chown = + fish_load_script_from_file (super->path_element->host, FISH_CHOWN_FILE, + FISH_CHOWN_DEF_CONTENT); + fish_super->scr_chmod = + fish_load_script_from_file (super->path_element->host, FISH_CHMOD_FILE, + FISH_CHMOD_DEF_CONTENT); + fish_super->scr_utime = + fish_load_script_from_file (super->path_element->host, FISH_UTIME_FILE, + FISH_UTIME_DEF_CONTENT); + fish_super->scr_rmdir = + fish_load_script_from_file (super->path_element->host, FISH_RMDIR_FILE, + FISH_RMDIR_DEF_CONTENT); + fish_super->scr_ln = + fish_load_script_from_file (super->path_element->host, FISH_LN_FILE, FISH_LN_DEF_CONTENT); + fish_super->scr_mv = + fish_load_script_from_file (super->path_element->host, FISH_MV_FILE, FISH_MV_DEF_CONTENT); + fish_super->scr_hardlink = + fish_load_script_from_file (super->path_element->host, FISH_HARDLINK_FILE, + FISH_HARDLINK_DEF_CONTENT); + fish_super->scr_get = + fish_load_script_from_file (super->path_element->host, FISH_GET_FILE, FISH_GET_DEF_CONTENT); + fish_super->scr_send = + fish_load_script_from_file (super->path_element->host, FISH_SEND_FILE, + FISH_SEND_DEF_CONTENT); + fish_super->scr_append = + fish_load_script_from_file (super->path_element->host, FISH_APPEND_FILE, + FISH_APPEND_DEF_CONTENT); + fish_super->scr_info = + fish_load_script_from_file (super->path_element->host, FISH_INFO_FILE, + FISH_INFO_DEF_CONTENT); + + return fish_open_archive_int (vpath_element->class, super); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_archive_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *super, + const vfs_path_t * vpath, void *cookie) +{ + vfs_path_element_t *path_element; + int result; + + (void) vpath; + (void) cookie; + + path_element = vfs_path_element_clone (vpath_element); + + if (path_element->user == NULL) + path_element->user = vfs_get_local_username (); + + result = ((strcmp (path_element->host, super->path_element->host) == 0) + && (strcmp (path_element->user, super->path_element->user) == 0) + && (path_element->port == super->path_element->port)) ? 1 : 0; + + vfs_path_element_free (path_element); + + return result; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +fish_parse_ls (char *buffer, struct vfs_s_entry *ent) +{ +#define ST ent->ino->st + + buffer++; + + switch (buffer[-1]) + { + case ':': + { + char *filename; + char *filename_bound; + char *temp; + + filename = buffer; + + if (strcmp (filename, "\".\"") == 0 || strcmp (filename, "\"..\"") == 0) + break; /* We'll do "." and ".." ourselves */ + + filename_bound = filename + strlen (filename); + + if (S_ISLNK (ST.st_mode)) + { + char *linkname; + char *linkname_bound; + /* we expect: "escaped-name" -> "escaped-name" + // -> cannot occur in filenames, + // because it will be escaped to -\> */ + + + linkname_bound = filename_bound; + + if (*filename == '"') + ++filename; + + linkname = strstr (filename, "\" -> \""); + if (linkname == NULL) + { + /* broken client, or smth goes wrong */ + linkname = filename_bound; + if (filename_bound > filename && *(filename_bound - 1) == '"') + --filename_bound; /* skip trailing " */ + } + else + { + filename_bound = linkname; + linkname += 6; /* strlen ("\" -> \"") */ + if (*(linkname_bound - 1) == '"') + --linkname_bound; /* skip trailing " */ + } + + ent->name = g_strndup (filename, filename_bound - filename); + temp = ent->name; + ent->name = strutils_shell_unescape (ent->name); + g_free (temp); + + ent->ino->linkname = g_strndup (linkname, linkname_bound - linkname); + temp = ent->ino->linkname; + ent->ino->linkname = strutils_shell_unescape (ent->ino->linkname); + g_free (temp); + } + else + { + /* we expect: "escaped-name" */ + if (filename_bound - filename > 2) + { + /* + there is at least 2 " + and we skip them + */ + if (*filename == '"') + ++filename; + if (*(filename_bound - 1) == '"') + --filename_bound; + } + + ent->name = g_strndup (filename, filename_bound - filename); + temp = ent->name; + ent->name = strutils_shell_unescape (ent->name); + g_free (temp); + } + break; + } + + case 'S': + ST.st_size = (off_t) g_ascii_strtoll (buffer, NULL, 10); + break; + + case 'P': + { + size_t skipped; + + vfs_parse_filemode (buffer, &skipped, &ST.st_mode); + break; + } + + case 'R': + { + /* + raw filemode: + we expect: Roctal-filemode octal-filetype uid.gid + */ + size_t skipped; + + vfs_parse_raw_filemode (buffer, &skipped, &ST.st_mode); + break; + } + + case 'd': + vfs_split_text (buffer); + if (vfs_parse_filedate (0, &ST.st_ctime) == 0) + break; + ST.st_atime = ST.st_mtime = ST.st_ctime; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + ST.st_atim.tv_nsec = ST.st_mtim.tv_nsec = ST.st_ctim.tv_nsec = 0; +#endif + break; + + case 'D': + { + struct tm tim; + + memset (&tim, 0, sizeof (tim)); + /* cppcheck-suppress invalidscanf */ + if (sscanf (buffer, "%d %d %d %d %d %d", &tim.tm_year, &tim.tm_mon, + &tim.tm_mday, &tim.tm_hour, &tim.tm_min, &tim.tm_sec) != 6) + break; + ST.st_atime = ST.st_mtime = ST.st_ctime = mktime (&tim); +#ifdef HAVE_STRUCT_STAT_ST_MTIM + ST.st_atim.tv_nsec = ST.st_mtim.tv_nsec = ST.st_ctim.tv_nsec = 0; +#endif + } + break; + + case 'E': + { + int maj, min; + + /* cppcheck-suppress invalidscanf */ + if (sscanf (buffer, "%d,%d", &maj, &min) != 2) + break; +#ifdef HAVE_STRUCT_STAT_ST_RDEV + ST.st_rdev = makedev (maj, min); +#endif + } + break; + + default: + break; + } + +#undef ST +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, const char *remote_path) +{ + struct vfs_s_super *super = dir->super; + char buffer[BUF_8K] = "\0"; + struct vfs_s_entry *ent = NULL; + char *quoted_path; + int reply_code; + + /* + * Simple FISH debug interface :] + */ +#if 0 + if (me->logfile == NULL) + me->logfile = fopen ("/tmp/mc-FISH.sh", "w"); +#endif + + vfs_print_message (_("fish: Reading directory %s..."), remote_path); + + dir->timestamp = g_get_monotonic_time () + fish_directory_timeout * G_USEC_PER_SEC; + + quoted_path = strutils_shell_escape (remote_path); + (void) fish_command_v (me, super, NONE, FISH_SUPER (super)->scr_ls, "FISH_FILENAME=%s;\n", + quoted_path); + g_free (quoted_path); + + ent = vfs_s_generate_entry (me, NULL, dir, 0); + + while (TRUE) + { + int res; + + res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), FISH_SUPER (super)->sockr); + + if ((res == 0) || (res == EINTR)) + { + vfs_s_free_entry (me, ent); + me->verrno = ECONNRESET; + goto error; + } + if (me->logfile != NULL) + { + fputs (buffer, me->logfile); + fputs ("\n", me->logfile); + fflush (me->logfile); + } + if (strncmp (buffer, "### ", 4) == 0) + break; + + if (buffer[0] != '\0') + fish_parse_ls (buffer, ent); + else if (ent->name != NULL) + { + vfs_s_insert_entry (me, dir, ent); + ent = vfs_s_generate_entry (me, NULL, dir, 0); + } + } + + vfs_s_free_entry (me, ent); + reply_code = fish_decode_reply (buffer + 4, 0); + if (reply_code == COMPLETE) + { + vfs_print_message (_("%s: done."), me->name); + return 0; + } + + me->verrno = reply_code == ERROR ? EACCES : E_REMOTE; + + error: + vfs_print_message (_("%s: failure"), me->name); + return -1; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_file_store (struct vfs_class *me, vfs_file_handler_t * fh, char *name, char *localname) +{ + fish_file_handler_t *fish = FISH_FILE_HANDLER (fh); + struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh); + fish_super_t *fish_super = FISH_SUPER (super); + int code; + off_t total = 0; + char buffer[BUF_8K]; + struct stat s; + int h; + char *quoted_name; + + h = open (localname, O_RDONLY); + if (h == -1) + ERRNOR (EIO, -1); + if (fstat (h, &s) < 0) + { + close (h); + ERRNOR (EIO, -1); + } + + /* First, try this as stor: + * + * ( head -c number ) | ( cat > file; cat >/dev/null ) + * + * If 'head' is not present on the remote system, 'dd' will be used. + * Unfortunately, we cannot trust most non-GNU 'head' implementations + * even if '-c' options is supported. Therefore, we separate GNU head + * (and other modern heads?) using '-q' and '-' . This causes another + * implementations to fail (because of "incorrect options"). + * + * Fallback is: + * + * rest=<number> + * while [ $rest -gt 0 ] + * do + * cnt=`expr \( $rest + 255 \) / 256` + * n=`dd bs=256 count=$cnt | tee -a <target_file> | wc -c` + * rest=`expr $rest - $n` + * done + * + * 'dd' was not designed for full filling of input buffers, + * and does not report exact number of bytes (not blocks). + * Therefore a more complex shell script is needed. + * + * On some systems non-GNU head writes "Usage:" error report to stdout + * instead of stderr. It makes impossible the use of "head || dd" + * algorithm for file appending case, therefore just "dd" is used for it. + */ + + quoted_name = strutils_shell_escape (name); + vfs_print_message (_("fish: store %s: sending command..."), quoted_name); + + /* FIXME: File size is limited to ULONG_MAX */ + code = + fish_command_v (me, super, WAIT_REPLY, + fish->append ? fish_super->scr_append : fish_super->scr_send, + "FISH_FILENAME=%s FISH_FILESIZE=%" PRIuMAX ";\n", quoted_name, + (uintmax_t) s.st_size); + g_free (quoted_name); + + if (code != PRELIM) + { + close (h); + ERRNOR (E_REMOTE, -1); + } + + while (TRUE) + { + ssize_t n, t; + + while ((n = read (h, buffer, sizeof (buffer))) < 0) + { + if ((errno == EINTR) && tty_got_interrupt ()) + continue; + vfs_print_message ("%s", _("fish: Local read failed, sending zeros")); + close (h); + h = open ("/dev/zero", O_RDONLY); + } + + if (n == 0) + break; + + t = write (fish_super->sockw, buffer, n); + if (t != n) + { + if (t == -1) + me->verrno = errno; + else + me->verrno = EIO; + goto error_return; + } + tty_disable_interrupt_key (); + total += n; + vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX, _("fish: storing file"), + (uintmax_t) total, (uintmax_t) s.st_size); + } + close (h); + + if (fish_get_reply (me, fish_super->sockr, NULL, 0) != COMPLETE) + ERRNOR (E_REMOTE, -1); + return 0; + + error_return: + close (h); + fish_get_reply (me, fish_super->sockr, NULL, 0); + return -1; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_linear_start (struct vfs_class *me, vfs_file_handler_t * fh, off_t offset) +{ + fish_file_handler_t *fish = FISH_FILE_HANDLER (fh); + struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh); + char *name; + char *quoted_name; + + name = vfs_s_fullpath (me, fh->ino); + if (name == NULL) + return 0; + quoted_name = strutils_shell_escape (name); + g_free (name); + fish->append = FALSE; + + /* + * Check whether the remote file is readable by using 'dd' to copy + * a single byte from the remote file to /dev/null. If 'dd' completes + * with exit status of 0 use 'cat' to send the file contents to the + * standard output (i.e. over the network). + */ + + offset = + fish_command_v (me, super, WANT_STRING, FISH_SUPER (super)->scr_get, + "FISH_FILENAME=%s FISH_START_OFFSET=%" PRIuMAX ";\n", quoted_name, + (uintmax_t) offset); + g_free (quoted_name); + + if (offset != PRELIM) + ERRNOR (E_REMOTE, 0); + fh->linear = LS_LINEAR_OPEN; + fish->got = 0; + errno = 0; +#if SIZEOF_OFF_T == SIZEOF_LONG + fish->total = (off_t) strtol (reply_str, NULL, 10); +#else + fish->total = (off_t) g_ascii_strtoll (reply_str, NULL, 10); +#endif + if (errno != 0) + ERRNOR (E_REMOTE, 0); + return 1; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +fish_linear_abort (struct vfs_class *me, vfs_file_handler_t * fh) +{ + fish_file_handler_t *fish = FISH_FILE_HANDLER (fh); + struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh); + char buffer[BUF_8K]; + ssize_t n; + + vfs_print_message ("%s", _("Aborting transfer...")); + + do + { + n = MIN ((off_t) sizeof (buffer), (fish->total - fish->got)); + if (n != 0) + { + n = read (FISH_SUPER (super)->sockr, buffer, n); + if (n < 0) + return; + fish->got += n; + } + } + while (n != 0); + + if (fish_get_reply (me, FISH_SUPER (super)->sockr, NULL, 0) != COMPLETE) + vfs_print_message ("%s", _("Error reported after abort.")); + else + vfs_print_message ("%s", _("Aborted transfer would be successful.")); +} + +/* --------------------------------------------------------------------------------------------- */ + +static ssize_t +fish_linear_read (struct vfs_class *me, vfs_file_handler_t * fh, void *buf, size_t len) +{ + fish_file_handler_t *fish = FISH_FILE_HANDLER (fh); + struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh); + ssize_t n = 0; + + len = MIN ((size_t) (fish->total - fish->got), len); + tty_disable_interrupt_key (); + while (len != 0 && ((n = read (FISH_SUPER (super)->sockr, buf, len)) < 0)) + { + if ((errno == EINTR) && !tty_got_interrupt ()) + continue; + break; + } + tty_enable_interrupt_key (); + + if (n > 0) + fish->got += n; + else if (n < 0) + fish_linear_abort (me, fh); + else if (fish_get_reply (me, FISH_SUPER (super)->sockr, NULL, 0) != COMPLETE) + ERRNOR (E_REMOTE, -1); + ERRNOR (errno, n); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +fish_linear_close (struct vfs_class *me, vfs_file_handler_t * fh) +{ + fish_file_handler_t *fish = FISH_FILE_HANDLER (fh); + + if (fish->total != fish->got) + fish_linear_abort (me, fh); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_ctl (void *fh, int ctlop, void *arg) +{ + (void) arg; + (void) fh; + (void) ctlop; + + return 0; + +#if 0 + switch (ctlop) + { + case VFS_CTL_IS_NOTREADY: + { + vfs_file_handler_t *file = VFS_FILE_HANDLER (fh); + int v; + + if (file->linear == LS_NOT_LINEAR) + vfs_die ("You may not do this"); + if (file->linear == LS_LINEAR_CLOSED || file->linear == LS_LINEAR_PREOPEN) + return 0; + + v = vfs_s_select_on_two (VFS_FILE_HANDLER_SUPER (fh)->u.fish.sockr, 0); + + return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0; + } + default: + return 0; + } +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2) +{ + const char *crpath1, *crpath2; + char *rpath1, *rpath2; + struct vfs_s_super *super, *super2; + struct vfs_class *me; + int ret; + + crpath1 = vfs_s_get_path (vpath1, &super, 0); + if (crpath1 == NULL) + return -1; + + crpath2 = vfs_s_get_path (vpath2, &super2, 0); + if (crpath2 == NULL) + return -1; + + rpath1 = strutils_shell_escape (crpath1); + rpath2 = strutils_shell_escape (crpath2); + + me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath1)); + + ret = + fish_send_command (me, super2, OPT_FLUSH, FISH_SUPER (super)->scr_mv, + "FISH_FILEFROM=%s FISH_FILETO=%s;\n", rpath1, rpath2); + + g_free (rpath1); + g_free (rpath2); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_link (const vfs_path_t * vpath1, const vfs_path_t * vpath2) +{ + const char *crpath1, *crpath2; + char *rpath1, *rpath2; + struct vfs_s_super *super, *super2; + struct vfs_class *me; + int ret; + + crpath1 = vfs_s_get_path (vpath1, &super, 0); + if (crpath1 == NULL) + return -1; + + crpath2 = vfs_s_get_path (vpath2, &super2, 0); + if (crpath2 == NULL) + return -1; + + rpath1 = strutils_shell_escape (crpath1); + rpath2 = strutils_shell_escape (crpath2); + + me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath1)); + + ret = + fish_send_command (me, super2, OPT_FLUSH, FISH_SUPER (super)->scr_hardlink, + "FISH_FILEFROM=%s FISH_FILETO=%s;\n", rpath1, rpath2); + + g_free (rpath1); + g_free (rpath2); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2) +{ + char *qsetto; + const char *crpath; + char *rpath; + struct vfs_s_super *super; + struct vfs_class *me; + int ret; + + crpath = vfs_s_get_path (vpath2, &super, 0); + if (crpath == NULL) + return -1; + + rpath = strutils_shell_escape (crpath); + qsetto = strutils_shell_escape (vfs_path_get_last_path_str (vpath1)); + + me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath2)); + + ret = + fish_send_command (me, super, OPT_FLUSH, FISH_SUPER (super)->scr_ln, + "FISH_FILEFROM=%s FISH_FILETO=%s;\n", qsetto, rpath); + + g_free (qsetto); + g_free (rpath); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_stat (const vfs_path_t * vpath, struct stat *buf) +{ + int ret; + + ret = vfs_s_stat (vpath, buf); + fish_set_blksize (buf); + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_lstat (const vfs_path_t * vpath, struct stat *buf) +{ + int ret; + + ret = vfs_s_lstat (vpath, buf); + fish_set_blksize (buf); + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_fstat (void *vfs_info, struct stat *buf) +{ + int ret; + + ret = vfs_s_fstat (vfs_info, buf); + fish_set_blksize (buf); + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_chmod (const vfs_path_t * vpath, mode_t mode) +{ + const char *crpath; + char *rpath; + struct vfs_s_super *super; + struct vfs_class *me; + int ret; + + crpath = vfs_s_get_path (vpath, &super, 0); + if (crpath == NULL) + return -1; + + rpath = strutils_shell_escape (crpath); + + me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); + + ret = + fish_send_command (me, super, OPT_FLUSH, FISH_SUPER (super)->scr_chmod, + "FISH_FILENAME=%s FISH_FILEMODE=%4.4o;\n", rpath, + (unsigned int) (mode & 07777)); + + g_free (rpath); + + return ret;; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_chown (const vfs_path_t * vpath, uid_t owner, gid_t group) +{ + char *sowner, *sgroup; + struct passwd *pw; + struct group *gr; + const char *crpath; + char *rpath; + struct vfs_s_super *super; + struct vfs_class *me; + int ret; + + pw = getpwuid (owner); + if (pw == NULL) + return 0; + + gr = getgrgid (group); + if (gr == NULL) + return 0; + + sowner = pw->pw_name; + sgroup = gr->gr_name; + + crpath = vfs_s_get_path (vpath, &super, 0); + if (crpath == NULL) + return -1; + + rpath = strutils_shell_escape (crpath); + + me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); + + /* FIXME: what should we report if chgrp succeeds but chown fails? */ + ret = + fish_send_command (me, super, OPT_FLUSH, FISH_SUPER (super)->scr_chown, + "FISH_FILENAME=%s FISH_FILEOWNER=%s FISH_FILEGROUP=%s;\n", rpath, sowner, + sgroup); + + g_free (rpath); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +fish_get_atime (mc_timesbuf_t * times, time_t * sec, long *nsec) +{ +#ifdef HAVE_UTIMENSAT + *sec = (*times)[0].tv_sec; + *nsec = (*times)[0].tv_nsec; +#else + *sec = times->actime; + *nsec = 0; +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +fish_get_mtime (mc_timesbuf_t * times, time_t * sec, long *nsec) +{ +#ifdef HAVE_UTIMENSAT + *sec = (*times)[1].tv_sec; + *nsec = (*times)[1].tv_nsec; +#else + *sec = times->modtime; + *nsec = 0; +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_utime (const vfs_path_t * vpath, mc_timesbuf_t * times) +{ + char utcatime[16], utcmtime[16]; + char utcatime_w_nsec[30], utcmtime_w_nsec[30]; + time_t atime, mtime; + long atime_nsec, mtime_nsec; + struct tm *gmt; + const char *crpath; + char *rpath; + struct vfs_s_super *super; + struct vfs_class *me; + int ret; + + crpath = vfs_s_get_path (vpath, &super, 0); + if (crpath == NULL) + return -1; + + rpath = strutils_shell_escape (crpath); + + fish_get_atime (times, &atime, &atime_nsec); + gmt = gmtime (&atime); + g_snprintf (utcatime, sizeof (utcatime), "%04d%02d%02d%02d%02d.%02d", + gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, + gmt->tm_hour, gmt->tm_min, gmt->tm_sec); + g_snprintf (utcatime_w_nsec, sizeof (utcatime_w_nsec), "%04d-%02d-%02d %02d:%02d:%02d.%09ld", + gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, + gmt->tm_hour, gmt->tm_min, gmt->tm_sec, atime_nsec); + + fish_get_mtime (times, &mtime, &mtime_nsec); + gmt = gmtime (&mtime); + g_snprintf (utcmtime, sizeof (utcmtime), "%04d%02d%02d%02d%02d.%02d", + gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, + gmt->tm_hour, gmt->tm_min, gmt->tm_sec); + g_snprintf (utcmtime_w_nsec, sizeof (utcmtime_w_nsec), "%04d-%02d-%02d %02d:%02d:%02d.%09ld", + gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, + gmt->tm_hour, gmt->tm_min, gmt->tm_sec, mtime_nsec); + + me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); + + ret = fish_send_command (me, super, OPT_FLUSH, FISH_SUPER (super)->scr_utime, + "FISH_FILENAME=%s FISH_FILEATIME=%ld FISH_FILEMTIME=%ld " + "FISH_TOUCHATIME=%s FISH_TOUCHMTIME=%s FISH_TOUCHATIME_W_NSEC=\"%s\" " + "FISH_TOUCHMTIME_W_NSEC=\"%s\";\n", rpath, (long) atime, (long) mtime, + utcatime, utcmtime, utcatime_w_nsec, utcmtime_w_nsec); + + g_free (rpath); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_unlink (const vfs_path_t * vpath) +{ + const char *crpath; + char *rpath; + struct vfs_s_super *super; + struct vfs_class *me; + int ret; + + crpath = vfs_s_get_path (vpath, &super, 0); + if (crpath == NULL) + return -1; + + rpath = strutils_shell_escape (crpath); + + me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); + + ret = + fish_send_command (me, super, OPT_FLUSH, FISH_SUPER (super)->scr_unlink, + "FISH_FILENAME=%s;\n", rpath); + + g_free (rpath); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_exists (const vfs_path_t * vpath) +{ + const char *crpath; + char *rpath; + struct vfs_s_super *super; + struct vfs_class *me; + int ret; + + crpath = vfs_s_get_path (vpath, &super, 0); + if (crpath == NULL) + return -1; + + rpath = strutils_shell_escape (crpath); + + me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); + + ret = + fish_send_command (me, super, OPT_FLUSH, FISH_SUPER (super)->scr_exists, + "FISH_FILENAME=%s;\n", rpath); + + g_free (rpath); + + return (ret == 0 ? 1 : 0); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_mkdir (const vfs_path_t * vpath, mode_t mode) +{ + const char *crpath; + char *rpath; + struct vfs_s_super *super; + struct vfs_class *me; + int ret; + + (void) mode; + + crpath = vfs_s_get_path (vpath, &super, 0); + if (crpath == NULL) + return -1; + + rpath = strutils_shell_escape (crpath); + + me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); + + ret = + fish_send_command (me, super, OPT_FLUSH, FISH_SUPER (super)->scr_mkdir, + "FISH_FILENAME=%s;\n", rpath); + g_free (rpath); + + if (ret != 0) + return ret; + + if (fish_exists (vpath) == 0) + { + me->verrno = EACCES; + return -1; + } + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_rmdir (const vfs_path_t * vpath) +{ + const char *crpath; + char *rpath; + struct vfs_s_super *super; + struct vfs_class *me; + int ret; + + crpath = vfs_s_get_path (vpath, &super, 0); + if (crpath == NULL) + return -1; + + rpath = strutils_shell_escape (crpath); + + me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); + + ret = + fish_send_command (me, super, OPT_FLUSH, FISH_SUPER (super)->scr_rmdir, + "FISH_FILENAME=%s;\n", rpath); + + g_free (rpath); + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static vfs_file_handler_t * +fish_fh_new (struct vfs_s_inode *ino, gboolean changed) +{ + fish_file_handler_t *fh; + + fh = g_new0 (fish_file_handler_t, 1); + vfs_s_init_fh (VFS_FILE_HANDLER (fh), ino, changed); + + return VFS_FILE_HANDLER (fh); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +fish_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode) +{ + fish_file_handler_t *fish = FISH_FILE_HANDLER (fh); + + (void) mode; + + /* File will be written only, so no need to retrieve it */ + if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0)) + { + /* user pressed the button [ Append ] in the "Copy" dialog */ + if ((flags & O_APPEND) != 0) + fish->append = TRUE; + + if (fh->ino->localname == NULL) + { + vfs_path_t *vpath = NULL; + int tmp_handle; + + tmp_handle = vfs_mkstemps (&vpath, me->name, fh->ino->ent->name); + if (tmp_handle == -1) + return (-1); + + fh->ino->localname = vfs_path_free (vpath, FALSE); + close (tmp_handle); + } + return 0; + } + + if (fh->ino->localname == NULL && vfs_s_retrieve_file (me, fh->ino) == -1) + return (-1); + + if (fh->ino->localname == NULL) + vfs_die ("retrieve_file failed to fill in localname"); + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +fish_fill_names (struct vfs_class *me, fill_names_f func) +{ + GList *iter; + + for (iter = VFS_SUBCLASS (me)->supers; iter != NULL; iter = g_list_next (iter)) + { + const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data; + + char *name; + char gbuf[10]; + const char *flags = ""; + + switch (super->path_element->port) + { + case FISH_FLAG_RSH: + flags = ":r"; + break; + case FISH_FLAG_COMPRESSED: + flags = ":C"; + break; + default: + if (super->path_element->port > FISH_FLAG_RSH) + { + g_snprintf (gbuf, sizeof (gbuf), ":%d", super->path_element->port); + flags = gbuf; + } + break; + } + + name = + g_strconcat (vfs_fish_ops->prefix, VFS_PATH_URL_DELIMITER, + super->path_element->user, "@", super->path_element->host, flags, + PATH_SEP_STR, super->path_element->path, (char *) NULL); + func (name); + g_free (name); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static void * +fish_open (const vfs_path_t * vpath, int flags, mode_t mode) +{ + /* + sorry, i've places hack here + cause fish don't able to open files with O_EXCL flag + */ + flags &= ~O_EXCL; + return vfs_s_open (vpath, flags, mode); +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +vfs_init_fish (void) +{ + tcp_init (); + + vfs_init_subclass (&fish_subclass, "fish", VFSF_REMOTE | VFSF_USETMP, "sh"); + vfs_fish_ops->fill_names = fish_fill_names; + vfs_fish_ops->stat = fish_stat; + vfs_fish_ops->lstat = fish_lstat; + vfs_fish_ops->fstat = fish_fstat; + vfs_fish_ops->chmod = fish_chmod; + vfs_fish_ops->chown = fish_chown; + vfs_fish_ops->utime = fish_utime; + vfs_fish_ops->open = fish_open; + vfs_fish_ops->symlink = fish_symlink; + vfs_fish_ops->link = fish_link; + vfs_fish_ops->unlink = fish_unlink; + vfs_fish_ops->rename = fish_rename; + vfs_fish_ops->mkdir = fish_mkdir; + vfs_fish_ops->rmdir = fish_rmdir; + vfs_fish_ops->ctl = fish_ctl; + fish_subclass.archive_same = fish_archive_same; + fish_subclass.new_archive = fish_new_archive; + fish_subclass.open_archive = fish_open_archive; + fish_subclass.free_archive = fish_free_archive; + fish_subclass.fh_new = fish_fh_new; + fish_subclass.fh_open = fish_fh_open; + fish_subclass.dir_load = fish_dir_load; + fish_subclass.file_store = fish_file_store; + fish_subclass.linear_start = fish_linear_start; + fish_subclass.linear_read = fish_linear_read; + fish_subclass.linear_close = fish_linear_close; + vfs_register_class (vfs_fish_ops); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/fish/fish.h b/src/vfs/fish/fish.h new file mode 100644 index 0000000..3c1fa06 --- /dev/null +++ b/src/vfs/fish/fish.h @@ -0,0 +1,28 @@ + +/** + * \file + * \brief Header: Virtual File System: FISH implementation for transferring files over + * shell connections + */ + + +#ifndef MC__VFS_FISH_H +#define MC__VFS_FISH_H + +/*** typedefs(not structures) and defined constants **********************************************/ + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +extern int fish_directory_timeout; + +/*** declarations of public functions ************************************************************/ + +void vfs_init_fish (void); + +/*** inline functions ****************************************************************************/ + +#endif diff --git a/src/vfs/fish/fishdef.h b/src/vfs/fish/fishdef.h new file mode 100644 index 0000000..129d2b9 --- /dev/null +++ b/src/vfs/fish/fishdef.h @@ -0,0 +1,236 @@ + +/** + * \file + * \brief Header: FISH script defaults + */ + +#ifndef MC__FISH_DEF_H +#define MC__FISH_DEF_H + +/*** typedefs(not structures) and defined constants **********************************************/ + +/* default 'ls' script */ +#define FISH_LS_DEF_CONTENT "" \ +"#LIST /${FISH_FILENAME}\n" \ +"export LC_TIME=C\n" \ +"ls -Qlan \"/${FISH_FILENAME}\" 2>/dev/null | grep '^[^cbt]' | (\n" \ +"while read p l u g s m d y n; do\n" \ +" echo \"P$p $u.$g\"\n" \ +" echo \"S$s\"\n" \ +" echo \"d$m $d $y\"\n" \ +" echo \":$n\"\n" \ +" echo\n" \ +"done\n" \ +")\n" \ +"ls -Qlan \"/${FISH_FILENAME}\" 2>/dev/null | grep '^[cb]' | (\n" \ +"while read p l u g a i m d y n; do\n" \ +" echo \"P$p $u.$g\"\n" \ +" echo \"E$a$i\"\n" \ +" echo \"d$m $d $y\"\n" \ +" echo \":$n\"\n" \ +" echo\n" \ +"done\n" \ +")\n" \ +"echo \"### 200\"\n" + +/* default file exists script */ +#define FISH_EXISTS_DEF_CONTENT "" \ +"#ISEXISTS $FISH_FILENAME\n" \ +"ls -l \"/${FISH_FILENAME}\" >/dev/null 2>/dev/null\n" \ +"echo '### '$?\n" + +/* default 'mkdir' script */ +#define FISH_MKDIR_DEF_CONTENT "" \ +"#MKD $FISH_FILENAME\n" \ +"if mkdir \"/${FISH_FILENAME}\" 2>/dev/null; then\n" \ +" echo \"### 000\"\n" \ +"else\n" \ +" echo \"### 500\"\n" \ +"fi\n" + +/* default 'unlink' script */ +#define FISH_UNLINK_DEF_CONTENT "" \ +"#DELE $FISH_FILENAME\n" \ +"if rm -f \"/${FISH_FILENAME}\" 2>/dev/null; then\n" \ +" echo \"### 000\"\n" \ +"else\n" \ +" echo \"### 500\"\n" \ +"fi\n" +/* default 'chown' script */ +#define FISH_CHOWN_DEF_CONTENT "" \ +"#CHOWN $FISH_FILEOWNER:$FISH_FILEGROUP $FISH_FILENAME\n" \ +"if chown ${FISH_FILEOWNER}:${FISH_FILEGROUP} \"/${FISH_FILENAME}\"; then\n"\ +" echo \"### 000\"\n" \ +"else\n" \ +" echo \"### 500\"\n" \ +"fi\n" + +/* default 'chmod' script */ +#define FISH_CHMOD_DEF_CONTENT "" \ +"#CHMOD $FISH_FILEMODE $FISH_FILENAME\n" \ +"if chmod ${FISH_FILEMODE} \"/${FISH_FILENAME}\" 2>/dev/null; then\n" \ +" echo \"### 000\"\n" \ +"else\n" \ +" echo \"### 500\"\n" \ +"fi\n" + +/* default 'utime' script */ +#define FISH_UTIME_DEF_CONTENT "" \ +"#UTIME \"$FISH_TOUCHATIME_W_NSEC\" \"$FISH_TOUCHMTIME_W_NSEC\" $FISH_FILENAME\n" \ +"if TZ=UTC touch -h -m -d \"$FISH_TOUCHMTIME_W_NSEC\" \"/${FISH_FILENAME}\" 2>/dev/null && \\\n" \ +" TZ=UTC touch -h -a -d \"$FISH_TOUCHATIME_W_NSEC\" \"/${FISH_FILENAME}\" 2>/dev/null; then\n" \ +" echo \"### 000\"\n" \ +"elif TZ=UTC touch -h -m -t $FISH_TOUCHMTIME \"/${FISH_FILENAME}\" 2>/dev/null && \\\n" \ +" TZ=UTC touch -h -a -t $FISH_TOUCHATIME \"/${FISH_FILENAME}\" 2>/dev/null; then\n" \ +" echo \"### 000\"\n" \ +"elif [ -n \"$FISH_HAVE_PERL\" ] && \\\n" \ +" perl -e 'utime '$FISH_FILEATIME','$FISH_FILEMTIME',@ARGV;' \"/${FISH_FILENAME}\" 2>/dev/null; then\n" \ +" echo \"### 000\"\n" \ +"else\n" \ +" echo \"### 500\"\n" \ +"fi\n" + + +/* default 'rmdir' script */ +#define FISH_RMDIR_DEF_CONTENT "" \ +"#RMD $FISH_FILENAME\n" \ +"if rmdir \"/${FISH_FILENAME}\" 2>/dev/null; then\n" \ +" echo \"### 000\"\n" \ +"else\n" \ +" echo \"### 500\"\n" \ +"fi\n" + +/* default 'ln -s' symlink script */ +#define FISH_LN_DEF_CONTENT "" \ +"#SYMLINK $FISH_FILEFROM $FISH_FILETO\n" \ +"if ln -s \"/${FISH_FILEFROM}\" \"/${FISH_FILETO}\" 2>/dev/null; then\n" \ +" echo \"### 000\"\n" \ +"else\n" \ +" echo \"### 500\"\n" \ +"fi\n" + +/* default 'mv' script */ +#define FISH_MV_DEF_CONTENT "" \ +"#RENAME $FISH_FILEFROM $FISH_FILETO\n" \ +"if mv \"/${FISH_FILEFROM}\" \"/${FISH_FILETO}\" 2>/dev/null; then\n" \ +" echo \"### 000\"\n" \ +"else\n" \ +" echo \"### 500\"\n" \ +"fi\n" + +/* default 'ln' hardlink script */ +#define FISH_HARDLINK_DEF_CONTENT "" \ +"#LINK $FISH_FILEFROM $FISH_FILETO\n" \ +"if ln \"/${FISH_FILEFROM}\" \"/${FISH_FILETO}\" 2>/dev/null; then\n" \ +" echo \"### 000\"\n" \ +"else\n" \ +" echo \"### 500\"\n" \ +"fi\n" + +/* default 'retr' script */ +#define FISH_GET_DEF_CONTENT "" \ +"export LC_TIME=C\n" \ +"#RETR $FISH_FILENAME\n" \ +"if dd if=\"/${FISH_FILENAME}\" of=/dev/null bs=1 count=1 2>/dev/null ; then\n" \ +" ls -ln \"/${FISH_FILENAME}\" 2>/dev/null | (\n" \ +" read p l u g s r\n" \ +" echo $s\n" \ +" )\n" \ +" echo \"### 100\"\n" \ +" cat \"/${FISH_FILENAME}\"\n" \ +" echo \"### 200\"\n" \ +"else\n" \ +" echo \"### 500\"\n" \ +"fi\n" + +/* default 'stor' script */ +#define FISH_SEND_DEF_CONTENT "" \ +"FILENAME=\"/${FISH_FILENAME}\"\n" \ +"FILESIZE=${FISH_FILESIZE}\n" \ +"#STOR $FILESIZE $FILENAME\n" \ +"echo \"### 001\"\n" \ +"{\n" \ +" while [ $FILESIZE -gt 0 ]; do\n" \ +" cnt=`expr \\( $FILESIZE + 255 \\) / 256`\n" \ +" n=`dd bs=256 count=$cnt | tee -a \"${FILENAME}\" | wc -c`\n" \ +" FILESIZE=`expr $FILESIZE - $n`\n" \ +" done\n" \ +"}; echo \"### 200\"\n" + +/* default 'appe' script */ +#define FISH_APPEND_DEF_CONTENT "" \ +"FILENAME=\"/${FISH_FILENAME}\"\n" \ +"FILESIZE=${FISH_FILESIZE}\n" \ +"#APPE $FILESIZE $FILENAME\n" \ +"echo \"### 001\"\n" \ +"res=`exec 3>&1\n" \ +"(\n" \ +" head -c $FILESIZE -q - || echo DD >&3\n" \ +") 2>/dev/null | (\n" \ +" cat > \"${FILENAME}\"\n" \ +" cat > /dev/null\n" \ +")`; [ \"$res\" = DD ] && {\n" \ +" > \"${FILENAME}\"\n" \ +" while [ $FILESIZE -gt 0 ]\n" \ +" do\n" \ +" cnt=`expr \\( $FILESIZE + 255 \\) / 256`\n" \ +" n=`dd bs=256 count=$cnt | tee -a \"${FILENAME}\" | wc -c`\n" \ +" FILESIZE=`expr $FILESIZE - $n`\n" \ +" done\n" \ +"}; echo \"### 200\"\n" + +/* default 'info' script */ +#define FISH_INFO_DEF_CONTENT "" \ +"export LC_TIME=C\n" \ +"#FISH_HAVE_HEAD 1\n" \ +"#FISH_HAVE_SED 2\n" \ +"#FISH_HAVE_AWK 4\n" \ +"#FISH_HAVE_PERL 8\n" \ +"#FISH_HAVE_LSQ 16\n" \ +"#FISH_HAVE_DATE_MDYT 32\n" \ +"#FISH_HAVE_TAIL 64\n" \ +"res=0\n" \ +"if `echo yes| head -c 1 > /dev/null 2>&1` ; then\n" \ +" res=`expr $res + 1`\n" \ +"fi\n" \ +"if `sed --version >/dev/null 2>&1` ; then\n" \ +" res=`expr $res + 2`\n" \ +"fi\n" \ +"if `awk --version > /dev/null 2>&1` ; then\n" \ +" res=`expr $res + 4`\n" \ +"fi\n" \ +"if `perl -v > /dev/null 2>&1` ; then\n" \ +" res=`expr $res + 8`\n" \ +"fi\n" \ +"if `ls -Q / >/dev/null 2>&1` ; then\n" \ +" res=`expr $res + 16`\n" \ +"fi\n" \ +"dat=`ls -lan / 2>/dev/null | head -n 3 | tail -n 1 | (\n" \ +" while read p l u g s rec; do\n" \ +" if [ -n \"$g\" ]; then\n" \ +" if [ -n \"$l\" ]; then\n" \ +" echo \"$rec\"\n" \ +" fi\n" \ +" fi\n" \ +" done\n" \ +") | cut -c1 2>/dev/null`\n" \ +"r=`echo \"0123456789\"| grep \"$dat\"`\n" \ +"if [ -z \"$r\" ]; then\n" \ +" res=`expr $res + 32`\n" \ +"fi\n" \ +"if `echo yes| tail -c +1 - > /dev/null 2>&1` ; then\n" \ +" res=`expr $res + 64`\n" \ +"fi\n" \ +"echo $res\n" \ +"echo \"### 200\"\n" + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +/*** declarations of public functions ************************************************************/ + +/*** inline functions ****************************************************************************/ +#endif diff --git a/src/vfs/fish/helpers/Makefile.am b/src/vfs/fish/helpers/Makefile.am new file mode 100644 index 0000000..e3ba15d --- /dev/null +++ b/src/vfs/fish/helpers/Makefile.am @@ -0,0 +1,10 @@ +fishdir = $(libexecdir)/@PACKAGE@/fish + +# Files to install and distribute other than fish scripts +FISH_MISC = README.fish + +# Install and distribute FISH helper scripts w/o shebang & executable bit as data +fish_DATA = $(FISH_MISC) ls mkdir fexists unlink chown chmod rmdir ln mv hardlink get send append info utime +fishconfdir = $(sysconfdir)/@PACKAGE@ + +EXTRA_DIST = $(fish_DATA) diff --git a/src/vfs/fish/helpers/Makefile.in b/src/vfs/fish/helpers/Makefile.in new file mode 100644 index 0000000..c17efbb --- /dev/null +++ b/src/vfs/fish/helpers/Makefile.in @@ -0,0 +1,642 @@ +# 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/fish/helpers +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 = +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 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(fishdir)" +DATA = $(fish_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in +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@ +fishdir = $(libexecdir)/@PACKAGE@/fish + +# Files to install and distribute other than fish scripts +FISH_MISC = README.fish + +# Install and distribute FISH helper scripts w/o shebang & executable bit as data +fish_DATA = $(FISH_MISC) ls mkdir fexists unlink chown chmod rmdir ln mv hardlink get send append info utime +fishconfdir = $(sysconfdir)/@PACKAGE@ +EXTRA_DIST = $(fish_DATA) +all: all-am + +.SUFFIXES: +$(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/fish/helpers/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/vfs/fish/helpers/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): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-fishDATA: $(fish_DATA) + @$(NORMAL_INSTALL) + @list='$(fish_DATA)'; test -n "$(fishdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(fishdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(fishdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(fishdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(fishdir)" || exit $$?; \ + done + +uninstall-fishDATA: + @$(NORMAL_UNINSTALL) + @list='$(fish_DATA)'; test -n "$(fishdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(fishdir)'; $(am__uninstall_files_from_dir) +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + +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 $(DATA) +installdirs: + for dir in "$(DESTDIR)$(fishdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +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 mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-fishDATA + +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 Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-fishDATA + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool 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-fishDATA \ + 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-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags-am uninstall uninstall-am uninstall-fishDATA + +.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/fish/helpers/README.fish b/src/vfs/fish/helpers/README.fish new file mode 100644 index 0000000..ac319c8 --- /dev/null +++ b/src/vfs/fish/helpers/README.fish @@ -0,0 +1,217 @@ + + FIles transferred over SHell protocol (V 0.0.3) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This protocol was designed for transferring files over a remote shell +connection (rsh and compatibles). It can be as well used for transfers over +rsh, and there may be other uses. + +Client sends requests of following form: + +#FISH_COMMAND +equivalent shell commands, +which may be multiline + +Only fish commands are defined here, shell equivalents are for your +information only and will probably vary from implementation to +implementation. Fish commands always have priority: server is +expected to execute fish command if it understands it. If it does not, +however, it can try the luck and execute shell command. + +Since version 4.7.3, the scripts that FISH sends to host machines after +a command is transmitted are no longer hardwired in the Midnight +Commander source code. + +First, mc looks for system-wide set of scripts, then it checks whether +current user has host-specific overrides in his per-user mc +configuration directory. User-defined overrides take priority over +sytem-wide scripts if they exist. The order in which the directories are +traversed is as follows: + + /usr/libexec/mc/fish + ~/.local/share/mc/fish/<hostname>/ + +Server's reply is multiline, but always ends with + +### 000<optional text> + +line. ### is prefix to mark this line, 000 is return code. Return +codes are superset to those used in ftp. + +There are few new exit codes defined: + +000 don't know; if there were no previous lines, this marks COMPLETE +success, if they were, it marks failure. + +001 don't know; if there were no previous lines, this marks +PRELIMinary success, if they were, it marks failure + + Connecting + ~~~~~~~~~~ +Client uses "echo FISH:;/bin/sh" as command executed on remote +machine. This should make it possible for server to distinguish FISH +connections from normal rsh/ssh. + + Commands + ~~~~~~~~ +#FISH +echo; start_fish_server; echo '### 200' + +This command is sent at the beginning. It marks that client wishes to +talk via FISH protocol. #VER command must follow. If server +understands FISH protocol, it has option to put FISH server somewhere +on system path and name it start_fish_server. + +#VER 0.0.2 <feature1> <feature2> <...> +echo '### 000' + +This command is the second one. It sends client version and extensions +to the server. Server should reply with protocol version to be used, +and list of extensions accepted. + +VER 0.0.0 <feature2> +### 200 + +#PWD +pwd; echo '### 200' + +Server should reply with current directory (in form /abc/def/ghi) +followed by line indicating success. + +#LIST /directory +ls -lLa $1 | grep '^[^cbt]' | ( while read p x u g s m d y n; do echo "P$p $u.$g +S$s +d$m $d $y +:$n +"; done ) +ls -lLa $1 | grep '^[cb]' | ( while read p x u g a i m d y n; do echo "P$p $u.$g +E$a$i +dD$m $d $y +:$n +"; done ) +echo '### 200' + +This allows client to list directory or get status information about +single file. Output is in following form (any line except :<filename> +may be omitted): + +P<unix permissions> <owner>.<group> +S<size> +d<3-letters month name> <day> <year or HH:MM> +D<year> <month> <day> <hour> <minute> <second>[.1234] +E<major-of-device>,<minor> +:<filename> +L<filename symlink points to> +<blank line to separate items> + +Unix permissions are of form X--------- where X is type of +file. Currently, '-' means regular file, 'd' means directory, 'c', 'b' +means character and block device, 'l' means symbolic link, 'p' means +FIFO and 's' means socket. + +'d' has three fields: month (one of strings Jan Feb Mar Apr May Jun +Jul Aug Sep Oct Nov Dec), day of month, and third is either single +number indicating year, or HH:MM field (assume current year in such +case). As you've probably noticed, this is pretty broken; it is for +compatibility with ls listing. + +#RETR /some/name +ls -l /some/name | ( read a b c d x e; echo $x ); echo '### 100'; cat /some/name; echo '### 200' + +Server sends line with filesize on it, followed by line with ### 100 +indicating partial success, then it sends binary data (exactly +filesize bytes) and follows them with (with no preceding newline) ### +200. + +Note that there's no way to abort running RETR command - except +closing the connection. + +#STOR <size> /file/name +> /file/name; echo '### 001'; ( dd bs=4096 count=<size/4096>; dd bs=<size%4096> count=1 ) 2>/dev/null | ( cat > %s; cat > /dev/null ); echo '### 200' + +This command is for storing /file/name, which is exactly size bytes +big. You probably think I went crazy. Well, I did not: that strange +cat > /dev/null has purpose to discard any extra data which was not +written to disk (due to for example out of space condition). + +[Why? Imagine uploading file with "rm -rf /" line in it.] + +#CWD /somewhere +cd /somewhere; echo '### 000' + +It is specified here, but I'm not sure how wise idea is to use this +one: it breaks stateless-ness of the protocol. + +Following commands should be rather self-explanatory: + +#CHMOD 1234 file +chmod 1234 file; echo '### 000' + +#DELE /some/path +rm -f /some/path; echo '### 000' + +#MKD /some/path +mkdir /some/path; echo '### 000' + +#RMD /some/path +rmdir /some/path; echo '### 000' + +#RENAME /path/a /path/b +mv /path/a /path/b; echo '### 000' + +#LINK /path/a /path/b +ln /path/a /path/b; echo '### 000' + +#SYMLINK /path/a /path/b +ln -s /path/a /path/b; echo '### 000' + +#CHOWN user /file/name +chown user /file/name; echo '### 000' + +#CHGRP group /file/name +chgrp group /file/name; echo '### 000' + +#INFO +...collect info about host into $result ... +echo $result +echo '### 200' + +#READ <offset> <size> /path/and/filename +cat /path/and/filename | ( dd bs=4096 count=<offset/4096> > /dev/null; +dd bs=<offset%4096> count=1 > /dev/null; +dd bs=4096 count=<offset/4096>; +dd bs=<offset%4096> count=1; ) + +Returns ### 200 on successful exit, ### 291 on successful exit when +reading ended at eof, ### 292 on successful exit when reading did not +end at eof. + +#WRITE <offset> <size> /path/and/filename + +Hmm, shall we define these ones if we know our client is not going to +use them? + +you can use follow parameters: +FISH_FILESIZE +FISH_FILENAME +FISH_FILEMODE +FISH_FILEOWNER +FISH_FILEGROUPE +FISH_FILEFROM +FISH_FILETO + +NB: +'FISH_FILESIZE' used if we operate with single file name in 'unlink', 'rmdir', 'chmod', etc... +'FISH_FILEFROM','FISH_FILETO' used if we operate with two files in 'ln', 'hardlink', 'mv' etc... +'FISH_FILEOWNER', 'FISH_FILEGROUPE' is a new user/group in chown + +also flags: +FISH_HAVE_HEAD +FISH_HAVE_SED +FISH_HAVE_AWK +FISH_HAVE_PERL +FISH_HAVE_LSQ +FISH_HAVE_DATE_MDYT + +That's all, folks! + pavel@ucw.cz diff --git a/src/vfs/fish/helpers/append b/src/vfs/fish/helpers/append new file mode 100644 index 0000000..81ded44 --- /dev/null +++ b/src/vfs/fish/helpers/append @@ -0,0 +1,16 @@ +#APPE $FISH_FILESIZE $FISH_FILENAME +FILENAME="/${FISH_FILENAME}" +echo "### 001" +{ + bss=4096 + bsl=4095 + if [ $FISH_FILESIZE -lt $bss ]; then + bss=1; + bsl=0; + fi + while [ $FISH_FILESIZE -gt 0 ]; do + cnt=`expr \\( $FISH_FILESIZE + $bsl \\) / $bss` + n=`dd bs=$bss count=$cnt | tee -a "${FILENAME}" | wc -c` + FISH_FILESIZE=`expr $FISH_FILESIZE - $n` + done +}; echo "### 200" diff --git a/src/vfs/fish/helpers/chmod b/src/vfs/fish/helpers/chmod new file mode 100644 index 0000000..a5a88b4 --- /dev/null +++ b/src/vfs/fish/helpers/chmod @@ -0,0 +1,6 @@ +#CHMOD $FISH_FILEMODE $FISH_FILENAME +if chmod ${FISH_FILEMODE} "/${FISH_FILENAME}" 2>/dev/null; then + echo "### 000" +else + echo "### 500" +fi diff --git a/src/vfs/fish/helpers/chown b/src/vfs/fish/helpers/chown new file mode 100644 index 0000000..469fdc1 --- /dev/null +++ b/src/vfs/fish/helpers/chown @@ -0,0 +1,6 @@ +#CHOWN $FISH_FILEOWNER:$FISH_FILEGROUP $FISH_FILENAME +if chown ${FISH_FILEOWNER}:${FISH_FILEGROUP} "/${FISH_FILENAME}" ; then + echo "### 000" +else + echo "### 500" +fi diff --git a/src/vfs/fish/helpers/fexists b/src/vfs/fish/helpers/fexists new file mode 100644 index 0000000..cf03b15 --- /dev/null +++ b/src/vfs/fish/helpers/fexists @@ -0,0 +1,3 @@ +#ISEXISTS $FISH_FILENAME +ls -l "/${FISH_FILENAME}" >/dev/null 2>/dev/null +echo '### '$? diff --git a/src/vfs/fish/helpers/get b/src/vfs/fish/helpers/get new file mode 100644 index 0000000..762267a --- /dev/null +++ b/src/vfs/fish/helpers/get @@ -0,0 +1,105 @@ +#RETR $FISH_FILENAME $FISH_START_OFFSET +LC_TIME=C +export LC_TIME +fish_get_perl () +{ +FILENAME=$1 +OFFSET=$2 +perl -e ' +use strict; +use POSIX; +use Fcntl; +my $filename = $ARGV[0]; +my $pos = $ARGV[1]; +my $content; +my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = lstat("$filename"); +my $n; +if (open IFILE,$filename) { + if ($size<$pos) { + printf("0\n"); + } else { + $size-=$pos; + printf("$size\n"); + } + printf("### 100\n"); + seek (IFILE, $pos, 0); + while ($n = read(IFILE,$content,$blksize)!= 0) { + print $content; + } + close IFILE; + printf("### 200\n"); +} else { + printf("### 500\n"); +} +exit 0 +' "${FILENAME}" $OFFSET +} + +fish_get_tail () +{ +FILENAME=$1 +OFFSET=$2 +LC_TIME=C +export LC_TIME +if dd if="${FILENAME}" of=/dev/null bs=1 count=1 2>/dev/null ; then + file_size=`ls -ln "${FILENAME}" 2>/dev/null | ( + read p l u g s r + echo $s + )` + if [ $OFFSET -gt 0 ]; then + file_size=`expr $file_size - $OFFSET` + OFFSET=`expr $OFFSET + 1` + fi + if [ $file_size -gt 0 ]; then + echo $file_size + else + echo 0 + fi + echo "### 100" + if [ $OFFSET -gt 0 ]; then + tail -c +${OFFSET} "${FILENAME}" + else + cat "${FILENAME}" + fi + echo "### 200" +else + echo "### 500" +fi +} + +fish_get_dd () +{ +FILENAME=$1 +OFFSET=$2 +LC_TIME=C +export LC_TIME +if dd if="${FILENAME}" of=/dev/null bs=1 count=1 2>/dev/null ; then + file_size=`ls -ln "${FILENAME}" 2>/dev/null | ( + read p l u g s r + echo $s + )` + file_size=`expr $file_size - $OFFSET` + if [ $file_size -gt 0 ]; then + echo $file_size + else + echo 0 + fi + echo "### 100" + if [ $OFFSET -gt 0 ]; then + dd skip=$OFFSET ibs=1 if="${FILENAME}" 2>/dev/null + else + cat "${FILENAME}" + fi + echo "### 200" +else + echo "### 500" +fi +} + +if [ -n "${FISH_HAVE_PERL}" ]; then + fish_get_perl "/${FISH_FILENAME}" ${FISH_START_OFFSET} +elif [ -n "${FISH_HAVE_TAIL}" ]; then + fish_get_tail "/${FISH_FILENAME}" ${FISH_START_OFFSET} +else + fish_get_dd "/${FISH_FILENAME}" ${FISH_START_OFFSET} +fi diff --git a/src/vfs/fish/helpers/hardlink b/src/vfs/fish/helpers/hardlink new file mode 100644 index 0000000..4f36b3f --- /dev/null +++ b/src/vfs/fish/helpers/hardlink @@ -0,0 +1,8 @@ +#LINK $FISH_FILEFROM $FISH_FILETO +FILEFROM="/${FISH_FILEFROM}" +FILETO="/${FISH_FILETO}" +if ln "${FILEFROM}" "${FILETO}" 2>/dev/null; then + echo "### 000" +else + echo "### 500" +fi diff --git a/src/vfs/fish/helpers/info b/src/vfs/fish/helpers/info new file mode 100644 index 0000000..b85b0a7 --- /dev/null +++ b/src/vfs/fish/helpers/info @@ -0,0 +1,44 @@ +LC_TIME=C +export LC_TIME +#FISH_HAVE_HEAD 1 +#FISH_HAVE_SED 2 +#FISH_HAVE_AWK 4 +#FISH_HAVE_PERL 8 +#FISH_HAVE_LSQ 16 +#FISH_HAVE_DATE_MDYT 32 +#FISH_HAVE_TAIL 64 +res=0 +if `echo yes| head -c 1 > /dev/null 2>&1` ; then + res=`expr $res + 1` +fi +if `echo 1 | sed 's/1/2/' >/dev/null 2>&1` ; then + res=`expr $res + 2` +fi +if `echo 1| awk '{print}' > /dev/null 2>&1` ; then + res=`expr $res + 4` +fi +if `perl -v > /dev/null 2>&1` ; then + res=`expr $res + 8` +fi +if `ls -Q / >/dev/null 2>&1` ; then + res=`expr $res + 16` +fi +dat=`ls -lan / 2>/dev/null | head -n 3 | ( + while read p l u g s rec; do + if [ -n "$g" ]; then + if [ -n "$l" ]; then + echo "$rec" + fi + fi + done +)` +dat=`echo $dat | cut -c1 2>/dev/null` +r=`echo "0123456789"| grep "$dat"` +if [ -z "$r" ]; then + res=`expr $res + 32` +fi +if `echo yes| tail -c +1 - > /dev/null 2>&1` ; then + res=`expr $res + 64` +fi +echo $res +echo "### 200" diff --git a/src/vfs/fish/helpers/ln b/src/vfs/fish/helpers/ln new file mode 100644 index 0000000..a8445d8 --- /dev/null +++ b/src/vfs/fish/helpers/ln @@ -0,0 +1,8 @@ +#SYMLINK $FISH_FILEFROM $FISH_FILETO +FILEFROM="${FISH_FILEFROM}" +FILETO="/${FISH_FILETO}" +if ln -s "${FILEFROM}" "${FILETO}" 2>/dev/null; then + echo "### 000" +else + echo "### 500" +fi diff --git a/src/vfs/fish/helpers/ls b/src/vfs/fish/helpers/ls new file mode 100644 index 0000000..7165b51 --- /dev/null +++ b/src/vfs/fish/helpers/ls @@ -0,0 +1,170 @@ +#LIST /${FISH_DIR} +LC_TIME=C +export LC_TIME +perl_res="1" +fish_list_lsq () +{ +FISH_DIR="$1" +ls -Qlan "${FISH_DIR}" 2>/dev/null | grep '^[^cbt]' | ( +while read p l u g s m d y n; do + echo "P$p $u.$g" + echo "S$s" + echo "d$m $d $y" + echo ":$n" + echo +done +) + +ls -Qlan "${FISH_DIR}" 2>/dev/null | grep '^[cb]' | ( +while read p l u g a i m d y n; do + echo "P$p $u.$g" + echo "E$a$i" + echo "d$m $d $y" + echo ":$n" + echo +done +) +echo '### 200' +} + +fish_list_sed () +{ +FISH_DIR="$1" +ls -lan "${FISH_DIR}" 2>/dev/null | grep '^[^cbt]' | ( +while read p l u g s rec; do + if [ -n "$g" ]; then + if [ -n "$FISH_HAVE_DATE_MDYT" ]; then + filename=`echo "$rec"| sed 's/[^[:space:]]\+ \+[^[:space:]]\+ \+[^[:space:]]\+ //'` + filedate=`echo "$rec"| sed 's/\([^[:space:]]\+ \+[^[:space:]]\+ \+[^[:space:]]\+\) .*/\1/'` + else + filename=`echo "$rec"| sed 's/[^[:space:]]\+ \+[^[:space:]]\+ //'` + filedate=`echo "$rec"| sed 's/\([^[:space:]]\+ \+[^[:space:]]\+\) .*/\1/'` + fi + pfile=\"`echo "$filename" | sed -e 's#^\(.*\) -> \(.*\)#\1" -> "\2#'`\" + echo "P$p $u.$g" + echo "S$s" + if [ -n "$FISH_HAVE_DATE_MDYT" ]; then + echo "d$filedate" + else + echo "D$filedate" + fi + echo ":$pfile" + echo + fi +done +) +ls -lan "${FISH_DIR}" 2>/dev/null | grep '^[cb]' | ( +while read p l u g a i rec; do + if [ -n "$g" ]; then + if [ -n "$FISH_HAVE_DATE_MDYT" ]; then + filename=`echo "$rec"| sed 's/[^[:space:]]\+ \+[^[:space:]]\+ \+[^[:space:]]\+ //'` + filedate=`echo "$rec"| sed 's/\([^[:space:]]\+ \+[^[:space:]]\+ \+[^[:space:]]\+\) .*/\1/'` + else + filename=`echo "$rec"| sed 's/[^[:space:]]\+ \+[^[:space:]]\+ //'` + filedate=`echo "$rec"| sed 's/\([^[:space:]]\+ \+[^[:space:]]\+\) .*/\1/'` + fi + pfile=\"`echo "$filename" | sed -e 's#^\(.*\) -> \(.*\)#\1" -> "\2#'`\" + echo "P$p $u.$g" + echo "E$a$i" + if [ -n "$FISH_HAVE_DATE_MDYT" ]; then + echo "d$filedate" + else + echo "D$filedate" + fi + echo ":$pfile" + echo + fi +done +) +echo '### 200' +} + +fish_list_poor_ls () +{ +FISH_DIR="$1" +ls -lan "${FISH_DIR}" 2>/dev/null | grep '^[^cbt]' | ( +while read p l u g s m d y n n2 n3; do + if [ -n "$g" ]; then + if [ "$m" = "0" ]; then + s=$d; m=$y; d=$n; y=$n2; n=$n3 + else + n=$n" "$n2" "$n3 + fi + echo "P$p $u $g" + echo "S$s" + echo "d$m $d $y" + echo ":"$n + echo + fi +done +) +ls -lan "${FISH_DIR}" 2>/dev/null | grep '^[cb]' | ( +while read p l u g a i m d y n n2 n3; do + if [ -n "$g" ]; then + if [ "$a" = "0" ]; then + a=$m; i=$d; m=$y; d=$n; y=$n2; n=$n3 + else + n=$n" "$n2" "$n3 + fi + echo "P$p $u $g" + echo "E$a$i" + echo "d$m $d $y" + echo ":"$n + echo + fi +done +) +echo '### 200' +} + +fish_list_perl () +{ +FISH_DIR=$1 +perl -e ' +use strict; +use POSIX; +use Fcntl; +use POSIX ":fcntl_h"; #S_ISLNK was here until 5.6 +import Fcntl ":mode" unless defined &S_ISLNK; #and is now here +my $dirname = $ARGV[0]; +if (opendir (DIR, $dirname)) { +while((my $filename = readdir (DIR))){ + my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = lstat("$dirname/$filename"); + my $mloctime= strftime("%m-%d-%Y %H:%M", localtime $mtime); + my $strutils_shell_escape_regex = s/([;<>\*\|`&\$!#\(\)\[\]\{\}:'\''"\ \\])/\\$1/g; + my $e_filename = $filename; + $e_filename =~ $strutils_shell_escape_regex; + if (S_ISLNK ($mode)) { + my $linkname = readlink ("$dirname/$filename"); + $linkname =~ $strutils_shell_escape_regex; + printf("R%o %o $uid.$gid\nS$size\nd$mloctime\n:\"%s\" -> \"%s\"\n\n", S_IMODE($mode), S_IFMT($mode), $e_filename, $linkname); + } elsif (S_ISCHR ($mode) || S_ISBLK ($mode)) { + my $minor = $rdev % 256; + my $major = int( $rdev / 256 ); + printf("R%o %o $uid.$gid\nE$major,$minor\nd$mloctime\n:\"%s\"\n\n", S_IMODE($mode), S_IFMT($mode), $e_filename); + } else { + printf("R%o %o $uid.$gid\nS$size\nd$mloctime\n:\"%s\"\n\n", S_IMODE($mode), S_IFMT($mode), $e_filename); + } +} + printf("### 200\n"); + closedir(DIR); +} else { + printf("### 500\n"); +} +exit 0 +' "/${FISH_DIR}" +perl_res=$? +} + +if [ -n "${FISH_HAVE_PERL}" ]; then + fish_list_perl "/${FISH_FILENAME}" +fi +if [ "${perl_res}" != "0" ]; then + if [ -n "${FISH_HAVE_LSQ}" ]; then + fish_list_lsq "/${FISH_FILENAME}" + elif [ -n "${FISH_HAVE_SED}" ]; then + fish_list_sed "/${FISH_FILENAME}" + else + fish_list_poor_ls "/${FISH_FILENAME}" + fi +fi diff --git a/src/vfs/fish/helpers/mkdir b/src/vfs/fish/helpers/mkdir new file mode 100644 index 0000000..b32e995 --- /dev/null +++ b/src/vfs/fish/helpers/mkdir @@ -0,0 +1,6 @@ +#MKD $FISH_FILENAME +if mkdir "/$FISH_FILENAME" 2>/dev/null; then + echo "### 000" +else + echo "### 500" +fi diff --git a/src/vfs/fish/helpers/mv b/src/vfs/fish/helpers/mv new file mode 100644 index 0000000..c8cf70c --- /dev/null +++ b/src/vfs/fish/helpers/mv @@ -0,0 +1,6 @@ +#RENAME $FISH_FILEFROM $FISH_FILETO +if mv "/${FISH_FILEFROM}" "/${FISH_FILETO}" 2>/dev/null; then + echo "### 000" +else + echo "### 500" +fi diff --git a/src/vfs/fish/helpers/rmdir b/src/vfs/fish/helpers/rmdir new file mode 100644 index 0000000..0f99bf6 --- /dev/null +++ b/src/vfs/fish/helpers/rmdir @@ -0,0 +1,6 @@ +#RMD $FISH_FILENAME +if rmdir "/${FISH_FILENAME}" 2>/dev/null; then + echo "### 000" +else + echo "### 500" +fi diff --git a/src/vfs/fish/helpers/send b/src/vfs/fish/helpers/send new file mode 100644 index 0000000..80dd22b --- /dev/null +++ b/src/vfs/fish/helpers/send @@ -0,0 +1,17 @@ +#STOR $FISH_FILESIZE $FISH_FILENAME +FILENAME="/${FISH_FILENAME}" +echo "### 001" +{ + > "${FILENAME}" + bss=4096 + bsl=4095 + if [ $FISH_FILESIZE -lt $bss ]; then + bss=1; + bsl=0; + fi + while [ $FISH_FILESIZE -gt 0 ]; do + cnt=`expr \\( $FISH_FILESIZE + $bsl \\) / $bss` + n=`dd bs=$bss count=$cnt | tee -a "${FILENAME}" | wc -c` + FISH_FILESIZE=`expr $FISH_FILESIZE - $n` + done +}; echo "### 200" diff --git a/src/vfs/fish/helpers/unlink b/src/vfs/fish/helpers/unlink new file mode 100644 index 0000000..79b9ad0 --- /dev/null +++ b/src/vfs/fish/helpers/unlink @@ -0,0 +1,6 @@ +#DELE $FISH_FILENAME +if rm -f "/${FISH_FILENAME}" 2>/dev/null; then + echo "### 000" +else + echo "### 500" +fi diff --git a/src/vfs/fish/helpers/utime b/src/vfs/fish/helpers/utime new file mode 100644 index 0000000..94395b4 --- /dev/null +++ b/src/vfs/fish/helpers/utime @@ -0,0 +1,13 @@ +#UTIME "$FISH_TOUCHATIME_W_NSEC" "$FISH_TOUCHMTIME_W_NSEC" "$FISH_FILENAME" +if TZ=UTC touch -h -m -d "$FISH_TOUCHMTIME_W_NSEC" "/${FISH_FILENAME}" 2>/dev/null && \ + TZ=UTC touch -h -a -d "$FISH_TOUCHATIME_W_NSEC" "/${FISH_FILENAME}" 2>/dev/null; then + echo "### 000" +elif TZ=UTC touch -h -m -t $FISH_TOUCHMTIME "/${FISH_FILENAME}" 2>/dev/null && \ + TZ=UTC touch -h -a -t $FISH_TOUCHATIME "/${FISH_FILENAME}" 2>/dev/null; then + echo "### 000" +elif [ -n "$FISH_HAVE_PERL" ] && + perl -e 'utime '$FISH_FILEATIME','$FISH_FILEMTIME',@ARGV;' "/${FISH_FILENAME}" 2>/dev/null; then + echo "### 000" +else + echo "### 500" +fi diff --git a/src/vfs/ftpfs/Makefile.am b/src/vfs/ftpfs/Makefile.am new file mode 100644 index 0000000..b581563 --- /dev/null +++ b/src/vfs/ftpfs/Makefile.am @@ -0,0 +1,8 @@ + +AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) + +noinst_LTLIBRARIES = libvfs-ftpfs.la + +libvfs_ftpfs_la_SOURCES = \ + ftpfs.c ftpfs.h \ + ftpfs_parse_ls.c
\ No newline at end of file diff --git a/src/vfs/ftpfs/Makefile.in b/src/vfs/ftpfs/Makefile.in new file mode 100644 index 0000000..e6e561f --- /dev/null +++ b/src/vfs/ftpfs/Makefile.in @@ -0,0 +1,740 @@ +# 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/ftpfs +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_ftpfs_la_LIBADD = +am_libvfs_ftpfs_la_OBJECTS = ftpfs.lo ftpfs_parse_ls.lo +libvfs_ftpfs_la_OBJECTS = $(am_libvfs_ftpfs_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)/ftpfs.Plo \ + ./$(DEPDIR)/ftpfs_parse_ls.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_ftpfs_la_SOURCES) +DIST_SOURCES = $(libvfs_ftpfs_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) +noinst_LTLIBRARIES = libvfs-ftpfs.la +libvfs_ftpfs_la_SOURCES = \ + ftpfs.c ftpfs.h \ + ftpfs_parse_ls.c + +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/ftpfs/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/vfs/ftpfs/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-ftpfs.la: $(libvfs_ftpfs_la_OBJECTS) $(libvfs_ftpfs_la_DEPENDENCIES) $(EXTRA_libvfs_ftpfs_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libvfs_ftpfs_la_OBJECTS) $(libvfs_ftpfs_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ftpfs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ftpfs_parse_ls.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)/ftpfs.Plo + -rm -f ./$(DEPDIR)/ftpfs_parse_ls.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)/ftpfs.Plo + -rm -f ./$(DEPDIR)/ftpfs_parse_ls.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/ftpfs/ftpfs.c b/src/vfs/ftpfs/ftpfs.c new file mode 100644 index 0000000..549ba32 --- /dev/null +++ b/src/vfs/ftpfs/ftpfs.c @@ -0,0 +1,2784 @@ +/* + Virtual File System: FTP file system. + + Copyright (C) 1995-2023 + Free Software Foundation, Inc. + + Written by: + Ching Hui, 1995 + Jakub Jelinek, 1995 + Miguel de Icaza, 1995, 1996, 1997 + Norbert Warmuth, 1997 + Pavel Machek, 1998 + Yury V. Zaytsev, 2010 + Slava Zanko <slavazanko@gmail.com>, 2010, 2013 + Andrew Borodin <aborodin@vmail.ru>, 2010-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/>. + */ + +/** + * \file + * \brief Source: Virtual File System: FTP file system + * \author Ching Hui + * \author Jakub Jelinek + * \author Miguel de Icaza + * \author Norbert Warmuth + * \author Pavel Machek + * \date 1995, 1997, 1998 + * + * \todo +- make it more robust - all the connects etc. should handle EADDRINUSE and + ERETRY (have I spelled these names correctly?) +- make the user able to flush a connection - all the caches will get empty + etc., (tarfs as well), we should give there a user selectable timeout + and assign a key sequence. +- use hash table instead of linklist to cache ftpfs directory. + +What to do with this? + + + * NOTE: Usage of tildes is deprecated, consider: + * \verbatim + cd ftp//:pavel@hobit + cd ~ + \endverbatim + * And now: what do I want to do? Do I want to go to /home/pavel or to + * ftp://hobit/home/pavel? I think first has better sense... + * + \verbatim + { + int f = !strcmp( remote_path, "/~" ); + if (f || !strncmp( remote_path, "/~/", 3 )) { + char *s; + s = mc_build_filename ( qhome (*bucket), remote_path +3-f, (char *) NULL ); + g_free (remote_path); + remote_path = s; + } + } + \endverbatim + */ + +/* \todo Fix: Namespace pollution: horrible */ + +#include <config.h> +#include <stdio.h> /* sscanf() */ +#include <stdlib.h> /* atoi() */ +#include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.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 <arpa/ftp.h> +#include <arpa/telnet.h> +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif +#include <errno.h> +#include <ctype.h> +#include <fcntl.h> +#include <inttypes.h> /* uintmax_t */ + +#include "lib/global.h" +#include "lib/file-entry.h" +#include "lib/util.h" +#include "lib/strutil.h" /* str_move() */ +#include "lib/mcconfig.h" + +#include "lib/tty/tty.h" /* enable/disable interrupt key */ +#include "lib/widget.h" /* message() */ + +#include "src/history.h" +#include "src/setup.h" /* for load_anon_passwd */ + +#include "lib/vfs/vfs.h" +#include "lib/vfs/utilvfs.h" +#include "lib/vfs/netutil.h" +#include "lib/vfs/xdirentry.h" +#include "lib/vfs/gc.h" /* vfs_stamp_create */ + +#include "ftpfs.h" + +/*** global variables ****************************************************************************/ + +/* Delay to retry a connection */ +int ftpfs_retry_seconds = 30; + +/* Method to use to connect to ftp sites */ +gboolean ftpfs_use_passive_connections = TRUE; +gboolean ftpfs_use_passive_connections_over_proxy = FALSE; + +/* Method used to get directory listings: + * 1: try 'LIST -la <path>', if it fails + * fall back to CWD <path>; LIST + * 0: always use CWD <path>; LIST + */ +gboolean ftpfs_use_unix_list_options = TRUE; + +/* First "CWD <path>", then "LIST -la ." */ +gboolean ftpfs_first_cd_then_ls = TRUE; + +/* Use the ~/.netrc */ +gboolean ftpfs_use_netrc = TRUE; + +/* Anonymous setup */ +char *ftpfs_anonymous_passwd = NULL; +int ftpfs_directory_timeout = 900; + +/* Proxy host */ +char *ftpfs_proxy_host = NULL; + +/* whether we have to use proxy by default? */ +gboolean ftpfs_always_use_proxy = FALSE; + +gboolean ftpfs_ignore_chattr_errors = TRUE; + +/*** file scope macro definitions ****************************************************************/ + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif + +#define FTP_SUPER(super) ((ftp_super_t *) (super)) +#define FTP_FILE_HANDLER(fh) ((ftp_file_handler_t *) (fh)) +#define FH_SOCK FTP_FILE_HANDLER(fh)->sock + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +#define RFC_AUTODETECT 0 +#define RFC_DARING 1 +#define RFC_STRICT 2 + +/* ftpfs_command wait_flag: */ +#define NONE 0x00 +#define WAIT_REPLY 0x01 +#define WANT_STRING 0x02 + +#define FTP_COMMAND_PORT 21 + +/* some defines only used by ftpfs_changetype */ +/* These two are valid values for the second parameter */ +#define TYPE_ASCII 0 +#define TYPE_BINARY 1 + +/* This one is only used to initialize bucket->isbinary, don't use it as + second parameter to ftpfs_changetype. */ +#define TYPE_UNKNOWN -1 + +#define ABORT_TIMEOUT (5 * G_USEC_PER_SEC) +/*** file scope type declarations ****************************************************************/ + +#ifndef HAVE_SOCKLEN_T +typedef int socklen_t; +#endif + +/* This should match the keywords[] array below */ +typedef enum +{ + NETRC_NONE = 0, + NETRC_DEFAULT, + NETRC_MACHINE, + NETRC_LOGIN, + NETRC_PASSWORD, + NETRC_PASSWD, + NETRC_ACCOUNT, + NETRC_MACDEF, + NETRC_UNKNOWN +} keyword_t; + +typedef struct +{ + struct vfs_s_super base; /* base class */ + + int sock; + + char *proxy; /* proxy server, NULL if no proxy */ + gboolean failed_on_login; /* used to pass the failure reason to upper levels */ + gboolean use_passive_connection; + gboolean remote_is_amiga; /* No leading slash allowed for AmiTCP (Amiga) */ + int isbinary; + gboolean cwd_deferred; /* current_directory was changed but CWD command hasn't + been sent yet */ + int strict; /* ftp server doesn't understand + * "LIST -la <path>"; use "CWD <path>"/ + * "LIST" instead + */ + gboolean ctl_connection_busy; + char *current_dir; +} ftp_super_t; + +typedef struct +{ + vfs_file_handler_t base; /* base class */ + + int sock; + gboolean append; +} ftp_file_handler_t; + +/*** forward declarations (file scope functions) *************************************************/ + +static char *ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super); +static int ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, + const char *remote_path); +static int ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super); +static gboolean ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, + const char *netrcpass); +static gboolean ftpfs_netrc_lookup (const char *host, char **login, char **pass); + +/*** file scope variables ************************************************************************/ + +static int code; + +static char reply_str[80]; + +static struct vfs_s_subclass ftpfs_subclass; +static struct vfs_class *vfs_ftpfs_ops = VFS_CLASS (&ftpfs_subclass); + +static GSList *no_proxy = NULL; + +static char buffer[BUF_MEDIUM]; +static char *netrc = NULL; +static const char *netrcp; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static void +ftpfs_set_blksize (struct stat *s) +{ +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + /* redefine block size */ + s->st_blksize = 64 * 1024; /* FIXME */ +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct stat * +ftpfs_default_stat (struct vfs_class *me) +{ + struct stat *s; + + s = vfs_s_default_stat (me, S_IFDIR | 0755); + ftpfs_set_blksize (s); + vfs_adjust_stat (s); + + return s; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* Translate a Unix path, i.e. MC's internal path representation (e.g. + /somedir/somefile) to a path valid for the remote server. Every path + transferred to the remote server has to be mangled by this function + right prior to sending it. + Currently only Amiga ftp servers are handled in a special manner. + + When the remote server is an amiga: + a) strip leading slash if necessary + b) replace first occurrence of ":/" with ":" + c) strip trailing "/." + */ +static char * +ftpfs_translate_path (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path) +{ + char *ret, *p; + + if (!FTP_SUPER (super)->remote_is_amiga) + return g_strdup (remote_path); + + if (me->logfile != NULL) + { + fprintf (me->logfile, "MC -- ftpfs_translate_path: %s\n", remote_path); + fflush (me->logfile); + } + + /* strip leading slash(es) */ + while (IS_PATH_SEP (*remote_path)) + remote_path++; + + /* Don't change "/" into "", e.g. "CWD " would be invalid. */ + if (*remote_path == '\0') + return g_strdup ("."); + + ret = g_strdup (remote_path); + + /* replace first occurrence of ":/" with ":" */ + p = strchr (ret, ':'); + if (p != NULL && IS_PATH_SEP (p[1])) + str_move (p + 1, p + 2); + + /* strip trailing "/." */ + p = strrchr (ret, PATH_SEP); + if ((p != NULL) && (*(p + 1) == '.') && (*(p + 2) == '\0')) + *p = '\0'; + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ +/** Extract the hostname and username from the path */ +/* + * path is in the form: [user@]hostname:port/remote-dir, e.g.: + * ftp://sunsite.unc.edu/pub/linux + * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc + * ftp://tsx-11.mit.edu:8192/ + * ftp://joe@foo.edu:11321/private + * If the user is empty, e.g. ftp://@roxanne/private, then your login name + * is supplied. + */ + +static vfs_path_element_t * +ftpfs_correct_url_parameters (const vfs_path_element_t * velement) +{ + vfs_path_element_t *path_element = vfs_path_element_clone (velement); + + if (path_element->port == 0) + path_element->port = FTP_COMMAND_PORT; + + if (path_element->user == NULL) + { + /* Look up user and password in netrc */ + if (ftpfs_use_netrc) + ftpfs_netrc_lookup (path_element->host, &path_element->user, &path_element->password); + } + if (path_element->user == NULL) + path_element->user = g_strdup ("anonymous"); + + /* Look up password in netrc for known user */ + if (ftpfs_use_netrc && path_element->password == NULL) + { + char *new_user = NULL; + char *new_passwd = NULL; + + ftpfs_netrc_lookup (path_element->host, &new_user, &new_passwd); + + /* If user is different, remove password */ + if (new_user != NULL && strcmp (path_element->user, new_user) != 0) + MC_PTR_FREE (path_element->password); + + g_free (new_user); + g_free (new_passwd); + } + + return path_element; +} + +/* --------------------------------------------------------------------------------------------- */ +/* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */ + +static int +ftpfs_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len) +{ + while (TRUE) + { + char answer[BUF_1K]; + + if (vfs_s_get_line (me, sock, answer, sizeof (answer), '\n') == 0) + { + if (string_buf != NULL) + *string_buf = '\0'; + code = 421; + return 4; + } + + /* cppcheck-suppress invalidscanf */ + switch (sscanf (answer, "%d", &code)) + { + case 0: + if (string_buf != NULL) + g_strlcpy (string_buf, answer, string_len); + code = 500; + return 5; + case 1: + if (answer[3] == '-') + { + while (TRUE) + { + int i; + + if (vfs_s_get_line (me, sock, answer, sizeof (answer), '\n') == 0) + { + if (string_buf != NULL) + *string_buf = '\0'; + code = 421; + return 4; + } + /* cppcheck-suppress invalidscanf */ + if ((sscanf (answer, "%d", &i) > 0) && (code == i) && (answer[3] == ' ')) + break; + } + } + if (string_buf != NULL) + g_strlcpy (string_buf, answer, string_len); + return code / 100; + default: + break; + } + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +ftpfs_reconnect (struct vfs_class *me, struct vfs_s_super *super) +{ + ftp_super_t *ftp_super = FTP_SUPER (super); + int sock; + + sock = ftpfs_open_socket (me, super); + if (sock != -1) + { + char *cwdir = ftp_super->current_dir; + + close (ftp_super->sock); + ftp_super->sock = sock; + ftp_super->current_dir = NULL; + + if (ftpfs_login_server (me, super, super->path_element->password)) + { + if (cwdir == NULL) + return TRUE; + + sock = ftpfs_chdir_internal (me, super, cwdir); + g_free (cwdir); + return (sock == COMPLETE); + } + + ftp_super->current_dir = cwdir; + } + + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +G_GNUC_PRINTF (4, 5) +ftpfs_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, + ...) +{ + ftp_super_t *ftp_super = FTP_SUPER (super); + va_list ap; + GString *cmdstr; + int status; + static gboolean retry = FALSE; + static int level = 0; /* ftpfs_login_server() use ftpfs_command() */ + + cmdstr = g_string_sized_new (32); + va_start (ap, fmt); + g_string_vprintf (cmdstr, fmt, ap); + va_end (ap); + g_string_append (cmdstr, "\r\n"); + + if (me->logfile != NULL) + { + if (strncmp (cmdstr->str, "PASS ", 5) == 0) + fputs ("PASS <Password not logged>\r\n", me->logfile); + else + { + size_t ret; + + ret = fwrite (cmdstr->str, cmdstr->len, 1, me->logfile); + (void) ret; + } + + fflush (me->logfile); + } + + got_sigpipe = 0; + tty_enable_interrupt_key (); + status = write (ftp_super->sock, cmdstr->str, cmdstr->len); + + if (status < 0) + { + code = 421; + + if (errno == EPIPE) + { /* Remote server has closed connection */ + if (level == 0) + { + level = 1; + status = ftpfs_reconnect (me, super) ? 1 : 0; + level = 0; + if (status != 0 && (write (ftp_super->sock, cmdstr->str, cmdstr->len) > 0)) + goto ok; + + } + got_sigpipe = 1; + } + g_string_free (cmdstr, TRUE); + tty_disable_interrupt_key (); + return TRANSIENT; + } + + retry = FALSE; + + ok: + tty_disable_interrupt_key (); + + if (wait_reply != NONE) + { + status = ftpfs_get_reply (me, ftp_super->sock, + (wait_reply & WANT_STRING) != 0 ? reply_str : NULL, + sizeof (reply_str) - 1); + if ((wait_reply & WANT_STRING) != 0 && !retry && level == 0 && code == 421) + { + retry = TRUE; + level = 1; + status = ftpfs_reconnect (me, super) ? 1 : 0; + level = 0; + if (status != 0 && (write (ftp_super->sock, cmdstr->str, cmdstr->len) > 0)) + goto ok; + } + retry = FALSE; + g_string_free (cmdstr, TRUE); + return status; + } + + g_string_free (cmdstr, TRUE); + return COMPLETE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct vfs_s_super * +ftpfs_new_archive (struct vfs_class *me) +{ + ftp_super_t *arch; + + arch = g_new0 (ftp_super_t, 1); + arch->base.me = me; + arch->base.name = g_strdup (PATH_SEP_STR); + arch->sock = -1; + arch->use_passive_connection = ftpfs_use_passive_connections; + arch->strict = ftpfs_use_unix_list_options ? RFC_AUTODETECT : RFC_STRICT; + arch->isbinary = TYPE_UNKNOWN; + + return VFS_SUPER (arch); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +ftpfs_free_archive (struct vfs_class *me, struct vfs_s_super *super) +{ + ftp_super_t *ftp_super = FTP_SUPER (super); + + if (ftp_super->sock != -1) + { + vfs_print_message (_("ftpfs: Disconnecting from %s"), super->path_element->host); + ftpfs_command (me, super, NONE, "%s", "QUIT"); + close (ftp_super->sock); + } + g_free (ftp_super->current_dir); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_changetype (struct vfs_class *me, struct vfs_s_super *super, int binary) +{ + if (binary != FTP_SUPER (super)->isbinary) + { + if (ftpfs_command (me, super, WAIT_REPLY, "TYPE %c", binary ? 'I' : 'A') != COMPLETE) + ERRNOR (EIO, -1); + FTP_SUPER (super)->isbinary = binary; + } + return binary; +} + +/* --------------------------------------------------------------------------------------------- */ +/* This routine logs the user in */ + +static int +ftpfs_login_server (struct vfs_class *me, struct vfs_s_super *super, const char *netrcpass) +{ + ftp_super_t *ftp_super = FTP_SUPER (super); + char *pass; + char *op; + char *name; /* login user name */ + gboolean anon = FALSE; + char reply_string[BUF_MEDIUM]; + + ftp_super->isbinary = TYPE_UNKNOWN; + + if (super->path_element->password != NULL) /* explicit password */ + op = g_strdup (super->path_element->password); + else if (netrcpass != NULL) /* password from netrc */ + op = g_strdup (netrcpass); + else if (strcmp (super->path_element->user, "anonymous") == 0 + || strcmp (super->path_element->user, "ftp") == 0) + { + if (ftpfs_anonymous_passwd == NULL) /* default anonymous password */ + ftpfs_init_passwd (); + op = g_strdup (ftpfs_anonymous_passwd); + anon = TRUE; + } + else + { /* ask user */ + char *p; + + p = g_strdup_printf (_("FTP: Password required for %s"), super->path_element->user); + op = vfs_get_password (p); + g_free (p); + if (op == NULL) + ERRNOR (EPERM, 0); + super->path_element->password = g_strdup (op); + } + + if (!anon || me->logfile != NULL) + pass = op; + else + { + pass = g_strconcat ("-", op, (char *) NULL); + wipe_password (op); + } + + /* Proxy server accepts: username@host-we-want-to-connect */ + if (ftp_super->proxy != NULL) + name = + g_strconcat (super->path_element->user, "@", + super->path_element->host[0] == + '!' ? super->path_element->host + 1 : super->path_element->host, + (char *) NULL); + else + name = g_strdup (super->path_element->user); + + if (ftpfs_get_reply (me, ftp_super->sock, reply_string, sizeof (reply_string) - 1) == COMPLETE) + { + char *reply_up; + + reply_up = g_ascii_strup (reply_string, -1); + ftp_super->remote_is_amiga = strstr (reply_up, "AMIGA") != NULL; + if (strstr (reply_up, " SPFTP/1.0.0000 SERVER ") != NULL) /* handles `LIST -la` in a weird way */ + ftp_super->strict = RFC_STRICT; + g_free (reply_up); + + if (me->logfile != NULL) + { + fprintf (me->logfile, "MC -- remote_is_amiga = %s\n", + ftp_super->remote_is_amiga ? "yes" : "no"); + fflush (me->logfile); + } + + vfs_print_message ("%s", _("ftpfs: sending login name")); + + switch (ftpfs_command (me, super, WAIT_REPLY, "USER %s", name)) + { + case CONTINUE: + vfs_print_message ("%s", _("ftpfs: sending user password")); + code = ftpfs_command (me, super, WAIT_REPLY, "PASS %s", pass); + if (code == CONTINUE) + { + char *p; + + p = g_strdup_printf (_("FTP: Account required for user %s"), + super->path_element->user); + op = input_dialog (p, _("Account:"), MC_HISTORY_FTPFS_ACCOUNT, "", + INPUT_COMPLETE_USERNAMES); + g_free (p); + if (op == NULL) + ERRNOR (EPERM, 0); + vfs_print_message ("%s", _("ftpfs: sending user account")); + code = ftpfs_command (me, super, WAIT_REPLY, "ACCT %s", op); + g_free (op); + } + if (code != COMPLETE) + break; + + MC_FALLTHROUGH; + + case COMPLETE: + vfs_print_message ("%s", _("ftpfs: logged in")); + wipe_password (pass); + g_free (name); + return TRUE; + + default: + ftp_super->failed_on_login = TRUE; + wipe_password (super->path_element->password); + super->path_element->password = NULL; + goto login_fail; + } + } + + message (D_ERROR, MSG_ERROR, _("ftpfs: Login incorrect for user %s "), + super->path_element->user); + + login_fail: + wipe_password (pass); + g_free (name); + ERRNOR (EPERM, FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +ftpfs_load_no_proxy_list (void) +{ + /* FixMe: shouldn't be hardcoded!!! */ + char *mc_file; + + mc_file = g_build_filename (mc_global.sysconfig_dir, "mc.no_proxy", (char *) NULL); + if (exist_file (mc_file)) + { + FILE *npf; + + npf = fopen (mc_file, "r"); + if (npf != NULL) + { + char s[BUF_LARGE]; /* provide for BUF_LARGE characters */ + + while (fgets (s, sizeof (s), npf) != NULL) + { + char *p; + + p = strchr (s, '\n'); + if (p == NULL) /* skip bogus entries */ + { + int c; + + while ((c = fgetc (npf)) != EOF && c != '\n') + ; + } + else if (p != s) + { + *p = '\0'; + no_proxy = g_slist_prepend (no_proxy, g_strdup (s)); + } + } + + fclose (npf); + } + } + + g_free (mc_file); +} + +/* --------------------------------------------------------------------------------------------- */ +/* Return TRUE if FTP proxy should be used for this host, FALSE otherwise */ + +static gboolean +ftpfs_check_proxy (const char *host) +{ + + if (ftpfs_proxy_host == NULL || *ftpfs_proxy_host == '\0' || host == NULL || *host == '\0') + return FALSE; /* sanity check */ + + if (*host == '!') + return TRUE; + + if (!ftpfs_always_use_proxy) + return FALSE; + + if (strchr (host, '.') == NULL) + return FALSE; + + if (no_proxy == NULL) + { + GSList *npe; + + ftpfs_load_no_proxy_list (); + + for (npe = no_proxy; npe != NULL; npe = g_slist_next (npe)) + { + const char *domain = (const char *) npe->data; + + if (domain[0] == '.') + { + size_t ld, lh; + + ld = strlen (domain); + lh = strlen (host); + + while (ld != 0 && lh != 0 && host[lh - 1] == domain[ld - 1]) + { + ld--; + lh--; + } + + if (ld == 0) + return FALSE; + } + else if (g_ascii_strcasecmp (host, domain) == 0) + return FALSE; + } + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +ftpfs_get_proxy_host_and_port (const char *proxy, char **host, int *port) +{ + vfs_path_element_t *path_element; + + path_element = vfs_url_split (proxy, FTP_COMMAND_PORT, URL_USE_ANONYMOUS); + *host = path_element->host; + path_element->host = NULL; + *port = path_element->port; + vfs_path_element_free (path_element); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_open_socket (struct vfs_class *me, struct vfs_s_super *super) +{ + struct addrinfo hints, *res, *curr_res; + int my_socket = 0; + char *host = NULL; + char port[8]; + int tmp_port = 0; + int e; + + (void) me; + + if (super->path_element->host == NULL || *super->path_element->host == '\0') + { + vfs_print_message ("%s", _("ftpfs: Invalid host name.")); + me->verrno = EINVAL; + return (-1); + } + + /* Use a proxy host? */ + /* Hosts to connect to that start with a ! should use proxy */ + if (FTP_SUPER (super)->proxy != NULL) + ftpfs_get_proxy_host_and_port (ftpfs_proxy_host, &host, &tmp_port); + else + { + host = g_strdup (super->path_element->host); + tmp_port = super->path_element->port; + } + + g_snprintf (port, sizeof (port), "%hu", (unsigned short) tmp_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 (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 (host, port, &hints, &res); + } +#endif + + *port = '\0'; + + if (e != 0) + { + tty_disable_interrupt_key (); + vfs_print_message (_("ftpfs: %s"), gai_strerror (e)); + g_free (host); + me->verrno = EINVAL; + return (-1); + } + + for (curr_res = res; curr_res != NULL; curr_res = curr_res->ai_next) + { + my_socket = socket (curr_res->ai_family, curr_res->ai_socktype, curr_res->ai_protocol); + + if (my_socket < 0) + { + if (curr_res->ai_next != NULL) + continue; + + tty_disable_interrupt_key (); + vfs_print_message (_("ftpfs: %s"), unix_error_string (errno)); + g_free (host); + freeaddrinfo (res); + me->verrno = errno; + return (-1); + } + + vfs_print_message (_("ftpfs: making connection to %s"), host); + MC_PTR_FREE (host); + + if (connect (my_socket, curr_res->ai_addr, curr_res->ai_addrlen) >= 0) + break; + + me->verrno = errno; + close (my_socket); + + if (errno == EINTR && tty_got_interrupt ()) + vfs_print_message ("%s", _("ftpfs: connection interrupted by user")); + else if (res->ai_next == NULL) + vfs_print_message (_("ftpfs: connection to server failed: %s"), + unix_error_string (errno)); + else + continue; + + freeaddrinfo (res); + tty_disable_interrupt_key (); + return (-1); + } + + freeaddrinfo (res); + tty_disable_interrupt_key (); + return my_socket; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_open_archive_int (struct vfs_class *me, struct vfs_s_super *super) +{ + ftp_super_t *ftp_super = FTP_SUPER (super); + int retry_seconds = 0; + + /* We do not want to use the passive if we are using proxies */ + if (ftp_super->proxy != NULL) + ftp_super->use_passive_connection = ftpfs_use_passive_connections_over_proxy; + + do + { + ftp_super->failed_on_login = FALSE; + + ftp_super->sock = ftpfs_open_socket (me, super); + if (ftp_super->sock == -1) + return (-1); + + if (ftpfs_login_server (me, super, NULL)) + { + /* Logged in, no need to retry the connection */ + break; + } + + if (!ftp_super->failed_on_login) + return (-1); + + /* Close only the socket descriptor */ + close (ftp_super->sock); + + if (ftpfs_retry_seconds != 0) + { + int count_down; + + retry_seconds = ftpfs_retry_seconds; + tty_enable_interrupt_key (); + for (count_down = retry_seconds; count_down != 0; count_down--) + { + vfs_print_message (_("Waiting to retry... %d (Control-G to cancel)"), count_down); + sleep (1); + if (tty_got_interrupt ()) + { + /* me->verrno = E; */ + tty_disable_interrupt_key (); + return 0; + } + } + tty_disable_interrupt_key (); + } + } + while (retry_seconds != 0); + + ftp_super->current_dir = ftpfs_get_current_directory (me, super); + if (ftp_super->current_dir == NULL) + ftp_super->current_dir = g_strdup (PATH_SEP_STR); + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_open_archive (struct vfs_s_super *super, + const vfs_path_t * vpath, const vfs_path_element_t * vpath_element) +{ + (void) vpath; + + super->path_element = ftpfs_correct_url_parameters (vpath_element); + if (ftpfs_check_proxy (super->path_element->host)) + FTP_SUPER (super)->proxy = ftpfs_proxy_host; + super->root = + vfs_s_new_inode (vpath_element->class, super, ftpfs_default_stat (vpath_element->class)); + + return ftpfs_open_archive_int (vpath_element->class, super); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_archive_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *super, + const vfs_path_t * vpath, void *cookie) +{ + vfs_path_element_t *path_element; + int result; + + (void) vpath; + (void) cookie; + + path_element = ftpfs_correct_url_parameters (vpath_element); + + result = ((strcmp (path_element->host, super->path_element->host) == 0) + && (strcmp (path_element->user, super->path_element->user) == 0) + && (path_element->port == super->path_element->port)) ? 1 : 0; + + vfs_path_element_free (path_element); + return result; +} + +/* --------------------------------------------------------------------------------------------- */ +/* The returned directory should always contain a trailing slash */ + +static char * +ftpfs_get_current_directory (struct vfs_class *me, struct vfs_s_super *super) +{ + char buf[MC_MAXPATHLEN + 1]; + + if (ftpfs_command (me, super, NONE, "%s", "PWD") == COMPLETE && + ftpfs_get_reply (me, FTP_SUPER (super)->sock, buf, sizeof (buf)) == COMPLETE) + { + char *bufp = NULL; + char *bufq; + + for (bufq = buf; *bufq != '\0'; bufq++) + if (*bufq == '"') + { + if (bufp == NULL) + bufp = bufq + 1; + else + { + *bufq = '\0'; + + if (*bufp != '\0') + { + if (!IS_PATH_SEP (bufq[-1])) + { + *bufq++ = PATH_SEP; + *bufq = '\0'; + } + + if (IS_PATH_SEP (*bufp)) + return g_strdup (bufp); + + /* If the remote server is an Amiga a leading slash + might be missing. MC needs it because it is used + as separator between hostname and path internally. */ + return g_strconcat (PATH_SEP_STR, bufp, (char *) NULL); + } + + break; + } + } + } + + me->verrno = EIO; + return NULL; +} + +/* --------------------------------------------------------------------------------------------- */ +/* Setup Passive PASV FTP connection */ + +static gboolean +ftpfs_setup_passive_pasv (struct vfs_class *me, struct vfs_s_super *super, + int my_socket, struct sockaddr_storage *sa, socklen_t * salen) +{ + char *c; + char n[6]; + int xa, xb, xc, xd, xe, xf; + + if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "%s", "PASV") != COMPLETE) + return FALSE; + + /* Parse remote parameters */ + for (c = reply_str + 4; *c != '\0' && !isdigit ((unsigned char) *c); c++) + ; + + if (*c == '\0' || !isdigit ((unsigned char) *c)) + return FALSE; + + /* cppcheck-suppress invalidscanf */ + if (sscanf (c, "%d,%d,%d,%d,%d,%d", &xa, &xb, &xc, &xd, &xe, &xf) != 6) + return FALSE; + + n[0] = (unsigned char) xa; + n[1] = (unsigned char) xb; + n[2] = (unsigned char) xc; + n[3] = (unsigned char) xd; + n[4] = (unsigned char) xe; + n[5] = (unsigned char) xf; + + memcpy (&(((struct sockaddr_in *) sa)->sin_addr.s_addr), (void *) n, 4); + memcpy (&(((struct sockaddr_in *) sa)->sin_port), (void *) &n[4], 2); + + return (connect (my_socket, (struct sockaddr *) sa, *salen) >= 0); +} + +/* --------------------------------------------------------------------------------------------- */ +/* Setup Passive EPSV FTP connection */ + +static gboolean +ftpfs_setup_passive_epsv (struct vfs_class *me, struct vfs_s_super *super, + int my_socket, struct sockaddr_storage *sa, socklen_t * salen) +{ + char *c; + int port; + + if (ftpfs_command (me, super, WAIT_REPLY | WANT_STRING, "%s", "EPSV") != COMPLETE) + return FALSE; + + /* (|||<port>|) */ + c = strchr (reply_str, '|'); + if (c == NULL || strlen (c) <= 3) + return FALSE; + + c += 3; + port = atoi (c); + if (port < 0 || port > 65535) + return FALSE; + + port = htons (port); + + switch (sa->ss_family) + { + case AF_INET: + ((struct sockaddr_in *) sa)->sin_port = port; + break; + case AF_INET6: + ((struct sockaddr_in6 *) sa)->sin6_port = port; + break; + default: + break; + } + + return (connect (my_socket, (struct sockaddr *) sa, *salen) >= 0); +} + +/* --------------------------------------------------------------------------------------------- */ +/* Setup Passive ftp connection, we use it for source routed connections */ + +static gboolean +ftpfs_setup_passive (struct vfs_class *me, struct vfs_s_super *super, + int my_socket, struct sockaddr_storage *sa, socklen_t * salen) +{ + /* It's IPV4, so try PASV first, some servers and ALGs get confused by EPSV */ + if (sa->ss_family == AF_INET) + { + if (!ftpfs_setup_passive_pasv (me, super, my_socket, sa, salen)) + /* An IPV4 FTP server might support EPSV, so if PASV fails we can try EPSV anyway */ + if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen)) + return FALSE; + } + /* It's IPV6, so EPSV is our only hope */ + else if (!ftpfs_setup_passive_epsv (me, super, my_socket, sa, salen)) + return FALSE; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* Setup Active PORT or EPRT FTP connection */ + +static int +ftpfs_setup_active (struct vfs_class *me, struct vfs_s_super *super, + struct sockaddr_storage data_addr, socklen_t data_addrlen) +{ + unsigned short int port; + char *addr; + unsigned int af; + int res; + + switch (data_addr.ss_family) + { + case AF_INET: + af = FTP_INET; + port = ((struct sockaddr_in *) &data_addr)->sin_port; + break; + case AF_INET6: + af = FTP_INET6; + port = ((struct sockaddr_in6 *) &data_addr)->sin6_port; + break; + default: + /* Not implemented */ + return 0; + } + + addr = g_try_malloc (NI_MAXHOST); + if (addr == NULL) + ERRNOR (ENOMEM, -1); + + res = + getnameinfo ((struct sockaddr *) &data_addr, data_addrlen, addr, NI_MAXHOST, NULL, 0, + NI_NUMERICHOST); + if (res != 0) + { + const char *err_str; + + g_free (addr); + + if (res == EAI_SYSTEM) + { + me->verrno = errno; + err_str = unix_error_string (me->verrno); + } + else + { + me->verrno = EIO; + err_str = gai_strerror (res); + } + + vfs_print_message (_("ftpfs: could not make address-to-name translation: %s"), err_str); + + return (-1); + } + + /* If we are talking to an IPV4 server, try PORT, and, only if it fails, go for EPRT */ + if (af == FTP_INET) + { + unsigned char *a = (unsigned char *) &((struct sockaddr_in *) &data_addr)->sin_addr; + unsigned char *p = (unsigned char *) &port; + + if (ftpfs_command (me, super, WAIT_REPLY, + "PORT %u,%u,%u,%u,%u,%u", a[0], a[1], a[2], a[3], + p[0], p[1]) == COMPLETE) + { + g_free (addr); + return 1; + } + } + + /* + * Converts network MSB first order to host byte order (LSB + * first on i386). If we do it earlier, we will run into an + * endianness issue, because the server actually expects to see + * "PORT A,D,D,R,MSB,LSB" in the PORT command. + */ + port = ntohs (port); + + /* We are talking to an IPV6 server or PORT failed, so we can try EPRT anyway */ + res = + (ftpfs_command (me, super, WAIT_REPLY, "EPRT |%u|%s|%hu|", af, addr, port) == + COMPLETE) ? 1 : 0; + g_free (addr); + return res; +} + +/* --------------------------------------------------------------------------------------------- */ +/* Initialize a socket for FTP DATA connection */ + +static int +ftpfs_init_data_socket (struct vfs_class *me, struct vfs_s_super *super, + struct sockaddr_storage *data_addr, socklen_t * data_addrlen) +{ + const unsigned int attempts = 10; + unsigned int i; + ftp_super_t *ftp_super = FTP_SUPER (super); + int result; + + for (i = 0; i < attempts; i++) + { + memset (data_addr, 0, sizeof (*data_addr)); + *data_addrlen = sizeof (*data_addr); + + if (ftp_super->use_passive_connection) + { + result = getpeername (ftp_super->sock, (struct sockaddr *) data_addr, data_addrlen); + if (result == 0) + break; + + me->verrno = errno; + + if (me->verrno == ENOTCONN) + { + vfs_print_message (_("ftpfs: try reconnect to server, attempt %u"), i); + if (ftpfs_reconnect (me, super)) + continue; /* get name of new socket */ + } + else + { + /* error -- stop loop */ + vfs_print_message (_("ftpfs: could not get socket name: %s"), + unix_error_string (me->verrno)); + } + } + else + { + result = getsockname (ftp_super->sock, (struct sockaddr *) data_addr, data_addrlen); + if (result == 0) + break; + + me->verrno = errno; + + vfs_print_message (_("ftpfs: try reconnect to server, attempt %u"), i); + if (ftpfs_reconnect (me, super)) + continue; /* get name of new socket */ + + /* error -- stop loop */ + vfs_print_message ("%s", _("ftpfs: could not reconnect to server")); + } + + i = attempts; + } + + if (i >= attempts) + return (-1); + + switch (data_addr->ss_family) + { + case AF_INET: + ((struct sockaddr_in *) data_addr)->sin_port = 0; + break; + case AF_INET6: + ((struct sockaddr_in6 *) data_addr)->sin6_port = 0; + break; + default: + vfs_print_message ("%s", _("ftpfs: invalid address family")); + ERRNOR (EINVAL, -1); + } + + result = socket (data_addr->ss_family, SOCK_STREAM, IPPROTO_TCP); + if (result < 0) + { + me->verrno = errno; + vfs_print_message (_("ftpfs: could not create socket: %s"), unix_error_string (me->verrno)); + result = -1; + } + + return result; +} + +/* --------------------------------------------------------------------------------------------- */ +/* Initialize FTP DATA connection */ + +static int +ftpfs_initconn (struct vfs_class *me, struct vfs_s_super *super) +{ + ftp_super_t *ftp_super = FTP_SUPER (super); + struct sockaddr_storage data_addr; + socklen_t data_addrlen; + + /* + * Don't factor socket initialization out of these conditionals, + * because ftpfs_init_data_socket initializes it in different way + * depending on use_passive_connection flag. + */ + + /* Try to establish a passive connection first (if requested) */ + if (ftp_super->use_passive_connection) + { + int data_sock; + + data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen); + if (data_sock < 0) + return (-1); + + if (ftpfs_setup_passive (me, super, data_sock, &data_addr, &data_addrlen)) + return data_sock; + + vfs_print_message ("%s", _("ftpfs: could not setup passive mode")); + ftp_super->use_passive_connection = FALSE; + + close (data_sock); + } + + /* If passive setup is disabled or failed, fallback to active connections */ + if (!ftp_super->use_passive_connection) + { + int data_sock; + + data_sock = ftpfs_init_data_socket (me, super, &data_addr, &data_addrlen); + if (data_sock < 0) + return (-1); + + if ((bind (data_sock, (struct sockaddr *) &data_addr, data_addrlen) != 0) || + (getsockname (data_sock, (struct sockaddr *) &data_addr, &data_addrlen) != 0) || + (listen (data_sock, 1) != 0)) + { + close (data_sock); + ERRNOR (errno, -1); + } + + if (ftpfs_setup_active (me, super, data_addr, data_addrlen) != 0) + return data_sock; + + close (data_sock); + } + + /* Restore the initial value of use_passive_connection (for subsequent retries) */ + ftp_super->use_passive_connection = + ftp_super->proxy != + NULL ? ftpfs_use_passive_connections_over_proxy : ftpfs_use_passive_connections; + + me->verrno = EIO; + return (-1); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_open_data_connection (struct vfs_class *me, struct vfs_s_super *super, const char *cmd, + const char *remote, int isbinary, int reget) +{ + ftp_super_t *ftp_super = FTP_SUPER (super); + int s, j, data; + + /* FTP doesn't allow to open more than one file at a time */ + if (ftp_super->ctl_connection_busy) + return (-1); + + s = ftpfs_initconn (me, super); + if (s == -1) + return (-1); + + if (ftpfs_changetype (me, super, isbinary) == -1) + { + close (s); + return (-1); + } + + if (reget > 0) + { + j = ftpfs_command (me, super, WAIT_REPLY, "REST %d", reget); + if (j != CONTINUE) + { + close (s); + ERRNOR (EIO, -1); + } + } + + if (remote == NULL) + j = ftpfs_command (me, super, WAIT_REPLY, "%s", cmd); + else + { + char *remote_path; + + remote_path = ftpfs_translate_path (me, super, remote); + j = ftpfs_command (me, super, WAIT_REPLY, "%s /%s", cmd, + /* WarFtpD can't STORE //filename */ + IS_PATH_SEP (*remote_path) ? remote_path + 1 : remote_path); + g_free (remote_path); + } + + if (j != PRELIM) + { + close (s); + ERRNOR (EPERM, -1); + } + + if (ftp_super->use_passive_connection) + data = s; + else + { + struct sockaddr_storage from; + socklen_t fromlen = sizeof (from); + + tty_enable_interrupt_key (); + data = accept (s, (struct sockaddr *) &from, &fromlen); + if (data < 0) + me->verrno = errno; + tty_disable_interrupt_key (); + close (s); + if (data < 0) + return (-1); + } + + ftp_super->ctl_connection_busy = TRUE; + return data; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +ftpfs_linear_abort (struct vfs_class *me, vfs_file_handler_t * fh) +{ + struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh); + ftp_super_t *ftp_super = FTP_SUPER (super); + static unsigned char const ipbuf[3] = { IAC, IP, IAC }; + fd_set mask; + int dsock = FH_SOCK; + + FH_SOCK = -1; + ftp_super->ctl_connection_busy = FALSE; + + vfs_print_message ("%s", _("ftpfs: aborting transfer.")); + + if (send (ftp_super->sock, ipbuf, sizeof (ipbuf), MSG_OOB) != sizeof (ipbuf)) + { + vfs_print_message (_("ftpfs: abort error: %s"), unix_error_string (errno)); + if (dsock != -1) + close (dsock); + return; + } + + if (ftpfs_command (me, super, NONE, "%cABOR", DM) != COMPLETE) + { + vfs_print_message ("%s", _("ftpfs: abort failed")); + if (dsock != -1) + close (dsock); + return; + } + + if (dsock != -1) + { + FD_ZERO (&mask); + FD_SET (dsock, &mask); + + if (select (dsock + 1, &mask, NULL, NULL, NULL) > 0) + { + gint64 start_tim; + char buf[BUF_8K]; + + start_tim = g_get_monotonic_time (); + + /* flush the remaining data */ + while (read (dsock, buf, sizeof (buf)) > 0) + { + gint64 tim; + + tim = g_get_monotonic_time (); + + if (tim > start_tim + ABORT_TIMEOUT) + { + /* server keeps sending, drop the connection and ftpfs_reconnect */ + close (dsock); + ftpfs_reconnect (me, super); + return; + } + } + } + close (dsock); + } + + if ((ftpfs_get_reply (me, ftp_super->sock, NULL, 0) == TRANSIENT) && (code == 426)) + ftpfs_get_reply (me, ftp_super->sock, NULL, 0); +} + +/* --------------------------------------------------------------------------------------------- */ + +#if 0 +static void +resolve_symlink_without_ls_options (struct vfs_class *me, struct vfs_s_super *super, + struct vfs_s_inode *dir) +{ + struct linklist *flist; + struct direntry *fe, *fel; + char tmp[MC_MAXPATHLEN]; + + dir->symlink_status = FTPFS_RESOLVING_SYMLINKS; + for (flist = dir->file_list->next; flist != dir->file_list; flist = flist->next) + { + /* flist->data->l_stat is already initialized with 0 */ + fel = flist->data; + if (S_ISLNK (fel->s.st_mode) && fel->linkname != NULL) + { + int depth; + + if (IS_PATH_SEP (fel->linkname[0])) + { + if (strlen (fel->linkname) >= MC_MAXPATHLEN) + continue; + strcpy (tmp, fel->linkname); + } + else + { + if ((strlen (dir->remote_path) + strlen (fel->linkname)) >= MC_MAXPATHLEN) + continue; + strcpy (tmp, dir->remote_path); + if (tmp[1] != '\0') + strcat (tmp, PATH_SEP_STR); + strcat (tmp + 1, fel->linkname); + } + + for (depth = 0; depth < 100; depth++) + { /* depth protects against recursive symbolic links */ + canonicalize_pathname (tmp); + fe = _get_file_entry_t (bucket, tmp, 0, 0); + if (fe != NULL) + { + if (S_ISLNK (fe->s.st_mode) && fe->l_stat == 0) + { + /* Symlink points to link which isn't resolved, yet. */ + if (IS_PATH_SEP (fe->linkname[0])) + { + if (strlen (fe->linkname) >= MC_MAXPATHLEN) + break; + strcpy (tmp, fe->linkname); + } + else + { + /* at this point tmp looks always like this + /directory/filename, i.e. no need to check + strrchr's return value */ + *(strrchr (tmp, PATH_SEP) + 1) = '\0'; /* dirname */ + if ((strlen (tmp) + strlen (fe->linkname)) >= MC_MAXPATHLEN) + break; + strcat (tmp, fe->linkname); + } + continue; + } + else + { + fel->l_stat = g_new (struct stat, 1); + if (S_ISLNK (fe->s.st_mode)) + *fel->l_stat = *fe->l_stat; + else + *fel->l_stat = fe->s; + (*fel->l_stat).st_ino = bucket->__inode_counter++; + } + } + break; + } + } + } + + dir->symlink_status = FTPFS_RESOLVED_SYMLINKS; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +resolve_symlink_with_ls_options (struct vfs_class *me, struct vfs_s_super *super, + struct vfs_s_inode *dir) +{ + char buffer[2048] = "", *filename; + int sock; + FILE *fp; + struct stat s; + struct linklist *flist; + struct direntry *fe; + int switch_method = 0; + + dir->symlink_status = FTPFS_RESOLVED_SYMLINKS; + if (strchr (dir->remote_path, ' ') == NULL) + sock = ftpfs_open_data_connection (bucket, "LIST -lLa", dir->remote_path, TYPE_ASCII, 0); + else + { + if (ftpfs_chdir_internal (bucket, dir->remote_path) != COMPLETE) + { + vfs_print_message ("%s", _("ftpfs: CWD failed.")); + return; + } + + sock = ftpfs_open_data_connection (bucket, "LIST -lLa", ".", TYPE_ASCII, 0); + } + + if (sock == -1) + { + vfs_print_message ("%s", _("ftpfs: couldn't resolve symlink")); + return; + } + + fp = fdopen (sock, "r"); + if (fp == NULL) + { + close (sock); + vfs_print_message ("%s", _("ftpfs: couldn't resolve symlink")); + return; + } + tty_enable_interrupt_key (); + flist = dir->file_list->next; + + while (TRUE) + { + do + { + if (flist == dir->file_list) + goto done; + + fe = flist->data; + flist = flist->next; + } + while (!S_ISLNK (fe->s.st_mode)); + + while (TRUE) + { + if (fgets (buffer, sizeof (buffer), fp) == NULL) + goto done; + + if (me->logfile != NULL) + { + fputs (buffer, me->logfile); + fflush (me->logfile); + } + + vfs_die ("This code should be commented out\n"); + + if (vfs_parse_ls_lga (buffer, &s, &filename, NULL)) + { + int r; + + r = strcmp (fe->name, filename); + g_free (filename); + if (r == 0) + { + if (S_ISLNK (s.st_mode)) + { + /* This server doesn't understand LIST -lLa */ + switch_method = 1; + goto done; + } + + fe->l_stat = g_try_new (struct stat, 1); + if (fe->l_stat == NULL) + goto done; + + *fe->l_stat = s; + (*fe->l_stat).st_ino = bucket->__inode_counter++; + break; + } + + if (r < 0) + break; + } + } + } + + done: + while (fgets (buffer, sizeof (buffer), fp) != NULL) + ; + tty_disable_interrupt_key (); + fclose (fp); + ftpfs_get_reply (me, FTP_SUPER (super)->sock, NULL, 0); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +resolve_symlink (struct vfs_class *me, struct vfs_s_super *super, struct vfs_s_inode *dir) +{ + vfs_print_message ("%s", _("Resolving symlink...")); + + if (FTP_SUPER (super)->strict_rfc959_list_cmd) + resolve_symlink_without_ls_options (me, super, dir); + else + resolve_symlink_with_ls_options (me, super, dir); +} +#endif + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, const char *remote_path) +{ + struct vfs_s_super *super = dir->super; + ftp_super_t *ftp_super = FTP_SUPER (super); + int sock; + char lc_buffer[BUF_8K]; + int res; + gboolean cd_first; + GSList *dirlist = NULL; + GSList *entlist; + GSList *iter; + int err_count = 0; + + cd_first = ftpfs_first_cd_then_ls || (ftp_super->strict == RFC_STRICT) + || (strchr (remote_path, ' ') != NULL); + + again: + vfs_print_message (_("ftpfs: Reading FTP directory %s... %s%s"), + remote_path, + ftp_super->strict == + RFC_STRICT ? _("(strict rfc959)") : "", cd_first ? _("(chdir first)") : ""); + + if (cd_first && ftpfs_chdir_internal (me, super, remote_path) != COMPLETE) + { + me->verrno = ENOENT; + vfs_print_message ("%s", _("ftpfs: CWD failed.")); + return (-1); + } + + dir->timestamp = g_get_monotonic_time () + ftpfs_directory_timeout * G_USEC_PER_SEC; + + if (ftp_super->strict == RFC_STRICT) + sock = ftpfs_open_data_connection (me, super, "LIST", 0, TYPE_ASCII, 0); + else if (cd_first) + /* Dirty hack to avoid autoprepending / to . */ + /* Wu-ftpd produces strange output for '/' if 'LIST -la .' used */ + sock = ftpfs_open_data_connection (me, super, "LIST -la", 0, TYPE_ASCII, 0); + else + { + char *path; + + /* Trailing "/." is necessary if remote_path is a symlink */ + path = g_strconcat (remote_path, PATH_SEP_STR ".", (char *) NULL); + sock = ftpfs_open_data_connection (me, super, "LIST -la", path, TYPE_ASCII, 0); + g_free (path); + } + + if (sock == -1) + { + fallback: + if (ftp_super->strict == RFC_AUTODETECT) + { + /* It's our first attempt to get a directory listing from this + server (UNIX style LIST command) */ + ftp_super->strict = RFC_STRICT; + /* I hate goto, but recursive call needs another 8K on stack */ + /* return ftpfs_dir_load (me, dir, remote_path); */ + cd_first = TRUE; + goto again; + } + + vfs_print_message ("%s", _("ftpfs: failed; nowhere to fallback to")); + ERRNOR (EACCES, -1); + } + + /* read full directory list, then parse it */ + while ((res = vfs_s_get_line_interruptible (me, lc_buffer, sizeof (lc_buffer), sock)) != 0) + { + if (res == EINTR) + { + me->verrno = ECONNRESET; + close (sock); + ftp_super->ctl_connection_busy = FALSE; + ftpfs_get_reply (me, ftp_super->sock, NULL, 0); + g_slist_free_full (dirlist, g_free); + vfs_print_message (_("%s: failure"), me->name); + return (-1); + } + + if (me->logfile != NULL) + { + fputs (lc_buffer, me->logfile); + fputs ("\n", me->logfile); + fflush (me->logfile); + } + + dirlist = g_slist_prepend (dirlist, g_strdup (lc_buffer)); + } + + close (sock); + ftp_super->ctl_connection_busy = FALSE; + me->verrno = E_REMOTE; + if ((ftpfs_get_reply (me, ftp_super->sock, NULL, 0) != COMPLETE)) + { + g_slist_free_full (dirlist, g_free); + goto fallback; + } + + if (dirlist == NULL && !cd_first) + { + /* The LIST command may produce an empty output. In such scenario + it is not clear whether this is caused by 'remote_path' being + a non-existent path or for some other reason (listing empty + directory without the -a option, non-readable directory, etc.). + + Since 'dir_load' is a crucial method, when it comes to determine + whether a given path is a _directory_, the code must try its best + to determine the type of 'remote_path'. The only reliable way to + achieve this is through issuing a CWD command. */ + + cd_first = TRUE; + goto again; + } + + /* parse server's reply */ + dirlist = g_slist_reverse (dirlist); /* restore order */ + entlist = ftpfs_parse_long_list (me, dir, dirlist, &err_count); + g_slist_free_full (dirlist, g_free); + + for (iter = entlist; iter != NULL; iter = g_slist_next (iter)) + vfs_s_insert_entry (me, dir, VFS_ENTRY (iter->data)); + + g_slist_free (entlist); + + if (ftp_super->strict == RFC_AUTODETECT) + ftp_super->strict = RFC_DARING; + + vfs_print_message (_("%s: done."), me->name); + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_file_store (struct vfs_class *me, vfs_file_handler_t * fh, char *name, char *localname) +{ + struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh); + ftp_super_t *ftp_super = FTP_SUPER (super); + ftp_file_handler_t *ftp = FTP_FILE_HANDLER (fh); + + int h, sock; + off_t n_stored = 0; +#ifdef HAVE_STRUCT_LINGER_L_LINGER + struct linger li; +#else + int flag_one = 1; +#endif + char lc_buffer[BUF_8K]; + struct stat s; + char *w_buf; + + h = open (localname, O_RDONLY); + if (h == -1) + ERRNOR (EIO, -1); + + if (fstat (h, &s) == -1) + { + me->verrno = errno; + close (h); + return (-1); + } + + sock = + ftpfs_open_data_connection (me, super, ftp->append ? "APPE" : "STOR", name, TYPE_BINARY, 0); + if (sock < 0) + { + close (h); + return (-1); + } +#ifdef HAVE_STRUCT_LINGER_L_LINGER + li.l_onoff = 1; + li.l_linger = 120; + setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof (li)); +#else + setsockopt (sock, SOL_SOCKET, SO_LINGER, &flag_one, sizeof (flag_one)); +#endif + + tty_enable_interrupt_key (); + while (TRUE) + { + ssize_t n_read, n_written; + + while ((n_read = read (h, lc_buffer, sizeof (lc_buffer))) == -1) + { + if (errno != EINTR) + { + me->verrno = errno; + goto error_return; + } + if (tty_got_interrupt ()) + { + me->verrno = EINTR; + goto error_return; + } + } + if (n_read == 0) + break; + + n_stored += n_read; + w_buf = lc_buffer; + + while ((n_written = write (sock, w_buf, n_read)) != n_read) + { + if (n_written == -1) + { + if (errno == EINTR && !tty_got_interrupt ()) + continue; + + me->verrno = errno; + goto error_return; + } + + w_buf += n_written; + n_read -= n_written; + } + + vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX, + _("ftpfs: storing file"), (uintmax_t) n_stored, (uintmax_t) s.st_size); + } + tty_disable_interrupt_key (); + + close (sock); + ftp_super->ctl_connection_busy = FALSE; + close (h); + + if (ftpfs_get_reply (me, ftp_super->sock, NULL, 0) != COMPLETE) + ERRNOR (EIO, -1); + return 0; + + error_return: + tty_disable_interrupt_key (); + close (sock); + ftp_super->ctl_connection_busy = FALSE; + close (h); + + ftpfs_get_reply (me, ftp_super->sock, NULL, 0); + return (-1); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_linear_start (struct vfs_class *me, vfs_file_handler_t * fh, off_t offset) +{ + char *name; + + name = vfs_s_fullpath (me, fh->ino); + if (name == NULL) + return 0; + + FH_SOCK = + ftpfs_open_data_connection (me, VFS_FILE_HANDLER_SUPER (fh), "RETR", name, TYPE_BINARY, + offset); + g_free (name); + if (FH_SOCK == -1) + ERRNOR (EACCES, 0); + + fh->linear = LS_LINEAR_OPEN; + FTP_FILE_HANDLER (fh)->append = FALSE; + return 1; +} + +/* --------------------------------------------------------------------------------------------- */ + +static ssize_t +ftpfs_linear_read (struct vfs_class *me, vfs_file_handler_t * fh, void *buf, size_t len) +{ + ssize_t n; + struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh); + + while ((n = read (FH_SOCK, buf, len)) < 0) + { + if ((errno == EINTR) && !tty_got_interrupt ()) + continue; + break; + } + + if (n < 0) + ftpfs_linear_abort (me, fh); + else if (n == 0) + { + FTP_SUPER (super)->ctl_connection_busy = FALSE; + close (FH_SOCK); + FH_SOCK = -1; + if ((ftpfs_get_reply (me, FTP_SUPER (super)->sock, NULL, 0) != COMPLETE)) + ERRNOR (E_REMOTE, -1); + return 0; + } + + ERRNOR (errno, n); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +ftpfs_linear_close (struct vfs_class *me, vfs_file_handler_t * fh) +{ + if (FH_SOCK != -1) + ftpfs_linear_abort (me, fh); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_ctl (void *fh, int ctlop, void *arg) +{ + (void) arg; + + switch (ctlop) + { + case VFS_CTL_IS_NOTREADY: + { + vfs_file_handler_t *file = VFS_FILE_HANDLER (fh); + int v; + + if (file->linear == LS_NOT_LINEAR) + vfs_die ("You may not do this"); + if (file->linear == LS_LINEAR_CLOSED || file->linear == LS_LINEAR_PREOPEN) + return 0; + + v = vfs_s_select_on_two (FH_SOCK, 0); + return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0; + } + default: + return 0; + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_send_command (const vfs_path_t * vpath, const char *cmd, int flags) +{ + const char *rpath; + char *p; + struct vfs_s_super *super; + int r; + struct vfs_class *me; + gboolean flush_directory_cache = (flags & OPT_FLUSH) != 0; + + me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); + + rpath = vfs_s_get_path (vpath, &super, 0); + if (rpath == NULL) + return (-1); + + p = ftpfs_translate_path (me, super, rpath); + r = ftpfs_command (me, super, WAIT_REPLY, cmd, p); + g_free (p); + vfs_stamp_create (vfs_ftpfs_ops, super); + if ((flags & OPT_IGNORE_ERROR) != 0) + r = COMPLETE; + if (r != COMPLETE) + { + me->verrno = EPERM; + return (-1); + } + if (flush_directory_cache) + vfs_s_invalidate (me, super); + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_stat (const vfs_path_t * vpath, struct stat *buf) +{ + int ret; + + ret = vfs_s_stat (vpath, buf); + ftpfs_set_blksize (buf); + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_lstat (const vfs_path_t * vpath, struct stat *buf) +{ + int ret; + + ret = vfs_s_lstat (vpath, buf); + ftpfs_set_blksize (buf); + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_fstat (void *vfs_info, struct stat *buf) +{ + int ret; + + ret = vfs_s_fstat (vfs_info, buf); + ftpfs_set_blksize (buf); + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_chmod (const vfs_path_t * vpath, mode_t mode) +{ + char buf[BUF_SMALL]; + int ret; + + g_snprintf (buf, sizeof (buf), "SITE CHMOD %4.4o /%%s", (unsigned int) (mode & 07777)); + ret = ftpfs_send_command (vpath, buf, OPT_FLUSH); + return ftpfs_ignore_chattr_errors ? 0 : ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group) +{ +#if 0 + (void) vpath; + (void) owner; + (void) group; + + me->verrno = EPERM; + return (-1); +#else + /* Everyone knows it is not possible to chown remotely, so why bother them. + If someone's root, then copy/move will always try to chown it... */ + (void) vpath; + (void) owner; + (void) group; + return 0; +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_unlink (const vfs_path_t * vpath) +{ + return ftpfs_send_command (vpath, "DELE /%s", OPT_FLUSH); +} + +/* --------------------------------------------------------------------------------------------- */ + +/* Return TRUE if path is the same directory as the one we are in now */ +static gboolean +ftpfs_is_same_dir (struct vfs_class *me, struct vfs_s_super *super, const char *path) +{ + (void) me; + + return (FTP_SUPER (super)->current_dir != NULL + && strcmp (path, FTP_SUPER (super)->current_dir) == 0); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_chdir_internal (struct vfs_class *me, struct vfs_s_super *super, const char *remote_path) +{ + ftp_super_t *ftp_super = FTP_SUPER (super); + int r; + char *p; + + if (!ftp_super->cwd_deferred && ftpfs_is_same_dir (me, super, remote_path)) + return COMPLETE; + + p = ftpfs_translate_path (me, super, remote_path); + r = ftpfs_command (me, super, WAIT_REPLY, "CWD /%s", p); + g_free (p); + + if (r != COMPLETE) + me->verrno = EIO; + else + { + g_free (ftp_super->current_dir); + ftp_super->current_dir = g_strdup (remote_path); + ftp_super->cwd_deferred = FALSE; + } + return r; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2) +{ + ftpfs_send_command (vpath1, "RNFR /%s", OPT_FLUSH); + return ftpfs_send_command (vpath2, "RNTO /%s", OPT_FLUSH); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_mkdir (const vfs_path_t * vpath, mode_t mode) +{ + (void) mode; /* FIXME: should be used */ + + return ftpfs_send_command (vpath, "MKD /%s", OPT_FLUSH); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_rmdir (const vfs_path_t * vpath) +{ + return ftpfs_send_command (vpath, "RMD /%s", OPT_FLUSH); +} + +/* --------------------------------------------------------------------------------------------- */ + +static vfs_file_handler_t * +ftpfs_fh_new (struct vfs_s_inode *ino, gboolean changed) +{ + ftp_file_handler_t *fh; + + fh = g_new0 (ftp_file_handler_t, 1); + vfs_s_init_fh (VFS_FILE_HANDLER (fh), ino, changed); + fh->sock = -1; + + return VFS_FILE_HANDLER (fh); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode) +{ + ftp_file_handler_t *ftp = FTP_FILE_HANDLER (fh); + + (void) mode; + + /* File will be written only, so no need to retrieve it from ftp server */ + if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0)) + { +#ifdef HAVE_STRUCT_LINGER_L_LINGER + struct linger li; +#else + int li = 1; +#endif + char *name; + + /* ftpfs_linear_start() called, so data will be written + * to local temporary file and stored to ftp server + * by vfs_s_close later + */ + if (FTP_SUPER (VFS_FILE_HANDLER_SUPER (fh))->ctl_connection_busy) + { + if (fh->ino->localname == NULL) + { + vfs_path_t *vpath; + int handle; + + handle = vfs_mkstemps (&vpath, me->name, fh->ino->ent->name); + if (handle == -1) + return (-1); + + close (handle); + fh->ino->localname = vfs_path_free (vpath, FALSE); + ftp->append = (flags & O_APPEND) != 0; + } + return 0; + } + name = vfs_s_fullpath (me, fh->ino); + if (name == NULL) + return (-1); + + fh->handle = + ftpfs_open_data_connection (me, VFS_FILE_HANDLER_SUPER (fh), + (flags & O_APPEND) != 0 ? "APPE" : "STOR", name, + TYPE_BINARY, 0); + g_free (name); + + if (fh->handle < 0) + return (-1); + +#ifdef HAVE_STRUCT_LINGER_L_LINGER + li.l_onoff = 1; + li.l_linger = 120; +#endif + setsockopt (fh->handle, SOL_SOCKET, SO_LINGER, &li, sizeof (li)); + + if (fh->ino->localname != NULL) + { + unlink (fh->ino->localname); + MC_PTR_FREE (fh->ino->localname); + } + return 0; + } + + if (fh->ino->localname == NULL && vfs_s_retrieve_file (me, fh->ino) == -1) + return (-1); + + if (fh->ino->localname == NULL) + vfs_die ("retrieve_file failed to fill in localname"); + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +ftpfs_fh_close (struct vfs_class *me, vfs_file_handler_t * fh) +{ + if (fh->handle != -1 && fh->ino->localname == NULL) + { + ftp_super_t *ftp = FTP_SUPER (VFS_FILE_HANDLER_SUPER (fh)); + + close (fh->handle); + fh->handle = -1; + ftp->ctl_connection_busy = FALSE; + /* File is stored to destination already, so + * we prevent VFS_SUBCLASS (me)->ftpfs_file_store() call from vfs_s_close () + */ + fh->changed = FALSE; + if (ftpfs_get_reply (me, ftp->sock, NULL, 0) != COMPLETE) + ERRNOR (EIO, -1); + vfs_s_invalidate (me, VFS_FILE_HANDLER_SUPER (fh)); + } + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +ftpfs_done (struct vfs_class *me) +{ + (void) me; + + g_slist_free_full (no_proxy, g_free); + + g_free (ftpfs_anonymous_passwd); + g_free (ftpfs_proxy_host); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +ftpfs_fill_names (struct vfs_class *me, fill_names_f func) +{ + GList *iter; + + for (iter = VFS_SUBCLASS (me)->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); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static keyword_t +ftpfs_netrc_next (void) +{ + char *p; + keyword_t i; + static const char *const keywords[] = { "default", "machine", + "login", "password", "passwd", "account", "macdef", NULL + }; + + while (TRUE) + { + netrcp = skip_separators (netrcp); + if (*netrcp != '\n') + break; + netrcp++; + } + if (*netrcp == '\0') + return NETRC_NONE; + + p = buffer; + if (*netrcp == '"') + { + for (netrcp++; *netrcp != '"' && *netrcp != '\0'; netrcp++) + { + if (*netrcp == '\\') + netrcp++; + *p++ = *netrcp; + } + } + else + { + for (; *netrcp != '\0' && !whiteness (*netrcp) && *netrcp != ','; netrcp++) + { + if (*netrcp == '\\') + netrcp++; + *p++ = *netrcp; + } + } + + *p = '\0'; + if (*buffer == '\0') + return NETRC_NONE; + + for (i = NETRC_DEFAULT; keywords[i - 1] != NULL; i++) + if (strcmp (keywords[i - 1], buffer) == 0) + return i; + + return NETRC_UNKNOWN; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +ftpfs_netrc_bad_mode (const char *netrcname) +{ + struct stat mystat; + + if (stat (netrcname, &mystat) >= 0 && (mystat.st_mode & 077) != 0) + { + static gboolean be_angry = TRUE; + + if (be_angry) + { + message (D_ERROR, MSG_ERROR, + _("~/.netrc file has incorrect mode\nRemove password or correct mode")); + be_angry = FALSE; + } + return TRUE; + } + + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* Scan .netrc until we find matching "machine" or "default" + * domain is used for additional matching + * No search is done after "default" in compliance with "man netrc" + * Return TRUE if found, FALSE otherwise */ + +static gboolean +ftpfs_find_machine (const char *host, const char *domain) +{ + keyword_t keyword; + + if (host == NULL) + host = ""; + if (domain == NULL) + domain = ""; + + while ((keyword = ftpfs_netrc_next ()) != NETRC_NONE) + { + if (keyword == NETRC_DEFAULT) + return TRUE; + + if (keyword == NETRC_MACDEF) + { + /* Scan for an empty line, which concludes "macdef" */ + do + { + while (*netrcp != '\0' && *netrcp != '\n') + netrcp++; + if (*netrcp != '\n') + break; + netrcp++; + } + while (*netrcp != '\0' && *netrcp != '\n'); + + continue; + } + + if (keyword != NETRC_MACHINE) + continue; + + /* Take machine name */ + if (ftpfs_netrc_next () == NETRC_NONE) + break; + + if (g_ascii_strcasecmp (host, buffer) != 0) + { + const char *host_domain; + + /* Try adding our domain to short names in .netrc */ + host_domain = strchr (host, '.'); + if (host_domain == NULL) + continue; + + /* Compare domain part */ + if (g_ascii_strcasecmp (host_domain, domain) != 0) + continue; + + /* Compare local part */ + if (g_ascii_strncasecmp (host, buffer, host_domain - host) != 0) + continue; + } + + return TRUE; + } + + /* end of .netrc */ + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* Extract login and password from .netrc for the host. + * pass may be NULL. + * Returns TRUE for success, FALSE for error */ + +static gboolean +ftpfs_netrc_lookup (const char *host, char **login, char **pass) +{ + char *netrcname; + char *tmp_pass = NULL; + char hostname[MAXHOSTNAMELEN]; + const char *domain; + static struct rupcache + { + struct rupcache *next; + char *host; + char *login; + char *pass; + } *rup_cache = NULL, *rupp; + + /* Initialize *login and *pass */ + MC_PTR_FREE (*login); + MC_PTR_FREE (*pass); + + /* Look up in the cache first */ + for (rupp = rup_cache; rupp != NULL; rupp = rupp->next) + if (strcmp (host, rupp->host) == 0) + { + *login = g_strdup (rupp->login); + *pass = g_strdup (rupp->pass); + return TRUE; + } + + /* Load current .netrc */ + netrcname = g_build_filename (mc_config_get_home_dir (), ".netrc", (char *) NULL); + if (!g_file_get_contents (netrcname, &netrc, NULL, NULL)) + { + g_free (netrcname); + return TRUE; + } + + netrcp = netrc; + + /* Find our own domain name */ + if (gethostname (hostname, sizeof (hostname)) < 0) + *hostname = '\0'; + + domain = strchr (hostname, '.'); + if (domain == NULL) + domain = ""; + + /* Scan for "default" and matching "machine" keywords */ + ftpfs_find_machine (host, domain); + + /* Scan for keywords following "default" and "machine" */ + while (TRUE) + { + keyword_t keyword; + + gboolean need_break = FALSE; + keyword = ftpfs_netrc_next (); + + switch (keyword) + { + case NETRC_LOGIN: + if (ftpfs_netrc_next () == NETRC_NONE) + { + need_break = TRUE; + break; + } + + /* We have another name already - should not happen */ + if (*login != NULL) + { + need_break = TRUE; + break; + } + + /* We have login name now */ + *login = g_strdup (buffer); + break; + + case NETRC_PASSWORD: + case NETRC_PASSWD: + if (ftpfs_netrc_next () == NETRC_NONE) + { + need_break = TRUE; + break; + } + + /* Ignore unsafe passwords */ + if (*login != NULL && + strcmp (*login, "anonymous") != 0 && strcmp (*login, "ftp") != 0 + && ftpfs_netrc_bad_mode (netrcname)) + { + need_break = TRUE; + break; + } + + /* Remember password. pass may be NULL, so use tmp_pass */ + if (tmp_pass == NULL) + tmp_pass = g_strdup (buffer); + break; + + case NETRC_ACCOUNT: + /* "account" is followed by a token which we ignore */ + if (ftpfs_netrc_next () == NETRC_NONE) + { + need_break = TRUE; + break; + } + + /* Ignore account, but warn user anyways */ + ftpfs_netrc_bad_mode (netrcname); + break; + + default: + /* Unexpected keyword or end of file */ + need_break = TRUE; + break; + } + + if (need_break) + break; + } + + MC_PTR_FREE (netrc); + g_free (netrcname); + + rupp = g_new (struct rupcache, 1); + rupp->host = g_strdup (host); + rupp->login = g_strdup (*login); + rupp->pass = g_strdup (tmp_pass); + + rupp->next = rup_cache; + rup_cache = rupp; + + *pass = tmp_pass; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/** This routine is called as the last step in load_setup */ +void +ftpfs_init_passwd (void) +{ + ftpfs_anonymous_passwd = load_anon_passwd (); + + if (ftpfs_anonymous_passwd == NULL) + { + /* If there is no anonymous ftp password specified + * then we'll just use anonymous@ + * We don't send any other thing because: + * - We want to remain anonymous + * - We want to stop SPAM + * - We don't want to let ftp sites to discriminate by the user, + * host or country. + */ + ftpfs_anonymous_passwd = g_strdup ("anonymous@"); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +void +vfs_init_ftpfs (void) +{ + tcp_init (); + + vfs_init_subclass (&ftpfs_subclass, "ftpfs", VFSF_NOLINKS | VFSF_REMOTE | VFSF_USETMP, "ftp"); + vfs_ftpfs_ops->done = ftpfs_done; + vfs_ftpfs_ops->fill_names = ftpfs_fill_names; + vfs_ftpfs_ops->stat = ftpfs_stat; + vfs_ftpfs_ops->lstat = ftpfs_lstat; + vfs_ftpfs_ops->fstat = ftpfs_fstat; + vfs_ftpfs_ops->chmod = ftpfs_chmod; + vfs_ftpfs_ops->chown = ftpfs_chown; + vfs_ftpfs_ops->unlink = ftpfs_unlink; + vfs_ftpfs_ops->rename = ftpfs_rename; + vfs_ftpfs_ops->mkdir = ftpfs_mkdir; + vfs_ftpfs_ops->rmdir = ftpfs_rmdir; + vfs_ftpfs_ops->ctl = ftpfs_ctl; + ftpfs_subclass.archive_same = ftpfs_archive_same; + ftpfs_subclass.new_archive = ftpfs_new_archive; + ftpfs_subclass.open_archive = ftpfs_open_archive; + ftpfs_subclass.free_archive = ftpfs_free_archive; + ftpfs_subclass.fh_new = ftpfs_fh_new; + ftpfs_subclass.fh_open = ftpfs_fh_open; + ftpfs_subclass.fh_close = ftpfs_fh_close; + ftpfs_subclass.dir_load = ftpfs_dir_load; + ftpfs_subclass.file_store = ftpfs_file_store; + ftpfs_subclass.linear_start = ftpfs_linear_start; + ftpfs_subclass.linear_read = ftpfs_linear_read; + ftpfs_subclass.linear_close = ftpfs_linear_close; + vfs_register_class (vfs_ftpfs_ops); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/ftpfs/ftpfs.h b/src/vfs/ftpfs/ftpfs.h new file mode 100644 index 0000000..d00c0b5 --- /dev/null +++ b/src/vfs/ftpfs/ftpfs.h @@ -0,0 +1,46 @@ +/** + * \file + * \brief Header: Virtual File System: FTP file system + */ + +#ifndef MC__VFS_FTPFS_H +#define MC__VFS_FTPFS_H + +#include "lib/vfs/xdirentry.h" + +/*** typedefs(not structures) and defined constants **********************************************/ + +#define FTP_INET 1 +#define FTP_INET6 2 + +#define OPT_FLUSH 1 +#define OPT_IGNORE_ERROR 2 + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +extern gboolean ftpfs_use_netrc; +extern char *ftpfs_anonymous_passwd; +extern char *ftpfs_proxy_host; +extern int ftpfs_directory_timeout; +extern gboolean ftpfs_always_use_proxy; +extern gboolean ftpfs_ignore_chattr_errors; + +extern int ftpfs_retry_seconds; +extern gboolean ftpfs_use_passive_connections; +extern gboolean ftpfs_use_passive_connections_over_proxy; +extern gboolean ftpfs_use_unix_list_options; +extern gboolean ftpfs_first_cd_then_ls; + +/*** declarations of public functions ************************************************************/ + +void ftpfs_init_passwd (void); +void vfs_init_ftpfs (void); +GSList *ftpfs_parse_long_list (struct vfs_class *me, struct vfs_s_inode *dir, GSList * buf, + int *err_ret); + +/*** inline functions ****************************************************************************/ +#endif diff --git a/src/vfs/ftpfs/ftpfs_parse_ls.c b/src/vfs/ftpfs/ftpfs_parse_ls.c new file mode 100644 index 0000000..5db79e0 --- /dev/null +++ b/src/vfs/ftpfs/ftpfs_parse_ls.c @@ -0,0 +1,1236 @@ +/* + Virtual File System: FTP file system + + Copyright (C) 2015-2023 + The Free Software Foundation, Inc. + + Written by: Andrew Borodin <aborodin@vmail.ru>, 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/>. + */ + +/** \file + * \brief Source: Virtual File System: FTP file system + * \author Andrew Borodin + * \date 2015 + * + * Parser of ftp long file list (reply to "LIST -la" command). + * Borrowed from lftp project (http://http://lftp.yar.ru/). + * Author of original lftp code: Alexander V. Lukyanov (lav@yars.free.net) + */ + +#include <config.h> + +#include <ctype.h> /* isdigit() */ +#include <stdio.h> /* sscanf() */ +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> /* mode_t */ +#include <time.h> +#include <unistd.h> +#include <sys/types.h> + +#include "lib/global.h" + +#include "lib/vfs/vfs.h" +#include "lib/vfs/utilvfs.h" + +#include "ftpfs.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#define number_of_parsers 7 + +#define NO_SIZE ((off_t) (-1L)) +#define NO_DATE ((time_t) (-1L)) + +#define FIRST_TOKEN strtok (line, " \t") +#define NEXT_TOKEN strtok (NULL, " \t") +#define FIRST_TOKEN_R strtok_r (line, " \t", &next) +#define NEXT_TOKEN_R strtok_r (NULL, " \t", &next) + +#define ERR2 do { (*err)++; return FALSE; } while (FALSE) + +/*** file scope type declarations ****************************************************************/ + +typedef enum +{ + UNKNOWN = 0, + DIRECTORY, + SYMLINK, + NORMAL +} filetype; + +typedef gboolean (*ftpfs_line_parser) (char *line, struct stat * s, char **filename, + char **linkname, int *err); + +/*** forward declarations (file scope functions) *************************************************/ + +static gboolean ftpfs_parse_long_list_UNIX (char *line, struct stat *s, char **filename, + char **linkname, int *err); +static gboolean ftpfs_parse_long_list_NT (char *line, struct stat *s, char **filename, + char **linkname, int *err); +static gboolean ftpfs_parse_long_list_EPLF (char *line, struct stat *s, char **filename, + char **linkname, int *err); +static gboolean ftpfs_parse_long_list_MLSD (char *line, struct stat *s, char **filename, + char **linkname, int *err); +static gboolean ftpfs_parse_long_list_AS400 (char *line, struct stat *s, char **filename, + char **linkname, int *err); +static gboolean ftpfs_parse_long_list_OS2 (char *line, struct stat *s, char **filename, + char **linkname, int *err); +static gboolean ftpfs_parse_long_list_MacWebStar (char *line, struct stat *s, char **filename, + char **linkname, int *err); + +/*** file scope variables ************************************************************************/ + +static time_t rawnow; +static struct tm now; + +static ftpfs_line_parser line_parsers[number_of_parsers] = { + ftpfs_parse_long_list_UNIX, + ftpfs_parse_long_list_NT, + ftpfs_parse_long_list_EPLF, + ftpfs_parse_long_list_MLSD, + ftpfs_parse_long_list_AS400, + ftpfs_parse_long_list_OS2, + ftpfs_parse_long_list_MacWebStar +}; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static inline uid_t +ftpfs_get_uid (const char *s) +{ + uid_t u; + + if (*s < '0' || *s > '9') + u = vfs_finduid (s); + else + u = (uid_t) atol (s); + + return u; +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline gid_t +ftpfs_get_gid (const char *s) +{ + gid_t g; + + if (*s < '0' || *s > '9') + g = vfs_findgid (s); + else + g = (gid_t) atol (s); + + return g; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +ftpfs_init_time (void) +{ + time (&rawnow); + now = *localtime (&rawnow); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +guess_year (int month, int day, int hour, int minute) +{ + int year; + + (void) hour; + (void) minute; + + year = now.tm_year + 1900; + + if (month * 32 + day > now.tm_mon * 32 + now.tm_mday + 6) + year--; + + return year; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +parse_year_or_time (const char *year_or_time, int *year, int *hour, int *minute) +{ + if (year_or_time[2] == ':') + { + if (sscanf (year_or_time, "%2d:%2d", hour, minute) != 2) + return FALSE; + + *year = -1; + } + else + { + if (sscanf (year_or_time, "%d", year) != 1) + return FALSE; + + *hour = *minute = 0; + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* Converts struct tm to time_t, assuming the data in tm is UTC rather + than local timezone (mktime assumes the latter). + + Contributed by Roger Beeman <beeman@cisco.com>, with the help of + Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */ +static time_t +mktime_from_utc (const struct tm *t) +{ + struct tm tc; + time_t tl, tb; + + memcpy (&tc, t, sizeof (struct tm)); + + /* UTC times are never DST; if we say -1, we'll introduce odd localtime- + * dependent errors. */ + + tc.tm_isdst = 0; + + tl = mktime (&tc); + if (tl == -1) + return (-1); + + tb = mktime (gmtime (&tl)); + + return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl))); +} + +/* --------------------------------------------------------------------------------------------- */ + +static time_t +ftpfs_convert_date (const char *s) +{ + struct tm tm; + int year, month, day, hour, minute, second; + int skip = 0; + int n; + + memset (&tm, 0, sizeof (tm)); + + n = sscanf (s, "%4d%n", &year, &skip); + + /* try to workaround server's y2k bug * + * I hope in the next 300 years the y2k bug will be finally fixed :) */ + if (n == 1 && year >= 1910 && year <= 1930) + { + n = sscanf (s, "%5d%n", &year, &skip); + year = year - 19100 + 2000; + } + + if (n != 1) + return NO_DATE; + + n = sscanf (s + skip, "%2d%2d%2d%2d%2d", &month, &day, &hour, &minute, &second); + + if (n != 5) + return NO_DATE; + + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = minute; + tm.tm_sec = second; + + return mktime_from_utc (&tm); +} + +/* --------------------------------------------------------------------------------------------- */ + +/* + -rwxr-xr-x 1 lav root 4771 Sep 12 1996 install-sh + -rw-r--r-- 1 lav root 1349 Feb 2 14:10 lftp.lsm + drwxr-xr-x 4 lav root 1024 Feb 22 15:32 lib + lrwxrwxrwx 1 lav root 33 Feb 14 17:45 ltconfig -> /usr/share/libtool/ltconfig + + NOTE: group may be missing. + */ + +static gboolean +parse_ls_line (char *line, struct stat *s, char **filename, char **linkname) +{ + char *next = NULL; + char *t; + mode_t type, mode = 0; + char *group_or_size; + struct tm date; + const char *day_of_month; + gboolean year_anomaly = FALSE; + char *name; + + /* parse perms */ + t = FIRST_TOKEN_R; + if (t == NULL) + return FALSE; + + if (!vfs_parse_filetype (t, NULL, &type)) + return FALSE; + + if (vfs_parse_fileperms (t + 1, NULL, &mode)) + mode |= type; + + s->st_mode = mode; + + /* link count */ + t = NEXT_TOKEN_R; + if (t == NULL) + return FALSE; + s->st_nlink = atol (t); + + /* user */ + t = NEXT_TOKEN_R; + if (t == NULL) + return FALSE; + + s->st_uid = ftpfs_get_uid (t); + + /* group or size */ + group_or_size = NEXT_TOKEN_R; + + /* size or month */ + t = NEXT_TOKEN_R; + if (t == NULL) + return FALSE; + if (isdigit ((unsigned char) *t)) + { + /* it's size, so the previous was group: */ + long long size; + int n; + + s->st_gid = ftpfs_get_gid (group_or_size); + + if (sscanf (t, "%lld%n", &size, &n) == 1 && t[n] == '\0') + s->st_size = (off_t) size; + t = NEXT_TOKEN_R; + if (t == NULL) + return FALSE; + } + else + { + /* it was month, so the previous was size: */ + long long size; + int n; + + if (sscanf (group_or_size, "%lld%n", &size, &n) == 1 && group_or_size[n] == '\0') + s->st_size = (off_t) size; + } + +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + s->st_blksize = 512; +#endif +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS + s->st_blocks = (s->st_size + 511) / 512; +#endif + + memset (&date, 0, sizeof (date)); + + if (!vfs_parse_month (t, &date)) + date.tm_mon = 0; + + day_of_month = NEXT_TOKEN_R; + if (day_of_month == NULL) + return FALSE; + date.tm_mday = atoi (day_of_month); + + /* time or year */ + t = NEXT_TOKEN_R; + if (t == NULL) + return FALSE; + date.tm_isdst = -1; + date.tm_hour = date.tm_min = 0; + date.tm_sec = 30; + + if (sscanf (t, "%2d:%2d", &date.tm_hour, &date.tm_min) == 2) + date.tm_year = guess_year (date.tm_mon, date.tm_mday, date.tm_hour, date.tm_min) - 1900; + else + { + if (day_of_month + strlen (day_of_month) + 1 == t) + year_anomaly = TRUE; + date.tm_year = atoi (t) - 1900; + /* We don't know the hour. Set it to something other than 0, or + * DST -1 will end up changing the date. */ + date.tm_hour = 12; + date.tm_min = 0; + date.tm_sec = 0; + } + + s->st_mtime = mktime (&date); + /* Use resulting time value */ + s->st_atime = s->st_ctime = s->st_mtime; + + name = strtok_r (NULL, "", &next); + if (name == NULL) + return FALSE; + + /* there are ls which output extra space after year. */ + if (year_anomaly && *name == ' ') + name++; + + if (!S_ISLNK (s->st_mode)) + *linkname = NULL; + else + { + char *arrow; + + for (arrow = name; (arrow = strstr (arrow, " -> ")) != NULL; arrow++) + if (arrow != name && arrow[4] != '\0') + { + *arrow = '\0'; + *linkname = g_strdup (arrow + 4); + break; + } + } + + *filename = g_strdup (name); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +ftpfs_parse_long_list_UNIX (char *line, struct stat *s, char **filename, char **linkname, int *err) +{ + int tmp; + gboolean ret; + + if (sscanf (line, "total %d", &tmp) == 1) + return FALSE; + + if (strncasecmp (line, "Status of ", 10) == 0) + return FALSE; /* STAT output. */ + + ret = parse_ls_line (line, s, filename, linkname); + if (!ret) + (*err)++; + + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* + 07-13-98 09:06PM <DIR> aix + 07-13-98 09:06PM <DIR> hpux + 07-13-98 09:06PM <DIR> linux + 07-13-98 09:06PM <DIR> ncr + 07-13-98 09:06PM <DIR> solaris + 03-18-98 06:01AM 2109440 nlxb318e.tar + 07-02-98 11:17AM 13844 Whatsnew.txt + */ + +static gboolean +ftpfs_parse_long_list_NT (char *line, struct stat *s, char **filename, char **linkname, int *err) +{ + char *t; + int month, day, year, hour, minute; + char am; + struct tm tms; + long long size; + + t = FIRST_TOKEN; + if (t == NULL) + ERR2; + if (sscanf (t, "%2d-%2d-%2d", &month, &day, &year) != 3) + ERR2; + if (year >= 70) + year += 1900; + else + year += 2000; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + am = 'A'; /* AM/PM is optional */ + if (sscanf (t, "%2d:%2d%c", &hour, &minute, &am) < 2) + ERR2; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + + if (am == 'P') /* PM - after noon */ + { + hour += 12; + if (hour == 24) + hour = 0; + } + + tms.tm_sec = 30; /* seconds after the minute [0, 61] */ + tms.tm_min = minute; /* minutes after the hour [0, 59] */ + tms.tm_hour = hour; /* hour since midnight [0, 23] */ + tms.tm_mday = day; /* day of the month [1, 31] */ + tms.tm_mon = month - 1; /* months since January [0, 11] */ + tms.tm_year = year - 1900; /* years since 1900 */ + tms.tm_isdst = -1; + + + s->st_mtime = mktime (&tms); + /* Use resulting time value */ + s->st_atime = s->st_ctime = s->st_mtime; + + if (strcmp (t, "<DIR>") == 0) + s->st_mode = S_IFDIR; + else + { + s->st_mode = S_IFREG; + if (sscanf (t, "%lld", &size) != 1) + ERR2; + s->st_size = (off_t) size; + } + + t = strtok (NULL, ""); + if (t == NULL) + ERR2; + while (*t == ' ') + t++; + if (*t == '\0') + ERR2; + + *filename = g_strdup (t); + *linkname = NULL; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* + +i774.71425,m951188401,/, users + +i774.49602,m917883130,r,s79126, jgr_www2.exe + + starts with + + comma separated + first character of field is type: + i - ? + m - modification time + / - means directory + r - means plain file + s - size + up - permissions in octal + \t - file name follows. + */ + +static gboolean +ftpfs_parse_long_list_EPLF (char *line, struct stat *s, char **filename, char **linkname, int *err) +{ + size_t len; + const char *b; + const char *name = NULL; + size_t name_len = 0; + off_t size = NO_SIZE; + time_t date = NO_DATE; + long date_l; + long long size_ll; + gboolean dir = FALSE; + gboolean type_known = FALSE; + int perms = -1; + const char *scan; + ssize_t scan_len; + + len = strlen (line); + b = line; + + if (len < 2 || b[0] != '+') + ERR2; + + scan = b + 1; + scan_len = len - 1; + + while (scan != NULL && scan_len > 0) + { + const char *comma; + + switch (*scan) + { + case '\t': /* the rest is file name. */ + name = scan + 1; + name_len = scan_len - 1; + scan = NULL; + break; + case 's': + if (sscanf (scan + 1, "%lld", &size_ll) != 1) + break; + size = size_ll; + break; + case 'm': + if (sscanf (scan + 1, "%ld", &date_l) != 1) + break; + date = date_l; + break; + case '/': + dir = TRUE; + type_known = TRUE; + break; + case 'r': + dir = FALSE; + type_known = TRUE; + break; + case 'i': + break; + case 'u': + if (scan[1] == 'p') /* permissions. */ + if (sscanf (scan + 2, "%o", (unsigned int *) &perms) != 1) + perms = -1; + break; + default: + name = NULL; + scan = NULL; + break; + } + if (scan == NULL || scan_len == 0) + break; + + comma = (const char *) memchr (scan, ',', scan_len); + if (comma == NULL) + break; + + scan_len -= comma + 1 - scan; + scan = comma + 1; + } + + if (name == NULL || !type_known) + ERR2; + + *filename = g_strndup (name, name_len); + *linkname = NULL; + + if (size != NO_SIZE) + s->st_size = size; + if (date != NO_DATE) + { + s->st_mtime = date; + /* Use resulting time value */ + s->st_atime = s->st_ctime = s->st_mtime; + } + if (type_known) + s->st_mode = dir ? S_IFDIR : S_IFREG; + if (perms != -1) + s->st_mode |= perms; /* FIXME */ + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* + Type=cdir;Modify=20021029173810;Perm=el;Unique=BP8AAjJufAA; / + Type=pdir;Modify=20021029173810;Perm=el;Unique=BP8AAjJufAA; .. + Type=dir;Modify=20010118144705;Perm=e;Unique=BP8AAjNufAA; bin + Type=dir;Modify=19981021003019;Perm=el;Unique=BP8AAlhufAA; pub + Type=file;Size=12303;Modify=19970124132601;Perm=r;Unique=BP8AAo9ufAA; mailserv.FAQ + modify=20161215062118;perm=flcdmpe;type=dir;UNIX.group=503;UNIX.mode=0700; directory-name + modify=20161213121618;perm=adfrw;size=6369064;type=file;UNIX.group=503;UNIX.mode=0644; file-name + modify=20120103123744;perm=adfrw;size=11;type=OS.unix=symlink;UNIX.group=0;UNIX.mode=0777; www + */ + +static gboolean +ftpfs_parse_long_list_MLSD (char *line, struct stat *s, char **filename, char **linkname, int *err) +{ + const char *name = NULL; + off_t size = NO_SIZE; + time_t date = NO_DATE; + const char *owner = NULL; + const char *group = NULL; + filetype type = UNKNOWN; + int perms = -1; + char *space; + char *tok; + + space = strstr (line, "; "); + if (space != NULL) + { + name = space + 2; + *space = '\0'; + } + else + { + /* NcFTPd does not put a semicolon after last fact, workaround it. */ + space = strchr (line, ' '); + if (space == NULL) + ERR2; + name = space + 1; + *space = '\0'; + } + + for (tok = strtok (line, ";"); tok != NULL; tok = strtok (NULL, ";")) + { + if (strcasecmp (tok, "Type=cdir") == 0 + || strcasecmp (tok, "Type=pdir") == 0 || strcasecmp (tok, "Type=dir") == 0) + { + type = DIRECTORY; + continue; + } + if (strcasecmp (tok, "Type=file") == 0) + { + type = NORMAL; + continue; + } + if (strcasecmp (tok, "Type=OS.unix=symlink") == 0) + { + type = SYMLINK; + continue; + } + if (strncasecmp (tok, "Modify=", 7) == 0) + { + date = ftpfs_convert_date (tok + 7); + continue; + } + if (strncasecmp (tok, "Size=", 5) == 0) + { + long long size_ll; + + if (sscanf (tok + 5, "%lld", &size_ll) == 1) + size = size_ll; + continue; + } + if (strncasecmp (tok, "Perm=", 5) == 0) + { + perms = 0; + for (tok += 5; *tok != '\0'; tok++) + { + switch (g_ascii_tolower (*tok)) + { + case 'e': + perms |= 0111; + break; + case 'l': + perms |= 0444; + break; + case 'r': + perms |= 0444; + break; + case 'c': + perms |= 0200; + break; + case 'w': + perms |= 0200; + break; + default: + break; + } + } + continue; + } + if (strncasecmp (tok, "UNIX.mode=", 10) == 0) + { + if (sscanf (tok + 10, "%o", (unsigned int *) &perms) != 1) + perms = -1; + continue; + } + if (strncasecmp (tok, "UNIX.owner=", 11) == 0) + { + owner = tok + 11; + continue; + } + if (strncasecmp (tok, "UNIX.group=", 11) == 0) + { + group = tok + 11; + continue; + } + if (strncasecmp (tok, "UNIX.uid=", 9) == 0) + { + if (owner == NULL) + owner = tok + 9; + continue; + } + if (strncasecmp (tok, "UNIX.gid=", 9) == 0) + { + if (group == NULL) + group = tok + 9; + continue; + } + } + if (name == NULL || name[0] == '\0' || type == UNKNOWN) + ERR2; + + *filename = g_strdup (name); + *linkname = NULL; + + if (size != NO_SIZE) + s->st_size = size; + if (date != NO_DATE) + { + s->st_mtime = date; + /* Use resulting time value */ + s->st_atime = s->st_ctime = s->st_mtime; + } + switch (type) + { + case DIRECTORY: + s->st_mode = S_IFDIR; + break; + case SYMLINK: + s->st_mode = S_IFLNK; + break; + case NORMAL: + s->st_mode = S_IFREG; + break; + default: + g_assert_not_reached (); + } + if (perms != -1) + s->st_mode |= perms; /* FIXME */ + if (owner != NULL) + s->st_uid = ftpfs_get_uid (owner); + if (group != NULL) + s->st_gid = ftpfs_get_gid (group); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* + ASUSER 8192 04/26/05 13:54:16 *DIR dir/ + ASUSER 8192 04/26/05 13:57:34 *DIR dir1/ + ASUSER 365255 02/28/01 15:41:40 *STMF readme.txt + ASUSER 8489625 03/18/03 09:37:00 *STMF saved.zip + ASUSER 365255 02/28/01 15:41:40 *STMF unist.old + */ + +static gboolean +ftpfs_parse_long_list_AS400 (char *line, struct stat *s, char **filename, char **linkname, int *err) +{ + char *t; + char *user; + long long size; + int month, day, year, hour, minute, second; + struct tm tms; + time_t mtime; + mode_t type; + char *slash; + + t = FIRST_TOKEN; + if (t == NULL) + ERR2; + user = t; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + if (sscanf (t, "%lld", &size) != 1) + ERR2; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + if (sscanf (t, "%2d/%2d/%2d", &month, &day, &year) != 3) + ERR2; + if (year >= 70) + year += 1900; + else + year += 2000; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + if (sscanf (t, "%2d:%2d:%2d", &hour, &minute, &second) != 3) + ERR2; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + + tms.tm_sec = second; /* seconds after the minute [0, 61] */ + tms.tm_min = minute; /* minutes after the hour [0, 59] */ + tms.tm_hour = hour; /* hour since midnight [0, 23] */ + tms.tm_mday = day; /* day of the month [1, 31] */ + tms.tm_mon = month - 1; /* months since January [0, 11] */ + tms.tm_year = year - 1900; /* years since 1900 */ + tms.tm_isdst = -1; + mtime = mktime (&tms); + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + if (strcmp (t, "*DIR") == 0) + type = S_IFDIR; + else + type = S_IFREG; + + t = strtok (NULL, ""); + if (t == NULL) + ERR2; + while (*t == ' ') + t++; + if (*t == '\0') + ERR2; + + *linkname = NULL; + + slash = strchr (t, '/'); + if (slash != NULL) + { + if (slash == t) + return FALSE; + + *slash = '\0'; + type = S_IFDIR; + if (slash[1] != '\0') + { + *filename = g_strdup (t); + s->st_mode = type; /* FIXME */ + return TRUE; + } + } + + *filename = g_strdup (t); + s->st_mode = type; + s->st_size = (off_t) size; + s->st_mtime = mtime; + /* Use resulting time value */ + s->st_atime = s->st_ctime = s->st_mtime; + s->st_uid = ftpfs_get_uid (user); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* + 0 DIR 06-27-96 11:57 PROTOCOL + 169 11-29-94 09:20 SYSLEVEL.MPT + */ + +static gboolean +ftpfs_parse_long_list_OS2 (char *line, struct stat *s, char **filename, char **linkname, int *err) +{ + char *t; + long long size; + int month, day, year, hour, minute; + struct tm tms; + + t = FIRST_TOKEN; + if (t == NULL) + ERR2; + + if (sscanf (t, "%lld", &size) != 1) + ERR2; + s->st_size = (off_t) size; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + s->st_mode = S_IFREG; + if (strcmp (t, "DIR") == 0) + { + s->st_mode = S_IFDIR; + t = NEXT_TOKEN; + + if (t == NULL) + ERR2; + } + + if (sscanf (t, "%2d-%2d-%2d", &month, &day, &year) != 3) + ERR2; + if (year >= 70) + year += 1900; + else + year += 2000; + + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + if (sscanf (t, "%2d:%2d", &hour, &minute) != 3) + ERR2; + + tms.tm_sec = 30; /* seconds after the minute [0, 61] */ + tms.tm_min = minute; /* minutes after the hour [0, 59] */ + tms.tm_hour = hour; /* hour since midnight [0, 23] */ + tms.tm_mday = day; /* day of the month [1, 31] */ + tms.tm_mon = month - 1; /* months since January [0, 11] */ + tms.tm_year = year - 1900; /* years since 1900 */ + tms.tm_isdst = -1; + s->st_mtime = mktime (&tms); + /* Use resulting time value */ + s->st_atime = s->st_ctime = s->st_mtime; + + t = strtok (NULL, ""); + if (t == NULL) + ERR2; + while (*t == ' ') + t++; + if (*t == '\0') + ERR2; + *filename = g_strdup (t); + *linkname = NULL; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +ftpfs_parse_long_list_MacWebStar (char *line, struct stat *s, char **filename, + char **linkname, int *err) +{ + char *t; + mode_t type, mode; + struct tm date; + const char *day_of_month; + char *name; + + t = FIRST_TOKEN; + if (t == NULL) + ERR2; + + if (!vfs_parse_filetype (t, NULL, &type)) + ERR2; + + s->st_mode = type; + + if (!vfs_parse_fileperms (t + 1, NULL, &mode)) + ERR2; + /* permissions are meaningless here. */ + + /* "folder" or 0 */ + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + + if (strcmp (t, "folder") != 0) + { + long long size; + + /* size? */ + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + /* size */ + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + if (!isdigit ((unsigned char) *t)) + ERR2; + + if (sscanf (t, "%lld", &size) == 1) + s->st_size = (off_t) size; + } + else + { + /* ?? */ + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + } + + /* month */ + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + + memset (&date, 0, sizeof (date)); + + if (!vfs_parse_month (t, &date)) + ERR2; + + day_of_month = NEXT_TOKEN; + if (day_of_month == NULL) + ERR2; + + date.tm_mday = atoi (day_of_month); + + /* time or year */ + t = NEXT_TOKEN; + if (t == NULL) + ERR2; + if (!parse_year_or_time (t, &date.tm_year, &date.tm_hour, &date.tm_min)) + ERR2; + + date.tm_isdst = -1; + date.tm_sec = 30; + if (date.tm_year == -1) + date.tm_year = guess_year (date.tm_mon, date.tm_mday, date.tm_hour, date.tm_min) - 1900; + else + date.tm_hour = 12; + + s->st_mtime = mktime (&date); + /* Use resulting time value */ + s->st_atime = s->st_ctime = s->st_mtime; + + name = strtok (NULL, ""); + if (name == NULL) + ERR2; + + /* no symlinks on Mac, but anyway. */ + if (!S_ISLNK (s->st_mode)) + *linkname = NULL; + else + { + char *arrow; + + for (arrow = name; (arrow = strstr (arrow, " -> ")) != NULL; arrow++) + if (arrow != name && arrow[4] != '\0') + { + *arrow = '\0'; + *linkname = g_strdup (arrow + 4); + break; + } + } + + *filename = g_strdup (name); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +GSList * +ftpfs_parse_long_list (struct vfs_class * me, struct vfs_s_inode * dir, GSList * buf, int *err_ret) +{ + int err[number_of_parsers]; + GSList *set[number_of_parsers]; /* arrays of struct vfs_s_entry */ + size_t i; + GSList *bufp; + ftpfs_line_parser guessed_parser = NULL; + GSList **the_set = NULL; + int *the_err = NULL; + int *best_err1 = &err[0]; + int *best_err2 = &err[1]; + + ftpfs_init_time (); + + if (err_ret != NULL) + *err_ret = 0; + + memset (&err, 0, sizeof (err)); + memset (&set, 0, sizeof (set)); + + for (bufp = buf; bufp != NULL; bufp = g_slist_next (bufp)) + { + char *b = (char *) bufp->data; + size_t blen; + + blen = strlen (b); + + if (b[blen - 1] == '\r') + { + b[blen - 1] = '\0'; + blen--; + } + + if (blen == 0) + continue; + + if (guessed_parser == NULL) + { + for (i = 0; i < number_of_parsers; i++) + { + struct vfs_s_entry *info; + gboolean ok; + char *tmp_line; + int nlink; + + /* parser can clobber the line - work on a copy */ + tmp_line = g_strndup (b, blen); + + info = vfs_s_generate_entry (me, NULL, dir, 0); + nlink = info->ino->st.st_nlink; + ok = (*line_parsers[i]) (tmp_line, &info->ino->st, &info->name, + &info->ino->linkname, &err[i]); + if (ok && strchr (info->name, '/') == NULL) + { + info->ino->st.st_nlink = nlink; /* Ouch, we need to preserve our counts :-( */ + set[i] = g_slist_prepend (set[i], info); + } + else + vfs_s_free_entry (me, info); + + g_free (tmp_line); + + if (*best_err1 > err[i]) + best_err1 = &err[i]; + if (*best_err2 > err[i] && best_err1 != &err[i]) + best_err2 = &err[i]; + + if (*best_err1 > 16) + goto leave; /* too many errors with best parser. */ + } + + if (*best_err2 > (*best_err1 + 1) * 16) + { + i = (size_t) (best_err1 - err); + guessed_parser = line_parsers[i]; + the_set = &set[i]; + the_err = &err[i]; + } + } + else + { + struct vfs_s_entry *info; + gboolean ok; + char *tmp_line; + int nlink; + + /* parser can clobber the line - work on a copy */ + tmp_line = g_strndup (b, blen); + + info = vfs_s_generate_entry (me, NULL, dir, 0); + nlink = info->ino->st.st_nlink; + ok = guessed_parser (tmp_line, &info->ino->st, &info->name, &info->ino->linkname, + the_err); + if (ok && strchr (info->name, '/') == NULL) + { + info->ino->st.st_nlink = nlink; /* Ouch, we need to preserve our counts :-( */ + *the_set = g_slist_prepend (*the_set, info); + } + else + vfs_s_free_entry (me, info); + + g_free (tmp_line); + } + } + + if (the_set == NULL) + { + i = best_err1 - err; + the_set = &set[i]; + the_err = &err[i]; + } + + leave: + for (i = 0; i < number_of_parsers; i++) + if (&set[i] != the_set) + { + for (bufp = set[i]; bufp != NULL; bufp = g_slist_next (bufp)) + vfs_s_free_entry (me, VFS_ENTRY (bufp->data)); + + g_slist_free (set[i]); + } + + if (err_ret != NULL && the_err != NULL) + *err_ret = *the_err; + + return the_set != NULL ? g_slist_reverse (*the_set) : NULL; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/local/Makefile.am b/src/vfs/local/Makefile.am new file mode 100644 index 0000000..0176d46 --- /dev/null +++ b/src/vfs/local/Makefile.am @@ -0,0 +1,7 @@ + +AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) + +noinst_LTLIBRARIES = libvfs-local.la + +libvfs_local_la_SOURCES = \ + local.c local.h diff --git a/src/vfs/local/Makefile.in b/src/vfs/local/Makefile.in new file mode 100644 index 0000000..6d79948 --- /dev/null +++ b/src/vfs/local/Makefile.in @@ -0,0 +1,735 @@ +# 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/local +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_local_la_LIBADD = +am_libvfs_local_la_OBJECTS = local.lo +libvfs_local_la_OBJECTS = $(am_libvfs_local_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)/local.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_local_la_SOURCES) +DIST_SOURCES = $(libvfs_local_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) +noinst_LTLIBRARIES = libvfs-local.la +libvfs_local_la_SOURCES = \ + local.c local.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/local/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/vfs/local/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-local.la: $(libvfs_local_la_OBJECTS) $(libvfs_local_la_DEPENDENCIES) $(EXTRA_libvfs_local_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libvfs_local_la_OBJECTS) $(libvfs_local_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/local.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)/local.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)/local.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/local/local.c b/src/vfs/local/local.c new file mode 100644 index 0000000..a777c84 --- /dev/null +++ b/src/vfs/local/local.c @@ -0,0 +1,523 @@ +/* + Virtual File System: local file system. + + Copyright (C) 1995-2023 + Free Software Foundation, Inc. + + 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/>. + */ + +/** + * \file + * \brief Source: local FS + */ + +#include <config.h> +#include <errno.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#ifdef ENABLE_EXT2FS_ATTR +#include <e2p/e2p.h> /* fgetflags(), fsetflags() */ +#endif + +#include "lib/global.h" + +#include "lib/vfs/xdirentry.h" /* vfs_s_subclass */ +#include "lib/vfs/utilvfs.h" + +#include "local.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +static struct vfs_s_subclass local_subclass; +static struct vfs_class *vfs_local_ops = VFS_CLASS (&local_subclass); + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static void * +local_open (const vfs_path_t * vpath, int flags, mode_t mode) +{ + int *local_info; + int fd; + const char *path; + + path = vfs_path_get_last_path_str (vpath); + fd = open (path, NO_LINEAR (flags), mode); + if (fd == -1) + return 0; + + local_info = g_new (int, 1); + *local_info = fd; + + return local_info; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void * +local_opendir (const vfs_path_t * vpath) +{ + DIR **local_info; + DIR *dir = NULL; + const char *path; + + path = vfs_path_get_last_path_str (vpath); + + /* On Linux >= 5.1, MC sometimes shows empty directories on mounted CIFS shares. + * Rereading directory restores the directory content. + * + * Reopen directory, if first readdir() returns NULL and errno == EINTR. + */ + while (dir == NULL) + { + dir = opendir (path); + if (dir == NULL) + return NULL; + + if (readdir (dir) == NULL && errno == EINTR) + { + closedir (dir); + dir = NULL; + } + else + rewinddir (dir); + } + + local_info = (DIR **) g_new (DIR *, 1); + *local_info = dir; + + return local_info; +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct vfs_dirent * +local_readdir (void *data) +{ + struct dirent *d; + + d = readdir (*(DIR **) data); + + return (d != NULL ? vfs_dirent_init (NULL, d->d_name, d->d_ino) : NULL); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_closedir (void *data) +{ + int i; + + i = closedir (*(DIR **) data); + g_free (data); + return i; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_stat (const vfs_path_t * vpath, struct stat *buf) +{ + const char *path; + + path = vfs_path_get_last_path_str (vpath); + return stat (path, buf); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_lstat (const vfs_path_t * vpath, struct stat *buf) +{ + const char *path; + + path = vfs_path_get_last_path_str (vpath); +#ifndef HAVE_STATLSTAT + return lstat (path, buf); +#else + return statlstat (path, buf); +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_chmod (const vfs_path_t * vpath, mode_t mode) +{ + const char *path; + + path = vfs_path_get_last_path_str (vpath); + return chmod (path, mode); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_chown (const vfs_path_t * vpath, uid_t owner, gid_t group) +{ + const char *path; + + path = vfs_path_get_last_path_str (vpath); + return chown (path, owner, group); +} + +/* --------------------------------------------------------------------------------------------- */ + +#ifdef ENABLE_EXT2FS_ATTR + +static int +local_fgetflags (const vfs_path_t * vpath, unsigned long *flags) +{ + const char *path; + + path = vfs_path_get_last_path_str (vpath); + return fgetflags (path, flags); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_fsetflags (const vfs_path_t * vpath, unsigned long flags) +{ + const char *path; + + path = vfs_path_get_last_path_str (vpath); + return fsetflags (path, flags); +} + +#endif /* ENABLE_EXT2FS_ATTR */ + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_utime (const vfs_path_t * vpath, mc_timesbuf_t * times) +{ + int ret; + const char *path; + + path = vfs_path_get_last_path_str (vpath); +#ifdef HAVE_UTIMENSAT + ret = utimensat (AT_FDCWD, path, *times, AT_SYMLINK_NOFOLLOW); +#else + ret = utime (path, times); +#endif + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_readlink (const vfs_path_t * vpath, char *buf, size_t size) +{ + const char *path; + + path = vfs_path_get_last_path_str (vpath); + return readlink (path, buf, size); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_unlink (const vfs_path_t * vpath) +{ + const char *path; + + path = vfs_path_get_last_path_str (vpath); + return unlink (path); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_symlink (const vfs_path_t * vpath1, const vfs_path_t * vpath2) +{ + const char *path1, *path2; + + path1 = vfs_path_get_last_path_str (vpath1); + path2 = vfs_path_get_last_path_str (vpath2); + return symlink (path1, path2); +} + +/* --------------------------------------------------------------------------------------------- */ + +static ssize_t +local_write (void *data, const char *buf, size_t nbyte) +{ + int fd; + int n; + + if (data == NULL) + return (-1); + + fd = *(int *) data; + + while ((n = write (fd, buf, nbyte)) == -1) + { +#ifdef EAGAIN + if (errno == EAGAIN) + continue; +#endif +#ifdef EINTR + if (errno == EINTR) + continue; +#endif + break; + } + + return n; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_rename (const vfs_path_t * vpath1, const vfs_path_t * vpath2) +{ + const char *path1, *path2; + + path1 = vfs_path_get_last_path_str (vpath1); + path2 = vfs_path_get_last_path_str (vpath2); + return rename (path1, path2); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_chdir (const vfs_path_t * vpath) +{ + const char *path; + + path = vfs_path_get_last_path_str (vpath); + return chdir (path); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_mknod (const vfs_path_t * vpath, mode_t mode, dev_t dev) +{ + const char *path; + + path = vfs_path_get_last_path_str (vpath); + return mknod (path, mode, dev); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_link (const vfs_path_t * vpath1, const vfs_path_t * vpath2) +{ + const char *path1, *path2; + + path1 = vfs_path_get_last_path_str (vpath1); + path2 = vfs_path_get_last_path_str (vpath2); + return link (path1, path2); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_mkdir (const vfs_path_t * vpath, mode_t mode) +{ + const char *path; + + path = vfs_path_get_last_path_str (vpath); + return mkdir (path, mode); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_rmdir (const vfs_path_t * vpath) +{ + const char *path; + + path = vfs_path_get_last_path_str (vpath); + return rmdir (path); +} + +/* --------------------------------------------------------------------------------------------- */ + +static vfs_path_t * +local_getlocalcopy (const vfs_path_t * vpath) +{ + return vfs_path_clone (vpath); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed) +{ + (void) vpath; + (void) local; + (void) has_changed; + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +local_which (struct vfs_class *me, const char *path) +{ + (void) me; + (void) path; + + return 0; /* Every path which other systems do not like is expected to be ours */ +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +ssize_t +local_read (void *data, char *buffer, size_t count) +{ + int n; + int fd; + + if (data == NULL) + return (-1); + + fd = *(int *) data; + + while ((n = read (fd, buffer, count)) == -1) + { +#ifdef EAGAIN + if (errno == EAGAIN) + continue; +#endif +#ifdef EINTR + if (errno == EINTR) + continue; +#endif + return (-1); + } + + return n; +} + +/* --------------------------------------------------------------------------------------------- */ + +int +local_close (void *data) +{ + int fd; + + if (data == NULL) + return (-1); + + fd = *(int *) data; + g_free (data); + return close (fd); +} + +/* --------------------------------------------------------------------------------------------- */ + +int +local_errno (struct vfs_class *me) +{ + (void) me; + return errno; +} + +/* --------------------------------------------------------------------------------------------- */ + +int +local_fstat (void *data, struct stat *buf) +{ + int fd = *(int *) data; + + return fstat (fd, buf); +} + +/* --------------------------------------------------------------------------------------------- */ + +off_t +local_lseek (void *data, off_t offset, int whence) +{ + int fd = *(int *) data; + + return lseek (fd, offset, whence); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +local_nothingisopen (vfsid id) +{ + (void) id; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +vfs_init_localfs (void) +{ + /* NULLize vfs_s_subclass members */ + memset (&local_subclass, 0, sizeof (local_subclass)); + + vfs_init_class (vfs_local_ops, "localfs", VFSF_LOCAL, NULL); + vfs_local_ops->which = local_which; + vfs_local_ops->open = local_open; + vfs_local_ops->close = local_close; + vfs_local_ops->read = local_read; + vfs_local_ops->write = local_write; + vfs_local_ops->opendir = local_opendir; + vfs_local_ops->readdir = local_readdir; + vfs_local_ops->closedir = local_closedir; + vfs_local_ops->stat = local_stat; + vfs_local_ops->lstat = local_lstat; + vfs_local_ops->fstat = local_fstat; + vfs_local_ops->chmod = local_chmod; + vfs_local_ops->chown = local_chown; +#ifdef ENABLE_EXT2FS_ATTR + vfs_local_ops->fgetflags = local_fgetflags; + vfs_local_ops->fsetflags = local_fsetflags; +#endif + vfs_local_ops->utime = local_utime; + vfs_local_ops->readlink = local_readlink; + vfs_local_ops->symlink = local_symlink; + vfs_local_ops->link = local_link; + vfs_local_ops->unlink = local_unlink; + vfs_local_ops->rename = local_rename; + vfs_local_ops->chdir = local_chdir; + vfs_local_ops->ferrno = local_errno; + vfs_local_ops->lseek = local_lseek; + vfs_local_ops->mknod = local_mknod; + vfs_local_ops->getlocalcopy = local_getlocalcopy; + vfs_local_ops->ungetlocalcopy = local_ungetlocalcopy; + vfs_local_ops->mkdir = local_mkdir; + vfs_local_ops->rmdir = local_rmdir; + vfs_local_ops->nothingisopen = local_nothingisopen; + vfs_register_class (vfs_local_ops); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/local/local.h b/src/vfs/local/local.h new file mode 100644 index 0000000..8929d10 --- /dev/null +++ b/src/vfs/local/local.h @@ -0,0 +1,32 @@ +/** + * \file + * \brief Header: local FS + */ + +#ifndef MC__VFS_LOCAL_H +#define MC__VFS_LOCAL_H + +#include "lib/vfs/vfs.h" + +/*** typedefs(not structures) and defined constants **********************************************/ + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +/*** declarations of public functions ************************************************************/ + +extern void vfs_init_localfs (void); + +/* these functions are used by other filesystems, so they are + * published here. */ +extern int local_close (void *data); +extern ssize_t local_read (void *data, char *buffer, size_t count); +extern int local_fstat (void *data, struct stat *buf); +extern int local_errno (struct vfs_class *me); +extern off_t local_lseek (void *data, off_t offset, int whence); + +/*** inline functions ****************************************************************************/ +#endif diff --git a/src/vfs/plugins_init.c b/src/vfs/plugins_init.c new file mode 100644 index 0000000..767e284 --- /dev/null +++ b/src/vfs/plugins_init.c @@ -0,0 +1,123 @@ +/* + Init VFS plugins. + + Copyright (C) 2011-2023 + Free Software Foundation, Inc. + + Written by: + Slava Zanko <slavazanko@gmail.com>, 2011. + + 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/>. + */ + +/** \file + * \brief This is a template file (here goes brief description). + * \author Author1 + * \author Author2 + * \date 20xx + * + * Detailed description. + */ + +#include <config.h> + +#include "lib/global.h" + +#include "local/local.h" + +#ifdef ENABLE_VFS_CPIO +#include "cpio/cpio.h" +#endif + +#ifdef ENABLE_VFS_EXTFS +#include "extfs/extfs.h" +#endif + +#ifdef ENABLE_VFS_FISH +#include "fish/fish.h" +#endif + +#ifdef ENABLE_VFS_FTP +#include "ftpfs/ftpfs.h" +#endif + +#ifdef ENABLE_VFS_SFTP +#include "sftpfs/sftpfs.h" +#endif + +#ifdef ENABLE_VFS_SFS +#include "sfs/sfs.h" +#endif + +#ifdef ENABLE_VFS_TAR +#include "tar/tar.h" +#endif + +#ifdef ENABLE_VFS_UNDELFS +#include "undelfs/undelfs.h" +#endif + +#include "plugins_init.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** file scope variables ************************************************************************/ + +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +vfs_plugins_init (void) +{ + /* localfs needs to be the first one */ + vfs_init_localfs (); + +#ifdef ENABLE_VFS_CPIO + vfs_init_cpiofs (); +#endif /* ENABLE_VFS_CPIO */ +#ifdef ENABLE_VFS_TAR + vfs_init_tarfs (); +#endif /* ENABLE_VFS_TAR */ +#ifdef ENABLE_VFS_SFS + vfs_init_sfs (); +#endif /* ENABLE_VFS_SFS */ +#ifdef ENABLE_VFS_EXTFS + vfs_init_extfs (); +#endif /* ENABLE_VFS_EXTFS */ +#ifdef ENABLE_VFS_UNDELFS + vfs_init_undelfs (); +#endif /* ENABLE_VFS_UNDELFS */ + +#ifdef ENABLE_VFS_FTP + vfs_init_ftpfs (); +#endif /* ENABLE_VFS_FTP */ +#ifdef ENABLE_VFS_SFTP + vfs_init_sftpfs (); +#endif /* ENABLE_VFS_SFTP */ +#ifdef ENABLE_VFS_FISH + vfs_init_fish (); +#endif /* ENABLE_VFS_FISH */ +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/plugins_init.h b/src/vfs/plugins_init.h new file mode 100644 index 0000000..9a36f18 --- /dev/null +++ b/src/vfs/plugins_init.h @@ -0,0 +1,18 @@ +#ifndef MC__VFS_PLUINS_INIT_H +#define MC__VFS_PLUINS_INIT_H + +/*** typedefs(not structures) and defined constants **********************************************/ + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +/*** declarations of public functions ************************************************************/ + +void vfs_plugins_init (void); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__VFS_PLUINS_INIT_H */ diff --git a/src/vfs/sfs/Makefile.am b/src/vfs/sfs/Makefile.am new file mode 100644 index 0000000..7de97d0 --- /dev/null +++ b/src/vfs/sfs/Makefile.am @@ -0,0 +1,16 @@ + +AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) + +noinst_LTLIBRARIES = libvfs-sfs.la + +libvfs_sfs_la_SOURCES = \ + sfs.c sfs.h + +SFSCONFFILES = sfs.ini + +if ENABLE_VFS_SFS +sfsconfdir = $(sysconfdir)/@PACKAGE@ +sfsconf_DATA = $(SFSCONFFILES) +endif + +EXTRA_DIST = $(SFSCONFFILES) diff --git a/src/vfs/sfs/Makefile.in b/src/vfs/sfs/Makefile.in new file mode 100644 index 0000000..f9893eb --- /dev/null +++ b/src/vfs/sfs/Makefile.in @@ -0,0 +1,794 @@ +# 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/sfs +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_sfs_la_LIBADD = +am_libvfs_sfs_la_OBJECTS = sfs.lo +libvfs_sfs_la_OBJECTS = $(am_libvfs_sfs_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)/sfs.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_sfs_la_SOURCES) +DIST_SOURCES = $(libvfs_sfs_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__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(sfsconfdir)" +DATA = $(sfsconf_DATA) +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) +noinst_LTLIBRARIES = libvfs-sfs.la +libvfs_sfs_la_SOURCES = \ + sfs.c sfs.h + +SFSCONFFILES = sfs.ini +@ENABLE_VFS_SFS_TRUE@sfsconfdir = $(sysconfdir)/@PACKAGE@ +@ENABLE_VFS_SFS_TRUE@sfsconf_DATA = $(SFSCONFFILES) +EXTRA_DIST = $(SFSCONFFILES) +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/sfs/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/vfs/sfs/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-sfs.la: $(libvfs_sfs_la_OBJECTS) $(libvfs_sfs_la_DEPENDENCIES) $(EXTRA_libvfs_sfs_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libvfs_sfs_la_OBJECTS) $(libvfs_sfs_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sfs.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 +install-sfsconfDATA: $(sfsconf_DATA) + @$(NORMAL_INSTALL) + @list='$(sfsconf_DATA)'; test -n "$(sfsconfdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sfsconfdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sfsconfdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(sfsconfdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(sfsconfdir)" || exit $$?; \ + done + +uninstall-sfsconfDATA: + @$(NORMAL_UNINSTALL) + @list='$(sfsconf_DATA)'; test -n "$(sfsconfdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(sfsconfdir)'; $(am__uninstall_files_from_dir) + +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) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(sfsconfdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +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)/sfs.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-sfsconfDATA + +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)/sfs.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: uninstall-sfsconfDATA + +.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-sfsconfDATA \ + 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 \ + uninstall-sfsconfDATA + +.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/sfs/sfs.c b/src/vfs/sfs/sfs.c new file mode 100644 index 0000000..fdcc823 --- /dev/null +++ b/src/vfs/sfs/sfs.c @@ -0,0 +1,604 @@ +/* + Single File fileSystem + + Copyright (C) 1998-2023 + Free Software Foundation, Inc. + + Written by: + Slava Zanko <slavazanko@gmail.com>, 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/>. + */ + +/** + * \file + * \brief Source: Single File fileSystem + * + * This defines whole class of filesystems which contain single file + * inside. It is somehow similar to extfs, except that extfs makes + * whole virtual trees and we do only single virtual files. + * + * If you want to gunzip something, you should open it with \verbatim #ugz \endverbatim + * suffix, DON'T try to gunzip it yourself. + * + * Namespace: exports vfs_sfs_ops + */ + +#include <config.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> + +#include "lib/global.h" +#include "lib/util.h" +#include "lib/widget.h" /* D_ERROR, D_NORMAL */ + +#include "src/execute.h" /* EXECUTE_AS_SHELL */ + +#include "lib/vfs/vfs.h" +#include "lib/vfs/utilvfs.h" +#include "lib/vfs/xdirentry.h" +#include "src/vfs/local/local.h" +#include "lib/vfs/gc.h" /* vfs_stamp_create */ + +#include "sfs.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#define MAXFS 32 + +typedef enum +{ + F_NONE = 0x0, + F_1 = 0x1, + F_2 = 0x2, + F_NOLOCALCOPY = 0x4, + F_FULLMATCH = 0x8 +} sfs_flags_t; + +#define COPY_CHAR \ + if ((size_t) (t - pad) > sizeof (pad)) \ + { \ + g_free (pqname); \ + return (-1); \ + } \ + else \ + *t++ = *s_iter; + +#define COPY_STRING(a) \ + if ((t - pad) + strlen (a) > sizeof (pad)) \ + { \ + g_free (pqname); \ + return (-1); \ + } \ + else \ + { \ + strcpy (t, a); \ + t += strlen (a); \ + } + +/*** file scope type declarations ****************************************************************/ + +typedef struct cachedfile +{ + char *name; + char *cache; +} cachedfile; + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +static GSList *head = NULL; + +static struct vfs_s_subclass sfs_subclass; +static struct vfs_class *vfs_sfs_ops = VFS_CLASS (&sfs_subclass); + +static int sfs_no = 0; +static struct +{ + char *prefix; + char *command; + sfs_flags_t flags; +} sfs_info[MAXFS]; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static int +cachedfile_compare (const void *a, const void *b) +{ + const cachedfile *cf = (const cachedfile *) a; + const char *name = (const char *) b; + + return strcmp (name, cf->name); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +sfs_vfmake (const vfs_path_t * vpath, vfs_path_t * cache_vpath) +{ + int w; + char pad[10240]; + char *s_iter, *t = pad; + gboolean was_percent = FALSE; + vfs_path_t *pname; /* name of parent archive */ + char *pqname; /* name of parent archive, quoted */ + const vfs_path_element_t *path_element; + mc_pipe_t *pip; + GError *error = NULL; + + path_element = vfs_path_get_by_index (vpath, -1); + pname = vfs_path_clone (vpath); + vfs_path_remove_element_by_index (pname, -1); + + w = path_element->class->which (path_element->class, path_element->vfs_prefix); + if (w == -1) + vfs_die ("This cannot happen... Hopefully.\n"); + + if ((sfs_info[w].flags & F_1) == 0 + && strcmp (vfs_path_get_last_path_str (pname), PATH_SEP_STR) != 0) + { + vfs_path_free (pname, TRUE); + return (-1); + } + + /* if ((sfs_info[w].flags & F_2) || (!inpath) || (!*inpath)); else return -1; */ + if ((sfs_info[w].flags & F_NOLOCALCOPY) != 0) + pqname = name_quote (vfs_path_as_str (pname), FALSE); + else + { + vfs_path_t *s; + + s = mc_getlocalcopy (pname); + if (s == NULL) + { + vfs_path_free (pname, TRUE); + return (-1); + } + + pqname = name_quote (vfs_path_get_last_path_str (s), FALSE); + vfs_path_free (s, TRUE); + } + + vfs_path_free (pname, TRUE); + + for (s_iter = sfs_info[w].command; *s_iter != '\0'; s_iter++) + { + if (was_percent) + { + const char *ptr = NULL; + + was_percent = FALSE; + + switch (*s_iter) + { + case '1': + ptr = pqname; + break; + case '2': + ptr = path_element->path; + break; + case '3': + ptr = vfs_path_get_last_path_str (cache_vpath); + break; + case '%': + COPY_CHAR; + continue; + default: + break; + } + + if (ptr != NULL) + { + COPY_STRING (ptr); + } + } + else if (*s_iter == '%') + was_percent = TRUE; + else + { + COPY_CHAR; + } + } + + g_free (pqname); + + /* don't read stdout */ + pip = mc_popen (pad, FALSE, TRUE, &error); + if (pip == NULL) + { + message (D_ERROR, MSG_ERROR, _("SFS virtual file system:\n%s"), error->message); + g_error_free (error); + return (-1); + } + + pip->err.null_term = TRUE; + + mc_pread (pip, &error); + if (error != NULL) + { + message (D_ERROR, MSG_ERROR, _("SFS virtual file system:\n%s"), error->message); + g_error_free (error); + mc_pclose (pip, NULL); + return (-1); + } + + if (pip->err.len > 0) + message (D_ERROR, MSG_ERROR, _("SFS virtual file system:\n%s"), pip->err.buf); + + mc_pclose (pip, NULL); + return 0; /* OK */ +} + +/* --------------------------------------------------------------------------------------------- */ + +static const char * +sfs_redirect (const vfs_path_t * vpath) +{ + GSList *cur; + cachedfile *cf; + vfs_path_t *cache_vpath; + int handle; + + cur = g_slist_find_custom (head, vfs_path_as_str (vpath), cachedfile_compare); + + if (cur != NULL) + { + cf = (cachedfile *) cur->data; + vfs_stamp (vfs_sfs_ops, cf); + return cf->cache; + } + + handle = vfs_mkstemps (&cache_vpath, "sfs", vfs_path_get_last_path_str (vpath)); + + if (handle == -1) + return "/SOMEONE_PLAYING_DIRTY_TMP_TRICKS_ON_US"; + + close (handle); + + if (sfs_vfmake (vpath, cache_vpath) == 0) + { + cf = g_new (cachedfile, 1); + cf->name = g_strdup (vfs_path_as_str (vpath)); + cf->cache = vfs_path_free (cache_vpath, FALSE); + head = g_slist_prepend (head, cf); + + vfs_stamp_create (vfs_sfs_ops, (cachedfile *) head->data); + return cf->cache; + } + + mc_unlink (cache_vpath); + vfs_path_free (cache_vpath, TRUE); + return "/I_MUST_NOT_EXIST"; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void * +sfs_open (const vfs_path_t * vpath /*struct vfs_class *me, const char *path */ , int flags, + mode_t mode) +{ + int *info; + int fd; + + fd = open (sfs_redirect (vpath), NO_LINEAR (flags), mode); + if (fd == -1) + return NULL; + + info = g_new (int, 1); + *info = fd; + + return info; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +sfs_stat (const vfs_path_t * vpath, struct stat *buf) +{ + return stat (sfs_redirect (vpath), buf); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +sfs_lstat (const vfs_path_t * vpath, struct stat *buf) +{ +#ifndef HAVE_STATLSTAT + return lstat (sfs_redirect (vpath), buf); +#else + return statlstat (sfs_redirect (vpath), buf); +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +sfs_chmod (const vfs_path_t * vpath, mode_t mode) +{ + return chmod (sfs_redirect (vpath), mode); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +sfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group) +{ + return chown (sfs_redirect (vpath), owner, group); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +sfs_utime (const vfs_path_t * vpath, mc_timesbuf_t * times) +{ +#ifdef HAVE_UTIMENSAT + return utimensat (AT_FDCWD, sfs_redirect (vpath), *times, 0); +#else + return utime (sfs_redirect (vpath), times); +#endif +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +sfs_readlink (const vfs_path_t * vpath, char *buf, size_t size) +{ + return readlink (sfs_redirect (vpath), buf, size); +} + +/* --------------------------------------------------------------------------------------------- */ + +static vfsid +sfs_getid (const vfs_path_t * vpath) +{ + GSList *cur; + + cur = g_slist_find_custom (head, vfs_path_as_str (vpath), cachedfile_compare); + + return (vfsid) (cur != NULL ? cur->data : NULL); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +sfs_free (vfsid id) +{ + struct cachedfile *which; + GSList *cur; + + which = (struct cachedfile *) id; + cur = g_slist_find (head, which); + if (cur == NULL) + vfs_die ("Free of thing which is unknown to me\n"); + + which = (struct cachedfile *) cur->data; + unlink (which->cache); + g_free (which->cache); + g_free (which->name); + g_free (which); + + head = g_slist_delete_link (head, cur); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +sfs_fill_names (struct vfs_class *me, fill_names_f func) +{ + GSList *cur; + + (void) me; + + for (cur = head; cur != NULL; cur = g_slist_next (cur)) + func (((cachedfile *) cur->data)->name); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sfs_nothingisopen (vfsid id) +{ + /* FIXME: Investigate whether have to guard this like in + the other VFSs (see fd_usage in extfs) -- Norbert */ + (void) id; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static vfs_path_t * +sfs_getlocalcopy (const vfs_path_t * vpath) +{ + return vfs_path_from_str (sfs_redirect (vpath)); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +sfs_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed) +{ + (void) vpath; + (void) local; + (void) has_changed; + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +sfs_init (struct vfs_class *me) +{ + char *mc_sfsini; + FILE *cfg; + char key[256]; + + (void) me; + + mc_sfsini = g_build_filename (mc_global.sysconfig_dir, "sfs.ini", (char *) NULL); + cfg = fopen (mc_sfsini, "r"); + + if (cfg == NULL) + { + fprintf (stderr, _("%s: Warning: file %s not found\n"), "sfs_init()", mc_sfsini); + g_free (mc_sfsini); + return 0; + } + g_free (mc_sfsini); + + sfs_no = 0; + while (sfs_no < MAXFS && fgets (key, sizeof (key), cfg) != NULL) + { + char *c, *semi = NULL; + sfs_flags_t flags = F_NONE; + + if (*key == '#' || *key == '\n') + continue; + + for (c = key; *c != '\0'; c++) + if (*c == ':' || IS_PATH_SEP (*c)) + { + semi = c; + if (IS_PATH_SEP (*c)) + { + *c = '\0'; + flags |= F_FULLMATCH; + } + break; + } + + if (semi == NULL) + { + invalid_line: + fprintf (stderr, _("Warning: Invalid line in %s:\n%s\n"), "sfs.ini", key); + continue; + } + + for (c = semi + 1; *c != '\0' && !whitespace (*c); c++) + switch (*c) + { + case '1': + flags |= F_1; + break; + case '2': + flags |= F_2; + break; + case 'R': + flags |= F_NOLOCALCOPY; + break; + default: + fprintf (stderr, _("Warning: Invalid flag %c in %s:\n%s\n"), *c, "sfs.ini", key); + } + + if (*c == '\0') + goto invalid_line; + + c++; + *(semi + 1) = '\0'; + semi = strchr (c, '\n'); + if (semi != NULL) + *semi = '\0'; + + sfs_info[sfs_no].prefix = g_strdup (key); + sfs_info[sfs_no].command = g_strdup (c); + sfs_info[sfs_no].flags = flags; + sfs_no++; + } + fclose (cfg); + + return 1; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +sfs_done (struct vfs_class *me) +{ + int i; + + (void) me; + + for (i = 0; i < sfs_no; i++) + { + MC_PTR_FREE (sfs_info[i].prefix); + MC_PTR_FREE (sfs_info[i].command); + } + sfs_no = 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +sfs_which (struct vfs_class *me, const char *path) +{ + int i; + + (void) me; + + for (i = 0; i < sfs_no; i++) + if ((sfs_info[i].flags & F_FULLMATCH) != 0) + { + if (strcmp (path, sfs_info[i].prefix) == 0) + return i; + } + else if (strncmp (path, sfs_info[i].prefix, strlen (sfs_info[i].prefix)) == 0) + return i; + + return (-1); +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +vfs_init_sfs (void) +{ + /* NULLize vfs_s_subclass members */ + memset (&sfs_subclass, 0, sizeof (sfs_subclass)); + + vfs_init_class (vfs_sfs_ops, "sfs", VFSF_UNKNOWN, NULL); + vfs_sfs_ops->init = sfs_init; + vfs_sfs_ops->done = sfs_done; + vfs_sfs_ops->fill_names = sfs_fill_names; + vfs_sfs_ops->which = sfs_which; + vfs_sfs_ops->open = sfs_open; + vfs_sfs_ops->close = local_close; + vfs_sfs_ops->read = local_read; + vfs_sfs_ops->stat = sfs_stat; + vfs_sfs_ops->lstat = sfs_lstat; + vfs_sfs_ops->fstat = local_fstat; + vfs_sfs_ops->chmod = sfs_chmod; + vfs_sfs_ops->chown = sfs_chown; + vfs_sfs_ops->utime = sfs_utime; + vfs_sfs_ops->readlink = sfs_readlink; + vfs_sfs_ops->ferrno = local_errno; + vfs_sfs_ops->lseek = local_lseek; + vfs_sfs_ops->getid = sfs_getid; + vfs_sfs_ops->nothingisopen = sfs_nothingisopen; + vfs_sfs_ops->free = sfs_free; + vfs_sfs_ops->getlocalcopy = sfs_getlocalcopy; + vfs_sfs_ops->ungetlocalcopy = sfs_ungetlocalcopy; + vfs_register_class (vfs_sfs_ops); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sfs/sfs.h b/src/vfs/sfs/sfs.h new file mode 100644 index 0000000..c846c93 --- /dev/null +++ b/src/vfs/sfs/sfs.h @@ -0,0 +1,18 @@ +#ifndef MC__VFS_SFS_H +#define MC__VFS_SFS_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_sfs (void); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__VFS_SFS_H */ diff --git a/src/vfs/sfs/sfs.ini b/src/vfs/sfs/sfs.ini new file mode 100644 index 0000000..d817dc9 --- /dev/null +++ b/src/vfs/sfs/sfs.ini @@ -0,0 +1,34 @@ +# +# This is config for Single File fileSystem +# +# Notice that output files (%3) are pre-created atomically in /tmp +# with 0600 rights, so it is safe to > %3 +# +gz/1 gzip < %1 > %3 +ugz/1 gzip -cdf < %1 > %3 +bz/1 bzip < %1 > %3 +ubz/1 bzip -d < %1 > %3 +bz2/1 bzip2 < %1 > %3 +ubz2/1 bzip2 -d < %1 > %3 +lz/1 lzip < %1 > %3 +ulz/1 lzip -d < %1 > %3 +lz4/1 lz4 < %1 > %3 +ulz4/1 lz4 -d < %1 > %3 +lzma/1 lzma < %1 > %3 +ulzma/1 lzma -d < %1 > %3 +xz/1 xz < %1 > %3 +uxz/1 xz -d < %1 > %3 +zst/1 zstd < %1 > %3 +uzst/1 zstd -d < %1 > %3 +tar/1 tar cf %3 %1 +tgz/1 tar czf %3 %1 +uhtml/1 lynx -force_html -dump %1 > %3 +uman/1 groff -Tascii -man %1 > %3 +uue/1 uuenpipe < %1 > %3 +uude/1 uudepipe < %1 > %3 +crlf/1 todos < %1 > %3 +cr/1 fromdos < %1 > %3 +# Fixme: we need it to fail whenever it should +url:2 lynx -source `echo "%2" | sed 's-|-/-g'` > %3 +nop/1 cat %1 > %3 +strings/1 strings %1 > %3 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 */ diff --git a/src/vfs/tar/Makefile.am b/src/vfs/tar/Makefile.am new file mode 100644 index 0000000..16642f0 --- /dev/null +++ b/src/vfs/tar/Makefile.am @@ -0,0 +1,10 @@ + +AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) + +noinst_LTLIBRARIES = libvfs-tar.la + +libvfs_tar_la_SOURCES = \ + tar-internal.c tar-internal.h \ + tar-sparse.c \ + tar-xheader.c \ + tar.c tar.h diff --git a/src/vfs/tar/Makefile.in b/src/vfs/tar/Makefile.in new file mode 100644 index 0000000..c89786e --- /dev/null +++ b/src/vfs/tar/Makefile.in @@ -0,0 +1,750 @@ +# 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/tar +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_tar_la_LIBADD = +am_libvfs_tar_la_OBJECTS = tar-internal.lo tar-sparse.lo \ + tar-xheader.lo tar.lo +libvfs_tar_la_OBJECTS = $(am_libvfs_tar_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)/tar-internal.Plo \ + ./$(DEPDIR)/tar-sparse.Plo ./$(DEPDIR)/tar-xheader.Plo \ + ./$(DEPDIR)/tar.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_tar_la_SOURCES) +DIST_SOURCES = $(libvfs_tar_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) +noinst_LTLIBRARIES = libvfs-tar.la +libvfs_tar_la_SOURCES = \ + tar-internal.c tar-internal.h \ + tar-sparse.c \ + tar-xheader.c \ + tar.c tar.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/tar/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/vfs/tar/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-tar.la: $(libvfs_tar_la_OBJECTS) $(libvfs_tar_la_DEPENDENCIES) $(EXTRA_libvfs_tar_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libvfs_tar_la_OBJECTS) $(libvfs_tar_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tar-internal.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tar-sparse.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tar-xheader.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tar.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)/tar-internal.Plo + -rm -f ./$(DEPDIR)/tar-sparse.Plo + -rm -f ./$(DEPDIR)/tar-xheader.Plo + -rm -f ./$(DEPDIR)/tar.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)/tar-internal.Plo + -rm -f ./$(DEPDIR)/tar-sparse.Plo + -rm -f ./$(DEPDIR)/tar-xheader.Plo + -rm -f ./$(DEPDIR)/tar.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/tar/tar-internal.c b/src/vfs/tar/tar-internal.c new file mode 100644 index 0000000..f77b1b3 --- /dev/null +++ b/src/vfs/tar/tar-internal.c @@ -0,0 +1,482 @@ +/* + Virtual File System: GNU Tar file system. + + Copyright (C) 2023 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin <aborodin@vmail.ru>, 2023 + + 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/>. + */ + +/** + * \file + * \brief Source: Virtual File System: GNU Tar file system + * \author Andrew Borodin + * \date 2022 + */ + +#include <config.h> + +#include <ctype.h> /* isspace() */ +#include <inttypes.h> /* uintmax_t */ +#include <stdint.h> /* UINTMAX_MAX, etc */ + +#include "lib/global.h" +#include "lib/widget.h" /* message() */ +#include "lib/vfs/vfs.h" /* mc_read() */ + +#include "tar-internal.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/* Log base 2 of common values. */ +#define LG_8 3 +#define LG_64 6 +#define LG_256 8 + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +/* Base 64 digits; see RFC 2045 Table 1. */ +static char const base_64_digits[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + +/* Table of base 64 digit values indexed by unsigned chars. + The value is 64 for unsigned chars that are not base 64 digits. */ +static char base64_map[1 + (unsigned char) (-1)]; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +tar_short_read (size_t status, tar_super_t * archive) +{ + size_t left; /* bytes left */ + char *more; /* pointer to next byte to read */ + + more = archive->record_start->buffer + status; + left = record_size - status; + + while (left % BLOCKSIZE != 0 || (left != 0 && status != 0)) + { + if (status != 0) + { + ssize_t r; + + r = mc_read (archive->fd, more, left); + if (r == -1) + return FALSE; + + status = (size_t) r; + } + + if (status == 0) + break; + + left -= status; + more += status; + } + + record_end = archive->record_start + (record_size - left) / BLOCKSIZE; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +tar_flush_read (tar_super_t * archive) +{ + size_t status; + + status = mc_read (archive->fd, archive->record_start->buffer, record_size); + if (status == record_size) + return TRUE; + + return tar_short_read (status, archive); +} + +/* --------------------------------------------------------------------------------------------- */ + +/** Flush the current buffer from the archive. + */ +static gboolean +tar_flush_archive (tar_super_t * archive) +{ + record_start_block += record_end - archive->record_start; + current_block = archive->record_start; + record_end = archive->record_start + blocking_factor; + + return tar_flush_read (archive); +} + +/* --------------------------------------------------------------------------------------------- */ + +static off_t +tar_seek_archive (tar_super_t * archive, off_t size) +{ + off_t start, offset; + off_t nrec, nblk; + off_t skipped; + + skipped = (blocking_factor - (current_block - archive->record_start)) * BLOCKSIZE; + if (size <= skipped) + return 0; + + /* Compute number of records to skip */ + nrec = (size - skipped) / record_size; + if (nrec == 0) + return 0; + + start = tar_current_block_ordinal (archive); + + offset = mc_lseek (archive->fd, nrec * record_size, SEEK_CUR); + if (offset < 0) + return offset; + +#if 0 + if ((offset % record_size) != 0) + { + message (D_ERROR, MSG_ERROR, _("tar: mc_lseek not stopped at a record boundary")); + return -1; + } +#endif + + /* Convert to number of records */ + offset /= BLOCKSIZE; + /* Compute number of skipped blocks */ + nblk = offset - start; + + /* Update buffering info */ + record_start_block = offset - blocking_factor; + current_block = record_end; + + return nblk; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +tar_base64_init (void) +{ + size_t i; + + memset (base64_map, 64, sizeof base64_map); + for (i = 0; i < 64; i++) + base64_map[(int) base_64_digits[i]] = i; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tar_assign_string (char **string, char *value) +{ + g_free (*string); + *string = value; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tar_assign_string_dup (char **string, const char *value) +{ + g_free (*string); + *string = g_strdup (value); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tar_assign_string_dup_n (char **string, const char *value, size_t n) +{ + g_free (*string); + *string = g_strndup (value, n); +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Convert buffer at @where0 of size @digs from external format to intmax_t. + * @digs must be positive. + * If @type is non-NULL, data are of type @type. + * The buffer must represent a value in the range -@minval through @maxval; + * if the mathematically correct result V would be greater than INTMAX_MAX, + * return a negative integer V such that (uintmax_t) V yields the correct result. + * If @octal_only, allow only octal numbers instead of the other GNU extensions. + * + * Result is -1 if the field is invalid. + */ +#if !(INTMAX_MAX <= UINTMAX_MAX && - (INTMAX_MIN + 1) <= UINTMAX_MAX) +#error "tar_from_header() internally represents intmax_t as uintmax_t + sign" +#endif +#if !(UINTMAX_MAX / 2 <= INTMAX_MAX) +#error "tar_from_header() returns intmax_t to represent uintmax_t" +#endif +intmax_t +tar_from_header (const char *where0, size_t digs, char const *type, intmax_t minval, + uintmax_t maxval, gboolean octal_only) +{ + uintmax_t value = 0; + uintmax_t uminval = minval; + uintmax_t minus_minval = -uminval; + const char *where = where0; + char const *lim = where + digs; + gboolean negative = FALSE; + + /* Accommodate buggy tar of unknown vintage, which outputs leading + NUL if the previous field overflows. */ + if (*where == '\0') + where++; + + /* Accommodate older tars, which output leading spaces. */ + while (TRUE) + { + if (where == lim) + return (-1); + + if (!isspace ((unsigned char) *where)) + break; + + where++; + } + + if (isodigit (*where)) + { + char const *where1 = where; + gboolean overflow = FALSE; + + while (TRUE) + { + value += *where++ - '0'; + if (where == lim || !isodigit (*where)) + break; + overflow |= value != (value << LG_8 >> LG_8); + value <<= LG_8; + } + + /* Parse the output of older, unportable tars, which generate + negative values in two's complement octal. If the leading + nonzero digit is 1, we can't recover the original value + reliably; so do this only if the digit is 2 or more. This + catches the common case of 32-bit negative time stamps. */ + if ((overflow || maxval < value) && *where1 >= 2 && type != NULL) + { + /* Compute the negative of the input value, assuming two's complement. */ + int digit; + + digit = (*where1 - '0') | 4; + overflow = FALSE; + value = 0; + where = where1; + + while (TRUE) + { + value += 7 - digit; + where++; + if (where == lim || !isodigit (*where)) + break; + digit = *where - '0'; + overflow |= value != (value << LG_8 >> LG_8); + value <<= LG_8; + } + + value++; + overflow |= value == 0; + + if (!overflow && value <= minus_minval) + negative = TRUE; + } + + if (overflow) + return (-1); + } + else if (octal_only) + { + /* Suppress the following extensions. */ + } + else if (*where == '-' || *where == '+') + { + /* Parse base-64 output produced only by tar test versions + 1.13.6 (1999-08-11) through 1.13.11 (1999-08-23). + Support for this will be withdrawn in future tar releases. */ + int dig; + + negative = *where++ == '-'; + + while (where != lim && (dig = base64_map[(unsigned char) *where]) < 64) + { + if (value << LG_64 >> LG_64 != value) + return (-1); + value = (value << LG_64) | dig; + where++; + } + } + else if (where <= lim - 2 && (*where == '\200' /* positive base-256 */ + || *where == '\377' /* negative base-256 */ )) + { + /* Parse base-256 output. A nonnegative number N is + represented as (256**DIGS)/2 + N; a negative number -N is + represented as (256**DIGS) - N, i.e. as two's complement. + The representation guarantees that the leading bit is + always on, so that we don't confuse this format with the + others (assuming ASCII bytes of 8 bits or more). */ + + int signbit; + uintmax_t topbits; + + signbit = *where & (1 << (LG_256 - 2)); + topbits = + (((uintmax_t) - signbit) << (CHAR_BIT * sizeof (uintmax_t) - LG_256 - (LG_256 - 2))); + + value = (*where++ & ((1 << (LG_256 - 2)) - 1)) - signbit; + + while (TRUE) + { + value = (value << LG_256) + (unsigned char) *where++; + if (where == lim) + break; + + if (((value << LG_256 >> LG_256) | topbits) != value) + return (-1); + } + + negative = signbit != 0; + if (negative) + value = -value; + } + + if (where != lim && *where != '\0' && !isspace ((unsigned char) *where)) + return (-1); + + if (value <= (negative ? minus_minval : maxval)) + return tar_represent_uintmax (negative ? -value : value); + + return (-1); +} + +/* --------------------------------------------------------------------------------------------- */ + +off_t +off_from_header (const char *p, size_t s) +{ + /* Negative offsets are not allowed in tar files, so invoke + from_header with minimum value 0, not TYPE_MINIMUM (off_t). */ + return tar_from_header (p, s, "off_t", 0, TYPE_MAXIMUM (off_t), FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Return the location of the next available input or output block. + * Return NULL for EOF. + */ +union block * +tar_find_next_block (tar_super_t * archive) +{ + if (current_block == record_end) + { + if (hit_eof) + return NULL; + + if (!tar_flush_archive (archive)) + { + message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive")); + return NULL; + } + + if (current_block == record_end) + { + hit_eof = TRUE; + return NULL; + } + } + + return current_block; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Indicate that we have used all blocks up thru @block. + */ +gboolean +tar_set_next_block_after (union block * block) +{ + while (block >= current_block) + current_block++; + + /* Do *not* flush the archive here. If we do, the same argument to tar_set_next_block_after() + could mean the next block (if the input record is exactly one block long), which is not + what is intended. */ + + return !(current_block > record_end); +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Compute and return the block ordinal at current_block. + */ +off_t +tar_current_block_ordinal (const tar_super_t * archive) +{ + return record_start_block + (current_block - archive->record_start); +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Skip over @size bytes of data in blocks in the archive. + */ +gboolean +tar_skip_file (tar_super_t * archive, off_t size) +{ + union block *x; + off_t nblk; + + nblk = tar_seek_archive (archive, size); + if (nblk >= 0) + size -= nblk * BLOCKSIZE; + + while (size > 0) + { + x = tar_find_next_block (archive); + if (x == NULL) + return FALSE; + + tar_set_next_block_after (x); + size -= BLOCKSIZE; + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/tar/tar-internal.h b/src/vfs/tar/tar-internal.h new file mode 100644 index 0000000..7b3bb53 --- /dev/null +++ b/src/vfs/tar/tar-internal.h @@ -0,0 +1,351 @@ + +#ifndef MC__VFS_TAR_INTERNAL_H +#define MC__VFS_TAR_INTERNAL_H + +#include <inttypes.h> /* (u)intmax_t */ +#include <limits.h> /* CHAR_BIT, INT_MAX, etc */ +#include <sys/stat.h> +#include <sys/types.h> + +#include "lib/vfs/xdirentry.h" /* vfs_s_super */ + +/*** typedefs(not structures) and defined constants **********************************************/ + +/* tar files are made in basic blocks of this size. */ +#define BLOCKSIZE 512 + +#define DEFAULT_BLOCKING 20 + +/* Sparse files are not supported in POSIX ustar format. For sparse files + with a POSIX header, a GNU extra header is provided which holds overall + sparse information and a few sparse descriptors. When an old GNU header + replaces both the POSIX header and the GNU extra header, it holds some + sparse descriptors too. Whether POSIX or not, if more sparse descriptors + are still needed, they are put into as many successive sparse headers as + necessary. The following constants tell how many sparse descriptors fit + in each kind of header able to hold them. */ + +#define SPARSES_IN_EXTRA_HEADER 16 +#define SPARSES_IN_OLDGNU_HEADER 4 +#define SPARSES_IN_SPARSE_HEADER 21 + +#define SPARSES_IN_STAR_HEADER 4 +#define SPARSES_IN_STAR_EXT_HEADER 21 + +/* *BEWARE* *BEWARE* *BEWARE* that the following information is still + boiling, and may change. Even if the OLDGNU format description should be + accurate, the so-called GNU format is not yet fully decided. It is + surely meant to use only extensions allowed by POSIX, but the sketch + below repeats some ugliness from the OLDGNU format, which should rather + go away. Sparse files should be saved in such a way that they do *not* + require two passes at archive creation time. Huge files get some POSIX + fields to overflow, alternate solutions have to be sought for this. */ + +/* This is a dir entry that contains the names of files that were in the + dir at the time the dump was made. */ +#define GNUTYPE_DUMPDIR 'D' + +/* Identifies the *next* file on the tape as having a long linkname. */ +#define GNUTYPE_LONGLINK 'K' + +/* Identifies the *next* file on the tape as having a long name. */ +#define GNUTYPE_LONGNAME 'L' + +/* Solaris extended header */ +#define SOLARIS_XHDTYPE 'X' + +#define GNUTYPE_SPARSE 'S' + + +/* These macros work even on ones'-complement hosts (!). + The extra casts work around some compiler bugs. */ +#define TYPE_SIGNED(t) (!((t) 0 < (t) (-1))) +#define TYPE_MINIMUM(t) (TYPE_SIGNED (t) ? ~(t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0) +#define TYPE_MAXIMUM(t) (~(t) 0 - TYPE_MINIMUM (t)) + +#define OFF_FROM_HEADER(where) off_from_header (where, sizeof (where)) + +#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') ) + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/* *INDENT-OFF* */ + +/* POSIX header */ +struct posix_header +{ /* byte offset */ + char name[100]; /* 0 */ + char mode[8]; /* 100 */ + char uid[8]; /* 108 */ + char gid[8]; /* 116 */ + char size[12]; /* 124 */ + char mtime[12]; /* 136 */ + char chksum[8]; /* 148 */ + char typeflag; /* 156 */ + char linkname[100]; /* 157 */ + char magic[6]; /* 257 */ + char version[2]; /* 263 */ + char uname[32]; /* 265 */ + char gname[32]; /* 297 */ + char devmajor[8]; /* 329 */ + char devminor[8]; /* 337 */ + char prefix[155]; /* 345 */ + /* 500 */ +}; + +/* Descriptor for a single file hole */ +struct sparse +{ /* byte offset */ + /* cppcheck-suppress unusedStructMember */ + char offset[12]; /* 0 */ + /* cppcheck-suppress unusedStructMember */ + char numbytes[12]; /* 12 */ + /* 24 */ +}; + +/* Extension header for sparse files, used immediately after the GNU extra + header, and used only if all sparse information cannot fit into that + extra header. There might even be many such extension headers, one after + the other, until all sparse information has been recorded. */ +struct sparse_header +{ /* byte offset */ + struct sparse sp[SPARSES_IN_SPARSE_HEADER]; + /* 0 */ + char isextended; /* 504 */ + /* 505 */ +}; + +/* The old GNU format header conflicts with POSIX format in such a way that + POSIX archives may fool old GNU tar's, and POSIX tar's might well be + fooled by old GNU tar archives. An old GNU format header uses the space + used by the prefix field in a POSIX header, and cumulates information + normally found in a GNU extra header. With an old GNU tar header, we + never see any POSIX header nor GNU extra header. Supplementary sparse + headers are allowed, however. */ +struct oldgnu_header +{ /* byte offset */ + char unused_pad1[345]; /* 0 */ + char atime[12]; /* 345 Incr. archive: atime of the file */ + char ctime[12]; /* 357 Incr. archive: ctime of the file */ + char offset[12]; /* 369 Multivolume archive: the offset of start of this volume */ + char longnames[4]; /* 381 Not used */ + char unused_pad2; /* 385 */ + struct sparse sp[SPARSES_IN_OLDGNU_HEADER]; + /* 386 */ + char isextended; /* 482 Sparse file: Extension sparse header follows */ + char realsize[12]; /* 483 Sparse file: Real size */ + /* 495 */ +}; + +/* J@"org Schilling star header */ +struct star_header +{ /* byte offset */ + char name[100]; /* 0 */ + char mode[8]; /* 100 */ + char uid[8]; /* 108 */ + char gid[8]; /* 116 */ + char size[12]; /* 124 */ + char mtime[12]; /* 136 */ + char chksum[8]; /* 148 */ + char typeflag; /* 156 */ + char linkname[100]; /* 157 */ + char magic[6]; /* 257 */ + char version[2]; /* 263 */ + char uname[32]; /* 265 */ + char gname[32]; /* 297 */ + char devmajor[8]; /* 329 */ + char devminor[8]; /* 337 */ + char prefix[131]; /* 345 */ + char atime[12]; /* 476 */ + char ctime[12]; /* 488 */ + /* 500 */ +}; + +struct star_in_header +{ + char fill[345]; /* 0 Everything that is before t_prefix */ + char prefix[1]; /* 345 t_name prefix */ + char fill2; /* 346 */ + char fill3[8]; /* 347 */ + char isextended; /* 355 */ + struct sparse sp[SPARSES_IN_STAR_HEADER]; /* 356 */ + char realsize[12]; /* 452 Actual size of the file */ + char offset[12]; /* 464 Offset of multivolume contents */ + char atime[12]; /* 476 */ + char ctime[12]; /* 488 */ + char mfill[8]; /* 500 */ + char xmagic[4]; /* 508 "tar" */ +}; + +struct star_ext_header +{ + struct sparse sp[SPARSES_IN_STAR_EXT_HEADER]; + char isextended; +}; + +/* *INDENT-ON* */ + +/* tar Header Block, overall structure */ +union block +{ + char buffer[BLOCKSIZE]; + struct posix_header header; + struct star_header star_header; + struct oldgnu_header oldgnu_header; + struct sparse_header sparse_header; + struct star_in_header star_in_header; + struct star_ext_header star_ext_header; +}; + +/* Information about a sparse file */ +struct sp_array +{ + off_t offset; /* chunk offset in file */ + off_t numbytes; /* length of chunk */ + off_t arch_offset; /* chunk offset in archive */ +}; + +enum dump_status +{ + dump_status_ok, + dump_status_short, + dump_status_fail, + dump_status_not_implemented +}; + +enum archive_format +{ + TAR_UNKNOWN = 0, /**< format to be decided later */ + TAR_V7, /**< old V7 tar format */ + TAR_OLDGNU, /**< GNU format as per before tar 1.12 */ + TAR_USTAR, /**< POSIX.1-1988 (ustar) format */ + TAR_POSIX, /**< POSIX.1-2001 format */ + TAR_STAR, /**< star format defined in 1994 */ + TAR_GNU /**< almost same as OLDGNU_FORMAT */ +}; + +typedef struct +{ + struct vfs_s_super base; /* base class */ + + int fd; + struct stat st; + enum archive_format type; /**< type of the archive */ + union block *record_start; /**< start of record of archive */ +} tar_super_t; + +struct xheader +{ + size_t size; + char *buffer; +}; + +struct tar_stat_info +{ + char *orig_file_name; /**< name of file read from the archive header */ + char *file_name; /**< name of file for the current archive entry after being normalized */ + char *link_name; /**< name of link for the current archive entry */ +#if 0 + char *uname; /**< user name of owner */ + char *gname; /**< group name of owner */ +#endif + struct stat stat; /**< regular filesystem stat */ + + /* stat() doesn't always have access, data modification, and status + change times in a convenient form, so store them separately. */ + struct timespec atime; + struct timespec mtime; + struct timespec ctime; + + off_t archive_file_size; /**< size of file as stored in the archive. + Equals stat.st_size for non-sparse files */ + gboolean is_sparse; /**< is the file sparse */ + + /* For sparse files */ + unsigned int sparse_major; + unsigned int sparse_minor; + GArray *sparse_map; /**< array of struct sp_array */ + + off_t real_size; /**< real size of sparse file */ + gboolean real_size_set; /**< TRUE when GNU.sparse.realsize is set in archived file */ + + gboolean sparse_name_done; /**< TRUE if 'GNU.sparse.name' header was processed pax header parsing. + Following 'path' header (lower priority) will be ignored. */ + + /* Extended headers */ + struct xheader xhdr; + + /* For dumpdirs */ + gboolean is_dumpdir; /**< is the member a dumpdir? */ + gboolean skipped; /**< the member contents is already read (for GNUTYPE_DUMPDIR) */ + char *dumpdir; /**< contents of the dump directory */ +}; + +/*** global variables defined in .c file *********************************************************/ + +extern const int blocking_factor; +extern const size_t record_size; + +extern union block *record_end; /* last+1 block of archive record */ +extern union block *current_block; /* current block of archive */ +extern off_t record_start_block; /* block ordinal at record_start */ + +extern union block *current_header; + +/* Have we hit EOF yet? */ +extern gboolean hit_eof; + +extern struct tar_stat_info current_stat_info; + +/*** declarations of public functions ************************************************************/ + +/* tar-internal.c */ +void tar_base64_init (void); +void tar_assign_string (char **string, char *value); +void tar_assign_string_dup (char **string, const char *value); +void tar_assign_string_dup_n (char **string, const char *value, size_t n); +intmax_t tar_from_header (const char *where0, size_t digs, char const *type, intmax_t minval, + uintmax_t maxval, gboolean octal_only); +off_t off_from_header (const char *p, size_t s); +union block *tar_find_next_block (tar_super_t * archive); +gboolean tar_set_next_block_after (union block *block); +off_t tar_current_block_ordinal (const tar_super_t * archive); +gboolean tar_skip_file (tar_super_t * archive, off_t size); + +/* tar-sparse.c */ +gboolean tar_sparse_member_p (tar_super_t * archive, struct tar_stat_info *st); +gboolean tar_sparse_fixup_header (tar_super_t * archive, struct tar_stat_info *st); +enum dump_status tar_sparse_skip_file (tar_super_t * archive, struct tar_stat_info *st); + +/* tar-xheader.c */ +gboolean tar_xheader_decode (struct tar_stat_info *st); +gboolean tar_xheader_read (tar_super_t * archive, struct xheader *xhdr, union block *header, + off_t size); +gboolean tar_xheader_decode_global (struct xheader *xhdr); +void tar_xheader_destroy (struct xheader *xhdr); + +/*** inline functions ****************************************************************************/ + +/** + * Represent @n using a signed integer I such that (uintmax_t) I == @n. + With a good optimizing compiler, this is equivalent to (intmax_t) i + and requires zero machine instructions. */ +#if !(UINTMAX_MAX / 2 <= INTMAX_MAX) +#error "tar_represent_uintmax() returns intmax_t to represent uintmax_t" +#endif +static inline intmax_t +tar_represent_uintmax (uintmax_t n) +{ + intmax_t nd; + + if (n <= INTMAX_MAX) + return n; + + /* Avoid signed integer overflow on picky platforms. */ + nd = n - INTMAX_MIN; + return nd + INTMAX_MIN; +} + +#endif /* MC__VFS_TAR_INTERNAL_H */ diff --git a/src/vfs/tar/tar-sparse.c b/src/vfs/tar/tar-sparse.c new file mode 100644 index 0000000..0bc169b --- /dev/null +++ b/src/vfs/tar/tar-sparse.c @@ -0,0 +1,777 @@ +/* + Virtual File System: GNU Tar file system. + + Copyright (C) 2003-2023 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin <aborodin@vmail.ru>, 2023 + + 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/>. + */ + +/** + * \file + * \brief Source: Virtual File System: GNU Tar file system + */ + +/* + * Avoid following error: + * comparison of unsigned expression < 0 is always false [-Werror=type-limits] + * + * https://www.boost.org/doc/libs/1_55_0/libs/integer/test/cstdint_test.cpp + * We can't suppress this warning on the command line as not all GCC versions support -Wno-type-limits + */ +#if defined(__GNUC__) && (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) +#pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +#include <config.h> + +#include <ctype.h> /* isdigit() */ +#include <errno.h> +#include <inttypes.h> /* uintmax_t */ + +#include "lib/global.h" + +#include "tar-internal.h" + +/* Old GNU Format. + The sparse file information is stored in the oldgnu_header in the following manner: + + The header is marked with type 'S'. Its 'size' field contains the cumulative size + of all non-empty blocks of the file. The actual file size is stored in `realsize' + member of oldgnu_header. + + The map of the file is stored in a list of 'struct sparse'. Each struct contains + offset to the block of data and its size (both as octal numbers). The first file + header contains at most 4 such structs (SPARSES_IN_OLDGNU_HEADER). If the map + contains more structs, then the field 'isextended' of the main header is set to + 1 (binary) and the 'struct sparse_header' header follows, containing at most + 21 following structs (SPARSES_IN_SPARSE_HEADER). If more structs follow, 'isextended' + field of the extended header is set and next next extension header follows, etc... + */ + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/* The width in bits of the integer type or expression T. + Do not evaluate T. T must not be a bit-field expression. + Padding bits are not supported; this is checked at compile-time below. */ +#define TYPE_WIDTH(t) (sizeof (t) * CHAR_BIT) + +/* Bound on length of the string representing an unsigned integer + value representable in B bits. log10 (2.0) < 146/485. The + smallest value of B where this bound is not tight is 2621. */ +#define INT_BITS_STRLEN_BOUND(b) (((b) * 146 + 484) / 485) + +/* Does the __typeof__ keyword work? This could be done by + 'configure', but for now it's easier to do it by hand. */ +#if (2 <= __GNUC__ \ + || (4 <= __clang_major__) \ + || (1210 <= __IBMC__ && defined __IBM__TYPEOF__) \ + || (0x5110 <= __SUNPRO_C && !__STDC__)) +#define _GL_HAVE___TYPEOF__ 1 +#else +#define _GL_HAVE___TYPEOF__ 0 +#endif + +/* Return 1 if the integer type or expression T might be signed. Return 0 + if it is definitely unsigned. T must not be a bit-field expression. + This macro does not evaluate its argument, and expands to an + integer constant expression. */ +#if _GL_HAVE___TYPEOF__ +#define _GL_SIGNED_TYPE_OR_EXPR(t) TYPE_SIGNED (__typeof__ (t)) +#else +#define _GL_SIGNED_TYPE_OR_EXPR(t) 1 +#endif + +/* Return a value with the common real type of E and V and the value of V. + Do not evaluate E. */ +#define _GL_INT_CONVERT(e, v) ((1 ? 0 : (e)) + (v)) + +/* Act like _GL_INT_CONVERT (E, -V) but work around a bug in IRIX 6.5 cc; see + <https://lists.gnu.org/r/bug-gnulib/2011-05/msg00406.html>. */ +#define _GL_INT_NEGATE_CONVERT(e, v) ((1 ? 0 : (e)) - (v)) + +/* Return 1 if the real expression E, after promotion, has a + signed or floating type. Do not evaluate E. */ +#define EXPR_SIGNED(e) (_GL_INT_NEGATE_CONVERT (e, 1) < 0) + +#define _GL_SIGNED_INT_MAXIMUM(e) \ + (((_GL_INT_CONVERT (e, 1) << (TYPE_WIDTH (+ (e)) - 2)) - 1) * 2 + 1) + +/* The maximum and minimum values for the type of the expression E, + after integer promotion. E is not evaluated. */ +#define _GL_INT_MINIMUM(e) \ + (EXPR_SIGNED (e) \ + ? ~_GL_SIGNED_INT_MAXIMUM (e) \ + : _GL_INT_CONVERT (e, 0)) +#define _GL_INT_MAXIMUM(e) \ + (EXPR_SIGNED (e) \ + ? _GL_SIGNED_INT_MAXIMUM (e) \ + : _GL_INT_NEGATE_CONVERT (e, 1)) + +/* Return 1 if the expression A <op> B would overflow, + where OP_RESULT_OVERFLOW (A, B, MIN, MAX) does the actual test, + assuming MIN and MAX are the minimum and maximum for the result type. + Arguments should be free of side effects. */ +#define _GL_BINARY_OP_OVERFLOW(a, b, op_result_overflow) \ + op_result_overflow (a, b, \ + _GL_INT_MINIMUM (_GL_INT_CONVERT (a, b)), \ + _GL_INT_MAXIMUM (_GL_INT_CONVERT (a, b))) + +#define INT_ADD_RANGE_OVERFLOW(a, b, min, max) \ + ((b) < 0 \ + ? (a) < (min) - (b) \ + : (max) - (b) < (a)) + + +/* True if __builtin_add_overflow_p (A, B, C) works, and similarly for + __builtin_sub_overflow_p and __builtin_mul_overflow_p. */ +#if defined __clang__ || defined __ICC +/* Clang 11 lacks __builtin_mul_overflow_p, and even if it did it + would presumably run afoul of Clang bug 16404. ICC 2021.1's + __builtin_add_overflow_p etc. are not treated as integral constant + expressions even when all arguments are. */ +#define _GL_HAS_BUILTIN_OVERFLOW_P 0 +#elif defined __has_builtin +#define _GL_HAS_BUILTIN_OVERFLOW_P __has_builtin (__builtin_mul_overflow_p) +#else +#define _GL_HAS_BUILTIN_OVERFLOW_P (7 <= __GNUC__) +#endif + +/* The _GL*_OVERFLOW macros have the same restrictions as the + *_RANGE_OVERFLOW macros, except that they do not assume that operands + (e.g., A and B) have the same type as MIN and MAX. Instead, they assume + that the result (e.g., A + B) has that type. */ +#if _GL_HAS_BUILTIN_OVERFLOW_P +#define _GL_ADD_OVERFLOW(a, b, min, max) \ + __builtin_add_overflow_p (a, b, (__typeof__ ((a) + (b))) 0) +#else +#define _GL_ADD_OVERFLOW(a, b, min, max) \ + ((min) < 0 ? INT_ADD_RANGE_OVERFLOW (a, b, min, max) \ + : (a) < 0 ? (b) <= (a) + (b) \ + : (b) < 0 ? (a) <= (a) + (b) \ + : (a) + (b) < (b)) +#endif + +/* Bound on length of the string representing an integer type or expression T. + T must not be a bit-field expression. + + Subtract 1 for the sign bit if T is signed, and then add 1 more for + a minus sign if needed. + + Because _GL_SIGNED_TYPE_OR_EXPR sometimes returns 1 when its argument is + unsigned, this macro may overestimate the true bound by one byte when + applied to unsigned types of size 2, 4, 16, ... bytes. */ +#define INT_STRLEN_BOUND(t) \ + (INT_BITS_STRLEN_BOUND (TYPE_WIDTH (t) - _GL_SIGNED_TYPE_OR_EXPR (t)) \ + + _GL_SIGNED_TYPE_OR_EXPR (t)) + +/* Bound on buffer size needed to represent an integer type or expression T, + including the terminating null. T must not be a bit-field expression. */ +#define INT_BUFSIZE_BOUND(t) (INT_STRLEN_BOUND (t) + 1) + +#define UINTMAX_STRSIZE_BOUND INT_BUFSIZE_BOUND (uintmax_t) + +#define INT_ADD_OVERFLOW(a, b) \ + _GL_BINARY_OP_OVERFLOW (a, b, _GL_ADD_OVERFLOW) + +#define SPARSES_INIT_COUNT SPARSES_IN_SPARSE_HEADER + +#define COPY_BUF(arch,b,buf,src) \ +do \ +{ \ + char *endp = b->buffer + BLOCKSIZE; \ + char *dst = buf; \ + do \ + { \ + if (dst == buf + UINTMAX_STRSIZE_BOUND - 1) \ + /* numeric overflow in sparse archive member */ \ + return FALSE; \ + if (src == endp) \ + { \ + tar_set_next_block_after (b); \ + b = tar_find_next_block (arch); \ + if (b == NULL) \ + /* unexpected EOF in archive */ \ + return FALSE; \ + src = b->buffer; \ + endp = b->buffer + BLOCKSIZE; \ + } \ + *dst = *src++; \ + } \ + while (*dst++ != '\n'); \ + dst[-1] = '\0'; \ +} \ +while (FALSE) + +/*** file scope type declarations ****************************************************************/ + +struct tar_sparse_file; + +struct tar_sparse_optab +{ + gboolean (*init) (struct tar_sparse_file * file); + gboolean (*done) (struct tar_sparse_file * file); + gboolean (*sparse_member_p) (struct tar_sparse_file * file); + gboolean (*fixup_header) (struct tar_sparse_file * file); + gboolean (*decode_header) (tar_super_t * archive, struct tar_sparse_file * file); +}; + +struct tar_sparse_file +{ + int fd; /**< File descriptor */ + off_t dumped_size; /**< Number of bytes actually written to the archive */ + struct tar_stat_info *stat_info; /**< Information about the file */ + struct tar_sparse_optab const *optab; + void *closure; /**< Any additional data optab calls might reqiure */ +}; + +enum oldgnu_add_status +{ + add_ok, + add_finish, + add_fail +}; + +/*** forward declarations (file scope functions) *************************************************/ + +static gboolean oldgnu_sparse_member_p (struct tar_sparse_file *file); +static gboolean oldgnu_fixup_header (struct tar_sparse_file *file); +static gboolean oldgnu_get_sparse_info (tar_super_t * archive, struct tar_sparse_file *file); + +static gboolean star_sparse_member_p (struct tar_sparse_file *file); +static gboolean star_fixup_header (struct tar_sparse_file *file); +static gboolean star_get_sparse_info (tar_super_t * archive, struct tar_sparse_file *file); + +static gboolean pax_sparse_member_p (struct tar_sparse_file *file); +static gboolean pax_decode_header (tar_super_t * archive, struct tar_sparse_file *file); + +/*** file scope variables ************************************************************************/ + +/* *INDENT-OFF* */ +static struct tar_sparse_optab const oldgnu_optab = +{ + .init = NULL, /* No init function */ + .done = NULL, /* No done function */ + .sparse_member_p = oldgnu_sparse_member_p, + .fixup_header = oldgnu_fixup_header, + .decode_header = oldgnu_get_sparse_info +}; +/* *INDENT-ON* */ + +/* *INDENT-OFF* */ +static struct tar_sparse_optab const star_optab = +{ + .init = NULL, /* No init function */ + .done = NULL, /* No done function */ + .sparse_member_p = star_sparse_member_p, + .fixup_header = star_fixup_header, + .decode_header = star_get_sparse_info +}; +/* *INDENT-ON* */ + +/* GNU PAX sparse file format. There are several versions: + * 0.0 + + The initial version of sparse format used by tar 1.14-1.15.1. + The sparse file map is stored in x header: + + GNU.sparse.size Real size of the stored file + GNU.sparse.numblocks Number of blocks in the sparse map repeat numblocks time + GNU.sparse.offset Offset of the next data block + GNU.sparse.numbytes Size of the next data block end repeat + + This has been reported as conflicting with the POSIX specs. The reason is + that offsets and sizes of non-zero data blocks were stored in multiple instances + of GNU.sparse.offset/GNU.sparse.numbytes variables, whereas POSIX requires the + latest occurrence of the variable to override all previous occurrences. + + To avoid this incompatibility two following versions were introduced. + + * 0.1 + + Used by tar 1.15.2 -- 1.15.91 (alpha releases). + + The sparse file map is stored in x header: + + GNU.sparse.size Real size of the stored file + GNU.sparse.numblocks Number of blocks in the sparse map + GNU.sparse.map Map of non-null data chunks. A string consisting of comma-separated + values "offset,size[,offset,size]..." + + The resulting GNU.sparse.map string can be *very* long. While POSIX does not impose + any limit on the length of a x header variable, this can confuse some tars. + + * 1.0 + + Starting from this version, the exact sparse format version is specified explicitly + in the header using the following variables: + + GNU.sparse.major Major version + GNU.sparse.minor Minor version + + X header keeps the following variables: + + GNU.sparse.name Real file name of the sparse file + GNU.sparse.realsize Real size of the stored file (corresponds to the old GNU.sparse.size + variable) + + The name field of the ustar header is constructed using the pattern "%d/GNUSparseFile.%p/%f". + + The sparse map itself is stored in the file data block, preceding the actual file data. + It consists of a series of octal numbers of arbitrary length, delimited by newlines. + The map is padded with nulls to the nearest block boundary. + + The first number gives the number of entries in the map. Following are map entries, each one + consisting of two numbers giving the offset and size of the data block it describes. + + The format is designed in such a way that non-posix aware tars and tars not supporting + GNU.sparse.* keywords will extract each sparse file in its condensed form with the file map + attached and will place it into a separate directory. Then, using a simple program it would be + possible to expand the file to its original form even without GNU tar. + + Bu default, v.1.0 archives are created. To use other formats, --sparse-version option is provided. + Additionally, v.0.0 can be obtained by deleting GNU.sparse.map from 0.1 format: + --sparse-version 0.1 --pax-option delete=GNU.sparse.map + */ + +static struct tar_sparse_optab const pax_optab = { + .init = NULL, /* No init function */ + .done = NULL, /* No done function */ + .sparse_member_p = pax_sparse_member_p, + .fixup_header = NULL, /* No fixup_header function */ + .decode_header = pax_decode_header +}; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +decode_num (uintmax_t * num, const char *arg, uintmax_t maxval) +{ + uintmax_t u; + char *arg_lim; + + if (!isdigit (*arg)) + return FALSE; + + errno = 0; + u = (uintmax_t) g_ascii_strtoll (arg, &arg_lim, 10); + + if (!(u <= maxval && errno != ERANGE) || *arg_lim != '\0') + return FALSE; + + *num = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_select_optab (const tar_super_t * archive, struct tar_sparse_file *file) +{ + switch (archive->type) + { + case TAR_V7: + case TAR_USTAR: + return FALSE; + + case TAR_OLDGNU: + case TAR_GNU: /* FIXME: This one should disappear? */ + file->optab = &oldgnu_optab; + break; + + case TAR_POSIX: + file->optab = &pax_optab; + break; + + case TAR_STAR: + file->optab = &star_optab; + break; + + default: + return FALSE; + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_init (tar_super_t * archive, struct tar_sparse_file *file) +{ + memset (file, 0, sizeof (*file)); + + if (!sparse_select_optab (archive, file)) + return FALSE; + + if (file->optab->init != NULL) + return file->optab->init (file); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_done (struct tar_sparse_file *file) +{ + if (file->optab->done != NULL) + return file->optab->done (file); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_member_p (struct tar_sparse_file *file) +{ + if (file->optab->sparse_member_p != NULL) + return file->optab->sparse_member_p (file); + + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_fixup_header (struct tar_sparse_file *file) +{ + if (file->optab->fixup_header != NULL) + return file->optab->fixup_header (file); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_decode_header (tar_super_t * archive, struct tar_sparse_file *file) +{ + if (file->optab->decode_header != NULL) + return file->optab->decode_header (archive, file); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline void +sparse_add_map (struct tar_stat_info *st, struct sp_array *sp) +{ + if (st->sparse_map == NULL) + st->sparse_map = g_array_sized_new (FALSE, FALSE, sizeof (struct sp_array), 1); + g_array_append_val (st->sparse_map, *sp); +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Add a sparse item to the sparse file + */ +static enum oldgnu_add_status +oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s) +{ + struct sp_array sp; + + if (s->numbytes[0] == '\0') + return add_finish; + + sp.offset = OFF_FROM_HEADER (s->offset); + sp.numbytes = OFF_FROM_HEADER (s->numbytes); + + if (sp.offset < 0 || sp.numbytes < 0 + || INT_ADD_OVERFLOW (sp.offset, sp.numbytes) + || file->stat_info->stat.st_size < sp.offset + sp.numbytes + || file->stat_info->archive_file_size < 0) + return add_fail; + + sparse_add_map (file->stat_info, &sp); + + return add_ok; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +oldgnu_sparse_member_p (struct tar_sparse_file *file) +{ + (void) file; + + return current_header->header.typeflag == GNUTYPE_SPARSE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +oldgnu_fixup_header (struct tar_sparse_file *file) +{ + /* NOTE! st_size was initialized from the header which actually contains archived size. + The following fixes it */ + off_t realsize; + + realsize = OFF_FROM_HEADER (current_header->oldgnu_header.realsize); + file->stat_info->archive_file_size = file->stat_info->stat.st_size; + file->stat_info->stat.st_size = MAX (0, realsize); + + return (realsize >= 0); +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Convert old GNU format sparse data to internal representation. + */ +static gboolean +oldgnu_get_sparse_info (tar_super_t * archive, struct tar_sparse_file *file) +{ + size_t i; + union block *h = current_header; + int ext_p; + enum oldgnu_add_status rc; + + if (file->stat_info->sparse_map != NULL) + g_array_set_size (file->stat_info->sparse_map, 0); + + for (i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++) + { + rc = oldgnu_add_sparse (file, &h->oldgnu_header.sp[i]); + if (rc != add_ok) + break; + } + + for (ext_p = h->oldgnu_header.isextended ? 1 : 0; rc == add_ok && ext_p != 0; + ext_p = h->sparse_header.isextended ? 1 : 0) + { + h = tar_find_next_block (archive); + if (h == NULL) + return FALSE; + + tar_set_next_block_after (h); + + for (i = 0; i < SPARSES_IN_SPARSE_HEADER && rc == add_ok; i++) + rc = oldgnu_add_sparse (file, &h->sparse_header.sp[i]); + } + + return (rc != add_fail); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +star_sparse_member_p (struct tar_sparse_file *file) +{ + (void) file; + + return current_header->header.typeflag == GNUTYPE_SPARSE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +star_fixup_header (struct tar_sparse_file *file) +{ + /* NOTE! st_size was initialized from the header which actually contains archived size. + The following fixes it */ + off_t realsize; + + realsize = OFF_FROM_HEADER (current_header->star_in_header.realsize); + file->stat_info->archive_file_size = file->stat_info->stat.st_size; + file->stat_info->stat.st_size = MAX (0, realsize); + + return (realsize >= 0); +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Convert STAR format sparse data to internal representation + */ +static gboolean +star_get_sparse_info (tar_super_t * archive, struct tar_sparse_file *file) +{ + size_t i; + union block *h = current_header; + int ext_p = 1; + enum oldgnu_add_status rc = add_ok; + + if (file->stat_info->sparse_map != NULL) + g_array_set_size (file->stat_info->sparse_map, 0); + + if (h->star_in_header.prefix[0] == '\0' && h->star_in_header.sp[0].offset[10] != '\0') + { + /* Old star format */ + for (i = 0; i < SPARSES_IN_STAR_HEADER; i++) + { + rc = oldgnu_add_sparse (file, &h->star_in_header.sp[i]); + if (rc != add_ok) + break; + } + + ext_p = h->star_in_header.isextended ? 1 : 0; + } + + for (; rc == add_ok && ext_p != 0; ext_p = h->star_ext_header.isextended ? 1 : 0) + { + h = tar_find_next_block (archive); + if (h == NULL) + return FALSE; + + tar_set_next_block_after (h); + + for (i = 0; i < SPARSES_IN_STAR_EXT_HEADER && rc == add_ok; i++) + rc = oldgnu_add_sparse (file, &h->star_ext_header.sp[i]); + + file->dumped_size += BLOCKSIZE; + } + + return (rc != add_fail); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +pax_sparse_member_p (struct tar_sparse_file *file) +{ + return file->stat_info->sparse_map != NULL && file->stat_info->sparse_map->len > 0 + && file->stat_info->sparse_major > 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +pax_decode_header (tar_super_t * archive, struct tar_sparse_file *file) +{ + if (file->stat_info->sparse_major > 0) + { + uintmax_t u; + char nbuf[UINTMAX_STRSIZE_BOUND]; + union block *blk; + char *p; + size_t sparse_map_len; + size_t i; + off_t start; + + start = tar_current_block_ordinal (archive); + tar_set_next_block_after (current_header); + blk = tar_find_next_block (archive); + if (blk == NULL) + /* unexpected EOF in archive */ + return FALSE; + p = blk->buffer; + COPY_BUF (archive, blk, nbuf, p); + + if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t))) + { + /* malformed sparse archive member */ + return FALSE; + } + + if (file->stat_info->sparse_map == NULL) + file->stat_info->sparse_map = + g_array_sized_new (FALSE, FALSE, sizeof (struct sp_array), u); + else + g_array_set_size (file->stat_info->sparse_map, u); + + sparse_map_len = u; + + for (i = 0; i < sparse_map_len; i++) + { + struct sp_array sp; + + COPY_BUF (archive, blk, nbuf, p); + if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t))) + { + /* malformed sparse archive member */ + return FALSE; + } + sp.offset = u; + COPY_BUF (archive, blk, nbuf, p); + if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t)) || INT_ADD_OVERFLOW (sp.offset, u) + || (uintmax_t) file->stat_info->stat.st_size < sp.offset + u) + { + /* malformed sparse archive member */ + return FALSE; + } + sp.numbytes = u; + sparse_add_map (file->stat_info, &sp); + } + + tar_set_next_block_after (blk); + + file->dumped_size += BLOCKSIZE * (tar_current_block_ordinal (archive) - start); + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +gboolean +tar_sparse_member_p (tar_super_t * archive, struct tar_stat_info * st) +{ + struct tar_sparse_file file; + + if (!sparse_init (archive, &file)) + return FALSE; + + file.stat_info = st; + return sparse_member_p (&file); +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +tar_sparse_fixup_header (tar_super_t * archive, struct tar_stat_info * st) +{ + struct tar_sparse_file file; + + if (!sparse_init (archive, &file)) + return FALSE; + + file.stat_info = st; + return sparse_fixup_header (&file); +} + +/* --------------------------------------------------------------------------------------------- */ + +enum dump_status +tar_sparse_skip_file (tar_super_t * archive, struct tar_stat_info *st) +{ + gboolean rc = TRUE; + struct tar_sparse_file file; + + if (!sparse_init (archive, &file)) + return dump_status_not_implemented; + + file.stat_info = st; + file.fd = -1; + + rc = sparse_decode_header (archive, &file); + (void) tar_skip_file (archive, file.stat_info->archive_file_size - file.dumped_size); + return (sparse_done (&file) && rc) ? dump_status_ok : dump_status_short; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/tar/tar-xheader.c b/src/vfs/tar/tar-xheader.c new file mode 100644 index 0000000..5062ed1 --- /dev/null +++ b/src/vfs/tar/tar-xheader.c @@ -0,0 +1,1051 @@ +/* + Virtual File System: GNU Tar file system. + + Copyright (C) 1995-2023 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin <aborodin@vmail.ru>, 2023 + + 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/>. + */ + +/** + * \file + * \brief Source: Virtual File System: GNU Tar file system + */ + +#include <config.h> + +#include <ctype.h> /* isdigit() */ +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "lib/global.h" +#include "lib/util.h" /* MC_PTR_FREE */ + +#include "tar-internal.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#define XHDR_PROTECTED 0x01 +#define XHDR_GLOBAL 0x02 + +/*** file scope type declarations ****************************************************************/ + +/* General Interface */ + +/* Since tar VFS is read-only, inplement decodes only */ +/* *INDENT-OFF* */ +struct xhdr_tab +{ + const char *keyword; + gboolean (*decoder) (struct tar_stat_info * st, const char *keyword, const char *arg, size_t size); + int flags; +}; +/* *INDENT-ON* */ + +/* Keyword options */ +struct keyword_item +{ + char *pattern; + char *value; +}; + +enum decode_record_status +{ + decode_record_ok, + decode_record_finish, + decode_record_fail +}; + +/*** forward declarations (file scope functions) *************************************************/ + +static gboolean dummy_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean atime_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean gid_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +#if 0 +static gboolean gname_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +#endif +static gboolean linkpath_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean mtime_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean ctime_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean path_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean size_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean uid_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +#if 0 +static gboolean uname_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +#endif +static gboolean sparse_path_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean sparse_major_decoder (struct tar_stat_info *st, const char *keyword, + const char *arg, size_t size); +static gboolean sparse_minor_decoder (struct tar_stat_info *st, const char *keyword, + const char *arg, size_t size); +static gboolean sparse_size_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean sparse_numblocks_decoder (struct tar_stat_info *st, const char *keyword, + const char *arg, size_t size); +static gboolean sparse_offset_decoder (struct tar_stat_info *st, const char *keyword, + const char *arg, size_t size); +static gboolean sparse_numbytes_decoder (struct tar_stat_info *st, const char *keyword, + const char *arg, size_t size); +static gboolean sparse_map_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean dumpdir_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); + +/*** file scope variables ************************************************************************/ + +enum +{ + BILLION = 1000000000, + LOG10_BILLION = 9 +}; + +/* *INDENT-OFF* */ +static struct xhdr_tab xhdr_tab[] = +{ + { "atime", atime_decoder, 0 }, + { "comment", dummy_decoder, 0 }, + { "charset", dummy_decoder, 0 }, + { "ctime", ctime_decoder, 0 }, + { "gid", gid_decoder, 0 }, +#if 0 + { "gname", gname_decoder, 0 }, +#endif + { "linkpath", linkpath_decoder, 0 }, + { "mtime", mtime_decoder, 0 }, + { "path", path_decoder, 0 }, + { "size", size_decoder, 0 }, + { "uid", uid_decoder, 0 }, +#if 0 + { "uname", uname_decoder, 0 }, +#endif + + /* Sparse file handling */ + { "GNU.sparse.name", sparse_path_decoder, XHDR_PROTECTED }, + { "GNU.sparse.major", sparse_major_decoder, XHDR_PROTECTED }, + { "GNU.sparse.minor", sparse_minor_decoder, XHDR_PROTECTED }, + { "GNU.sparse.realsize", sparse_size_decoder, XHDR_PROTECTED }, + { "GNU.sparse.numblocks", sparse_numblocks_decoder, XHDR_PROTECTED }, + + { "GNU.sparse.size", sparse_size_decoder, XHDR_PROTECTED }, + /* tar 1.14 - 1.15.1 keywords. Multiple instances of these appeared in 'x' + headers, and each of them was meaningful. It confilcted with POSIX specs, + which requires that "when extended header records conflict, the last one + given in the header shall take precedence." */ + { "GNU.sparse.offset", sparse_offset_decoder, XHDR_PROTECTED }, + { "GNU.sparse.numbytes", sparse_numbytes_decoder, XHDR_PROTECTED }, + /* tar 1.15.90 keyword, introduced to remove the above-mentioned conflict. */ + { "GNU.sparse.map", sparse_map_decoder, 0 }, + + { "GNU.dumpdir", dumpdir_decoder, XHDR_PROTECTED }, + + { NULL, NULL, 0 } +}; +/* *INDENT-ON* */ + +/* List of keyword/value pairs decoded from the last 'g' type header */ +static GSList *global_header_override_list = NULL; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/* Convert a prefix of the string @arg to a system integer type whose minimum value is @minval + and maximum @maxval. If @minval is negative, negative integers @minval .. -1 are assumed + to be represented using leading '-' in the usual way. If the represented value exceeds INTMAX_MAX, + return a negative integer V such that (uintmax_t) V yields the represented value. If @arglim is + nonnull, store into *@arglim a pointer to the first character after the prefix. + + This is the inverse of sysinttostr. + + On a normal return, set errno = 0. + On conversion error, return 0 and set errno = EINVAL. + On overflow, return an extreme value and set errno = ERANGE. + */ +#if ! (INTMAX_MAX <= UINTMAX_MAX) +#error "strtosysint: nonnegative intmax_t does not fit in uintmax_t" +#endif +static intmax_t +strtosysint (const char *arg, char **arglim, intmax_t minval, uintmax_t maxval) +{ + errno = 0; + + if (maxval <= INTMAX_MAX) + { + if (isdigit (arg[*arg == '-' ? 1 : 0])) + { + gint64 i; + + i = g_ascii_strtoll (arg, arglim, 10); + if ((gint64) minval <= i && i <= (gint64) maxval) + return (intmax_t) i; + + errno = ERANGE; + return i < (gint64) minval ? minval : (intmax_t) maxval; + } + } + else + { + if (isdigit (*arg)) + { + guint64 i; + + i = g_ascii_strtoull (arg, arglim, 10); + if (i <= (guint64) maxval) + return tar_represent_uintmax ((uintmax_t) i); + + errno = ERANGE; + return maxval; + } + } + + errno = EINVAL; + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct xhdr_tab * +locate_handler (const char *keyword) +{ + struct xhdr_tab *p; + + for (p = xhdr_tab; p->keyword != NULL; p++) + if (strcmp (p->keyword, keyword) == 0) + return p; + + return NULL; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +keyword_item_run (gpointer data, gpointer user_data) +{ + struct keyword_item *kp = (struct keyword_item *) data; + struct tar_stat_info *st = (struct tar_stat_info *) user_data; + struct xhdr_tab const *t; + + t = locate_handler (kp->pattern); + if (t != NULL) + return t->decoder (st, t->keyword, kp->value, strlen (kp->value)); + + return TRUE; /* FIXME */ +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +keyword_item_free (gpointer data) +{ + struct keyword_item *kp = (struct keyword_item *) data; + + g_free (kp->pattern); + g_free (kp->value); + g_free (kp); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +xheader_list_append (GSList ** root, const char *kw, const char *value) +{ + struct keyword_item *kp; + + kp = g_new (struct keyword_item, 1); + kp->pattern = g_strdup (kw); + kp->value = g_strdup (value); + *root = g_slist_prepend (*root, kp); +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline void +xheader_list_destroy (GSList ** root) +{ + g_slist_free_full (*root, keyword_item_free); + *root = NULL; +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline void +run_override_list (GSList * kp, struct tar_stat_info *st) +{ + g_slist_foreach (kp, (GFunc) keyword_item_run, st); +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct timespec +decode_timespec (const char *arg, char **arg_lim, gboolean parse_fraction) +{ + time_t s = TYPE_MINIMUM (time_t); + int ns = -1; + const char *p = arg; + gboolean negative = *arg == '-'; + struct timespec r; + + if (!isdigit (arg[negative])) + errno = EINVAL; + else + { + errno = 0; + + if (negative) + { + gint64 i; + + i = g_ascii_strtoll (arg, arg_lim, 10); + if (TYPE_SIGNED (time_t) ? TYPE_MINIMUM (time_t) <= i : 0 <= i) + s = (intmax_t) i; + else + errno = ERANGE; + } + else + { + guint64 i; + + i = g_ascii_strtoull (arg, arg_lim, 10); + if (i <= TYPE_MAXIMUM (time_t)) + s = (uintmax_t) i; + else + errno = ERANGE; + } + + p = *arg_lim; + ns = 0; + + if (parse_fraction && *p == '.') + { + int digits = 0; + gboolean trailing_nonzero = FALSE; + + while (isdigit (*++p)) + if (digits < LOG10_BILLION) + { + digits++; + ns = 10 * ns + (*p - '0'); + } + else if (*p != '0') + trailing_nonzero = TRUE; + + while (digits < LOG10_BILLION) + { + digits++; + ns *= 10; + } + + if (negative) + { + /* Convert "-1.10000000000001" to s == -2, ns == 89999999. + I.e., truncate time stamps towards minus infinity while + converting them to internal form. */ + if (trailing_nonzero) + ns++; + if (ns != 0) + { + if (s == TYPE_MINIMUM (time_t)) + ns = -1; + else + { + s--; + ns = BILLION - ns; + } + } + } + } + + if (errno == ERANGE) + ns = -1; + } + + *arg_lim = (char *) p; + r.tv_sec = s; + r.tv_nsec = ns; + return r; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +decode_time (struct timespec *ts, const char *arg, const char *keyword) +{ + char *arg_lim; + struct timespec t; + + (void) keyword; + + t = decode_timespec (arg, &arg_lim, TRUE); + + if (t.tv_nsec < 0) + /* Malformed extended header */ + return FALSE; + + if (*arg_lim != '\0') + /* Malformed extended header */ + return FALSE; + + *ts = t; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +decode_signed_num (intmax_t * num, const char *arg, intmax_t minval, uintmax_t maxval, + const char *keyword) +{ + char *arg_lim; + intmax_t u; + + (void) keyword; + + if (!isdigit (*arg)) + return FALSE; /* malformed extended header */ + + u = strtosysint (arg, &arg_lim, minval, maxval); + + if (errno == EINVAL || *arg_lim != '\0') + return FALSE; /* malformed extended header */ + + if (errno == ERANGE) + return FALSE; /* out of range */ + + *num = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +decode_num (uintmax_t * num, const char *arg, uintmax_t maxval, const char *keyword) +{ + intmax_t i; + + if (!decode_signed_num (&i, arg, 0, maxval, keyword)) + return FALSE; + + *num = i; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +raw_path_decoder (struct tar_stat_info *st, const char *arg) +{ + if (*arg != '\0') + { + tar_assign_string_dup (&st->orig_file_name, arg); + tar_assign_string_dup (&st->file_name, arg); + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +dummy_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + (void) st; + (void) keyword; + (void) arg; + (void) size; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +atime_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + struct timespec ts; + + (void) size; + + if (!decode_time (&ts, arg, keyword)) + return FALSE; + + st->atime = ts; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +gid_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + intmax_t u; + + (void) size; + + if (!decode_signed_num (&u, arg, TYPE_MINIMUM (gid_t), TYPE_MINIMUM (gid_t), keyword)) + return FALSE; + + st->stat.st_gid = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +#if 0 +static gboolean +gname_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + (void) keyword; + (void) size; + + tar_assign_string_dup (&st->gname, arg); + return TRUE; +} +#endif + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +linkpath_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + (void) keyword; + (void) size; + + tar_assign_string_dup (&st->link_name, arg); + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +ctime_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + struct timespec ts; + + (void) size; + + if (!decode_time (&ts, arg, keyword)) + return FALSE; + + st->ctime = ts; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +mtime_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + struct timespec ts; + + (void) size; + + if (!decode_time (&ts, arg, keyword)) + return FALSE; + + st->mtime = ts; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +path_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + (void) keyword; + (void) size; + + if (!st->sparse_name_done) + return raw_path_decoder (st, arg); + + return TRUE; /* FIXME */ +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +size_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + uintmax_t u; + + (void) size; + + if (!decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword)) + return FALSE; + + st->stat.st_size = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +uid_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + intmax_t u; + + (void) size; + + if (!decode_signed_num (&u, arg, TYPE_MINIMUM (uid_t), TYPE_MAXIMUM (uid_t), keyword)) + return FALSE; + + st->stat.st_uid = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +#if 0 +static gboolean +uname_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + (void) keyword; + (void) size; + + tar_assign_string_dup (&st->uname, arg); + return TRUE; +} +#endif + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +dumpdir_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + (void) keyword; + +#if GLIB_CHECK_VERSION (2, 68, 0) + st->dumpdir = g_memdup2 (arg, size); +#else + st->dumpdir = g_memdup (arg, size); +#endif + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Decodes a single extended header record, advancing @ptr to the next record. + * + * @param p pointer to extended header record + * @param st stat info + * + * @return decode_record_ok or decode_record_finish on success, decode_record_fail otherwize + */ +static enum decode_record_status +decode_record (struct xheader *xhdr, char **ptr, + gboolean (*handler) (void *data, const char *keyword, const char *value, + size_t size), void *data) +{ + char *start = *ptr; + char *p = start; + size_t len; + char *len_lim; + const char *keyword; + char *nextp; + size_t len_max; + gboolean ret; + + len_max = xhdr->buffer + xhdr->size - start; + + while (*p == ' ' || *p == '\t') + p++; + + if (!isdigit (*p)) + return (*p != '\0' ? decode_record_fail : decode_record_finish); + + len = (uintmax_t) g_ascii_strtoull (p, &len_lim, 10); + if (len_max < len) + return decode_record_fail; + + nextp = start + len; + + for (p = len_lim; *p == ' ' || *p == '\t'; p++) + ; + + if (p == len_lim) + return decode_record_fail; + + keyword = p; + p = strchr (p, '='); + if (!(p != NULL && p < nextp)) + return decode_record_fail; + + if (nextp[-1] != '\n') + return decode_record_fail; + + *p = nextp[-1] = '\0'; + ret = handler (data, keyword, p + 1, nextp - p - 2); /* '=' + trailing '\n' */ + *p = '='; + nextp[-1] = '\n'; + *ptr = nextp; + + return (ret ? decode_record_ok : decode_record_fail); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +decg (void *data, const char *keyword, const char *value, size_t size) +{ + GSList **kwl = (GSList **) data; + struct xhdr_tab const *tab; + + (void) size; + + tab = locate_handler (keyword); + if (tab != NULL && (tab->flags & XHDR_GLOBAL) != 0) + { + if (!tab->decoder (data, keyword, value, size)) + return FALSE; + } + else + xheader_list_append (kwl, keyword, value); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +decx (void *data, const char *keyword, const char *value, size_t size) +{ + struct keyword_item kp = { + .pattern = (char *) keyword, + .value = (char *) value + }; + + (void) size; + + return keyword_item_run (&kp, data); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_path_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + (void) keyword; + (void) size; + + st->sparse_name_done = TRUE; + return raw_path_decoder (st, arg); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_major_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + uintmax_t u; + + (void) size; + + if (!decode_num (&u, arg, TYPE_MAXIMUM (unsigned), keyword)) + return FALSE; + + st->sparse_major = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_minor_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + uintmax_t u; + + (void) size; + + if (!decode_num (&u, arg, TYPE_MAXIMUM (unsigned), keyword)) + return FALSE; + + st->sparse_minor = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_size_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + uintmax_t u; + + (void) size; + + if (!decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword)) + return FALSE; + + st->real_size_set = TRUE; + st->real_size = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_numblocks_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size) +{ + uintmax_t u; + + (void) size; + + if (!decode_num (&u, arg, SIZE_MAX, keyword)) + return FALSE; + + if (st->sparse_map == NULL) + st->sparse_map = g_array_sized_new (FALSE, FALSE, sizeof (struct sp_array), u); + else + g_array_set_size (st->sparse_map, u); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_offset_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + uintmax_t u; + struct sp_array *s; + + (void) size; + + if (!decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword)) + return FALSE; + + s = &g_array_index (st->sparse_map, struct sp_array, st->sparse_map->len - 1); + s->offset = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_numbytes_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size) +{ + uintmax_t u; + struct sp_array s; + + (void) size; + + if (!decode_num (&u, arg, SIZE_MAX, keyword)) + return FALSE; + + s.offset = 0; + s.numbytes = u; + g_array_append_val (st->sparse_map, s); + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_map_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + gboolean offset = TRUE; + struct sp_array e; + + (void) keyword; + (void) size; + + if (st->sparse_map != NULL) + g_array_set_size (st->sparse_map, 0); + + while (TRUE) + { + gint64 u; + char *delim; + + if (!isdigit (*arg)) + { + /* malformed extended header */ + return FALSE; + } + + errno = 0; + u = g_ascii_strtoll (arg, &delim, 10); + if (TYPE_MAXIMUM (off_t) < u) + { + u = TYPE_MAXIMUM (off_t); + errno = ERANGE; + } + if (offset) + { + e.offset = u; + if (errno == ERANGE) + { + /* out of range */ + return FALSE; + } + } + else + { + e.numbytes = u; + if (errno == ERANGE) + { + /* out of range */ + return FALSE; + } + + g_array_append_val (st->sparse_map, e); + } + + offset = !offset; + + if (*delim == '\0') + break; + if (*delim != ',') + { + /* malformed extended header */ + return FALSE; + } + + arg = delim + 1; + } + + if (!offset) + { + /* malformed extended header */ + return FALSE; + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/** + * Decodes an extended header. + * + * @param st stat info + * + * @return TRUE on success, FALSE otherwize + */ +gboolean +tar_xheader_decode (struct tar_stat_info * st) +{ + char *p; + enum decode_record_status status; + + run_override_list (global_header_override_list, st); + + p = st->xhdr.buffer + BLOCKSIZE; + + while ((status = decode_record (&st->xhdr, &p, decx, st)) == decode_record_ok) + ; + + if (status == decode_record_fail) + return FALSE; + + /* The archived (effective) file size is always set directly in tar header + field, possibly overridden by "size" extended header - in both cases, + result is now decoded in st->stat.st_size */ + st->archive_file_size = st->stat.st_size; + + /* The real file size (given by stat()) may be redefined for sparse + files in "GNU.sparse.realsize" extended header */ + if (st->real_size_set) + st->stat.st_size = st->real_size; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +tar_xheader_read (tar_super_t * archive, struct xheader * xhdr, union block * p, off_t size) +{ + size_t j = 0; + + size = MAX (0, size); + size += BLOCKSIZE; + + xhdr->size = size; + xhdr->buffer = g_malloc (size + 1); + xhdr->buffer[size] = '\0'; + + do + { + size_t len; + + if (p == NULL) + return FALSE; /* Unexpected EOF in archive */ + + len = MIN (size, BLOCKSIZE); + + memcpy (xhdr->buffer + j, p->buffer, len); + tar_set_next_block_after (p); + p = tar_find_next_block (archive); + + j += len; + size -= len; + } + while (size > 0); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +tar_xheader_decode_global (struct xheader * xhdr) +{ + char *p; + gboolean ret; + + p = xhdr->buffer + BLOCKSIZE; + + xheader_list_destroy (&global_header_override_list); + + while ((ret = decode_record (xhdr, &p, decg, &global_header_override_list)) == decode_record_ok) + ; + + return (ret == decode_record_finish); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tar_xheader_destroy (struct xheader *xhdr) +{ + MC_PTR_FREE (xhdr->buffer); + xhdr->size = 0; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/tar/tar.c b/src/vfs/tar/tar.c new file mode 100644 index 0000000..2d32111 --- /dev/null +++ b/src/vfs/tar/tar.c @@ -0,0 +1,1302 @@ +/* + Virtual File System: GNU Tar file system. + + Copyright (C) 1995-2023 + Free Software Foundation, Inc. + + Written by: + Jakub Jelinek, 1995 + Pavel Machek, 1998 + Slava Zanko <slavazanko@gmail.com>, 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/>. + */ + +/** + * \file + * \brief Source: Virtual File System: GNU Tar file system + * \author Jakub Jelinek + * \author Pavel Machek + * \date 1995, 1998 + */ + +#include <config.h> + +#include <errno.h> +#include <string.h> /* memset() */ + +#ifdef hpux +/* major() and minor() macros (among other things) defined here for hpux */ +#include <sys/mknod.h> +#endif + +#include "lib/global.h" +#include "lib/util.h" +#include "lib/unixcompat.h" /* makedev() */ +#include "lib/widget.h" /* message() */ + +#include "lib/vfs/vfs.h" +#include "lib/vfs/utilvfs.h" +#include "lib/vfs/gc.h" /* vfs_rmstamp */ + +#include "tar-internal.h" +#include "tar.h" + +/*** global variables ****************************************************************************/ + +/* Size of each record, once in blocks, once in bytes. Those two variables are always related, + the second being BLOCKSIZE times the first. */ +const int blocking_factor = DEFAULT_BLOCKING; +const size_t record_size = DEFAULT_BLOCKING * BLOCKSIZE; + +/* As we open one archive at a time, it is safe to have these static */ +union block *record_end; /* last+1 block of archive record */ +union block *current_block; /* current block of archive */ +off_t record_start_block; /* block ordinal at record_start */ + +union block *current_header; + +/* Have we hit EOF yet? */ +gboolean hit_eof; + +struct tar_stat_info current_stat_info; + +/*** file scope macro definitions ****************************************************************/ + +#define TAR_SUPER(super) ((tar_super_t *) (super)) + +/* tar Header Block, from POSIX 1003.1-1990. */ + +/* The magic field is filled with this if uname and gname are valid. */ +#define TMAGIC "ustar" /* ustar and a null */ + +#define XHDTYPE 'x' /* Extended header referring to the next file in the archive */ +#define XGLTYPE 'g' /* Global extended header */ + +/* Values used in typeflag field. */ +#define LNKTYPE '1' /* link */ +#define SYMTYPE '2' /* symbolic link */ +#define CHRTYPE '3' /* character special */ +#define BLKTYPE '4' /* block special */ +#define DIRTYPE '5' /* directory */ +#define FIFOTYPE '6' /* FIFO special */ + + +/* OLDGNU_MAGIC uses both magic and version fields, which are contiguous. + Found in an archive, it indicates an old GNU header format, which will be + hopefully become obsolescent. With OLDGNU_MAGIC, uname and gname are + valid, though the header is not truly POSIX conforming. */ +#define OLDGNU_MAGIC "ustar " /* 7 chars and a null */ + + +/* Bits used in the mode field, values in octal. */ +#define TSUID 04000 /* set UID on execution */ +#define TSGID 02000 /* set GID on execution */ +#define TSVTX 01000 /* reserved */ + /* file permissions */ +#define TUREAD 00400 /* read by owner */ +#define TUWRITE 00200 /* write by owner */ +#define TUEXEC 00100 /* execute/search by owner */ +#define TGREAD 00040 /* read by group */ +#define TGWRITE 00020 /* write by group */ +#define TGEXEC 00010 /* execute/search by group */ +#define TOREAD 00004 /* read by other */ +#define TOWRITE 00002 /* write by other */ +#define TOEXEC 00001 /* execute/search by other */ + +#define GID_FROM_HEADER(where) gid_from_header (where, sizeof (where)) +#define MAJOR_FROM_HEADER(where) major_from_header (where, sizeof (where)) +#define MINOR_FROM_HEADER(where) minor_from_header (where, sizeof (where)) +#define MODE_FROM_HEADER(where,hbits) mode_from_header (where, sizeof (where), hbits) +#define TIME_FROM_HEADER(where) time_from_header (where, sizeof (where)) +#define UID_FROM_HEADER(where) uid_from_header (where, sizeof (where)) +#define UINTMAX_FROM_HEADER(where) uintmax_from_header (where, sizeof (where)) + +/*** file scope type declarations ****************************************************************/ + +typedef enum +{ + HEADER_STILL_UNREAD, /* for when read_header has not been called */ + HEADER_SUCCESS, /* header successfully read and checksummed */ + HEADER_ZERO_BLOCK, /* zero block where header expected */ + HEADER_END_OF_FILE, /* true end of file while header expected */ + HEADER_FAILURE /* ill-formed header, or bad checksum */ +} read_header; + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +static struct vfs_s_subclass tarfs_subclass; +static struct vfs_class *vfs_tarfs_ops = VFS_CLASS (&tarfs_subclass); + +static struct timespec start_time; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static void +tar_stat_destroy (struct tar_stat_info *st) +{ + g_free (st->orig_file_name); + g_free (st->file_name); + g_free (st->link_name); +#if 0 + g_free (st->uname); + g_free (st->gname); +#endif + if (st->sparse_map != NULL) + { + g_array_free (st->sparse_map, TRUE); + st->sparse_map = NULL; + } + tar_xheader_destroy (&st->xhdr); + memset (st, 0, sizeof (*st)); +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline gid_t +gid_from_header (const char *p, size_t s) +{ + return tar_from_header (p, s, "gid_t", TYPE_MINIMUM (gid_t), TYPE_MAXIMUM (gid_t), FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline major_t +major_from_header (const char *p, size_t s) +{ + return tar_from_header (p, s, "major_t", TYPE_MINIMUM (major_t), TYPE_MAXIMUM (major_t), FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline minor_t +minor_from_header (const char *p, size_t s) +{ + return tar_from_header (p, s, "minor_t", TYPE_MINIMUM (minor_t), TYPE_MAXIMUM (minor_t), FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Convert @p to the file mode, as understood by tar. + * Store unrecognized mode bits (from 10th up) in @hbits. + * Set *hbits if there are any unrecognized bits. + * */ +static inline mode_t +mode_from_header (const char *p, size_t s, gboolean * hbits) +{ + unsigned int u; + mode_t mode; + + /* Do not complain about unrecognized mode bits. */ + u = tar_from_header (p, s, "mode_t", INTMAX_MIN, UINTMAX_MAX, FALSE); + + /* *INDENT-OFF* */ + mode = ((u & TSUID ? S_ISUID : 0) + | (u & TSGID ? S_ISGID : 0) + | (u & TSVTX ? S_ISVTX : 0) + | (u & TUREAD ? S_IRUSR : 0) + | (u & TUWRITE ? S_IWUSR : 0) + | (u & TUEXEC ? S_IXUSR : 0) + | (u & TGREAD ? S_IRGRP : 0) + | (u & TGWRITE ? S_IWGRP : 0) + | (u & TGEXEC ? S_IXGRP : 0) + | (u & TOREAD ? S_IROTH : 0) + | (u & TOWRITE ? S_IWOTH : 0) + | (u & TOEXEC ? S_IXOTH : 0)); + /* *INDENT-ON* */ + + if (hbits != NULL) + *hbits = (u & ~07777) != 0; + + return mode; +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline time_t +time_from_header (const char *p, size_t s) +{ + return tar_from_header (p, s, "time_t", TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t), FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline uid_t +uid_from_header (const char *p, size_t s) +{ + return tar_from_header (p, s, "uid_t", TYPE_MINIMUM (uid_t), TYPE_MAXIMUM (uid_t), FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline uintmax_t +uintmax_from_header (const char *p, size_t s) +{ + return tar_from_header (p, s, "uintmax_t", 0, UINTMAX_MAX, FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +tar_calc_sparse_offsets (struct vfs_s_inode *inode) +{ + off_t begin = inode->data_offset; + GArray *sm = (GArray *) inode->user_data; + size_t i; + + for (i = 0; i < sm->len; i++) + { + struct sp_array *sp; + + sp = &g_array_index (sm, struct sp_array, i); + sp->arch_offset = begin; + begin += BLOCKSIZE * (sp->numbytes / BLOCKSIZE + sp->numbytes % BLOCKSIZE); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +tar_skip_member (tar_super_t * archive, struct vfs_s_inode *inode) +{ + char save_typeflag; + + if (current_stat_info.skipped) + return TRUE; + + save_typeflag = current_header->header.typeflag; + + tar_set_next_block_after (current_header); + + if (current_stat_info.is_sparse) + { + if (inode != NULL) + inode->data_offset = BLOCKSIZE * tar_current_block_ordinal (archive); + + (void) tar_sparse_skip_file (archive, ¤t_stat_info); + + if (inode != NULL) + { + /* use vfs_s_inode::user_data to keep the sparse map */ + inode->user_data = current_stat_info.sparse_map; + current_stat_info.sparse_map = NULL; + + tar_calc_sparse_offsets (inode); + } + } + else if (save_typeflag != DIRTYPE) + { + if (inode != NULL) + inode->data_offset = BLOCKSIZE * tar_current_block_ordinal (archive); + + return tar_skip_file (archive, current_stat_info.stat.st_size); + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Return the number of bytes comprising the space between @pointer through the end + * of the current buffer of blocks. This space is available for filling with data, + * or taking data from. @pointer is usually (but not always) the result previous + * tar_find_next_block() call. + */ +static inline size_t +tar_available_space_after (const union block *pointer) +{ + return record_end->buffer - pointer->buffer; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** Check header checksum. + */ +static read_header +tar_checksum (const union block *header) +{ + size_t i; + int unsigned_sum = 0; /* the POSIX one :-) */ + int signed_sum = 0; /* the Sun one :-( */ + int recorded_sum; + int parsed_sum; + const char *p = header->buffer; + + for (i = sizeof (*header); i-- != 0;) + { + unsigned_sum += (unsigned char) *p; + signed_sum += (signed char) (*p++); + } + + if (unsigned_sum == 0) + return HEADER_ZERO_BLOCK; + + /* Adjust checksum to count the "chksum" field as blanks. */ + for (i = sizeof (header->header.chksum); i-- != 0;) + { + unsigned_sum -= (unsigned char) header->header.chksum[i]; + signed_sum -= (signed char) (header->header.chksum[i]); + } + + unsigned_sum += ' ' * sizeof (header->header.chksum); + signed_sum += ' ' * sizeof (header->header.chksum); + + parsed_sum = + tar_from_header (header->header.chksum, sizeof (header->header.chksum), NULL, 0, + INT_MAX, TRUE); + if (parsed_sum < 0) + return HEADER_FAILURE; + + recorded_sum = parsed_sum; + + if (unsigned_sum != recorded_sum && signed_sum != recorded_sum) + return HEADER_FAILURE; + + return HEADER_SUCCESS; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +tar_decode_header (union block *header, tar_super_t * arch) +{ + gboolean hbits = FALSE; + + current_stat_info.stat.st_mode = MODE_FROM_HEADER (header->header.mode, &hbits); + + /* + * Try to determine the archive format. + */ + if (arch->type == TAR_UNKNOWN) + { + if (strcmp (header->header.magic, TMAGIC) == 0) + { + if (header->star_header.prefix[130] == 0 && isodigit (header->star_header.atime[0]) + && header->star_header.atime[11] == ' ' && isodigit (header->star_header.ctime[0]) + && header->star_header.ctime[11] == ' ') + arch->type = TAR_STAR; + else if (current_stat_info.xhdr.buffer != NULL) + arch->type = TAR_POSIX; + else + arch->type = TAR_USTAR; + } + else if (strcmp (header->buffer + offsetof (struct posix_header, magic), OLDGNU_MAGIC) == 0) + arch->type = hbits ? TAR_OLDGNU : TAR_GNU; + else + arch->type = TAR_V7; + } + + /* + * typeflag on BSDI tar (pax) always '\000' + */ + if (header->header.typeflag == '\000') + { + size_t len; + + if (header->header.name[sizeof (header->header.name) - 1] != '\0') + len = sizeof (header->header.name); + else + len = strlen (header->header.name); + + if (len != 0 && IS_PATH_SEP (header->header.name[len - 1])) + header->header.typeflag = DIRTYPE; + } + + if (header->header.typeflag == GNUTYPE_DUMPDIR) + if (arch->type == TAR_UNKNOWN) + arch->type = TAR_GNU; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +tar_fill_stat (struct vfs_s_super *archive, union block *header) +{ + tar_super_t *arch = TAR_SUPER (archive); + + /* Adjust current_stat_info.stat.st_mode because there are tar-files with + * typeflag==SYMTYPE and S_ISLNK(mod)==0. I don't + * know about the other modes but I think I cause no new + * problem when I adjust them, too. -- Norbert. + */ + if (header->header.typeflag == DIRTYPE || header->header.typeflag == GNUTYPE_DUMPDIR) + current_stat_info.stat.st_mode |= S_IFDIR; + else if (header->header.typeflag == SYMTYPE) + current_stat_info.stat.st_mode |= S_IFLNK; + else if (header->header.typeflag == CHRTYPE) + current_stat_info.stat.st_mode |= S_IFCHR; + else if (header->header.typeflag == BLKTYPE) + current_stat_info.stat.st_mode |= S_IFBLK; + else if (header->header.typeflag == FIFOTYPE) + current_stat_info.stat.st_mode |= S_IFIFO; + else + current_stat_info.stat.st_mode |= S_IFREG; + + current_stat_info.stat.st_dev = 0; +#ifdef HAVE_STRUCT_STAT_ST_RDEV + current_stat_info.stat.st_rdev = 0; +#endif + + switch (arch->type) + { + case TAR_USTAR: + case TAR_POSIX: + case TAR_GNU: + case TAR_OLDGNU: + /* *INDENT-OFF* */ + current_stat_info.stat.st_uid = *header->header.uname != '\0' + ? (uid_t) vfs_finduid (header->header.uname) + : UID_FROM_HEADER (header->header.uid); + current_stat_info.stat.st_gid = *header->header.gname != '\0' + ? (gid_t) vfs_findgid (header->header.gname) + : GID_FROM_HEADER (header->header.gid); + /* *INDENT-ON* */ + + switch (header->header.typeflag) + { + case BLKTYPE: + case CHRTYPE: +#ifdef HAVE_STRUCT_STAT_ST_RDEV + current_stat_info.stat.st_rdev = + makedev (MAJOR_FROM_HEADER (header->header.devmajor), + MINOR_FROM_HEADER (header->header.devminor)); +#endif + break; + default: + break; + } + break; + + default: + current_stat_info.stat.st_uid = UID_FROM_HEADER (header->header.uid); + current_stat_info.stat.st_gid = GID_FROM_HEADER (header->header.gid); + break; + } + + current_stat_info.atime.tv_nsec = 0; + current_stat_info.mtime.tv_nsec = 0; + current_stat_info.ctime.tv_nsec = 0; + + current_stat_info.mtime.tv_sec = TIME_FROM_HEADER (header->header.mtime); + if (arch->type == TAR_GNU || arch->type == TAR_OLDGNU) + { + current_stat_info.atime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.atime); + current_stat_info.ctime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.ctime); + } + else if (arch->type == TAR_STAR) + { + current_stat_info.atime.tv_sec = TIME_FROM_HEADER (header->star_header.atime); + current_stat_info.ctime.tv_sec = TIME_FROM_HEADER (header->star_header.ctime); + } + else + current_stat_info.atime = current_stat_info.ctime = start_time; + +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + current_stat_info.stat.st_blksize = 8 * 1024; /* FIXME */ +#endif + vfs_adjust_stat (¤t_stat_info.stat); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +tar_free_inode (struct vfs_class *me, struct vfs_s_inode *ino) +{ + (void) me; + + /* free sparse_map */ + if (ino->user_data != NULL) + g_array_free ((GArray *) ino->user_data, TRUE); +} + +/* --------------------------------------------------------------------------------------------- */ + +static read_header +tar_insert_entry (struct vfs_class *me, struct vfs_s_super *archive, union block *header, + struct vfs_s_inode **inode) +{ + char *p, *q; + char *file_name = current_stat_info.file_name; + char *link_name = current_stat_info.link_name; + size_t len; + struct vfs_s_inode *parent; + struct vfs_s_entry *entry; + + p = strrchr (file_name, PATH_SEP); + if (p == NULL) + { + len = strlen (file_name); + p = file_name; + q = file_name + len; /* "" */ + } + else + { + *(p++) = '\0'; + q = file_name; + } + + parent = vfs_s_find_inode (me, archive, q, LINK_NO_FOLLOW, FL_MKDIR); + if (parent == NULL) + return HEADER_FAILURE; + + *inode = NULL; + + if (header->header.typeflag == LNKTYPE) + { + if (*link_name != '\0') + { + len = strlen (link_name); + if (IS_PATH_SEP (link_name[len - 1])) + link_name[len - 1] = '\0'; + + *inode = vfs_s_find_inode (me, archive, link_name, LINK_NO_FOLLOW, FL_NONE); + } + + if (*inode == NULL) + return HEADER_FAILURE; + } + else + { + if (S_ISDIR (current_stat_info.stat.st_mode)) + { + entry = VFS_SUBCLASS (me)->find_entry (me, parent, p, LINK_NO_FOLLOW, FL_NONE); + if (entry != NULL) + return HEADER_SUCCESS; + } + + *inode = vfs_s_new_inode (me, archive, ¤t_stat_info.stat); + /* assgin timestamps after decoding of extended headers */ + (*inode)->st.st_mtime = current_stat_info.mtime.tv_sec; + (*inode)->st.st_atime = current_stat_info.atime.tv_sec; + (*inode)->st.st_ctime = current_stat_info.ctime.tv_sec; + (*inode)->data_offset = BLOCKSIZE * tar_current_block_ordinal (TAR_SUPER (archive)); + + if (link_name != NULL && *link_name != '\0') + (*inode)->linkname = g_strdup (link_name); + } + + entry = vfs_s_new_entry (me, p, *inode); + vfs_s_insert_entry (me, parent, entry); + + return HEADER_SUCCESS; +} + +/* --------------------------------------------------------------------------------------------- */ + +static read_header +tar_read_header (struct vfs_class *me, struct vfs_s_super *archive) +{ + tar_super_t *arch = TAR_SUPER (archive); + union block *header; + union block *next_long_name = NULL, *next_long_link = NULL; + read_header status = HEADER_SUCCESS; + + while (TRUE) + { + header = tar_find_next_block (arch); + current_header = header; + if (header == NULL) + { + status = HEADER_END_OF_FILE; + goto ret; + } + + status = tar_checksum (header); + if (status != HEADER_SUCCESS) + goto ret; + + if (header->header.typeflag == LNKTYPE || header->header.typeflag == DIRTYPE) + current_stat_info.stat.st_size = 0; /* Links 0 size on tape */ + else + { + current_stat_info.stat.st_size = OFF_FROM_HEADER (header->header.size); + if (current_stat_info.stat.st_size < 0) + { + status = HEADER_FAILURE; + goto ret; + } + } + + tar_decode_header (header, arch); + tar_fill_stat (archive, header); + + if (header->header.typeflag == GNUTYPE_LONGNAME + || header->header.typeflag == GNUTYPE_LONGLINK) + { + size_t name_size = current_stat_info.stat.st_size; + size_t n; + off_t size; + union block *header_copy; + char *bp; + size_t written; + + if (arch->type == TAR_UNKNOWN) + arch->type = TAR_GNU; + + n = name_size % BLOCKSIZE; + size = name_size + BLOCKSIZE; + if (n != 0) + size += BLOCKSIZE - n; + if ((off_t) name_size != current_stat_info.stat.st_size || size < (off_t) name_size) + { + message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive")); + status = HEADER_FAILURE; + goto ret; + } + + header_copy = g_malloc (size + 1); + + if (header->header.typeflag == GNUTYPE_LONGNAME) + { + g_free (next_long_name); + next_long_name = header_copy; + } + else + { + g_free (next_long_link); + next_long_link = header_copy; + } + + tar_set_next_block_after (header); + *header_copy = *header; + bp = header_copy->buffer + BLOCKSIZE; + + for (size -= BLOCKSIZE; size > 0; size -= written) + { + union block *data_block; + + data_block = tar_find_next_block (arch); + if (data_block == NULL) + { + g_free (header_copy); + message (D_ERROR, MSG_ERROR, _("Unexpected EOF on archive file")); + status = HEADER_FAILURE; + goto ret; + } + + written = tar_available_space_after (data_block); + if ((off_t) written > size) + written = (size_t) size; + + memcpy (bp, data_block->buffer, written); + bp += written; + tar_set_next_block_after ((union block *) (data_block->buffer + written - 1)); + } + + *bp = '\0'; + } + else if (header->header.typeflag == XHDTYPE || header->header.typeflag == SOLARIS_XHDTYPE) + { + if (arch->type == TAR_UNKNOWN) + arch->type = TAR_POSIX; + if (!tar_xheader_read + (arch, ¤t_stat_info.xhdr, header, OFF_FROM_HEADER (header->header.size))) + { + message (D_ERROR, MSG_ERROR, _("Unexpected EOF on archive file")); + status = HEADER_FAILURE; + goto ret; + } + } + else if (header->header.typeflag == XGLTYPE) + { + struct xheader xhdr; + gboolean ok; + + if (arch->type == TAR_UNKNOWN) + arch->type = TAR_POSIX; + + memset (&xhdr, 0, sizeof (xhdr)); + tar_xheader_read (arch, &xhdr, header, OFF_FROM_HEADER (header->header.size)); + ok = tar_xheader_decode_global (&xhdr); + tar_xheader_destroy (&xhdr); + + if (!ok) + { + message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive")); + status = HEADER_FAILURE; + goto ret; + } + } + else + break; + } + + { + static union block *recent_long_name = NULL, *recent_long_link = NULL; + struct posix_header const *h = &header->header; + char *file_name = NULL; + char *link_name; + struct vfs_s_inode *inode = NULL; + + g_free (recent_long_name); + + if (next_long_name != NULL) + { + file_name = g_strdup (next_long_name->buffer + BLOCKSIZE); + recent_long_name = next_long_name; + } + else + { + /* Accept file names as specified by POSIX.1-1996 section 10.1.1. */ + char *s1 = NULL; + char *s2; + + /* Don't parse TAR_OLDGNU incremental headers as POSIX prefixes. */ + if (h->prefix[0] != '\0' && strcmp (h->magic, TMAGIC) == 0) + s1 = g_strndup (h->prefix, sizeof (h->prefix)); + + s2 = g_strndup (h->name, sizeof (h->name)); + + if (s1 == NULL) + file_name = s2; + else + { + file_name = g_strconcat (s1, PATH_SEP_STR, s2, (char *) NULL); + g_free (s1); + g_free (s2); + } + + recent_long_name = NULL; + } + + tar_assign_string_dup (¤t_stat_info.orig_file_name, file_name); + canonicalize_pathname (file_name); + tar_assign_string (¤t_stat_info.file_name, file_name); + + g_free (recent_long_link); + + if (next_long_link != NULL) + { + link_name = g_strdup (next_long_link->buffer + BLOCKSIZE); + recent_long_link = next_long_link; + } + else + { + link_name = g_strndup (h->linkname, sizeof (h->linkname)); + recent_long_link = NULL; + } + + tar_assign_string (¤t_stat_info.link_name, link_name); + + if (current_stat_info.xhdr.buffer != NULL && !tar_xheader_decode (¤t_stat_info)) + { + status = HEADER_FAILURE; + goto ret; + } + + if (tar_sparse_member_p (arch, ¤t_stat_info)) + { + if (!tar_sparse_fixup_header (arch, ¤t_stat_info)) + { + status = HEADER_FAILURE; + goto ret; + } + + current_stat_info.is_sparse = TRUE; + } + else + { + current_stat_info.is_sparse = FALSE; + + if (((arch->type == TAR_GNU || arch->type == TAR_OLDGNU) + && current_header->header.typeflag == GNUTYPE_DUMPDIR) + || current_stat_info.dumpdir != NULL) + current_stat_info.is_dumpdir = TRUE; + } + + status = tar_insert_entry (me, archive, header, &inode); + if (status != HEADER_SUCCESS) + { + message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive")); + goto ret; + } + + if (recent_long_name == next_long_name) + recent_long_name = NULL; + + if (recent_long_link == next_long_link) + recent_long_link = NULL; + + if (tar_skip_member (arch, inode)) + status = HEADER_SUCCESS; + else if (hit_eof) + status = HEADER_END_OF_FILE; + else + status = HEADER_FAILURE; + } + + ret: + g_free (next_long_name); + g_free (next_long_link); + + return status; +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct vfs_s_super * +tar_new_archive (struct vfs_class *me) +{ + tar_super_t *arch; + gint64 usec; + + arch = g_new0 (tar_super_t, 1); + arch->base.me = me; + arch->fd = -1; + arch->type = TAR_UNKNOWN; + + /* Prepare global data needed for tar_find_next_block: */ + record_start_block = 0; + arch->record_start = g_malloc (record_size); + record_end = arch->record_start; /* set up for 1st record = # 0 */ + current_block = arch->record_start; + hit_eof = FALSE; + + /* time in microseconds */ + usec = g_get_real_time (); + /* time in seconds and nanoseconds */ + start_time.tv_sec = usec / G_USEC_PER_SEC; + start_time.tv_nsec = (usec % G_USEC_PER_SEC) * 1000; + + return VFS_SUPER (arch); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +tar_free_archive (struct vfs_class *me, struct vfs_s_super *archive) +{ + tar_super_t *arch = TAR_SUPER (archive); + + (void) me; + + if (arch->fd != -1) + { + mc_close (arch->fd); + arch->fd = -1; + } + + g_free (arch->record_start); + tar_stat_destroy (¤t_stat_info); +} + +/* --------------------------------------------------------------------------------------------- */ + +/* Returns status of the tar archive open */ +static gboolean +tar_open_archive_int (struct vfs_class *me, const vfs_path_t * vpath, struct vfs_s_super *archive) +{ + tar_super_t *arch = TAR_SUPER (archive); + int result, type; + mode_t mode; + struct vfs_s_inode *root; + + result = mc_open (vpath, O_RDONLY); + if (result == -1) + { + message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), vfs_path_as_str (vpath)); + ERRNOR (ENOENT, FALSE); + } + + archive->name = g_strdup (vfs_path_as_str (vpath)); + mc_stat (vpath, &arch->st); + + /* Find out the method to handle this tar file */ + type = get_compression_type (result, archive->name); + if (type == COMPRESSION_NONE) + mc_lseek (result, 0, SEEK_SET); + else + { + char *s; + vfs_path_t *tmp_vpath; + + mc_close (result); + s = g_strconcat (archive->name, decompress_extension (type), (char *) NULL); + tmp_vpath = vfs_path_from_str_flags (s, VPF_NO_CANON); + result = mc_open (tmp_vpath, O_RDONLY); + vfs_path_free (tmp_vpath, TRUE); + if (result == -1) + message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), s); + g_free (s); + if (result == -1) + { + MC_PTR_FREE (archive->name); + ERRNOR (ENOENT, FALSE); + } + } + + arch->fd = result; + mode = arch->st.st_mode & 07777; + if (mode & 0400) + mode |= 0100; + if (mode & 0040) + mode |= 0010; + if (mode & 0004) + mode |= 0001; + mode |= S_IFDIR; + + root = vfs_s_new_inode (me, archive, &arch->st); + root->st.st_mode = mode; + root->data_offset = -1; + root->st.st_nlink++; + root->st.st_dev = VFS_SUBCLASS (me)->rdev++; + + archive->root = root; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Main loop for reading an archive. + * Returns 0 on success, -1 on error. + */ +static int +tar_open_archive (struct vfs_s_super *archive, const vfs_path_t * vpath, + const vfs_path_element_t * vpath_element) +{ + tar_super_t *arch = TAR_SUPER (archive); + /* Initial status at start of archive */ + read_header status = HEADER_STILL_UNREAD; + + /* Open for reading */ + if (!tar_open_archive_int (vpath_element->class, vpath, archive)) + return -1; + + tar_find_next_block (arch); + + while (TRUE) + { + read_header prev_status; + + prev_status = status; + tar_stat_destroy (¤t_stat_info); + status = tar_read_header (vpath_element->class, archive); + + switch (status) + { + case HEADER_STILL_UNREAD: + message (D_ERROR, MSG_ERROR, _("%s\ndoesn't look like a tar archive"), + vfs_path_as_str (vpath)); + return -1; + + case HEADER_SUCCESS: + continue; + + /* Record of zeroes */ + case HEADER_ZERO_BLOCK: + tar_set_next_block_after (current_header); + (void) tar_read_header (vpath_element->class, archive); + status = prev_status; + continue; + + case HEADER_END_OF_FILE: + break; + + /* Invalid header: + * If the previous header was good, tell them that we are skipping bad ones. */ + case HEADER_FAILURE: + tar_set_next_block_after (current_header); + + switch (prev_status) + { + case HEADER_STILL_UNREAD: + message (D_ERROR, MSG_ERROR, _("%s\ndoesn't look like a tar archive"), + vfs_path_as_str (vpath)); + return -1; + + case HEADER_ZERO_BLOCK: + case HEADER_SUCCESS: + /* Skipping to next header. */ + break; /* AB: FIXME */ + + case HEADER_END_OF_FILE: + case HEADER_FAILURE: + /* We are in the middle of a cascade of errors. */ + /* AB: FIXME: TODO: show an error message here */ + return -1; + + default: + break; + } + continue; + + default: + break; + } + break; + } + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void * +tar_super_check (const vfs_path_t * vpath) +{ + static struct stat stat_buf; + int stat_result; + + stat_result = mc_stat (vpath, &stat_buf); + + return (stat_result != 0) ? NULL : &stat_buf; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +tar_super_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *parc, + const vfs_path_t * vpath, void *cookie) +{ + struct stat *archive_stat = cookie; /* stat of main archive */ + + (void) vpath_element; + + if (strcmp (parc->name, vfs_path_as_str (vpath)) != 0) + return 0; + + /* Has the cached archive been changed on the disk? */ + if (parc != NULL && TAR_SUPER (parc)->st.st_mtime < archive_stat->st_mtime) + { + /* Yes, reload! */ + vfs_tarfs_ops->free ((vfsid) parc); + vfs_rmstamp (vfs_tarfs_ops, (vfsid) parc); + return 2; + } + /* Hasn't been modified, give it a new timeout */ + vfs_stamp (vfs_tarfs_ops, (vfsid) parc); + return 1; +} + +/* --------------------------------------------------------------------------------------------- */ +/* Get indes of current data chunk in a sparse file. + * + * @param sparse_map map of the sparse file + * @param offset offset in the sparse file + * + * @return an index of ahole or a data chunk + * positive: pointer to the data chunk; + * negative: pointer to the hole before data chunk; + * zero: pointer to the hole after last data chunk + * + * +--------+--------+-------+--------+-----+-------+--------+---------+ + * | hole1 | chunk1 | hole2 | chunk2 | ... | holeN | chunkN | holeN+1 | + * +--------+--------+-------+--------+-----+-------+--------+---------+ + * -1 1 -2 2 -N N 0 + */ + +static ssize_t +tar_get_sparse_chunk_idx (const GArray * sparse_map, off_t offset) +{ + size_t k; + + for (k = 1; k <= sparse_map->len; k++) + { + const struct sp_array *chunk; + + chunk = &g_array_index (sparse_map, struct sp_array, k - 1); + + /* are we in the current chunk? */ + if (offset >= chunk->offset && offset < chunk->offset + chunk->numbytes) + return k; + + /* are we before the current chunk? */ + if (offset < chunk->offset) + return -k; + } + + /* after the last chunk */ + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static ssize_t +tar_read_sparse (vfs_file_handler_t * fh, char *buffer, size_t count) +{ + int fd = TAR_SUPER (fh->ino->super)->fd; + const GArray *sm = (const GArray *) fh->ino->user_data; + ssize_t chunk_idx; + const struct sp_array *chunk; + off_t remain; + ssize_t res; + + chunk_idx = tar_get_sparse_chunk_idx (sm, fh->pos); + if (chunk_idx > 0) + { + /* we are in the chunk -- read data until chunk end */ + chunk = &g_array_index (sm, struct sp_array, chunk_idx - 1); + remain = MIN ((off_t) count, chunk->offset + chunk->numbytes - fh->pos); + res = mc_read (fd, buffer, (size_t) remain); + } + else + { + if (chunk_idx == 0) + { + /* we are in the hole after last chunk -- return zeros until file end */ + remain = MIN ((off_t) count, fh->ino->st.st_size - fh->pos); + /* FIXME: can remain be negative? */ + remain = MAX (remain, 0); + } + else /* chunk_idx < 0 */ + { + /* we are in the hole -- return zeros until next chunk start */ + chunk = &g_array_index (sm, struct sp_array, -chunk_idx - 1); + remain = MIN ((off_t) count, chunk->offset - fh->pos); + } + + memset (buffer, 0, (size_t) remain); + res = (ssize_t) remain; + } + + return res; +} + +/* --------------------------------------------------------------------------------------------- */ + +static off_t +tar_lseek_sparse (vfs_file_handler_t * fh, off_t offset) +{ + off_t saved_offset = offset; + int fd = TAR_SUPER (fh->ino->super)->fd; + const GArray *sm = (const GArray *) fh->ino->user_data; + ssize_t chunk_idx; + const struct sp_array *chunk; + off_t res; + + chunk_idx = tar_get_sparse_chunk_idx (sm, offset); + if (chunk_idx > 0) + { + /* we are in the chunk */ + + chunk = &g_array_index (sm, struct sp_array, chunk_idx - 1); + /* offset in the chunk */ + offset -= chunk->offset; + /* offset in the archive */ + offset += chunk->arch_offset; + } + else + { + /* we are in the hole */ + + /* we cannot lseek in hole so seek to the hole begin or end */ + switch (chunk_idx) + { + case -1: + offset = fh->ino->data_offset; + break; + + case 0: + chunk = &g_array_index (sm, struct sp_array, sm->len - 1); + /* FIXME: can we seek beyond tar archive EOF here? */ + offset = chunk->arch_offset + chunk->numbytes; + break; + + default: + chunk = &g_array_index (sm, struct sp_array, -chunk_idx - 1); + offset = chunk->arch_offset + chunk->numbytes; + break; + } + } + + res = mc_lseek (fd, offset, SEEK_SET); + /* return requested offset in success */ + if (res == offset) + res = saved_offset; + + return res; +} + +/* --------------------------------------------------------------------------------------------- */ + +static ssize_t +tar_read (void *fh, char *buffer, size_t count) +{ + struct vfs_class *me = VFS_FILE_HANDLER_SUPER (fh)->me; + vfs_file_handler_t *file = VFS_FILE_HANDLER (fh); + int fd = TAR_SUPER (VFS_FILE_HANDLER_SUPER (fh))->fd; + off_t begin = file->pos; + ssize_t res; + + if (file->ino->user_data != NULL) + { + if (tar_lseek_sparse (file, begin) != begin) + ERRNOR (EIO, -1); + + res = tar_read_sparse (file, buffer, count); + } + else + { + begin += file->ino->data_offset; + + if (mc_lseek (fd, begin, SEEK_SET) != begin) + ERRNOR (EIO, -1); + + count = (size_t) MIN ((off_t) count, file->ino->st.st_size - file->pos); + res = mc_read (fd, buffer, count); + } + + if (res == -1) + ERRNOR (errno, -1); + + file->pos += res; + return res; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +tar_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode) +{ + (void) fh; + (void) mode; + + if ((flags & O_ACCMODE) != O_RDONLY) + ERRNOR (EROFS, -1); + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +vfs_init_tarfs (void) +{ + /* FIXME: tarfs used own temp files */ + vfs_init_subclass (&tarfs_subclass, "tarfs", VFSF_READONLY, "utar"); + vfs_tarfs_ops->read = tar_read; + vfs_tarfs_ops->setctl = NULL; + tarfs_subclass.archive_check = tar_super_check; + tarfs_subclass.archive_same = tar_super_same; + tarfs_subclass.new_archive = tar_new_archive; + tarfs_subclass.open_archive = tar_open_archive; + tarfs_subclass.free_archive = tar_free_archive; + tarfs_subclass.free_inode = tar_free_inode; + tarfs_subclass.fh_open = tar_fh_open; + vfs_register_class (vfs_tarfs_ops); + + tar_base64_init (); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/tar/tar.h b/src/vfs/tar/tar.h new file mode 100644 index 0000000..5ad11b5 --- /dev/null +++ b/src/vfs/tar/tar.h @@ -0,0 +1,18 @@ +#ifndef MC__VFS_TAR_H +#define MC__VFS_TAR_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_tarfs (void); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__VFS_TAR_H */ diff --git a/src/vfs/undelfs/Makefile.am b/src/vfs/undelfs/Makefile.am new file mode 100644 index 0000000..4e7a77d --- /dev/null +++ b/src/vfs/undelfs/Makefile.am @@ -0,0 +1,7 @@ + +AM_CPPFLAGS = $(GLIB_CFLAGS) -I$(top_srcdir) + +noinst_LTLIBRARIES = libvfs-undelfs.la + +libvfs_undelfs_la_SOURCES = \ + undelfs.c undelfs.h diff --git a/src/vfs/undelfs/Makefile.in b/src/vfs/undelfs/Makefile.in new file mode 100644 index 0000000..4f258d7 --- /dev/null +++ b/src/vfs/undelfs/Makefile.in @@ -0,0 +1,735 @@ +# 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/undelfs +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_undelfs_la_LIBADD = +am_libvfs_undelfs_la_OBJECTS = undelfs.lo +libvfs_undelfs_la_OBJECTS = $(am_libvfs_undelfs_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)/undelfs.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_undelfs_la_SOURCES) +DIST_SOURCES = $(libvfs_undelfs_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) +noinst_LTLIBRARIES = libvfs-undelfs.la +libvfs_undelfs_la_SOURCES = \ + undelfs.c undelfs.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/undelfs/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/vfs/undelfs/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-undelfs.la: $(libvfs_undelfs_la_OBJECTS) $(libvfs_undelfs_la_DEPENDENCIES) $(EXTRA_libvfs_undelfs_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libvfs_undelfs_la_OBJECTS) $(libvfs_undelfs_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/undelfs.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)/undelfs.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)/undelfs.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/undelfs/undelfs.c b/src/vfs/undelfs/undelfs.c new file mode 100644 index 0000000..de54440 --- /dev/null +++ b/src/vfs/undelfs/undelfs.c @@ -0,0 +1,844 @@ +/* + UnDel File System: Midnight Commander file system. + + This file system is intended to be used together with the + ext2fs library to recover files from ext2fs file systems. + + Parts of this program were taken from the lsdel.c and dump.c files + written by Ted Ts'o (tytso@mit.edu) for the ext2fs package. + + Copyright (C) 1995-2023 + Free Software Foundation, Inc. + + Written by: + Miguel de Icaza, 1995 + Norbert Warmuth, 1997 + Pavel Machek, 2000 + + 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/>. + */ + +/** + * \file + * \brief Source: UnDel File System + * + * Assumptions: + * + * 1. We don't handle directories (thus undelfs_get_path is easy to write). + * 2. Files are on the local file system (we do not support vfs files + * because we would have to provide an io_manager for the ext2fs tools, + * and I don't think it would be too useful to undelete files + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> /* memset() */ +#include <ext2fs/ext2_fs.h> +#include <ext2fs/ext2fs.h> +#include <ctype.h> + +#include "lib/global.h" + +#include "lib/util.h" +#include "lib/widget.h" /* message() */ +#include "lib/vfs/xdirentry.h" +#include "lib/vfs/utilvfs.h" +#include "lib/vfs/vfs.h" + +#include "undelfs.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/* To generate the . and .. entries use -2 */ +#define READDIR_PTR_INIT 0 + +#define undelfs_stat undelfs_lstat + +/*** file scope type declarations ****************************************************************/ + +struct deleted_info +{ + ext2_ino_t ino; + unsigned short mode; + unsigned short uid; + unsigned short gid; + unsigned long size; + time_t dtime; + int num_blocks; + int free_blocks; +}; + +struct lsdel_struct +{ + ext2_ino_t inode; + int num_blocks; + int free_blocks; + int bad_blocks; +}; + +typedef struct +{ + int f_index; /* file index into delarray */ + char *buf; + int error_code; /* */ + off_t pos; /* file position */ + off_t current; /* used to determine current position in itereate */ + gboolean finished; + ext2_ino_t inode; + int bytes_read; + off_t size; + + /* Used by undelfs_read: */ + char *dest_buffer; /* destination buffer */ + size_t count; /* bytes to read */ +} undelfs_file; + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +/* We only allow one opened ext2fs */ +static char *ext2_fname; +static ext2_filsys fs = NULL; +static struct lsdel_struct lsd; +static struct deleted_info *delarray; +static int num_delarray, max_delarray; +static char *block_buf; +static const char *undelfserr = N_("undelfs: error"); +static int readdir_ptr; +static int undelfs_usage; + +static struct vfs_s_subclass undelfs_subclass; +static struct vfs_class *vfs_undelfs_ops = VFS_CLASS (&undelfs_subclass); + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static void +undelfs_shutdown (void) +{ + if (fs) + ext2fs_close (fs); + fs = NULL; + MC_PTR_FREE (ext2_fname); + MC_PTR_FREE (delarray); + MC_PTR_FREE (block_buf); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +undelfs_get_path (const vfs_path_t * vpath, char **fsname, char **file) +{ + const char *p, *dirname; + + dirname = vfs_path_get_last_path_str (vpath); + + /* To look like filesystem, we have virtual directories + undel://XXX, which have no subdirectories. XXX is replaced with + hda5, sdb8 etc, which is assumed to live under /dev. + -- pavel@ucw.cz */ + + *fsname = NULL; + + if (strncmp (dirname, "undel://", 8) != 0) + return; + + dirname += 8; + + /* Since we don't allow subdirectories, it's easy to get a filename, + * just scan backwards for a slash */ + if (*dirname == '\0') + return; + + p = dirname + strlen (dirname); +#if 0 + /* Strip trailing ./ + */ + if (p - dirname > 2 && IS_PATH_SEP (p[-1]) && p[-2] == '.') + *(p = p - 2) = 0; +#endif + + while (p > dirname) + { + if (IS_PATH_SEP (*p)) + { + char *tmp; + + *file = g_strdup (p + 1); + tmp = g_strndup (dirname, p - dirname); + *fsname = g_strconcat ("/dev/", tmp, (char *) NULL); + g_free (tmp); + return; + } + p--; + } + *file = g_strdup (""); + *fsname = g_strconcat ("/dev/", dirname, (char *) NULL); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +undelfs_lsdel_proc (ext2_filsys _fs, blk_t * block_nr, int blockcnt, void *private) +{ + struct lsdel_struct *_lsd = (struct lsdel_struct *) private; + (void) blockcnt; + _lsd->num_blocks++; + + if (*block_nr < _fs->super->s_first_data_block || *block_nr >= _fs->super->s_blocks_count) + { + _lsd->bad_blocks++; + return BLOCK_ABORT; + } + + if (!ext2fs_test_block_bitmap (_fs->block_map, *block_nr)) + _lsd->free_blocks++; + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Load information about deleted files. + * Don't abort if there is not enough memory - load as much as we can. + */ + +static int +undelfs_loaddel (void) +{ + int retval, count; + ext2_ino_t ino; + struct ext2_inode inode; + ext2_inode_scan scan; + + max_delarray = 100; + num_delarray = 0; + delarray = g_try_malloc (sizeof (struct deleted_info) * max_delarray); + if (!delarray) + { + message (D_ERROR, undelfserr, "%s", _("not enough memory")); + return 0; + } + block_buf = g_try_malloc (fs->blocksize * 3); + if (!block_buf) + { + message (D_ERROR, undelfserr, "%s", _("while allocating block buffer")); + goto free_delarray; + } + retval = ext2fs_open_inode_scan (fs, 0, &scan); + if (retval != 0) + { + message (D_ERROR, undelfserr, _("open_inode_scan: %d"), retval); + goto free_block_buf; + } + retval = ext2fs_get_next_inode (scan, &ino, &inode); + if (retval != 0) + { + message (D_ERROR, undelfserr, _("while starting inode scan %d"), retval); + goto error_out; + } + count = 0; + while (ino) + { + if ((count++ % 1024) == 0) + vfs_print_message (_("undelfs: loading deleted files information %d inodes"), count); + if (inode.i_dtime == 0) + goto next; + + if (S_ISDIR (inode.i_mode)) + goto next; + + lsd.inode = ino; + lsd.num_blocks = 0; + lsd.free_blocks = 0; + lsd.bad_blocks = 0; + + retval = ext2fs_block_iterate (fs, ino, 0, block_buf, undelfs_lsdel_proc, &lsd); + if (retval) + { + message (D_ERROR, undelfserr, _("while calling ext2_block_iterate %d"), retval); + goto next; + } + if (lsd.free_blocks && !lsd.bad_blocks) + { + if (num_delarray >= max_delarray) + { + struct deleted_info *delarray_new = g_try_realloc (delarray, + sizeof (struct deleted_info) * + (max_delarray + 50)); + if (!delarray_new) + { + message (D_ERROR, undelfserr, "%s", + _("no more memory while reallocating array")); + goto error_out; + } + delarray = delarray_new; + max_delarray += 50; + } + + delarray[num_delarray].ino = ino; + delarray[num_delarray].mode = inode.i_mode; + delarray[num_delarray].uid = inode.i_uid; + delarray[num_delarray].gid = inode.i_gid; + delarray[num_delarray].size = inode.i_size; + delarray[num_delarray].dtime = inode.i_dtime; + delarray[num_delarray].num_blocks = lsd.num_blocks; + delarray[num_delarray].free_blocks = lsd.free_blocks; + num_delarray++; + } + + next: + retval = ext2fs_get_next_inode (scan, &ino, &inode); + if (retval) + { + message (D_ERROR, undelfserr, _("while doing inode scan %d"), retval); + goto error_out; + } + } + readdir_ptr = READDIR_PTR_INIT; + ext2fs_close_inode_scan (scan); + return 1; + + error_out: + ext2fs_close_inode_scan (scan); + free_block_buf: + MC_PTR_FREE (block_buf); + free_delarray: + MC_PTR_FREE (delarray); + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void * +undelfs_opendir (const vfs_path_t * vpath) +{ + char *file, *f = NULL; + const char *class_name; + + class_name = vfs_path_get_last_path_vfs (vpath)->name; + undelfs_get_path (vpath, &file, &f); + if (file == NULL) + { + g_free (f); + return 0; + } + + /* We don't use the file name */ + g_free (f); + + if (!ext2_fname || strcmp (ext2_fname, file)) + { + undelfs_shutdown (); + ext2_fname = file; + } + else + { + /* To avoid expensive re-scannings */ + readdir_ptr = READDIR_PTR_INIT; + g_free (file); + return fs; + } + + if (ext2fs_open (ext2_fname, 0, 0, 0, unix_io_manager, &fs)) + { + message (D_ERROR, undelfserr, _("Cannot open file %s"), ext2_fname); + return 0; + } + vfs_print_message ("%s", _("undelfs: reading inode bitmap...")); + if (ext2fs_read_inode_bitmap (fs)) + { + message (D_ERROR, undelfserr, _("Cannot load inode bitmap from:\n%s"), ext2_fname); + goto quit_opendir; + } + vfs_print_message ("%s", _("undelfs: reading block bitmap...")); + if (ext2fs_read_block_bitmap (fs)) + { + message (D_ERROR, undelfserr, _("Cannot load block bitmap from:\n%s"), ext2_fname); + goto quit_opendir; + } + /* Now load the deleted information */ + if (!undelfs_loaddel ()) + goto quit_opendir; + vfs_print_message (_("%s: done."), class_name); + return fs; + quit_opendir: + vfs_print_message (_("%s: failure"), class_name); + ext2fs_close (fs); + fs = NULL; + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct vfs_dirent * +undelfs_readdir (void *vfs_info) +{ + struct vfs_dirent *dirent; + + if (vfs_info != fs) + { + message (D_ERROR, undelfserr, "%s", _("vfs_info is not fs!")); + return NULL; + } + if (readdir_ptr == num_delarray) + return NULL; + if (readdir_ptr < 0) + dirent = vfs_dirent_init (NULL, readdir_ptr == -2 ? "." : "..", 0); /* FIXME: inode */ + else + { + char dirent_dest[MC_MAXPATHLEN]; + + g_snprintf (dirent_dest, MC_MAXPATHLEN, "%ld:%d", + (long) delarray[readdir_ptr].ino, delarray[readdir_ptr].num_blocks); + dirent = vfs_dirent_init (NULL, dirent_dest, 0); /* FIXME: inode */ + } + readdir_ptr++; + + return dirent; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +undelfs_closedir (void *vfs_info) +{ + (void) vfs_info; + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/* We do not support lseek */ + +static void * +undelfs_open (const vfs_path_t * vpath, int flags, mode_t mode) +{ + char *file, *f = NULL; + ext2_ino_t inode, i; + undelfs_file *p = NULL; + (void) flags; + (void) mode; + + /* Only allow reads on this file system */ + undelfs_get_path (vpath, &file, &f); + if (file == NULL) + { + g_free (f); + return 0; + } + + if (!ext2_fname || strcmp (ext2_fname, file)) + { + message (D_ERROR, undelfserr, "%s", _("You have to chdir to extract files first")); + g_free (file); + g_free (f); + return 0; + } + inode = atol (f); + + /* Search the file into delarray */ + for (i = 0; i < (ext2_ino_t) num_delarray; i++) + { + if (inode != delarray[i].ino) + continue; + + /* Found: setup all the structures needed by read */ + p = (undelfs_file *) g_try_malloc (((gsize) sizeof (undelfs_file))); + if (!p) + { + g_free (file); + g_free (f); + return 0; + } + p->buf = g_try_malloc (fs->blocksize); + if (!p->buf) + { + g_free (p); + g_free (file); + g_free (f); + return 0; + } + p->inode = inode; + p->finished = FALSE; + p->f_index = i; + p->error_code = 0; + p->pos = 0; + p->size = delarray[i].size; + } + g_free (file); + g_free (f); + undelfs_usage++; + return p; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +undelfs_close (void *vfs_info) +{ + undelfs_file *p = vfs_info; + g_free (p->buf); + g_free (p); + undelfs_usage--; + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +undelfs_dump_read (ext2_filsys param_fs, blk_t * blocknr, int blockcnt, void *private) +{ + int copy_count; + undelfs_file *p = (undelfs_file *) private; + + if (blockcnt < 0) + return 0; + + if (*blocknr) + { + p->error_code = io_channel_read_blk (param_fs->io, *blocknr, 1, p->buf); + if (p->error_code) + return BLOCK_ABORT; + } + else + memset (p->buf, 0, param_fs->blocksize); + + if (p->pos + (off_t) p->count < p->current) + { + p->finished = TRUE; + return BLOCK_ABORT; + } + if (p->pos > p->current + param_fs->blocksize) + { + p->current += param_fs->blocksize; + return 0; /* we have not arrived yet */ + } + + /* Now, we know we have to extract some data */ + if (p->pos >= p->current) + { + + /* First case: starting pointer inside this block */ + if (p->pos + (off_t) p->count <= p->current + param_fs->blocksize) + { + /* Fully contained */ + copy_count = p->count; + p->finished = (p->count != 0); + } + else + { + /* Still some more data */ + copy_count = param_fs->blocksize - (p->pos - p->current); + } + memcpy (p->dest_buffer, p->buf + (p->pos - p->current), copy_count); + } + else + { + /* Second case: we already have passed p->pos */ + if (p->pos + (off_t) p->count < p->current + param_fs->blocksize) + { + copy_count = (p->pos + p->count) - p->current; + p->finished = (p->count != 0); + } + else + { + copy_count = param_fs->blocksize; + } + memcpy (p->dest_buffer, p->buf, copy_count); + } + p->dest_buffer += copy_count; + p->current += param_fs->blocksize; + if (p->finished) + { + return BLOCK_ABORT; + } + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static ssize_t +undelfs_read (void *vfs_info, char *buffer, size_t count) +{ + undelfs_file *p = vfs_info; + int retval; + + p->dest_buffer = buffer; + p->current = 0; + p->finished = FALSE; + p->count = count; + + if (p->pos + (off_t) p->count > p->size) + { + p->count = p->size - p->pos; + } + retval = ext2fs_block_iterate (fs, p->inode, 0, NULL, undelfs_dump_read, p); + if (retval) + { + message (D_ERROR, undelfserr, "%s", _("while iterating over blocks")); + return -1; + } + if (p->error_code && !p->finished) + return 0; + p->pos = p->pos + (p->dest_buffer - buffer); + return p->dest_buffer - buffer; +} + +/* --------------------------------------------------------------------------------------------- */ + +static long +undelfs_getindex (char *path) +{ + ext2_ino_t inode = atol (path); + int i; + + for (i = 0; i < num_delarray; i++) + { + if (delarray[i].ino == inode) + return i; + } + return -1; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +undelfs_stat_int (int inode_index, struct stat *buf) +{ + buf->st_dev = 0; + buf->st_ino = delarray[inode_index].ino; + buf->st_mode = delarray[inode_index].mode; + buf->st_nlink = 1; + buf->st_uid = delarray[inode_index].uid; + buf->st_gid = delarray[inode_index].gid; + buf->st_size = delarray[inode_index].size; + buf->st_atime = delarray[inode_index].dtime; + buf->st_ctime = delarray[inode_index].dtime; + buf->st_mtime = delarray[inode_index].dtime; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + buf->st_atim.tv_nsec = buf->st_mtim.tv_nsec = buf->st_ctim.tv_nsec = 0; +#endif + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +undelfs_lstat (const vfs_path_t * vpath, struct stat *buf) +{ + int inode_index; + char *file, *f = NULL; + + undelfs_get_path (vpath, &file, &f); + if (file == NULL) + { + g_free (f); + return 0; + } + + /* When called from save_cwd_stats we get an incorrect file and f here: + e.g. incorrect correct + path = "undel:/dev/sda1" path="undel:/dev/sda1/401:1" + file = "/dev" file="/dev/sda1" + f = "sda1" f ="401:1" + If the first char in f is no digit -> return error */ + if (!isdigit (*f)) + { + g_free (file); + g_free (f); + return -1; + } + + if (!ext2_fname || strcmp (ext2_fname, file)) + { + g_free (file); + g_free (f); + message (D_ERROR, undelfserr, "%s", _("You have to chdir to extract files first")); + return 0; + } + inode_index = undelfs_getindex (f); + g_free (file); + g_free (f); + + if (inode_index == -1) + return -1; + + return undelfs_stat_int (inode_index, buf); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +undelfs_fstat (void *vfs_info, struct stat *buf) +{ + undelfs_file *p = vfs_info; + + return undelfs_stat_int (p->f_index, buf); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +undelfs_chdir (const vfs_path_t * vpath) +{ + char *file, *f = NULL; + int fd; + + undelfs_get_path (vpath, &file, &f); + if (file == NULL) + { + g_free (f); + return (-1); + } + + /* We may use access because ext2 file systems are local */ + /* this could be fixed by making an ext2fs io manager to use */ + /* our vfs, but that is left as an exercise for the reader */ + fd = open (file, O_RDONLY); + if (fd == -1) + { + message (D_ERROR, undelfserr, _("Cannot open file \"%s\""), file); + g_free (f); + g_free (file); + return -1; + } + close (fd); + g_free (f); + g_free (file); + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* this has to stay here for now: vfs layer does not know how to emulate it */ +static off_t +undelfs_lseek (void *vfs_info, off_t offset, int whence) +{ + (void) vfs_info; + (void) offset; + (void) whence; + + return -1; +} + +/* --------------------------------------------------------------------------------------------- */ + +static vfsid +undelfs_getid (const vfs_path_t * vpath) +{ + char *fname = NULL, *fsname; + gboolean ok; + + undelfs_get_path (vpath, &fsname, &fname); + ok = fsname != NULL; + + g_free (fname); + g_free (fsname); + + return ok ? (vfsid) fs : NULL; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +undelfs_nothingisopen (vfsid id) +{ + (void) id; + + return (undelfs_usage == 0); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +undelfs_free (vfsid id) +{ + (void) id; + + undelfs_shutdown (); +} + +/* --------------------------------------------------------------------------------------------- */ + +#ifdef ENABLE_NLS +static int +undelfs_init (struct vfs_class *me) +{ + (void) me; + + undelfserr = _(undelfserr); + return 1; +} +#else +#define undelfs_init NULL +#endif + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ +/** + * This function overrides com_err() from libcom_err library. + * It is used in libext2fs to report errors. + */ + +void +com_err (const char *whoami, long err_code, const char *fmt, ...) +{ + va_list ap; + char *str; + + va_start (ap, fmt); + str = g_strdup_vprintf (fmt, ap); + va_end (ap); + + message (D_ERROR, _("Ext2lib error"), "%s (%s: %ld)", str, whoami, err_code); + g_free (str); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +vfs_init_undelfs (void) +{ + /* NULLize vfs_s_subclass members */ + memset (&undelfs_subclass, 0, sizeof (undelfs_subclass)); + + vfs_init_class (vfs_undelfs_ops, "undelfs", VFSF_UNKNOWN, "undel"); + vfs_undelfs_ops->init = undelfs_init; + vfs_undelfs_ops->open = undelfs_open; + vfs_undelfs_ops->close = undelfs_close; + vfs_undelfs_ops->read = undelfs_read; + vfs_undelfs_ops->opendir = undelfs_opendir; + vfs_undelfs_ops->readdir = undelfs_readdir; + vfs_undelfs_ops->closedir = undelfs_closedir; + vfs_undelfs_ops->stat = undelfs_stat; + vfs_undelfs_ops->lstat = undelfs_lstat; + vfs_undelfs_ops->fstat = undelfs_fstat; + vfs_undelfs_ops->chdir = undelfs_chdir; + vfs_undelfs_ops->lseek = undelfs_lseek; + vfs_undelfs_ops->getid = undelfs_getid; + vfs_undelfs_ops->nothingisopen = undelfs_nothingisopen; + vfs_undelfs_ops->free = undelfs_free; + vfs_register_class (vfs_undelfs_ops); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/undelfs/undelfs.h b/src/vfs/undelfs/undelfs.h new file mode 100644 index 0000000..9e32458 --- /dev/null +++ b/src/vfs/undelfs/undelfs.h @@ -0,0 +1,18 @@ +#ifndef MC__VFS_UNDELFS_H +#define MC__VFS_UNDELFS_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_undelfs (void); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__VFS_UNDELFS_H */ |