root/src/vfs/extfs/extfs.c

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

DEFINITIONS

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

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