root/src/vfs/sfs/sfs.c

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

DEFINITIONS

This source file includes following definitions.
  1. cachedfile_compare
  2. sfs_vfmake
  3. sfs_redirect
  4. sfs_open
  5. sfs_stat
  6. sfs_lstat
  7. sfs_chmod
  8. sfs_chown
  9. sfs_utime
  10. sfs_readlink
  11. sfs_getid
  12. sfs_free
  13. sfs_fill_names
  14. sfs_nothingisopen
  15. sfs_getlocalcopy
  16. sfs_ungetlocalcopy
  17. sfs_init
  18. sfs_done
  19. sfs_which
  20. vfs_init_sfs

   1 /*
   2    Single File fileSystem
   3 
   4    Copyright (C) 1998-2024
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Slava Zanko <slavazanko@gmail.com>, 2013
   9 
  10    This file is part of the Midnight Commander.
  11 
  12    The Midnight Commander is free software: you can redistribute it
  13    and/or modify it under the terms of the GNU General Public License as
  14    published by the Free Software Foundation, either version 3 of the License,
  15    or (at your option) any later version.
  16 
  17    The Midnight Commander is distributed in the hope that it will be useful,
  18    but WITHOUT ANY WARRANTY; without even the implied warranty of
  19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20    GNU General Public License for more details.
  21 
  22    You should have received a copy of the GNU General Public License
  23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  24  */
  25 
  26 /**
  27  * \file
  28  * \brief Source: Single File fileSystem
  29  *
  30  * This defines whole class of filesystems which contain single file
  31  * inside. It is somehow similar to extfs, except that extfs makes
  32  * whole virtual trees and we do only single virtual files.
  33  *
  34  * If you want to gunzip something, you should open it with \verbatim #ugz \endverbatim
  35  * suffix, DON'T try to gunzip it yourself.
  36  *
  37  * Namespace: exports vfs_sfs_ops
  38  */
  39 
  40 #include <config.h>
  41 #include <sys/types.h>
  42 #include <unistd.h>
  43 #include <stdio.h>
  44 #include <string.h>
  45 
  46 #include "lib/global.h"
  47 #include "lib/util.h"
  48 #include "lib/widget.h"         /* D_ERROR, D_NORMAL */
  49 
  50 #include "src/execute.h"        /* EXECUTE_AS_SHELL */
  51 
  52 #include "lib/vfs/vfs.h"
  53 #include "lib/vfs/utilvfs.h"
  54 #include "lib/vfs/xdirentry.h"
  55 #include "src/vfs/local/local.h"
  56 #include "lib/vfs/gc.h"         /* vfs_stamp_create */
  57 
  58 #include "sfs.h"
  59 
  60 /*** global variables ****************************************************************************/
  61 
  62 /*** file scope macro definitions ****************************************************************/
  63 
  64 #define MAXFS 32
  65 
  66 typedef enum
  67 {
  68     F_NONE = 0x0,
  69     F_1 = 0x1,
  70     F_2 = 0x2,
  71     F_NOLOCALCOPY = 0x4,
  72     F_FULLMATCH = 0x8
  73 } sfs_flags_t;
  74 
  75 #define COPY_CHAR \
  76     if ((size_t) (t - pad) > sizeof (pad)) \
  77     { \
  78         g_free (pqname); \
  79         return (-1); \
  80     } \
  81     else \
  82         *t++ = *s_iter;
  83 
  84 #define COPY_STRING(a) \
  85     if ((t - pad) + strlen (a) > sizeof (pad)) \
  86     { \
  87         g_free (pqname); \
  88         return (-1); \
  89     } \
  90     else \
  91     { \
  92         strcpy (t, a); \
  93         t += strlen (a); \
  94     }
  95 
  96 /*** file scope type declarations ****************************************************************/
  97 
  98 typedef struct cachedfile
  99 {
 100     char *name;
 101     char *cache;
 102 } cachedfile;
 103 
 104 /*** forward declarations (file scope functions) *************************************************/
 105 
 106 /*** file scope variables ************************************************************************/
 107 
 108 static GSList *head = NULL;
 109 
 110 static struct vfs_s_subclass sfs_subclass;
 111 static struct vfs_class *vfs_sfs_ops = VFS_CLASS (&sfs_subclass);
 112 
 113 static int sfs_no = 0;
 114 static struct
 115 {
 116     char *prefix;
 117     char *command;
 118     sfs_flags_t flags;
 119 } sfs_info[MAXFS];
 120 
 121 /* --------------------------------------------------------------------------------------------- */
 122 /*** file scope functions ************************************************************************/
 123 /* --------------------------------------------------------------------------------------------- */
 124 
 125 static int
 126 cachedfile_compare (const void *a, const void *b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 127 {
 128     const cachedfile *cf = (const cachedfile *) a;
 129     const char *name = (const char *) b;
 130 
 131     return strcmp (name, cf->name);
 132 }
 133 
 134 /* --------------------------------------------------------------------------------------------- */
 135 
 136 static int
 137 sfs_vfmake (const vfs_path_t * vpath, vfs_path_t * cache_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 138 {
 139     int w;
 140     char pad[10240];
 141     char *s_iter, *t = pad;
 142     gboolean was_percent = FALSE;
 143     vfs_path_t *pname;          /* name of parent archive */
 144     char *pqname;               /* name of parent archive, quoted */
 145     const vfs_path_element_t *path_element;
 146     mc_pipe_t *pip;
 147     GError *error = NULL;
 148 
 149     path_element = vfs_path_get_by_index (vpath, -1);
 150     pname = vfs_path_clone (vpath);
 151     vfs_path_remove_element_by_index (pname, -1);
 152 
 153     w = path_element->class->which (path_element->class, path_element->vfs_prefix);
 154     if (w == -1)
 155         vfs_die ("This cannot happen... Hopefully.\n");
 156 
 157     if ((sfs_info[w].flags & F_1) == 0
 158         && strcmp (vfs_path_get_last_path_str (pname), PATH_SEP_STR) != 0)
 159     {
 160         vfs_path_free (pname, TRUE);
 161         return (-1);
 162     }
 163 
 164     /*    if ((sfs_info[w].flags & F_2) || (!inpath) || (!*inpath)); else return -1; */
 165     if ((sfs_info[w].flags & F_NOLOCALCOPY) != 0)
 166         pqname = name_quote (vfs_path_as_str (pname), FALSE);
 167     else
 168     {
 169         vfs_path_t *s;
 170 
 171         s = mc_getlocalcopy (pname);
 172         if (s == NULL)
 173         {
 174             vfs_path_free (pname, TRUE);
 175             return (-1);
 176         }
 177 
 178         pqname = name_quote (vfs_path_get_last_path_str (s), FALSE);
 179         vfs_path_free (s, TRUE);
 180     }
 181 
 182     vfs_path_free (pname, TRUE);
 183 
 184     for (s_iter = sfs_info[w].command; *s_iter != '\0'; s_iter++)
 185     {
 186         if (was_percent)
 187         {
 188             const char *ptr = NULL;
 189 
 190             was_percent = FALSE;
 191 
 192             switch (*s_iter)
 193             {
 194             case '1':
 195                 ptr = pqname;
 196                 break;
 197             case '2':
 198                 ptr = path_element->path;
 199                 break;
 200             case '3':
 201                 ptr = vfs_path_get_last_path_str (cache_vpath);
 202                 break;
 203             case '%':
 204                 COPY_CHAR;
 205                 continue;
 206             default:
 207                 break;
 208             }
 209 
 210             if (ptr != NULL)
 211             {
 212                 COPY_STRING (ptr);
 213             }
 214         }
 215         else if (*s_iter == '%')
 216             was_percent = TRUE;
 217         else
 218         {
 219             COPY_CHAR;
 220         }
 221     }
 222 
 223     g_free (pqname);
 224 
 225     /* don't read stdout */
 226     pip = mc_popen (pad, FALSE, TRUE, &error);
 227     if (pip == NULL)
 228     {
 229         message (D_ERROR, MSG_ERROR, _("SFS virtual file system:\n%s"), error->message);
 230         g_error_free (error);
 231         return (-1);
 232     }
 233 
 234     pip->err.null_term = TRUE;
 235 
 236     mc_pread (pip, &error);
 237     if (error != NULL)
 238     {
 239         message (D_ERROR, MSG_ERROR, _("SFS virtual file system:\n%s"), error->message);
 240         g_error_free (error);
 241         mc_pclose (pip, NULL);
 242         return (-1);
 243     }
 244 
 245     if (pip->err.len > 0)
 246         message (D_ERROR, MSG_ERROR, _("SFS virtual file system:\n%s"), pip->err.buf);
 247 
 248     mc_pclose (pip, NULL);
 249     return 0;                   /* OK */
 250 }
 251 
 252 /* --------------------------------------------------------------------------------------------- */
 253 
 254 static const char *
 255 sfs_redirect (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 256 {
 257     GSList *cur;
 258     cachedfile *cf;
 259     vfs_path_t *cache_vpath;
 260     int handle;
 261 
 262     cur = g_slist_find_custom (head, vfs_path_as_str (vpath), cachedfile_compare);
 263 
 264     if (cur != NULL)
 265     {
 266         cf = (cachedfile *) cur->data;
 267         vfs_stamp (vfs_sfs_ops, cf);
 268         return cf->cache;
 269     }
 270 
 271     handle = vfs_mkstemps (&cache_vpath, "sfs", vfs_path_get_last_path_str (vpath));
 272 
 273     if (handle == -1)
 274         return "/SOMEONE_PLAYING_DIRTY_TMP_TRICKS_ON_US";
 275 
 276     close (handle);
 277 
 278     if (sfs_vfmake (vpath, cache_vpath) == 0)
 279     {
 280         cf = g_new (cachedfile, 1);
 281         cf->name = g_strdup (vfs_path_as_str (vpath));
 282         cf->cache = vfs_path_free (cache_vpath, FALSE);
 283         head = g_slist_prepend (head, cf);
 284 
 285         vfs_stamp_create (vfs_sfs_ops, (cachedfile *) head->data);
 286         return cf->cache;
 287     }
 288 
 289     mc_unlink (cache_vpath);
 290     vfs_path_free (cache_vpath, TRUE);
 291     return "/I_MUST_NOT_EXIST";
 292 }
 293 
 294 /* --------------------------------------------------------------------------------------------- */
 295 
 296 static void *
 297 sfs_open (const vfs_path_t * vpath /*struct vfs_class *me, const char *path */ , int flags,
     /* [previous][next][first][last][top][bottom][index][help]  */
 298           mode_t mode)
 299 {
 300     int *info;
 301     int fd;
 302 
 303     fd = open (sfs_redirect (vpath), NO_LINEAR (flags), mode);
 304     if (fd == -1)
 305         return NULL;
 306 
 307     info = g_new (int, 1);
 308     *info = fd;
 309 
 310     return info;
 311 }
 312 
 313 /* --------------------------------------------------------------------------------------------- */
 314 
 315 static int
 316 sfs_stat (const vfs_path_t * vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 317 {
 318     return stat (sfs_redirect (vpath), buf);
 319 }
 320 
 321 /* --------------------------------------------------------------------------------------------- */
 322 
 323 static int
 324 sfs_lstat (const vfs_path_t * vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
 325 {
 326 #ifndef HAVE_STATLSTAT
 327     return lstat (sfs_redirect (vpath), buf);
 328 #else
 329     return statlstat (sfs_redirect (vpath), buf);
 330 #endif
 331 }
 332 
 333 /* --------------------------------------------------------------------------------------------- */
 334 
 335 static int
 336 sfs_chmod (const vfs_path_t * vpath, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
 337 {
 338     return chmod (sfs_redirect (vpath), mode);
 339 }
 340 
 341 /* --------------------------------------------------------------------------------------------- */
 342 
 343 static int
 344 sfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
     /* [previous][next][first][last][top][bottom][index][help]  */
 345 {
 346     return chown (sfs_redirect (vpath), owner, group);
 347 }
 348 
 349 /* --------------------------------------------------------------------------------------------- */
 350 
 351 static int
 352 sfs_utime (const vfs_path_t * vpath, mc_timesbuf_t * times)
     /* [previous][next][first][last][top][bottom][index][help]  */
 353 {
 354 #ifdef HAVE_UTIMENSAT
 355     return utimensat (AT_FDCWD, sfs_redirect (vpath), *times, 0);
 356 #else
 357     return utime (sfs_redirect (vpath), times);
 358 #endif
 359 }
 360 
 361 /* --------------------------------------------------------------------------------------------- */
 362 
 363 static int
 364 sfs_readlink (const vfs_path_t * vpath, char *buf, size_t size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 365 {
 366     return readlink (sfs_redirect (vpath), buf, size);
 367 }
 368 
 369 /* --------------------------------------------------------------------------------------------- */
 370 
 371 static vfsid
 372 sfs_getid (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 373 {
 374     GSList *cur;
 375 
 376     cur = g_slist_find_custom (head, vfs_path_as_str (vpath), cachedfile_compare);
 377 
 378     return (vfsid) (cur != NULL ? cur->data : NULL);
 379 }
 380 
 381 /* --------------------------------------------------------------------------------------------- */
 382 
 383 static void
 384 sfs_free (vfsid id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 385 {
 386     struct cachedfile *which;
 387     GSList *cur;
 388 
 389     which = (struct cachedfile *) id;
 390     cur = g_slist_find (head, which);
 391     if (cur == NULL)
 392         vfs_die ("Free of thing which is unknown to me\n");
 393 
 394     which = (struct cachedfile *) cur->data;
 395     unlink (which->cache);
 396     g_free (which->cache);
 397     g_free (which->name);
 398     g_free (which);
 399 
 400     head = g_slist_delete_link (head, cur);
 401 }
 402 
 403 /* --------------------------------------------------------------------------------------------- */
 404 
 405 static void
 406 sfs_fill_names (struct vfs_class *me, fill_names_f func)
     /* [previous][next][first][last][top][bottom][index][help]  */
 407 {
 408     GSList *cur;
 409 
 410     (void) me;
 411 
 412     for (cur = head; cur != NULL; cur = g_slist_next (cur))
 413         func (((cachedfile *) cur->data)->name);
 414 }
 415 
 416 /* --------------------------------------------------------------------------------------------- */
 417 
 418 static gboolean
 419 sfs_nothingisopen (vfsid id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 420 {
 421     /* FIXME: Investigate whether have to guard this like in
 422        the other VFSs (see fd_usage in extfs) -- Norbert */
 423     (void) id;
 424     return TRUE;
 425 }
 426 
 427 /* --------------------------------------------------------------------------------------------- */
 428 
 429 static vfs_path_t *
 430 sfs_getlocalcopy (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 431 {
 432     return vfs_path_from_str (sfs_redirect (vpath));
 433 }
 434 
 435 /* --------------------------------------------------------------------------------------------- */
 436 
 437 static int
 438 sfs_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed)
     /* [previous][next][first][last][top][bottom][index][help]  */
 439 {
 440     (void) vpath;
 441     (void) local;
 442     (void) has_changed;
 443     return 0;
 444 }
 445 
 446 /* --------------------------------------------------------------------------------------------- */
 447 
 448 static int
 449 sfs_init (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 450 {
 451     char *mc_sfsini;
 452     FILE *cfg;
 453     char key[256];
 454 
 455     (void) me;
 456 
 457     mc_sfsini = g_build_filename (mc_global.sysconfig_dir, "sfs.ini", (char *) NULL);
 458     cfg = fopen (mc_sfsini, "r");
 459 
 460     if (cfg == NULL)
 461     {
 462         fprintf (stderr, _("%s: Warning: file %s not found\n"), "sfs_init()", mc_sfsini);
 463         g_free (mc_sfsini);
 464         return 0;
 465     }
 466     g_free (mc_sfsini);
 467 
 468     sfs_no = 0;
 469     while (sfs_no < MAXFS && fgets (key, sizeof (key), cfg) != NULL)
 470     {
 471         char *c, *semi = NULL;
 472         sfs_flags_t flags = F_NONE;
 473 
 474         if (*key == '#' || *key == '\n')
 475             continue;
 476 
 477         for (c = key; *c != '\0'; c++)
 478             if (*c == ':' || IS_PATH_SEP (*c))
 479             {
 480                 semi = c;
 481                 if (IS_PATH_SEP (*c))
 482                 {
 483                     *c = '\0';
 484                     flags |= F_FULLMATCH;
 485                 }
 486                 break;
 487             }
 488 
 489         if (semi == NULL)
 490         {
 491           invalid_line:
 492             fprintf (stderr, _("Warning: Invalid line in %s:\n%s\n"), "sfs.ini", key);
 493             continue;
 494         }
 495 
 496         for (c = semi + 1; *c != '\0' && !whitespace (*c); c++)
 497             switch (*c)
 498             {
 499             case '1':
 500                 flags |= F_1;
 501                 break;
 502             case '2':
 503                 flags |= F_2;
 504                 break;
 505             case 'R':
 506                 flags |= F_NOLOCALCOPY;
 507                 break;
 508             default:
 509                 fprintf (stderr, _("Warning: Invalid flag %c in %s:\n%s\n"), *c, "sfs.ini", key);
 510             }
 511 
 512         if (*c == '\0')
 513             goto invalid_line;
 514 
 515         c++;
 516         *(semi + 1) = '\0';
 517         semi = strchr (c, '\n');
 518         if (semi != NULL)
 519             *semi = '\0';
 520 
 521         sfs_info[sfs_no].prefix = g_strdup (key);
 522         sfs_info[sfs_no].command = g_strdup (c);
 523         sfs_info[sfs_no].flags = flags;
 524         sfs_no++;
 525     }
 526     fclose (cfg);
 527 
 528     return 1;
 529 }
 530 
 531 /* --------------------------------------------------------------------------------------------- */
 532 
 533 static void
 534 sfs_done (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 535 {
 536     int i;
 537 
 538     (void) me;
 539 
 540     for (i = 0; i < sfs_no; i++)
 541     {
 542         MC_PTR_FREE (sfs_info[i].prefix);
 543         MC_PTR_FREE (sfs_info[i].command);
 544     }
 545     sfs_no = 0;
 546 }
 547 
 548 /* --------------------------------------------------------------------------------------------- */
 549 
 550 static int
 551 sfs_which (struct vfs_class *me, const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 552 {
 553     int i;
 554 
 555     (void) me;
 556 
 557     for (i = 0; i < sfs_no; i++)
 558         if ((sfs_info[i].flags & F_FULLMATCH) != 0)
 559         {
 560             if (strcmp (path, sfs_info[i].prefix) == 0)
 561                 return i;
 562         }
 563         else if (strncmp (path, sfs_info[i].prefix, strlen (sfs_info[i].prefix)) == 0)
 564             return i;
 565 
 566     return (-1);
 567 }
 568 
 569 /* --------------------------------------------------------------------------------------------- */
 570 /*** public functions ****************************************************************************/
 571 /* --------------------------------------------------------------------------------------------- */
 572 
 573 void
 574 vfs_init_sfs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 575 {
 576     /* NULLize vfs_s_subclass members */
 577     memset (&sfs_subclass, 0, sizeof (sfs_subclass));
 578 
 579     vfs_init_class (vfs_sfs_ops, "sfs", VFSF_UNKNOWN, NULL);
 580     vfs_sfs_ops->init = sfs_init;
 581     vfs_sfs_ops->done = sfs_done;
 582     vfs_sfs_ops->fill_names = sfs_fill_names;
 583     vfs_sfs_ops->which = sfs_which;
 584     vfs_sfs_ops->open = sfs_open;
 585     vfs_sfs_ops->close = local_close;
 586     vfs_sfs_ops->read = local_read;
 587     vfs_sfs_ops->stat = sfs_stat;
 588     vfs_sfs_ops->lstat = sfs_lstat;
 589     vfs_sfs_ops->fstat = local_fstat;
 590     vfs_sfs_ops->chmod = sfs_chmod;
 591     vfs_sfs_ops->chown = sfs_chown;
 592     vfs_sfs_ops->utime = sfs_utime;
 593     vfs_sfs_ops->readlink = sfs_readlink;
 594     vfs_sfs_ops->ferrno = local_errno;
 595     vfs_sfs_ops->lseek = local_lseek;
 596     vfs_sfs_ops->getid = sfs_getid;
 597     vfs_sfs_ops->nothingisopen = sfs_nothingisopen;
 598     vfs_sfs_ops->free = sfs_free;
 599     vfs_sfs_ops->getlocalcopy = sfs_getlocalcopy;
 600     vfs_sfs_ops->ungetlocalcopy = sfs_ungetlocalcopy;
 601     vfs_register_class (vfs_sfs_ops);
 602 }
 603 
 604 /* --------------------------------------------------------------------------------------------- */

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