Manual pages: mcmcdiffmceditmcview

root/src/vfs/extfs/extfs.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. extfs_super_new
  2. extfs_entry_new
  3. extfs_fill_name
  4. extfs_cmp_archive
  5. extfs_generate_entry
  6. extfs_find_entry_int
  7. extfs_find_entry
  8. extfs_fill_names
  9. extfs_free_inode
  10. extfs_free_archive
  11. extfs_skip_leading_dotslash
  12. extfs_add_file
  13. extfs_open_archive
  14. extfs_read_archive
  15. extfs_which
  16. extfs_open_and_read_archive
  17. extfs_get_path
  18. extfs_get_path_from_entry
  19. extfs_resolve_symlinks_int
  20. extfs_resolve_symlinks
  21. extfs_get_archive_name
  22. extfs_cmd
  23. extfs_run
  24. extfs_open
  25. extfs_read
  26. extfs_close
  27. extfs_errno
  28. extfs_opendir
  29. extfs_readdir
  30. extfs_closedir
  31. extfs_stat_move
  32. extfs_internal_stat
  33. extfs_stat
  34. extfs_lstat
  35. extfs_fstat
  36. extfs_readlink
  37. extfs_write
  38. extfs_unlink
  39. extfs_mkdir
  40. extfs_rmdir
  41. extfs_chdir
  42. extfs_lseek
  43. extfs_getid
  44. extfs_getlocalcopy
  45. extfs_ungetlocalcopy
  46. extfs_get_plugins
  47. extfs_init
  48. extfs_done
  49. extfs_setctl
  50. vfs_init_extfs

   1 /*
   2    Virtual File System: External file system.
   3 
   4    Copyright (C) 1995-2026
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Jakub Jelinek, 1995
   9    Pavel Machek, 1998
  10    Andrew T. Veliath, 1999
  11    Slava Zanko <slavazanko@gmail.com>, 2013
  12 
  13    This file is part of the Midnight Commander.
  14 
  15    The Midnight Commander is free software: you can redistribute it
  16    and/or modify it under the terms of the GNU General Public License as
  17    published by the Free Software Foundation, either version 3 of the License,
  18    or (at your option) any later version.
  19 
  20    The Midnight Commander is distributed in the hope that it will be useful,
  21    but WITHOUT ANY WARRANTY; without even the implied warranty of
  22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23    GNU General Public License for more details.
  24 
  25    You should have received a copy of the GNU General Public License
  26    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  27  */
  28 
  29 /**
  30  * \file
  31  * \brief Source: Virtual File System: External file system
  32  * \author Jakub Jelinek
  33  * \author Pavel Machek
  34  * \author Andrew T. Veliath
  35  * \date 1995, 1998, 1999
  36  */
  37 
  38 /* Namespace: init_extfs */
  39 
  40 #include <config.h>
  41 
  42 #include <stdio.h>
  43 #include <ctype.h>
  44 #include <string.h>
  45 #include <stdlib.h>
  46 #include <sys/types.h>
  47 #include <sys/stat.h>
  48 #include <unistd.h>
  49 #include <signal.h>
  50 #include <errno.h>
  51 #include <sys/wait.h>
  52 
  53 #include "lib/global.h"
  54 #include "lib/fileloc.h"
  55 #include "lib/mcconfig.h"
  56 #include "lib/util.h"
  57 #include "lib/widget.h"  // message()
  58 
  59 #include "src/execute.h"  // For shell_execute
  60 
  61 #include "lib/vfs/vfs.h"
  62 #include "lib/vfs/utilvfs.h"
  63 #include "lib/vfs/xdirentry.h"
  64 #include "lib/vfs/gc.h"  // vfs_rmstamp
  65 
  66 #include "extfs.h"
  67 
  68 /*** global variables ****************************************************************************/
  69 
  70 /*** file scope macro definitions ****************************************************************/
  71 
  72 #undef ERRNOR
  73 #define ERRNOR(x, y)                                                                               \
  74     do                                                                                             \
  75     {                                                                                              \
  76         my_errno = x;                                                                              \
  77         return y;                                                                                  \
  78     }                                                                                              \
  79     while (0)
  80 
  81 #define RECORDSIZE     512
  82 
  83 #define EXTFS_SUPER(a) ((struct extfs_super_t *) (a))
  84 
  85 /*** file scope type declarations ****************************************************************/
  86 
  87 struct extfs_super_t
  88 {
  89     struct vfs_s_super base;  // base class
  90 
  91     int fstype;
  92     char *local_name;
  93     struct stat local_stat;
  94     dev_t rdev;
  95 };
  96 
  97 typedef struct
  98 {
  99     char *path;
 100     char *prefix;
 101     gboolean need_archive;
 102 } extfs_plugin_info_t;
 103 
 104 /*** forward declarations (file scope functions) *************************************************/
 105 
 106 static struct vfs_s_entry *extfs_resolve_symlinks_int (struct vfs_s_entry *entry, GSList *list);
 107 
 108 /*** file scope variables ************************************************************************/
 109 
 110 static GArray *extfs_plugins = NULL;
 111 
 112 static gboolean errloop;
 113 static gboolean notadir;
 114 
 115 static struct vfs_s_subclass extfs_subclass;
 116 static struct vfs_class *vfs_extfs_ops = VFS_CLASS (&extfs_subclass);
 117 
 118 static int my_errno = 0;
 119 
 120 /* --------------------------------------------------------------------------------------------- */
 121 /*** file scope functions ************************************************************************/
 122 /* --------------------------------------------------------------------------------------------- */
 123 
 124 static struct extfs_super_t *
 125 extfs_super_new (struct vfs_class *me, const char *name, const vfs_path_t *local_name_vpath,
     /* [previous][next][first][last][top][bottom][index][help]  */
 126                  int fstype)
 127 {
 128     struct extfs_super_t *super;
 129     struct vfs_s_super *vsuper;
 130 
 131     super = g_new0 (struct extfs_super_t, 1);
 132     vsuper = VFS_SUPER (super);
 133 
 134     vsuper->me = me;
 135     vsuper->name = g_strdup (name);
 136 
 137     super->fstype = fstype;
 138 
 139     if (local_name_vpath != NULL)
 140     {
 141         super->local_name = g_strdup (vfs_path_get_last_path_str (local_name_vpath));
 142         mc_stat (local_name_vpath, &super->local_stat);
 143     }
 144 
 145     VFS_SUBCLASS (me)->supers = g_list_prepend (VFS_SUBCLASS (me)->supers, super);
 146 
 147     return super;
 148 }
 149 
 150 /* --------------------------------------------------------------------------------------------- */
 151 
 152 /* unlike vfs_s_new_entry(), inode->ent is kept */
 153 static struct vfs_s_entry *
 154 extfs_entry_new (struct vfs_class *me, const char *name, struct vfs_s_inode *inode)
     /* [previous][next][first][last][top][bottom][index][help]  */
 155 {
 156     struct vfs_s_entry *entry;
 157 
 158     (void) me;
 159 
 160     entry = g_new0 (struct vfs_s_entry, 1);
 161 
 162     entry->name = g_strdup (name);
 163     entry->ino = inode;
 164 
 165     return entry;
 166 }
 167 
 168 /* --------------------------------------------------------------------------------------------- */
 169 
 170 static void
 171 extfs_fill_name (void *data, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 172 {
 173     struct vfs_s_super *a = VFS_SUPER (data);
 174     fill_names_f func = (fill_names_f) user_data;
 175     extfs_plugin_info_t *info;
 176     char *name;
 177 
 178     info = &g_array_index (extfs_plugins, extfs_plugin_info_t, EXTFS_SUPER (a)->fstype);
 179     name = g_strconcat (a->name != NULL ? a->name : "", PATH_SEP_STR, info->prefix,
 180                         VFS_PATH_URL_DELIMITER, (char *) NULL);
 181     func (name);
 182     g_free (name);
 183 }
 184 
 185 /* --------------------------------------------------------------------------------------------- */
 186 
 187 static gint
 188 extfs_cmp_archive (const void *a, const void *b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 189 {
 190     const struct vfs_s_super *ar = (const struct vfs_s_super *) a;
 191     const char *archive_name = (const char *) b;
 192 
 193     return (ar->name != NULL && strcmp (ar->name, archive_name) == 0) ? 0 : 1;
 194 }
 195 
 196 /* --------------------------------------------------------------------------------------------- */
 197 
 198 static struct vfs_s_entry *
 199 extfs_generate_entry (struct extfs_super_t *archive, const char *name, struct vfs_s_inode *parent,
     /* [previous][next][first][last][top][bottom][index][help]  */
 200                       mode_t mode)
 201 {
 202     struct vfs_class *me = VFS_SUPER (archive)->me;
 203     struct stat st;
 204     mode_t myumask;
 205     struct vfs_s_inode *inode;
 206     struct vfs_s_entry *entry;
 207 
 208     memset (&st, 0, sizeof (st));
 209     st.st_ino = VFS_SUPER (archive)->ino_usage++;
 210     st.st_dev = archive->rdev;
 211     myumask = umask (022);
 212     umask (myumask);
 213     st.st_mode = mode & ~myumask;
 214     st.st_uid = getuid ();
 215     st.st_gid = getgid ();
 216     st.st_mtime = time (NULL);
 217     st.st_atime = st.st_mtime;
 218     st.st_ctime = st.st_mtime;
 219     st.st_nlink = 1;
 220 
 221     inode = vfs_s_new_inode (me, VFS_SUPER (archive), &st);
 222     entry = vfs_s_new_entry (me, name, inode);
 223     if (parent != NULL)
 224         vfs_s_insert_entry (me, parent, entry);
 225 
 226     return entry;
 227 }
 228 
 229 /* --------------------------------------------------------------------------------------------- */
 230 
 231 static struct vfs_s_entry *
 232 extfs_find_entry_int (struct vfs_s_inode *dir, const char *name, GSList *list, int flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 233 {
 234     struct vfs_s_entry *pent, *pdir;
 235     const char *p, *name_end;
 236     char *q;
 237     char c = PATH_SEP;
 238     struct extfs_super_t *super;
 239 
 240     if (g_path_is_absolute (name))
 241     {
 242         // Handle absolute paths
 243         name = g_path_skip_root (name);
 244         dir = dir->super->root;
 245     }
 246 
 247     super = EXTFS_SUPER (dir->super);
 248     pent = dir->ent;
 249     p = name;
 250     name_end = name + strlen (name);
 251 
 252     while ((pent != NULL) && (c != '\0') && (*p != '\0'))
 253     {
 254         q = strchr (p, PATH_SEP);
 255         if (q == NULL)
 256             q = (char *) name_end;
 257 
 258         c = *q;
 259         *q = '\0';
 260 
 261         if (DIR_IS_DOTDOT (p))
 262             pent = pent->dir != NULL ? pent->dir->ent : NULL;
 263         else
 264         {
 265             GList *pl;
 266 
 267             pent = extfs_resolve_symlinks_int (pent, list);
 268             if (pent == NULL)
 269             {
 270                 *q = c;
 271                 return NULL;
 272             }
 273 
 274             if (!S_ISDIR (pent->ino->st.st_mode))
 275             {
 276                 *q = c;
 277                 notadir = TRUE;
 278                 return NULL;
 279             }
 280 
 281             pdir = pent;
 282             pl = g_queue_find_custom (pent->ino->subdir, p, vfs_s_entry_compare);
 283             pent = pl != NULL ? VFS_ENTRY (pl->data) : NULL;
 284             if (pent != NULL && q + 1 > name_end)
 285             {
 286                 // Hack: I keep the original semanthic unless q+1 would break in the strchr
 287                 *q = c;
 288                 notadir = !S_ISDIR (pent->ino->st.st_mode);
 289                 return pent;
 290             }
 291 
 292             // When we load archive, we create automagically non-existent directories
 293             if (pent == NULL && (flags & FL_MKDIR) != 0)
 294                 pent = extfs_generate_entry (super, p, pdir->ino, S_IFDIR | 0777);
 295             if (pent == NULL && (flags & FL_MKFILE) != 0)
 296                 pent = extfs_generate_entry (super, p, pdir->ino, S_IFREG | 0666);
 297         }
 298 
 299         // Next iteration
 300         *q = c;
 301         if (c != '\0')
 302             p = q + 1;
 303     }
 304     if (pent == NULL)
 305         my_errno = ENOENT;
 306     return pent;
 307 }
 308 
 309 /* --------------------------------------------------------------------------------------------- */
 310 
 311 static struct vfs_s_entry *
 312 extfs_find_entry (struct vfs_s_inode *dir, const char *name, int flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 313 {
 314     struct vfs_s_entry *res;
 315 
 316     errloop = FALSE;
 317     notadir = FALSE;
 318 
 319     res = extfs_find_entry_int (dir, name, NULL, flags);
 320     if (res == NULL)
 321     {
 322         if (errloop)
 323             my_errno = ELOOP;
 324         else if (notadir)
 325             my_errno = ENOTDIR;
 326     }
 327     return res;
 328 }
 329 
 330 /* --------------------------------------------------------------------------------------------- */
 331 
 332 static void
 333 extfs_fill_names (struct vfs_class *me, fill_names_f func)
     /* [previous][next][first][last][top][bottom][index][help]  */
 334 {
 335     g_list_foreach (VFS_SUBCLASS (me)->supers, extfs_fill_name, func);
 336 }
 337 
 338 /* --------------------------------------------------------------------------------------------- */
 339 
 340 /* Create this function because VFSF_USETMP flag is not used in extfs */
 341 static void
 342 extfs_free_inode (struct vfs_class *me, struct vfs_s_inode *ino)
     /* [previous][next][first][last][top][bottom][index][help]  */
 343 {
 344     (void) me;
 345 
 346     if (ino->localname != NULL)
 347     {
 348         unlink (ino->localname);
 349         MC_PTR_FREE (ino->localname);
 350     }
 351 }
 352 
 353 /* --------------------------------------------------------------------------------------------- */
 354 
 355 static void
 356 extfs_free_archive (struct vfs_class *me, struct vfs_s_super *psup)
     /* [previous][next][first][last][top][bottom][index][help]  */
 357 {
 358     struct extfs_super_t *archive = EXTFS_SUPER (psup);
 359 
 360     (void) me;
 361 
 362     if (archive->local_name != NULL)
 363     {
 364         struct stat my;
 365         vfs_path_t *local_name_vpath, *name_vpath;
 366 
 367         local_name_vpath = vfs_path_from_str (archive->local_name);
 368         name_vpath = vfs_path_from_str (psup->name);
 369         mc_stat (local_name_vpath, &my);
 370         mc_ungetlocalcopy (name_vpath, local_name_vpath,
 371                            archive->local_stat.st_mtime != my.st_mtime);
 372         vfs_path_free (local_name_vpath, TRUE);
 373         vfs_path_free (name_vpath, TRUE);
 374         g_free (archive->local_name);
 375     }
 376 }
 377 
 378 /* --------------------------------------------------------------------------------------------- */
 379 
 380 static inline char *
 381 extfs_skip_leading_dotslash (char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 382 {
 383     /* Skip leading "./" (if present).
 384      * Some programs don't understand it:
 385      *
 386      * $ zip file.zip ./-file2.txt file1.txt
 387      *   adding: -file2.txt (stored 0%)
 388      *   adding: file1.txt (stored 0%)
 389      * $ /usr/lib/mc/extfs.d/uzip copyout file.zip ./-file2.txt ./tmp-file2.txt
 390      * caution: filename not matched:  ./-file2.txt
 391      */
 392     if (s[0] == '.' && s[1] == PATH_SEP)
 393         s += 2;
 394 
 395     return s;
 396 }
 397 
 398 /* --------------------------------------------------------------------------------------------- */
 399 
 400 static int
 401 extfs_add_file (struct extfs_super_t *archive, const char *file_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 402 {
 403     struct vfs_s_super *super = VFS_SUPER (archive);
 404     struct stat hstat;
 405     char *current_file_name = NULL, *current_link_name = NULL;
 406     int ret = 0;
 407 
 408     if (vfs_parse_ls_lga (file_name, &hstat, &current_file_name, &current_link_name, NULL))
 409     {
 410         char *cfn = current_file_name;
 411 
 412         if (*cfn != '\0')
 413         {
 414             struct vfs_s_entry *entry;
 415             struct vfs_s_entry *pent = NULL;
 416             struct vfs_s_inode *inode;
 417             char *p, *q;
 418 
 419             cfn = extfs_skip_leading_dotslash (cfn);
 420             if (IS_PATH_SEP (*cfn))
 421                 cfn++;
 422             p = strchr (cfn, '\0');
 423             if (p != cfn && IS_PATH_SEP (p[-1]))
 424                 p[-1] = '\0';
 425             p = strrchr (cfn, PATH_SEP);
 426             if (p == NULL)
 427             {
 428                 p = cfn;
 429                 q = strchr (cfn, '\0');
 430             }
 431             else
 432             {
 433                 *(p++) = '\0';
 434                 q = cfn;
 435             }
 436 
 437             if (*q != '\0')
 438             {
 439                 pent = extfs_find_entry (super->root, q, FL_MKDIR);
 440                 if (pent == NULL)
 441                 {
 442                     ret = -1;
 443                     goto done;
 444                 }
 445             }
 446 
 447             if (pent != NULL)
 448             {
 449                 entry = extfs_entry_new (super->me, p, pent->ino);
 450                 entry->dir = pent->ino;
 451                 g_queue_push_tail (pent->ino->subdir, entry);
 452             }
 453             else
 454             {
 455                 entry = extfs_entry_new (super->me, p, super->root);
 456                 entry->dir = super->root;
 457                 g_queue_push_tail (super->root->subdir, entry);
 458             }
 459 
 460             if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL))
 461             {
 462                 pent = extfs_find_entry (super->root, current_link_name, FL_NONE);
 463                 if (pent == NULL)
 464                 {
 465                     ret = -1;
 466                     goto done;
 467                 }
 468 
 469                 pent->ino->st.st_nlink++;
 470                 entry->ino = pent->ino;
 471             }
 472             else
 473             {
 474                 struct stat st;
 475 
 476                 memset (&st, 0, sizeof (st));
 477                 st.st_ino = super->ino_usage++;
 478                 st.st_nlink = 1;
 479                 st.st_dev = archive->rdev;
 480                 st.st_mode = hstat.st_mode;
 481 #ifdef HAVE_STRUCT_STAT_ST_RDEV
 482                 st.st_rdev = hstat.st_rdev;
 483 #endif
 484                 st.st_uid = hstat.st_uid;
 485                 st.st_gid = hstat.st_gid;
 486                 st.st_size = hstat.st_size;
 487                 st.st_mtime = hstat.st_mtime;
 488                 st.st_atime = hstat.st_atime;
 489                 st.st_ctime = hstat.st_ctime;
 490 
 491                 if (current_link_name == NULL && S_ISLNK (hstat.st_mode))
 492                     st.st_mode &= ~S_IFLNK;  // You *DON'T* want to do this always
 493 
 494                 inode = vfs_s_new_inode (super->me, super, &st);
 495                 inode->ent = entry;
 496                 entry->ino = inode;
 497 
 498                 if (current_link_name != NULL && S_ISLNK (hstat.st_mode))
 499                 {
 500                     inode->linkname = current_link_name;
 501                     current_link_name = NULL;
 502                 }
 503             }
 504         }
 505 
 506     done:
 507         g_free (current_file_name);
 508         g_free (current_link_name);
 509     }
 510 
 511     return ret;
 512 }
 513 
 514 /* --------------------------------------------------------------------------------------------- */
 515 
 516 static mc_pipe_t *
 517 extfs_open_archive (int fstype, const char *name, struct extfs_super_t **pparc, GError **error)
     /* [previous][next][first][last][top][bottom][index][help]  */
 518 {
 519     const extfs_plugin_info_t *info;
 520     static dev_t archive_counter = 0;
 521     mc_pipe_t *result = NULL;
 522     mode_t mode;
 523     GString *cmd;
 524     struct stat mystat;
 525     struct extfs_super_t *current_archive;
 526     struct vfs_s_entry *root_entry;
 527     GString *quoted_name = NULL;
 528     vfs_path_t *local_name_vpath = NULL;
 529     vfs_path_t *name_vpath;
 530 
 531     memset (&mystat, 0, sizeof (mystat));
 532 
 533     name_vpath = vfs_path_from_str (name);
 534     info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
 535 
 536     if (info->need_archive)
 537     {
 538         if (mc_stat (name_vpath, &mystat) == -1)
 539         {
 540             mc_propagate_error (error, 0, "%s", unix_error_string (errno));
 541             goto ret;
 542         }
 543 
 544         if (!vfs_file_is_local (name_vpath))
 545         {
 546             local_name_vpath = mc_getlocalcopy (name_vpath);
 547             if (local_name_vpath == NULL)
 548                 goto ret;
 549         }
 550 
 551         quoted_name = name_quote (vfs_path_get_last_path_str (name_vpath), FALSE);
 552     }
 553 
 554     const char *local_last_path = vfs_path_get_last_path_str (local_name_vpath);
 555 
 556     cmd = g_string_new (info->path);
 557     g_string_append (cmd, info->prefix);
 558     g_string_append (cmd, " list ");
 559 
 560     if (local_last_path != NULL)
 561         g_string_append (cmd, local_last_path);
 562     else if (quoted_name != NULL)
 563     {
 564         mc_g_string_concat (cmd, quoted_name);
 565         g_string_free (quoted_name, TRUE);
 566     }
 567 
 568     result = mc_popen (cmd->str, TRUE, TRUE, error);
 569     g_string_free (cmd, TRUE);
 570 
 571     if (result == NULL)
 572     {
 573         if (local_name_vpath != NULL)
 574         {
 575             mc_ungetlocalcopy (name_vpath, local_name_vpath, FALSE);
 576             vfs_path_free (local_name_vpath, TRUE);
 577         }
 578         goto ret;
 579     }
 580 
 581     current_archive = extfs_super_new (vfs_extfs_ops, name, local_name_vpath, fstype);
 582     current_archive->rdev = archive_counter++;
 583     vfs_path_free (local_name_vpath, TRUE);
 584 
 585     mode = mystat.st_mode & 07777;
 586     if (mode & 0400)
 587         mode |= 0100;
 588     if (mode & 0040)
 589         mode |= 0010;
 590     if (mode & 0004)
 591         mode |= 0001;
 592     mode |= S_IFDIR;
 593 
 594     root_entry = extfs_generate_entry (current_archive, PATH_SEP_STR, NULL, mode);
 595     root_entry->ino->st.st_uid = mystat.st_uid;
 596     root_entry->ino->st.st_gid = mystat.st_gid;
 597     root_entry->ino->st.st_atime = mystat.st_atime;
 598     root_entry->ino->st.st_ctime = mystat.st_ctime;
 599     root_entry->ino->st.st_mtime = mystat.st_mtime;
 600     root_entry->ino->ent = root_entry;
 601     VFS_SUPER (current_archive)->root = root_entry->ino;
 602 
 603     *pparc = current_archive;
 604 
 605 ret:
 606     vfs_path_free (name_vpath, TRUE);
 607     return result;
 608 }
 609 
 610 /* --------------------------------------------------------------------------------------------- */
 611 /**
 612  * Main loop for reading an archive.
 613  * Return 0 on success, -1 on error.
 614  */
 615 
 616 static int
 617 extfs_read_archive (mc_pipe_t *pip, struct extfs_super_t *archive, GError **error)
     /* [previous][next][first][last][top][bottom][index][help]  */
 618 {
 619     int ret = 0;
 620     GString *buffer;
 621     GString *err_msg = NULL;
 622     GString *remain_file_name = NULL;
 623 
 624     while (ret != -1)
 625     {
 626         // init buffers before call of mc_pread()
 627         pip->out.len = MC_PIPE_BUFSIZE;
 628         pip->err.len = MC_PIPE_BUFSIZE;
 629 
 630         mc_pread (pip, error);
 631 
 632         if (*error != NULL)
 633             return (-1);
 634 
 635         if (pip->err.len > 0)
 636         {
 637             // join errors/warnings
 638             if (err_msg == NULL)
 639                 err_msg = g_string_new_len (pip->err.buf, pip->err.len);
 640             else
 641             {
 642                 if (err_msg->str[err_msg->len - 1] != '\n')
 643                     g_string_append_c (err_msg, '\n');
 644                 g_string_append_len (err_msg, pip->err.buf, pip->err.len);
 645             }
 646         }
 647 
 648         if (pip->out.len == MC_PIPE_STREAM_EOF)
 649             break;
 650 
 651         if (pip->out.len == 0)
 652             continue;
 653 
 654         if (pip->out.len == MC_PIPE_ERROR_READ)
 655             return (-1);
 656 
 657         while (ret != -1 && (buffer = mc_pstream_get_string (&pip->out)) != NULL)
 658         {
 659             // handle a \n-separated file list
 660 
 661             if (buffer->str[buffer->len - 1] == '\n')
 662             {
 663                 // entire file name or last chunk
 664 
 665                 g_string_truncate (buffer, buffer->len - 1);
 666 
 667                 // join filename chunks
 668                 if (remain_file_name != NULL)
 669                 {
 670                     g_string_append_len (remain_file_name, buffer->str, buffer->len);
 671                     g_string_free (buffer, TRUE);
 672                     buffer = remain_file_name;
 673                     remain_file_name = NULL;
 674                 }
 675             }
 676             else
 677             {
 678                 // first or middle chunk of file name
 679 
 680                 if (remain_file_name == NULL)
 681                     remain_file_name = buffer;
 682                 else
 683                 {
 684                     g_string_append_len (remain_file_name, buffer->str, buffer->len);
 685                     g_string_free (buffer, TRUE);
 686                 }
 687 
 688                 continue;
 689             }
 690 
 691             ret = extfs_add_file (archive, buffer->str);
 692 
 693             g_string_free (buffer, TRUE);
 694         }
 695     }
 696 
 697     if (remain_file_name != NULL)
 698         g_string_free (remain_file_name, TRUE);
 699 
 700     if (err_msg != NULL)
 701     {
 702         if (*error == NULL)
 703             mc_propagate_error (error, 0, "%s", err_msg->str);
 704 
 705         g_string_free (err_msg, TRUE);
 706     }
 707     else if (ret == -1)
 708         mc_propagate_error (error, 0, "%s", _ ("Inconsistent archive"));
 709 
 710     return ret;
 711 }
 712 
 713 /* --------------------------------------------------------------------------------------------- */
 714 
 715 static int
 716 extfs_which (struct vfs_class *me, const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 717 {
 718     size_t path_len;
 719     size_t i;
 720 
 721     (void) me;
 722 
 723     path_len = strlen (path);
 724 
 725     for (i = 0; i < extfs_plugins->len; i++)
 726     {
 727         extfs_plugin_info_t *info;
 728 
 729         info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
 730 
 731         if ((strncmp (path, info->prefix, path_len) == 0)
 732             && ((info->prefix[path_len] == '\0') || (info->prefix[path_len] == '+')))
 733             return i;
 734     }
 735     return -1;
 736 }
 737 
 738 /* --------------------------------------------------------------------------------------------- */
 739 
 740 static int
 741 extfs_open_and_read_archive (int fstype, const char *name, struct extfs_super_t **archive)
     /* [previous][next][first][last][top][bottom][index][help]  */
 742 {
 743     int result = -1;
 744     struct extfs_super_t *a;
 745     mc_pipe_t *pip;
 746     GError *error = NULL;
 747 
 748     pip = extfs_open_archive (fstype, name, archive, &error);
 749 
 750     a = *archive;
 751 
 752     if (pip == NULL)
 753     {
 754         const extfs_plugin_info_t *info;
 755 
 756         info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
 757         if (error == NULL)
 758             message (D_ERROR, MSG_ERROR, _ ("Cannot open %s archive\n%s"), info->prefix, name);
 759         else
 760         {
 761             message (D_ERROR, MSG_ERROR, _ ("Cannot open %s archive\n%s:\n%s"), info->prefix, name,
 762                      error->message);
 763             g_error_free (error);
 764         }
 765     }
 766     else
 767     {
 768         result = extfs_read_archive (pip, a, &error);
 769 
 770         if (result != 0)
 771             VFS_SUPER (a)->me->free (VFS_SUPER (a));
 772 
 773         if (error != NULL)
 774         {
 775             message (D_ERROR, MSG_ERROR, _ ("EXTFS virtual file system:\n%s"), error->message);
 776             g_error_free (error);
 777         }
 778 
 779         mc_pclose (pip, NULL);
 780     }
 781 
 782     return result;
 783 }
 784 
 785 /* --------------------------------------------------------------------------------------------- */
 786 /**
 787  * Dissect the path and create corresponding superblock.
 788  */
 789 static const char *
 790 extfs_get_path (const vfs_path_t *vpath, struct extfs_super_t **archive, int flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 791 {
 792     char *archive_name;
 793     int result = -1;
 794     GList *parc;
 795     int fstype;
 796     const vfs_path_element_t *path_element;
 797     struct extfs_super_t *a = NULL;
 798 
 799     path_element = vfs_path_get_by_index (vpath, -1);
 800 
 801     fstype = extfs_which (path_element->class, path_element->vfs_prefix);
 802     if (fstype == -1)
 803         return NULL;
 804 
 805     archive_name = vfs_path_to_str_elements_count (vpath, -1);
 806 
 807     // All filesystems should have some local archive, at least it can be PATH_SEP ('/')
 808     parc = g_list_find_custom (extfs_subclass.supers, archive_name, extfs_cmp_archive);
 809     if (parc != NULL)
 810     {
 811         a = EXTFS_SUPER (parc->data);
 812         vfs_stamp (vfs_extfs_ops, (vfsid) a);
 813         g_free (archive_name);
 814     }
 815     else
 816     {
 817         if ((flags & FL_NO_OPEN) == 0)
 818             result = extfs_open_and_read_archive (fstype, archive_name, &a);
 819 
 820         g_free (archive_name);
 821 
 822         if (result == -1)
 823         {
 824             path_element->class->verrno = EIO;
 825             return NULL;
 826         }
 827     }
 828 
 829     *archive = a;
 830     return path_element->path;
 831 }
 832 
 833 /* --------------------------------------------------------------------------------------------- */
 834 /* Return allocated path (without leading slash) inside the archive  */
 835 
 836 static char *
 837 extfs_get_path_from_entry (const struct vfs_s_entry *entry)
     /* [previous][next][first][last][top][bottom][index][help]  */
 838 {
 839     const struct vfs_s_entry *e;
 840     GString *localpath;
 841 
 842     localpath = g_string_new ("");
 843 
 844     for (e = entry; e->dir != NULL; e = e->dir->ent)
 845     {
 846         g_string_prepend (localpath, e->name);
 847         if (e->dir->ent->dir != NULL)
 848             g_string_prepend_c (localpath, PATH_SEP);
 849     }
 850 
 851     return g_string_free (localpath, FALSE);
 852 }
 853 
 854 /* --------------------------------------------------------------------------------------------- */
 855 
 856 static struct vfs_s_entry *
 857 extfs_resolve_symlinks_int (struct vfs_s_entry *entry, GSList *list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 858 {
 859     struct vfs_s_entry *pent = NULL;
 860 
 861     if (!S_ISLNK (entry->ino->st.st_mode))
 862         return entry;
 863 
 864     if (g_slist_find (list, entry) != NULL)
 865     {
 866         // Here we protect us against symlink looping
 867         errloop = TRUE;
 868     }
 869     else
 870     {
 871         GSList *looping;
 872 
 873         looping = g_slist_prepend (list, entry);
 874         pent = extfs_find_entry_int (entry->dir, entry->ino->linkname, looping, FL_NONE);
 875         looping = g_slist_delete_link (looping, looping);
 876 
 877         if (pent == NULL)
 878             my_errno = ENOENT;
 879     }
 880 
 881     return pent;
 882 }
 883 
 884 /* --------------------------------------------------------------------------------------------- */
 885 
 886 static struct vfs_s_entry *
 887 extfs_resolve_symlinks (struct vfs_s_entry *entry)
     /* [previous][next][first][last][top][bottom][index][help]  */
 888 {
 889     struct vfs_s_entry *res;
 890 
 891     errloop = FALSE;
 892     notadir = FALSE;
 893     res = extfs_resolve_symlinks_int (entry, NULL);
 894     if (res == NULL)
 895     {
 896         if (errloop)
 897             my_errno = ELOOP;
 898         else if (notadir)
 899             my_errno = ENOTDIR;
 900     }
 901     return res;
 902 }
 903 
 904 /* --------------------------------------------------------------------------------------------- */
 905 
 906 static char *
 907 extfs_get_archive_name (const struct extfs_super_t *archive)
     /* [previous][next][first][last][top][bottom][index][help]  */
 908 {
 909     const char *archive_name;
 910 
 911     if (archive->local_name != NULL)
 912         archive_name = archive->local_name;
 913     else
 914         archive_name = CONST_VFS_SUPER (archive)->name;
 915 
 916     if (archive_name == NULL || *archive_name == '\0')
 917         return g_strdup ("no_archive_name");
 918     else
 919     {
 920         char *ret_str;
 921         vfs_path_t *vpath;
 922         const char *path;
 923 
 924         vpath = vfs_path_from_str (archive_name);
 925         path = vfs_path_get_last_path_str (vpath);
 926         ret_str = g_strdup (path);
 927         vfs_path_free (vpath, TRUE);
 928         return ret_str;
 929     }
 930 }
 931 
 932 /* --------------------------------------------------------------------------------------------- */
 933 /** Don't pass localname as NULL */
 934 
 935 static int
 936 extfs_cmd (const char *str_extfs_cmd, const struct extfs_super_t *archive,
     /* [previous][next][first][last][top][bottom][index][help]  */
 937            const struct vfs_s_entry *entry, const char *localname)
 938 {
 939     char *file;
 940     GString *quoted_file;
 941     char *archive_name;
 942     GString *quoted_archive_name;
 943     const extfs_plugin_info_t *info;
 944     GString *cmd;
 945     int retval = 0;
 946     GError *error = NULL;
 947     mc_pipe_t *pip;
 948 
 949     file = extfs_get_path_from_entry (entry);
 950     quoted_file = name_quote (file, FALSE);
 951     g_free (file);
 952 
 953     if (quoted_file == NULL)
 954     {
 955         message (D_ERROR, MSG_ERROR, _ ("EXTFS virtual file system:\nwrong file name"));
 956         return (-1);
 957     }
 958 
 959     archive_name = extfs_get_archive_name (archive);
 960     quoted_archive_name = name_quote (archive_name, FALSE);
 961     g_free (archive_name);
 962 
 963     if (quoted_archive_name == NULL)
 964     {
 965         message (D_ERROR, MSG_ERROR, _ ("EXTFS virtual file system:\nwrong archive name"));
 966         g_string_free (quoted_file, TRUE);
 967         return (-1);
 968     }
 969 
 970     info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
 971 
 972     // Skip leading "./" (if present) added in name_quote()
 973     file = extfs_skip_leading_dotslash (quoted_file->str);
 974 
 975     cmd = g_string_new (info->path);
 976     g_string_append (cmd, info->prefix);
 977     g_string_append (cmd, str_extfs_cmd);
 978     mc_g_string_concat (cmd, quoted_archive_name);
 979     g_string_append_c (cmd, ' ');
 980     g_string_append (cmd, file);
 981 
 982     g_string_free (quoted_file, TRUE);
 983     g_string_free (quoted_archive_name, TRUE);
 984 
 985     if (localname != NULL && *localname != '\0')
 986     {
 987         GString *quoted_localname;
 988 
 989         quoted_localname = name_quote (localname, FALSE);
 990         g_string_append_c (cmd, ' ');
 991         mc_g_string_concat (cmd, quoted_localname);
 992         g_string_free (quoted_localname, TRUE);
 993     }
 994 
 995     // don't read stdout
 996     pip = mc_popen (cmd->str, FALSE, TRUE, &error);
 997     g_string_free (cmd, TRUE);
 998 
 999     if (pip == NULL)
1000     {
1001         message (D_ERROR, MSG_ERROR, _ ("EXTFS virtual file system:\n%s"), error->message);
1002         g_error_free (error);
1003         return (-1);
1004     }
1005 
1006     pip->err.null_term = TRUE;
1007 
1008     mc_pread (pip, &error);
1009     if (error != NULL)
1010     {
1011         message (D_ERROR, MSG_ERROR, _ ("EXTFS virtual file system:\n%s"), error->message);
1012         g_error_free (error);
1013         retval = -1;
1014     }
1015     else if (pip->err.len > 0)
1016         message (D_ERROR, MSG_ERROR, _ ("EXTFS virtual file system:\n%s"), pip->err.buf);
1017 
1018     mc_pclose (pip, NULL);
1019 
1020     return retval;
1021 }
1022 
1023 /* --------------------------------------------------------------------------------------------- */
1024 
1025 static void
1026 extfs_run (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1027 {
1028     struct extfs_super_t *archive = NULL;
1029     const char *p;
1030     GString *quoted_name;
1031     char *archive_name;
1032     GString *quoted_archive_name;
1033     GString *cmd;
1034     const extfs_plugin_info_t *info;
1035 
1036     p = extfs_get_path (vpath, &archive, FL_NONE);
1037     if (p == NULL)
1038         return;
1039 
1040     quoted_name = name_quote (p, FALSE);
1041     archive_name = extfs_get_archive_name (archive);
1042     quoted_archive_name = name_quote (archive_name, FALSE);
1043     g_free (archive_name);
1044 
1045     info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
1046 
1047     cmd = g_string_new (info->path);
1048     g_string_append (cmd, info->prefix);
1049     g_string_append (cmd, " run ");
1050     mc_g_string_concat (cmd, quoted_archive_name);
1051     g_string_append_c (cmd, ' ');
1052     mc_g_string_concat (cmd, quoted_name);
1053 
1054     g_string_free (quoted_archive_name, TRUE);
1055     g_string_free (quoted_name, TRUE);
1056 
1057     shell_execute (cmd->str, 0);
1058 
1059     g_string_free (cmd, TRUE);
1060 }
1061 
1062 /* --------------------------------------------------------------------------------------------- */
1063 
1064 static void *
1065 extfs_open (const vfs_path_t *vpath, int flags, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1066 {
1067     vfs_file_handler_t *extfs_info;
1068     struct extfs_super_t *archive = NULL;
1069     const char *q;
1070     struct vfs_s_entry *entry;
1071     int local_handle;
1072     gboolean created = FALSE;
1073 
1074     q = extfs_get_path (vpath, &archive, FL_NONE);
1075     if (q == NULL)
1076         return NULL;
1077     entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
1078     if ((entry == NULL) && ((flags & O_CREAT) != 0))
1079     {
1080         // Create new entry
1081         entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_MKFILE);
1082         created = (entry != NULL);
1083     }
1084 
1085     if (entry == NULL)
1086         return NULL;
1087     entry = extfs_resolve_symlinks (entry);
1088     if (entry == NULL)
1089         return NULL;
1090 
1091     if (S_ISDIR (entry->ino->st.st_mode))
1092         ERRNOR (EISDIR, NULL);
1093 
1094     if (entry->ino->localname == NULL)
1095     {
1096         vfs_path_t *local_filename_vpath;
1097         const char *local_filename;
1098 
1099         local_handle = vfs_mkstemps (&local_filename_vpath, "extfs", entry->name);
1100 
1101         if (local_handle == -1)
1102             return NULL;
1103         close (local_handle);
1104         local_filename = vfs_path_get_last_path_str (local_filename_vpath);
1105 
1106         if (!created && ((flags & O_TRUNC) == 0)
1107             && extfs_cmd (" copyout ", archive, entry, local_filename))
1108         {
1109             unlink (local_filename);
1110             vfs_path_free (local_filename_vpath, TRUE);
1111             my_errno = EIO;
1112             return NULL;
1113         }
1114         entry->ino->localname = g_strdup (local_filename);
1115         vfs_path_free (local_filename_vpath, TRUE);
1116     }
1117 
1118     local_handle = open (entry->ino->localname, NO_LINEAR (flags), mode);
1119 
1120     if (local_handle == -1)
1121     {
1122         // file exists(may be). Need to drop O_CREAT flag and truncate file content
1123         flags = ~O_CREAT & (NO_LINEAR (flags) | O_TRUNC);
1124         local_handle = open (entry->ino->localname, flags, mode);
1125     }
1126 
1127     if (local_handle == -1)
1128         ERRNOR (EIO, NULL);
1129 
1130     extfs_info = g_new (vfs_file_handler_t, 1);
1131     vfs_s_init_fh (extfs_info, entry->ino, created);
1132     extfs_info->handle = local_handle;
1133 
1134     // i.e. we had no open files and now we have one
1135     vfs_rmstamp (vfs_extfs_ops, (vfsid) archive);
1136     VFS_SUPER (archive)->fd_usage++;
1137     return extfs_info;
1138 }
1139 
1140 /* --------------------------------------------------------------------------------------------- */
1141 
1142 static ssize_t
1143 extfs_read (void *fh, char *buffer, size_t count)
     /* [previous][next][first][last][top][bottom][index][help]  */
1144 {
1145     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
1146 
1147     return read (file->handle, buffer, count);
1148 }
1149 
1150 /* --------------------------------------------------------------------------------------------- */
1151 
1152 static int
1153 extfs_close (void *fh)
     /* [previous][next][first][last][top][bottom][index][help]  */
1154 {
1155     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
1156     int errno_code = 0;
1157 
1158     close (file->handle);
1159     file->handle = -1;
1160 
1161     // Commit the file if it has changed
1162     if (file->changed)
1163     {
1164         struct stat file_status;
1165 
1166         if (extfs_cmd (" copyin ", EXTFS_SUPER (VFS_FILE_HANDLER_SUPER (fh)), file->ino->ent,
1167                        file->ino->localname))
1168             errno_code = EIO;
1169 
1170         if (stat (file->ino->localname, &file_status) != 0)
1171             errno_code = EIO;
1172         else
1173             file->ino->st.st_size = file_status.st_size;
1174 
1175         file->ino->st.st_mtime = time (NULL);
1176     }
1177 
1178     if (--VFS_FILE_HANDLER_SUPER (fh)->fd_usage == 0)
1179         vfs_stamp_create (vfs_extfs_ops, VFS_FILE_HANDLER_SUPER (fh));
1180 
1181     g_free (fh);
1182     if (errno_code != 0)
1183         ERRNOR (EIO, -1);
1184     return 0;
1185 }
1186 
1187 /* --------------------------------------------------------------------------------------------- */
1188 
1189 static int
1190 extfs_errno (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
1191 {
1192     (void) me;
1193     return my_errno;
1194 }
1195 
1196 /* --------------------------------------------------------------------------------------------- */
1197 
1198 static void *
1199 extfs_opendir (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1200 {
1201     struct extfs_super_t *archive = NULL;
1202     const char *q;
1203     struct vfs_s_entry *entry;
1204     GList **info;
1205 
1206     q = extfs_get_path (vpath, &archive, FL_NONE);
1207     if (q == NULL)
1208         return NULL;
1209     entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
1210     if (entry == NULL)
1211         return NULL;
1212     entry = extfs_resolve_symlinks (entry);
1213     if (entry == NULL)
1214         return NULL;
1215     if (!S_ISDIR (entry->ino->st.st_mode))
1216         ERRNOR (ENOTDIR, NULL);
1217 
1218     info = g_new (GList *, 1);
1219     *info = g_queue_peek_head_link (entry->ino->subdir);
1220 
1221     return info;
1222 }
1223 
1224 /* --------------------------------------------------------------------------------------------- */
1225 
1226 static struct vfs_dirent *
1227 extfs_readdir (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
1228 {
1229     struct vfs_dirent *dir;
1230     GList **info = (GList **) data;
1231 
1232     if (*info == NULL)
1233         return NULL;
1234 
1235     dir = vfs_dirent_init (NULL, VFS_ENTRY ((*info)->data)->name, 0, DT_UNKNOWN);  // FIXME: inode
1236 
1237     *info = g_list_next (*info);
1238 
1239     return dir;
1240 }
1241 
1242 /* --------------------------------------------------------------------------------------------- */
1243 
1244 static int
1245 extfs_closedir (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
1246 {
1247     g_free (data);
1248     return 0;
1249 }
1250 
1251 /* --------------------------------------------------------------------------------------------- */
1252 
1253 static void
1254 extfs_stat_move (struct stat *buf, const struct vfs_s_inode *inode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1255 {
1256     const time_t atime = inode->st.st_atime;
1257     const time_t mtime = inode->st.st_mtime;
1258     const time_t ctime = inode->st.st_ctime;
1259 
1260     *buf = inode->st;
1261 
1262 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1263     buf->st_blksize = RECORDSIZE;
1264 #endif
1265 
1266     vfs_adjust_stat (buf);
1267     vfs_zero_stat_times (buf);
1268 
1269     buf->st_atime = atime;
1270     buf->st_mtime = mtime;
1271     buf->st_ctime = ctime;
1272 }
1273 
1274 /* --------------------------------------------------------------------------------------------- */
1275 
1276 static int
1277 extfs_internal_stat (const vfs_path_t *vpath, struct stat *buf, gboolean resolve)
     /* [previous][next][first][last][top][bottom][index][help]  */
1278 {
1279     struct extfs_super_t *archive;
1280     const char *q;
1281     struct vfs_s_entry *entry;
1282     int result = -1;
1283 
1284     q = extfs_get_path (vpath, &archive, FL_NONE);
1285     if (q == NULL)
1286         goto cleanup;
1287     entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
1288     if (entry == NULL)
1289         goto cleanup;
1290     if (resolve)
1291     {
1292         entry = extfs_resolve_symlinks (entry);
1293         if (entry == NULL)
1294             goto cleanup;
1295     }
1296     extfs_stat_move (buf, entry->ino);
1297     result = 0;
1298 cleanup:
1299     return result;
1300 }
1301 
1302 /* --------------------------------------------------------------------------------------------- */
1303 
1304 static int
1305 extfs_stat (const vfs_path_t *vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
1306 {
1307     return extfs_internal_stat (vpath, buf, TRUE);
1308 }
1309 
1310 /* --------------------------------------------------------------------------------------------- */
1311 
1312 static int
1313 extfs_lstat (const vfs_path_t *vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
1314 {
1315     return extfs_internal_stat (vpath, buf, FALSE);
1316 }
1317 
1318 /* --------------------------------------------------------------------------------------------- */
1319 
1320 static int
1321 extfs_fstat (void *fh, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
1322 {
1323     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
1324 
1325     extfs_stat_move (buf, file->ino);
1326     return 0;
1327 }
1328 
1329 /* --------------------------------------------------------------------------------------------- */
1330 
1331 static int
1332 extfs_readlink (const vfs_path_t *vpath, char *buf, size_t size)
     /* [previous][next][first][last][top][bottom][index][help]  */
1333 {
1334     struct extfs_super_t *archive;
1335     const char *q;
1336     size_t len;
1337     struct vfs_s_entry *entry;
1338     int result = -1;
1339 
1340     q = extfs_get_path (vpath, &archive, FL_NONE);
1341     if (q == NULL)
1342         goto cleanup;
1343     entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
1344     if (entry == NULL)
1345         goto cleanup;
1346     if (!S_ISLNK (entry->ino->st.st_mode))
1347     {
1348         VFS_CLASS (vfs_path_get_last_path_vfs (vpath))->verrno = EINVAL;
1349         goto cleanup;
1350     }
1351     len = strlen (entry->ino->linkname);
1352     if (size < len)
1353         len = size;
1354     // readlink() does not append a NUL character to buf
1355     result = len;
1356     memcpy (buf, entry->ino->linkname, result);
1357 cleanup:
1358     return result;
1359 }
1360 
1361 /* --------------------------------------------------------------------------------------------- */
1362 
1363 static ssize_t
1364 extfs_write (void *fh, const char *buf, size_t nbyte)
     /* [previous][next][first][last][top][bottom][index][help]  */
1365 {
1366     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
1367 
1368     file->changed = TRUE;
1369     return write (file->handle, buf, nbyte);
1370 }
1371 
1372 /* --------------------------------------------------------------------------------------------- */
1373 
1374 static int
1375 extfs_unlink (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1376 {
1377     struct extfs_super_t *archive;
1378     const char *q;
1379     struct vfs_s_entry *entry;
1380     int result = -1;
1381 
1382     q = extfs_get_path (vpath, &archive, FL_NONE);
1383     if (q == NULL)
1384         goto cleanup;
1385     entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
1386     if (entry == NULL)
1387         goto cleanup;
1388     entry = extfs_resolve_symlinks (entry);
1389     if (entry == NULL)
1390         goto cleanup;
1391     if (S_ISDIR (entry->ino->st.st_mode))
1392     {
1393         VFS_CLASS (vfs_path_get_last_path_vfs (vpath))->verrno = EISDIR;
1394         goto cleanup;
1395     }
1396     if (extfs_cmd (" rm ", archive, entry, ""))
1397     {
1398         my_errno = EIO;
1399         goto cleanup;
1400     }
1401     vfs_s_free_entry (VFS_SUPER (archive)->me, entry);
1402     result = 0;
1403 cleanup:
1404     return result;
1405 }
1406 
1407 /* --------------------------------------------------------------------------------------------- */
1408 
1409 static int
1410 extfs_mkdir (const vfs_path_t *vpath, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1411 {
1412     struct extfs_super_t *archive;
1413     const char *q;
1414     struct vfs_s_entry *entry;
1415     int result = -1;
1416     struct vfs_class *me;
1417 
1418     (void) mode;
1419 
1420     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1421     q = extfs_get_path (vpath, &archive, FL_NONE);
1422     if (q == NULL)
1423         goto cleanup;
1424     entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
1425     if (entry != NULL)
1426     {
1427         me->verrno = EEXIST;
1428         goto cleanup;
1429     }
1430     entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_MKDIR);
1431     if (entry == NULL)
1432         goto cleanup;
1433     entry = extfs_resolve_symlinks (entry);
1434     if (entry == NULL)
1435         goto cleanup;
1436     if (!S_ISDIR (entry->ino->st.st_mode))
1437     {
1438         me->verrno = ENOTDIR;
1439         goto cleanup;
1440     }
1441 
1442     if (extfs_cmd (" mkdir ", archive, entry, ""))
1443     {
1444         my_errno = EIO;
1445         vfs_s_free_entry (VFS_SUPER (archive)->me, entry);
1446         goto cleanup;
1447     }
1448     result = 0;
1449 cleanup:
1450     return result;
1451 }
1452 
1453 /* --------------------------------------------------------------------------------------------- */
1454 
1455 static int
1456 extfs_rmdir (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1457 {
1458     struct extfs_super_t *archive;
1459     const char *q;
1460     struct vfs_s_entry *entry;
1461     int result = -1;
1462 
1463     q = extfs_get_path (vpath, &archive, FL_NONE);
1464     if (q == NULL)
1465         goto cleanup;
1466     entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
1467     if (entry == NULL)
1468         goto cleanup;
1469     entry = extfs_resolve_symlinks (entry);
1470     if (entry == NULL)
1471         goto cleanup;
1472     if (!S_ISDIR (entry->ino->st.st_mode))
1473     {
1474         VFS_CLASS (vfs_path_get_last_path_vfs (vpath))->verrno = ENOTDIR;
1475         goto cleanup;
1476     }
1477 
1478     if (extfs_cmd (" rmdir ", archive, entry, ""))
1479     {
1480         my_errno = EIO;
1481         goto cleanup;
1482     }
1483     vfs_s_free_entry (VFS_SUPER (archive)->me, entry);
1484     result = 0;
1485 cleanup:
1486     return result;
1487 }
1488 
1489 /* --------------------------------------------------------------------------------------------- */
1490 
1491 static int
1492 extfs_chdir (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1493 {
1494     void *data;
1495 
1496     my_errno = ENOTDIR;
1497     data = extfs_opendir (vpath);
1498     if (data == NULL)
1499         return (-1);
1500     extfs_closedir (data);
1501     my_errno = 0;
1502     return 0;
1503 }
1504 
1505 /* --------------------------------------------------------------------------------------------- */
1506 
1507 static off_t
1508 extfs_lseek (void *fh, off_t offset, int whence)
     /* [previous][next][first][last][top][bottom][index][help]  */
1509 {
1510     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
1511 
1512     return lseek (file->handle, offset, whence);
1513 }
1514 
1515 /* --------------------------------------------------------------------------------------------- */
1516 
1517 static vfsid
1518 extfs_getid (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1519 {
1520     struct extfs_super_t *archive = NULL;
1521     const char *p;
1522 
1523     p = extfs_get_path (vpath, &archive, FL_NO_OPEN);
1524     return (p == NULL ? NULL : (vfsid) archive);
1525 }
1526 
1527 /* --------------------------------------------------------------------------------------------- */
1528 
1529 static vfs_path_t *
1530 extfs_getlocalcopy (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1531 {
1532     vfs_file_handler_t *fh;
1533     vfs_path_t *p;
1534 
1535     fh = VFS_FILE_HANDLER (extfs_open (vpath, O_RDONLY, 0));
1536     if (fh == NULL)
1537         return NULL;
1538     if (fh->ino->localname == NULL)
1539     {
1540         extfs_close ((void *) fh);
1541         return NULL;
1542     }
1543     p = vfs_path_from_str (fh->ino->localname);
1544     VFS_FILE_HANDLER_SUPER (fh)->fd_usage++;
1545     extfs_close ((void *) fh);
1546     return p;
1547 }
1548 
1549 /* --------------------------------------------------------------------------------------------- */
1550 
1551 static int
1552 extfs_ungetlocalcopy (const vfs_path_t *vpath, const vfs_path_t *local, gboolean has_changed)
     /* [previous][next][first][last][top][bottom][index][help]  */
1553 {
1554     vfs_file_handler_t *fh;
1555 
1556     fh = VFS_FILE_HANDLER (extfs_open (vpath, O_RDONLY, 0));
1557     if (fh == NULL)
1558         return 0;
1559 
1560     if (strcmp (fh->ino->localname, vfs_path_get_last_path_str (local)) == 0)
1561     {
1562         VFS_FILE_HANDLER_SUPER (fh)->fd_usage--;
1563         if (has_changed)
1564             fh->changed = TRUE;
1565         extfs_close ((void *) fh);
1566         return 0;
1567     }
1568     else
1569     {
1570         // Should not happen
1571         extfs_close ((void *) fh);
1572         return 0;
1573     }
1574 }
1575 
1576 /* --------------------------------------------------------------------------------------------- */
1577 
1578 static gboolean
1579 extfs_get_plugins (const char *where, gboolean silent)
     /* [previous][next][first][last][top][bottom][index][help]  */
1580 {
1581     char *dirname;
1582     GDir *dir;
1583     const char *filename;
1584 
1585     dirname = g_build_path (PATH_SEP_STR, where, MC_EXTFS_DIR, (char *) NULL);
1586     dir = g_dir_open (dirname, 0, NULL);
1587 
1588     /* We may not use vfs_die() message or message or similar,
1589      * UI is not initialized at this time and message would not
1590      * appear on screen. */
1591     if (dir == NULL)
1592     {
1593         if (!silent)
1594             fprintf (stderr, _ ("Warning: cannot open %s directory\n"), dirname);
1595         g_free (dirname);
1596         return FALSE;
1597     }
1598 
1599     if (extfs_plugins == NULL)
1600         extfs_plugins = g_array_sized_new (FALSE, TRUE, sizeof (extfs_plugin_info_t), 32);
1601 
1602     while ((filename = g_dir_read_name (dir)) != NULL)
1603     {
1604         char fullname[MC_MAXPATHLEN];
1605         struct stat s;
1606 
1607         g_snprintf (fullname, sizeof (fullname), "%s" PATH_SEP_STR "%s", dirname, filename);
1608 
1609         if ((stat (fullname, &s) == 0) && S_ISREG (s.st_mode) && !S_ISDIR (s.st_mode)
1610             && is_exe (s.st_mode))
1611         {
1612             int f;
1613 
1614             f = open (fullname, O_RDONLY);
1615 
1616             if (f >= 0)
1617             {
1618                 size_t len, i;
1619                 extfs_plugin_info_t info;
1620                 gboolean found = FALSE;
1621 
1622                 close (f);
1623 
1624                 /* Handle those with a trailing '+', those flag that the
1625                  * file system does not require an archive to work
1626                  */
1627                 len = strlen (filename);
1628                 info.need_archive = (filename[len - 1] != '+');
1629                 info.path = g_strconcat (dirname, PATH_SEP_STR, (char *) NULL);
1630                 info.prefix = g_strndup (filename, len);
1631 
1632                 // prepare to compare file names without trailing '+'
1633                 if (!info.need_archive)
1634                     info.prefix[len - 1] = '\0';
1635 
1636                 // don't overload already found plugin
1637                 for (i = 0; i < extfs_plugins->len && !found; i++)
1638                 {
1639                     extfs_plugin_info_t *p;
1640 
1641                     p = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1642 
1643                     // 2 files with same names cannot be in a directory
1644                     found =
1645                         strcmp (info.path, p->path) != 0 && strcmp (info.prefix, p->prefix) == 0;
1646                 }
1647 
1648                 if (found)
1649                 {
1650                     g_free (info.path);
1651                     g_free (info.prefix);
1652                 }
1653                 else
1654                 {
1655                     // restore file name
1656                     if (!info.need_archive)
1657                         info.prefix[len - 1] = '+';
1658                     g_array_append_val (extfs_plugins, info);
1659                 }
1660             }
1661         }
1662     }
1663 
1664     g_dir_close (dir);
1665     g_free (dirname);
1666 
1667     return TRUE;
1668 }
1669 
1670 /* --------------------------------------------------------------------------------------------- */
1671 
1672 static int
1673 extfs_init (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
1674 {
1675     gboolean d1, d2;
1676 
1677     (void) me;
1678 
1679     // 1st: scan user directory
1680     d1 = extfs_get_plugins (mc_config_get_data_path (), TRUE);  // silent about user dir
1681     // 2nd: scan system dir
1682     d2 = extfs_get_plugins (LIBEXECDIR, d1);
1683 
1684     return (d1 || d2 ? 1 : 0);
1685 }
1686 
1687 /* --------------------------------------------------------------------------------------------- */
1688 
1689 static void
1690 extfs_done (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
1691 {
1692     size_t i;
1693 
1694     while (VFS_SUBCLASS (me)->supers != NULL)
1695         me->free ((vfsid) VFS_SUBCLASS (me)->supers->data);
1696 
1697     if (extfs_plugins == NULL)
1698         return;
1699 
1700     for (i = 0; i < extfs_plugins->len; i++)
1701     {
1702         extfs_plugin_info_t *info;
1703 
1704         info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1705         g_free (info->path);
1706         g_free (info->prefix);
1707     }
1708 
1709     g_array_free (extfs_plugins, TRUE);
1710 }
1711 
1712 /* --------------------------------------------------------------------------------------------- */
1713 
1714 static int
1715 extfs_setctl (const vfs_path_t *vpath, int ctlop, void *arg)
     /* [previous][next][first][last][top][bottom][index][help]  */
1716 {
1717     (void) arg;
1718 
1719     if (ctlop == VFS_SETCTL_RUN)
1720     {
1721         extfs_run (vpath);
1722         return 1;
1723     }
1724     return 0;
1725 }
1726 
1727 /* --------------------------------------------------------------------------------------------- */
1728 /*** public functions ****************************************************************************/
1729 /* --------------------------------------------------------------------------------------------- */
1730 
1731 void
1732 vfs_init_extfs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1733 {
1734     vfs_init_subclass (&extfs_subclass, "extfs", VFSF_UNKNOWN, NULL);
1735     vfs_extfs_ops->init = extfs_init;
1736     vfs_extfs_ops->done = extfs_done;
1737     vfs_extfs_ops->fill_names = extfs_fill_names;
1738     vfs_extfs_ops->which = extfs_which;
1739     vfs_extfs_ops->open = extfs_open;
1740     vfs_extfs_ops->close = extfs_close;
1741     vfs_extfs_ops->read = extfs_read;
1742     vfs_extfs_ops->write = extfs_write;
1743     vfs_extfs_ops->opendir = extfs_opendir;
1744     vfs_extfs_ops->readdir = extfs_readdir;
1745     vfs_extfs_ops->closedir = extfs_closedir;
1746     vfs_extfs_ops->stat = extfs_stat;
1747     vfs_extfs_ops->lstat = extfs_lstat;
1748     vfs_extfs_ops->fstat = extfs_fstat;
1749     vfs_extfs_ops->readlink = extfs_readlink;
1750     vfs_extfs_ops->unlink = extfs_unlink;
1751     vfs_extfs_ops->chdir = extfs_chdir;
1752     vfs_extfs_ops->ferrno = extfs_errno;
1753     vfs_extfs_ops->lseek = extfs_lseek;
1754     vfs_extfs_ops->getid = extfs_getid;
1755     vfs_extfs_ops->getlocalcopy = extfs_getlocalcopy;
1756     vfs_extfs_ops->ungetlocalcopy = extfs_ungetlocalcopy;
1757     vfs_extfs_ops->mkdir = extfs_mkdir;
1758     vfs_extfs_ops->rmdir = extfs_rmdir;
1759     vfs_extfs_ops->setctl = extfs_setctl;
1760     extfs_subclass.free_inode = extfs_free_inode;
1761     extfs_subclass.free_archive = extfs_free_archive;
1762     vfs_register_class (vfs_extfs_ops);
1763 }
1764 
1765 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */