root/src/vfs/extfs/extfs.c

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

DEFINITIONS

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

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

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