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

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