summaryrefslogtreecommitdiffstats
path: root/src/vfs/sfs/sfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vfs/sfs/sfs.c')
-rw-r--r--src/vfs/sfs/sfs.c603
1 files changed, 603 insertions, 0 deletions
diff --git a/src/vfs/sfs/sfs.c b/src/vfs/sfs/sfs.c
new file mode 100644
index 0000000..ad313b5
--- /dev/null
+++ b/src/vfs/sfs/sfs.c
@@ -0,0 +1,603 @@
+/*
+ Single File fileSystem
+
+ Copyright (C) 1998-2022
+ 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 <errno.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;
+
+/*** 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_by_index (cache_vpath, -1)->path;
+ 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;
+ const vfs_path_element_t *path_element;
+
+ path_element = vfs_path_get_by_index (vpath, -1);
+ 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", path_element->path);
+
+ 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);
+}
+
+/* --------------------------------------------------------------------------------------------- */