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

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