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

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