root/lib/vfs/direntry.c

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

DEFINITIONS

This source file includes following definitions.
  1. vfs_s_automake
  2. vfs_s_resolve_symlink
  3. vfs_s_find_entry_tree
  4. vfs_s_find_entry_linear
  5. vfs_s_new_super
  6. vfs_s_insert_super
  7. vfs_s_free_super
  8. vfs_s_new_fh
  9. vfs_s_free_fh
  10. vfs_s_inode_from_path
  11. vfs_s_opendir
  12. vfs_s_readdir
  13. vfs_s_closedir
  14. vfs_s_chdir
  15. vfs_s_internal_stat
  16. vfs_s_readlink
  17. vfs_s_read
  18. vfs_s_write
  19. vfs_s_lseek
  20. vfs_s_close
  21. vfs_s_print_stats
  22. vfs_s_fill_names
  23. vfs_s_ferrno
  24. vfs_s_getlocalcopy
  25. vfs_s_ungetlocalcopy
  26. vfs_s_setctl
  27. vfs_s_getid
  28. vfs_s_nothingisopen
  29. vfs_s_free
  30. vfs_s_dir_uptodate
  31. vfs_s_new_inode
  32. vfs_s_free_inode
  33. vfs_s_new_entry
  34. vfs_s_free_entry
  35. vfs_s_insert_entry
  36. vfs_s_entry_compare
  37. vfs_s_default_stat
  38. vfs_adjust_stat
  39. vfs_s_generate_entry
  40. vfs_s_find_inode
  41. vfs_get_super_by_vpath
  42. vfs_s_get_path
  43. vfs_s_invalidate
  44. vfs_s_fullpath
  45. vfs_s_init_fh
  46. vfs_s_open
  47. vfs_s_stat
  48. vfs_s_lstat
  49. vfs_s_fstat
  50. vfs_s_retrieve_file
  51. vfs_init_class
  52. vfs_init_subclass
  53. vfs_getid
  54. vfs_s_select_on_two
  55. vfs_s_get_line
  56. vfs_s_get_line_interruptible
  57. vfs_s_normalize_filename_leading_spaces

   1 /*
   2    Directory cache support
   3 
   4    Copyright (C) 1998-2024
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Pavel Machek <pavel@ucw.cz>, 1998
   9    Slava Zanko <slavazanko@gmail.com>, 2010-2013
  10    Andrew Borodin <aborodin@vmail.ru> 2010-2022
  11 
  12    This file is part of the Midnight Commander.
  13 
  14    The Midnight Commander is free software: you can redistribute it
  15    and/or modify it under the terms of the GNU General Public License as
  16    published by the Free Software Foundation, either version 3 of the License,
  17    or (at your option) any later version.
  18 
  19    The Midnight Commander is distributed in the hope that it will be useful,
  20    but WITHOUT ANY WARRANTY; without even the implied warranty of
  21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22    GNU General Public License for more details.
  23 
  24    You should have received a copy of the GNU General Public License
  25    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  26 
  27    \warning Paths here do _not_ begin with '/', so root directory of
  28    archive/site is simply "".
  29  */
  30 
  31 /** \file
  32  *  \brief Source: directory cache support
  33  *
  34  *  So that you do not have copy of this in each and every filesystem.
  35  *
  36  *  Very loosely based on tar.c from midnight and archives.[ch] from
  37  *  avfs by Miklos Szeredi (mszeredi@inf.bme.hu)
  38  *
  39  *  Unfortunately, I was unable to keep all filesystems
  40  *  uniform. tar-like filesystems use tree structure where each
  41  *  directory has pointers to its subdirectories. We can do this
  42  *  because we have full information about our archive.
  43  *
  44  *  At ftp-like filesystems, situation is a little bit different. When
  45  *  you cd /usr/src/linux/drivers/char, you do _not_ want /usr,
  46  *  /usr/src, /usr/src/linux and /usr/src/linux/drivers to be
  47  *  listed. That means that we do not have complete information, and if
  48  *  /usr is symlink to /4, we will not know. Also we have to time out
  49  *  entries and things would get messy with tree-like approach. So we
  50  *  do different trick: root directory is completely special and
  51  *  completely fake, it contains entries such as 'usr', 'usr/src', ...,
  52  *  and we'll try to use custom find_entry function.
  53  *
  54  *  \author Pavel Machek <pavel@ucw.cz>
  55  *  \date 1998
  56  *
  57  */
  58 
  59 #include <config.h>
  60 
  61 #include <errno.h>
  62 #include <inttypes.h>           /* uintmax_t */
  63 #include <stdarg.h>
  64 #ifdef HAVE_SYS_SELECT_H
  65 #include <sys/select.h>
  66 #endif
  67 #include <sys/types.h>
  68 #include <unistd.h>
  69 
  70 #include "lib/global.h"
  71 
  72 #include "lib/tty/tty.h"        /* enable/disable interrupt key */
  73 #include "lib/util.h"           /* canonicalize_pathname_custom() */
  74 #if 0
  75 #include "lib/widget.h"         /* message() */
  76 #endif
  77 
  78 #include "vfs.h"
  79 #include "utilvfs.h"
  80 #include "xdirentry.h"
  81 #include "gc.h"                 /* vfs_rmstamp */
  82 
  83 /*** global variables ****************************************************************************/
  84 
  85 /*** file scope macro definitions ****************************************************************/
  86 
  87 #define CALL(x) \
  88         if (VFS_SUBCLASS (me)->x != NULL) \
  89             VFS_SUBCLASS (me)->x
  90 
  91 /*** file scope type declarations ****************************************************************/
  92 
  93 struct dirhandle
  94 {
  95     GList *cur;
  96     struct vfs_s_inode *dir;
  97 };
  98 
  99 /*** file scope variables ************************************************************************/
 100 
 101 /*** forward declarations (file scope functions) *************************************************/
 102 
 103 /* --------------------------------------------------------------------------------------------- */
 104 /*** file scope functions ************************************************************************/
 105 /* --------------------------------------------------------------------------------------------- */
 106 
 107 /* We were asked to create entries automagically */
 108 
 109 static struct vfs_s_entry *
 110 vfs_s_automake (struct vfs_class *me, struct vfs_s_inode *dir, char *path, int flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 111 {
 112     struct vfs_s_entry *res;
 113     char *sep;
 114 
 115     sep = strchr (path, PATH_SEP);
 116     if (sep != NULL)
 117         *sep = '\0';
 118 
 119     res = vfs_s_generate_entry (me, path, dir, (flags & FL_MKDIR) != 0 ? (0777 | S_IFDIR) : 0777);
 120     vfs_s_insert_entry (me, dir, res);
 121 
 122     if (sep != NULL)
 123         *sep = PATH_SEP;
 124 
 125     return res;
 126 }
 127 
 128 /* --------------------------------------------------------------------------------------------- */
 129 /* If the entry is a symlink, find the entry for its target */
 130 
 131 static struct vfs_s_entry *
 132 vfs_s_resolve_symlink (struct vfs_class *me, struct vfs_s_entry *entry, int follow)
     /* [previous][next][first][last][top][bottom][index][help]  */
 133 {
 134     char *linkname;
 135     char *fullname = NULL;
 136     struct vfs_s_entry *target;
 137 
 138     if (follow == LINK_NO_FOLLOW)
 139         return entry;
 140     if (follow == 0)
 141         ERRNOR (ELOOP, NULL);
 142     if (entry == NULL)
 143         ERRNOR (ENOENT, NULL);
 144     if (!S_ISLNK (entry->ino->st.st_mode))
 145         return entry;
 146 
 147     linkname = entry->ino->linkname;
 148     if (linkname == NULL)
 149         ERRNOR (EFAULT, NULL);
 150 
 151     /* make full path from relative */
 152     if (!IS_PATH_SEP (*linkname))
 153     {
 154         char *fullpath;
 155 
 156         fullpath = vfs_s_fullpath (me, entry->dir);
 157         if (fullpath != NULL)
 158         {
 159             fullname = g_strconcat (fullpath, PATH_SEP_STR, linkname, (char *) NULL);
 160             linkname = fullname;
 161             g_free (fullpath);
 162         }
 163     }
 164 
 165     target =
 166         VFS_SUBCLASS (me)->find_entry (me, entry->dir->super->root, linkname, follow - 1, FL_NONE);
 167     g_free (fullname);
 168     return target;
 169 }
 170 
 171 /* --------------------------------------------------------------------------------------------- */
 172 /*
 173  * Follow > 0: follow links, serves as loop protect,
 174  *       == -1: do not follow links
 175  */
 176 
 177 static struct vfs_s_entry *
 178 vfs_s_find_entry_tree (struct vfs_class *me, struct vfs_s_inode *root,
     /* [previous][next][first][last][top][bottom][index][help]  */
 179                        const char *a_path, int follow, int flags)
 180 {
 181     size_t pseg;
 182     struct vfs_s_entry *ent = NULL;
 183     char *const pathref = g_strdup (a_path);
 184     char *path = pathref;
 185 
 186     /* canonicalize as well, but don't remove '../' from path */
 187     canonicalize_pathname_custom (path, CANON_PATH_ALL & (~CANON_PATH_REMDOUBLEDOTS));
 188 
 189     while (root != NULL)
 190     {
 191         GList *iter;
 192 
 193         while (IS_PATH_SEP (*path))     /* Strip leading '/' */
 194             path++;
 195 
 196         if (path[0] == '\0')
 197         {
 198             g_free (pathref);
 199             return ent;
 200         }
 201 
 202         for (pseg = 0; path[pseg] != '\0' && !IS_PATH_SEP (path[pseg]); pseg++)
 203             ;
 204 
 205         for (iter = g_queue_peek_head_link (root->subdir); iter != NULL; iter = g_list_next (iter))
 206         {
 207             ent = VFS_ENTRY (iter->data);
 208             if (strlen (ent->name) == pseg && strncmp (ent->name, path, pseg) == 0)
 209                 /* FOUND! */
 210                 break;
 211         }
 212 
 213         ent = iter != NULL ? VFS_ENTRY (iter->data) : NULL;
 214 
 215         if (ent == NULL && (flags & (FL_MKFILE | FL_MKDIR)) != 0)
 216             ent = vfs_s_automake (me, root, path, flags);
 217         if (ent == NULL)
 218         {
 219             me->verrno = ENOENT;
 220             goto cleanup;
 221         }
 222 
 223         path += pseg;
 224         /* here we must follow leading directories always;
 225            only the actual file is optional */
 226         ent = vfs_s_resolve_symlink (me, ent,
 227                                      strchr (path, PATH_SEP) != NULL ? LINK_FOLLOW : follow);
 228         if (ent == NULL)
 229             goto cleanup;
 230         root = ent->ino;
 231     }
 232   cleanup:
 233     g_free (pathref);
 234     return NULL;
 235 }
 236 
 237 /* --------------------------------------------------------------------------------------------- */
 238 
 239 static struct vfs_s_entry *
 240 vfs_s_find_entry_linear (struct vfs_class *me, struct vfs_s_inode *root,
     /* [previous][next][first][last][top][bottom][index][help]  */
 241                          const char *a_path, int follow, int flags)
 242 {
 243     struct vfs_s_entry *ent = NULL;
 244     char *const path = g_strdup (a_path);
 245     GList *iter;
 246 
 247     if (root->super->root != root)
 248         vfs_die ("We have to use _real_ root. Always. Sorry.");
 249 
 250     /* canonicalize as well, but don't remove '../' from path */
 251     canonicalize_pathname_custom (path, CANON_PATH_ALL & (~CANON_PATH_REMDOUBLEDOTS));
 252 
 253     if ((flags & FL_DIR) == 0)
 254     {
 255         char *dirname, *name;
 256         struct vfs_s_inode *ino;
 257 
 258         dirname = g_path_get_dirname (path);
 259         name = g_path_get_basename (path);
 260         ino = vfs_s_find_inode (me, root->super, dirname, follow, flags | FL_DIR);
 261         ent = vfs_s_find_entry_tree (me, ino, name, follow, flags);
 262         g_free (dirname);
 263         g_free (name);
 264         g_free (path);
 265         return ent;
 266     }
 267 
 268     iter = g_queue_find_custom (root->subdir, path, (GCompareFunc) vfs_s_entry_compare);
 269     ent = iter != NULL ? VFS_ENTRY (iter->data) : NULL;
 270 
 271     if (ent != NULL && !VFS_SUBCLASS (me)->dir_uptodate (me, ent->ino))
 272     {
 273 #if 1
 274         vfs_print_message (_("Directory cache expired for %s"), path);
 275 #endif
 276         vfs_s_free_entry (me, ent);
 277         ent = NULL;
 278     }
 279 
 280     if (ent == NULL)
 281     {
 282         struct vfs_s_inode *ino;
 283 
 284         ino = vfs_s_new_inode (me, root->super, vfs_s_default_stat (me, S_IFDIR | 0755));
 285         ent = vfs_s_new_entry (me, path, ino);
 286         if (VFS_SUBCLASS (me)->dir_load (me, ino, path) == -1)
 287         {
 288             vfs_s_free_entry (me, ent);
 289             g_free (path);
 290             return NULL;
 291         }
 292 
 293         vfs_s_insert_entry (me, root, ent);
 294 
 295         iter = g_queue_find_custom (root->subdir, path, (GCompareFunc) vfs_s_entry_compare);
 296         ent = iter != NULL ? VFS_ENTRY (iter->data) : NULL;
 297     }
 298     if (ent == NULL)
 299         vfs_die ("find_linear: success but directory is not there\n");
 300 
 301 #if 0
 302     if (vfs_s_resolve_symlink (me, ent, follow) == NULL)
 303     {
 304         g_free (path);
 305         return NULL;
 306     }
 307 #endif
 308     g_free (path);
 309     return ent;
 310 }
 311 
 312 /* --------------------------------------------------------------------------------------------- */
 313 /* Ook, these were functions around directory entries / inodes */
 314 /* -------------------------------- superblock games -------------------------- */
 315 
 316 static struct vfs_s_super *
 317 vfs_s_new_super (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 318 {
 319     struct vfs_s_super *super;
 320 
 321     super = g_new0 (struct vfs_s_super, 1);
 322     super->me = me;
 323     return super;
 324 }
 325 
 326 /* --------------------------------------------------------------------------------------------- */
 327 
 328 static inline void
 329 vfs_s_insert_super (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 330 {
 331     VFS_SUBCLASS (me)->supers = g_list_prepend (VFS_SUBCLASS (me)->supers, super);
 332 }
 333 
 334 /* --------------------------------------------------------------------------------------------- */
 335 
 336 static void
 337 vfs_s_free_super (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 338 {
 339     if (super->root != NULL)
 340     {
 341         vfs_s_free_inode (me, super->root);
 342         super->root = NULL;
 343     }
 344 
 345 #if 0
 346     /* FIXME: We currently leak small amount of memory, sometimes. Fix it if you can. */
 347     if (super->ino_usage != 0)
 348         message (D_ERROR, "Direntry warning",
 349                  "Super ino_usage is %d, memory leak", super->ino_usage);
 350 
 351     if (super->want_stale)
 352         message (D_ERROR, "Direntry warning", "%s", "Super has want_stale set");
 353 #endif
 354 
 355     VFS_SUBCLASS (me)->supers = g_list_remove (VFS_SUBCLASS (me)->supers, super);
 356 
 357     CALL (free_archive) (me, super);
 358 #ifdef ENABLE_VFS_NET
 359     vfs_path_element_free (super->path_element);
 360 #endif
 361     g_free (super->name);
 362     g_free (super);
 363 }
 364 
 365 /* --------------------------------------------------------------------------------------------- */
 366 
 367 static vfs_file_handler_t *
 368 vfs_s_new_fh (struct vfs_s_inode *ino, gboolean changed)
     /* [previous][next][first][last][top][bottom][index][help]  */
 369 {
 370     vfs_file_handler_t *fh;
 371 
 372     fh = g_new0 (vfs_file_handler_t, 1);
 373     vfs_s_init_fh (fh, ino, changed);
 374 
 375     return fh;
 376 }
 377 
 378 /* --------------------------------------------------------------------------------------------- */
 379 
 380 static void
 381 vfs_s_free_fh (struct vfs_s_subclass *s, vfs_file_handler_t * fh)
     /* [previous][next][first][last][top][bottom][index][help]  */
 382 {
 383     if (s->fh_free != NULL)
 384         s->fh_free (fh);
 385 
 386     g_free (fh);
 387 }
 388 
 389 /* --------------------------------------------------------------------------------------------- */
 390 /* Support of archives */
 391 /* ------------------------ readdir & friends ----------------------------- */
 392 
 393 static struct vfs_s_inode *
 394 vfs_s_inode_from_path (const vfs_path_t * vpath, int flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 395 {
 396     struct vfs_s_super *super;
 397     struct vfs_s_inode *ino;
 398     const char *q;
 399     struct vfs_class *me;
 400 
 401     q = vfs_s_get_path (vpath, &super, 0);
 402     if (q == NULL)
 403         return NULL;
 404 
 405     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
 406 
 407     ino =
 408         vfs_s_find_inode (me, super, q,
 409                           (flags & FL_FOLLOW) != 0 ? LINK_FOLLOW : LINK_NO_FOLLOW,
 410                           flags & ~FL_FOLLOW);
 411     if (ino == NULL && *q == '\0')
 412         /* We are asking about / directory of ftp server: assume it exists */
 413         ino =
 414             vfs_s_find_inode (me, super, q,
 415                               (flags & FL_FOLLOW) != 0 ? LINK_FOLLOW : LINK_NO_FOLLOW,
 416                               FL_DIR | (flags & ~FL_FOLLOW));
 417     return ino;
 418 }
 419 
 420 /* --------------------------------------------------------------------------------------------- */
 421 
 422 static void *
 423 vfs_s_opendir (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 424 {
 425     struct vfs_s_inode *dir;
 426     struct dirhandle *info;
 427     struct vfs_class *me;
 428 
 429     dir = vfs_s_inode_from_path (vpath, FL_DIR | FL_FOLLOW);
 430     if (dir == NULL)
 431         return NULL;
 432 
 433     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
 434 
 435     if (!S_ISDIR (dir->st.st_mode))
 436     {
 437         me->verrno = ENOTDIR;
 438         return NULL;
 439     }
 440 
 441     dir->st.st_nlink++;
 442 #if 0
 443     if (dir->subdir == NULL)    /* This can actually happen if we allow empty directories */
 444     {
 445         me->verrno = EAGAIN;
 446         return NULL;
 447     }
 448 #endif
 449     info = g_new (struct dirhandle, 1);
 450     info->cur = g_queue_peek_head_link (dir->subdir);
 451     info->dir = dir;
 452 
 453     return info;
 454 }
 455 
 456 /* --------------------------------------------------------------------------------------------- */
 457 
 458 static struct vfs_dirent *
 459 vfs_s_readdir (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 460 {
 461     struct vfs_dirent *dir = NULL;
 462     struct dirhandle *info = (struct dirhandle *) data;
 463     const char *name;
 464 
 465     if (info->cur == NULL || info->cur->data == NULL)
 466         return NULL;
 467 
 468     name = VFS_ENTRY (info->cur->data)->name;
 469     if (name != NULL)
 470         dir = vfs_dirent_init (NULL, name, 0);
 471     else
 472         vfs_die ("Null in structure-cannot happen");
 473 
 474     info->cur = g_list_next (info->cur);
 475 
 476     return dir;
 477 }
 478 
 479 /* --------------------------------------------------------------------------------------------- */
 480 
 481 static int
 482 vfs_s_closedir (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 483 {
 484     struct dirhandle *info = (struct dirhandle *) data;
 485     struct vfs_s_inode *dir = info->dir;
 486 
 487     vfs_s_free_inode (dir->super->me, dir);
 488     g_free (data);
 489     return 0;
 490 }
 491 
 492 /* --------------------------------------------------------------------------------------------- */
 493 
 494 static int
 495 vfs_s_chdir (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 496 {
 497     void *data;
 498 
 499     data = vfs_s_opendir (vpath);
 500     if (data == NULL)
 501         return (-1);
 502     vfs_s_closedir (data);
 503     return 0;
 504 }
 505 
 506 /* --------------------------------------------------------------------------------------------- */
 507 /* --------------------------- stat and friends ---------------------------- */
 508 
 509 static int
 510 vfs_s_internal_stat (const vfs_path_t * vpath, struct stat *buf, int flag)
     /* [previous][next][first][last][top][bottom][index][help]  */
 511 {
 512     struct vfs_s_inode *ino;
 513 
 514     ino = vfs_s_inode_from_path (vpath, flag);
 515     if (ino == NULL)
 516         return (-1);
 517     *buf = ino->st;
 518     return 0;
 519 }
 520 
 521 /* --------------------------------------------------------------------------------------------- */
 522 
 523 static int
 524 vfs_s_readlink (const vfs_path_t * vpath, char *buf, size_t size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 525 {
 526     struct vfs_s_inode *ino;
 527     size_t len;
 528     struct vfs_class *me;
 529 
 530     ino = vfs_s_inode_from_path (vpath, 0);
 531     if (ino == NULL)
 532         return (-1);
 533 
 534     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
 535 
 536     if (!S_ISLNK (ino->st.st_mode))
 537     {
 538         me->verrno = EINVAL;
 539         return (-1);
 540     }
 541 
 542     if (ino->linkname == NULL)
 543     {
 544         me->verrno = EFAULT;
 545         return (-1);
 546     }
 547 
 548     len = strlen (ino->linkname);
 549     if (size < len)
 550         len = size;
 551     /* readlink() does not append a NUL character to buf */
 552     memcpy (buf, ino->linkname, len);
 553     return len;
 554 }
 555 
 556 /* --------------------------------------------------------------------------------------------- */
 557 
 558 static ssize_t
 559 vfs_s_read (void *fh, char *buffer, size_t count)
     /* [previous][next][first][last][top][bottom][index][help]  */
 560 {
 561     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
 562     struct vfs_class *me = VFS_FILE_HANDLER_SUPER (fh)->me;
 563 
 564     if (file->linear == LS_LINEAR_PREOPEN)
 565     {
 566         if (VFS_SUBCLASS (me)->linear_start (me, file, file->pos) == 0)
 567             return (-1);
 568     }
 569 
 570     if (file->linear == LS_LINEAR_CLOSED)
 571         vfs_die ("linear_start() did not set linear_state!");
 572 
 573     if (file->linear == LS_LINEAR_OPEN)
 574         return VFS_SUBCLASS (me)->linear_read (me, file, buffer, count);
 575 
 576     if (file->handle != -1)
 577     {
 578         ssize_t n;
 579 
 580         n = read (file->handle, buffer, count);
 581         if (n < 0)
 582             me->verrno = errno;
 583         return n;
 584     }
 585     vfs_die ("vfs_s_read: This should not happen\n");
 586     return (-1);
 587 }
 588 
 589 /* --------------------------------------------------------------------------------------------- */
 590 
 591 static ssize_t
 592 vfs_s_write (void *fh, const char *buffer, size_t count)
     /* [previous][next][first][last][top][bottom][index][help]  */
 593 {
 594     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
 595     struct vfs_class *me = VFS_FILE_HANDLER_SUPER (fh)->me;
 596 
 597     if (file->linear != LS_NOT_LINEAR)
 598         vfs_die ("no writing to linear files, please");
 599 
 600     file->changed = TRUE;
 601     if (file->handle != -1)
 602     {
 603         ssize_t n;
 604 
 605         n = write (file->handle, buffer, count);
 606         if (n < 0)
 607             me->verrno = errno;
 608         return n;
 609     }
 610     vfs_die ("vfs_s_write: This should not happen\n");
 611     return 0;
 612 }
 613 
 614 /* --------------------------------------------------------------------------------------------- */
 615 
 616 static off_t
 617 vfs_s_lseek (void *fh, off_t offset, int whence)
     /* [previous][next][first][last][top][bottom][index][help]  */
 618 {
 619     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
 620     off_t size = file->ino->st.st_size;
 621 
 622     if (file->linear == LS_LINEAR_OPEN)
 623         vfs_die ("cannot lseek() after linear_read!");
 624 
 625     if (file->handle != -1)
 626     {                           /* If we have local file opened, we want to work with it */
 627         off_t retval;
 628 
 629         retval = lseek (file->handle, offset, whence);
 630         if (retval == -1)
 631             VFS_FILE_HANDLER_SUPER (fh)->me->verrno = errno;
 632         return retval;
 633     }
 634 
 635     switch (whence)
 636     {
 637     case SEEK_CUR:
 638         offset += file->pos;
 639         break;
 640     case SEEK_END:
 641         offset += size;
 642         break;
 643     default:
 644         break;
 645     }
 646     if (offset < 0)
 647         file->pos = 0;
 648     else if (offset < size)
 649         file->pos = offset;
 650     else
 651         file->pos = size;
 652     return file->pos;
 653 }
 654 
 655 /* --------------------------------------------------------------------------------------------- */
 656 
 657 static int
 658 vfs_s_close (void *fh)
     /* [previous][next][first][last][top][bottom][index][help]  */
 659 {
 660     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
 661     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
 662     struct vfs_class *me = super->me;
 663     struct vfs_s_subclass *sub = VFS_SUBCLASS (me);
 664     int res = 0;
 665 
 666     if (me == NULL)
 667         return (-1);
 668 
 669     super->fd_usage--;
 670     if (super->fd_usage == 0)
 671         vfs_stamp_create (me, VFS_FILE_HANDLER_SUPER (fh));
 672 
 673     if (file->linear == LS_LINEAR_OPEN)
 674         sub->linear_close (me, fh);
 675     if (sub->fh_close != NULL)
 676         res = sub->fh_close (me, fh);
 677     if ((me->flags & VFSF_USETMP) != 0 && file->changed && sub->file_store != NULL)
 678     {
 679         char *s;
 680 
 681         s = vfs_s_fullpath (me, file->ino);
 682 
 683         if (s == NULL)
 684             res = -1;
 685         else
 686         {
 687             res = sub->file_store (me, fh, s, file->ino->localname);
 688             g_free (s);
 689         }
 690         vfs_s_invalidate (me, super);
 691     }
 692 
 693     if (file->handle != -1)
 694     {
 695         close (file->handle);
 696         file->handle = -1;
 697     }
 698 
 699     vfs_s_free_inode (me, file->ino);
 700     vfs_s_free_fh (sub, fh);
 701 
 702     return res;
 703 }
 704 
 705 /* --------------------------------------------------------------------------------------------- */
 706 
 707 static void
 708 vfs_s_print_stats (const char *fs_name, const char *action,
     /* [previous][next][first][last][top][bottom][index][help]  */
 709                    const char *file_name, off_t have, off_t need)
 710 {
 711     if (need != 0)
 712         vfs_print_message (_("%s: %s: %s %3d%% (%lld) bytes transferred"), fs_name, action,
 713                            file_name, (int) ((double) have * 100 / need), (long long) have);
 714     else
 715         vfs_print_message (_("%s: %s: %s %lld bytes transferred"), fs_name, action, file_name,
 716                            (long long) have);
 717 }
 718 
 719 /* --------------------------------------------------------------------------------------------- */
 720 /* ------------------------------- mc support ---------------------------- */
 721 
 722 static void
 723 vfs_s_fill_names (struct vfs_class *me, fill_names_f func)
     /* [previous][next][first][last][top][bottom][index][help]  */
 724 {
 725     GList *iter;
 726 
 727     for (iter = VFS_SUBCLASS (me)->supers; iter != NULL; iter = g_list_next (iter))
 728     {
 729         const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
 730         char *name;
 731 
 732         name = g_strconcat (super->name, PATH_SEP_STR, me->prefix, VFS_PATH_URL_DELIMITER,
 733                             /* super->current_dir->name, */ (char *) NULL);
 734         func (name);
 735         g_free (name);
 736     }
 737 }
 738 
 739 /* --------------------------------------------------------------------------------------------- */
 740 
 741 static int
 742 vfs_s_ferrno (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 743 {
 744     return me->verrno;
 745 }
 746 
 747 /* --------------------------------------------------------------------------------------------- */
 748 /**
 749  * Get local copy of the given file.  We reuse the existing file cache
 750  * for remote filesystems.  Archives use standard VFS facilities.
 751  */
 752 
 753 static vfs_path_t *
 754 vfs_s_getlocalcopy (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 755 {
 756     vfs_file_handler_t *fh;
 757     vfs_path_t *local = NULL;
 758 
 759     if (vpath == NULL)
 760         return NULL;
 761 
 762     fh = vfs_s_open (vpath, O_RDONLY, 0);
 763 
 764     if (fh != NULL)
 765     {
 766         const struct vfs_class *me;
 767 
 768         me = vfs_path_get_last_path_vfs (vpath);
 769         if ((me->flags & VFSF_USETMP) != 0 && fh->ino != NULL)
 770             local = vfs_path_from_str_flags (fh->ino->localname, VPF_NO_CANON);
 771 
 772         vfs_s_close (fh);
 773     }
 774 
 775     return local;
 776 }
 777 
 778 /* --------------------------------------------------------------------------------------------- */
 779 /**
 780  * Return the local copy.  Since we are using our cache, we do nothing -
 781  * the cache will be removed when the archive is closed.
 782  */
 783 
 784 static int
 785 vfs_s_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed)
     /* [previous][next][first][last][top][bottom][index][help]  */
 786 {
 787     (void) vpath;
 788     (void) local;
 789     (void) has_changed;
 790     return 0;
 791 }
 792 
 793 /* --------------------------------------------------------------------------------------------- */
 794 
 795 static int
 796 vfs_s_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
     /* [previous][next][first][last][top][bottom][index][help]  */
 797 {
 798     struct vfs_class *me;
 799 
 800     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
 801 
 802     switch (ctlop)
 803     {
 804     case VFS_SETCTL_STALE_DATA:
 805         {
 806             struct vfs_s_inode *ino;
 807 
 808             ino = vfs_s_inode_from_path (vpath, 0);
 809             if (ino == NULL)
 810                 return 0;
 811             if (arg != NULL)
 812                 ino->super->want_stale = TRUE;
 813             else
 814             {
 815                 ino->super->want_stale = FALSE;
 816                 vfs_s_invalidate (me, ino->super);
 817             }
 818             return 1;
 819         }
 820     case VFS_SETCTL_LOGFILE:
 821         me->logfile = fopen ((char *) arg, "w");
 822         return 1;
 823     case VFS_SETCTL_FLUSH:
 824         me->flush = TRUE;
 825         return 1;
 826     default:
 827         return 0;
 828     }
 829 }
 830 
 831 /* --------------------------------------------------------------------------------------------- */
 832 /* ----------------------------- Stamping support -------------------------- */
 833 
 834 static vfsid
 835 vfs_s_getid (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 836 {
 837     struct vfs_s_super *archive = NULL;
 838     const char *p;
 839 
 840     p = vfs_s_get_path (vpath, &archive, FL_NO_OPEN);
 841     if (p == NULL)
 842         return NULL;
 843 
 844     return (vfsid) archive;
 845 }
 846 
 847 /* --------------------------------------------------------------------------------------------- */
 848 
 849 static gboolean
 850 vfs_s_nothingisopen (vfsid id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 851 {
 852     return (VFS_SUPER (id)->fd_usage <= 0);
 853 }
 854 
 855 /* --------------------------------------------------------------------------------------------- */
 856 
 857 static void
 858 vfs_s_free (vfsid id)
     /* [previous][next][first][last][top][bottom][index][help]  */
 859 {
 860     vfs_s_free_super (VFS_SUPER (id)->me, VFS_SUPER (id));
 861 }
 862 
 863 /* --------------------------------------------------------------------------------------------- */
 864 
 865 static gboolean
 866 vfs_s_dir_uptodate (struct vfs_class *me, struct vfs_s_inode *ino)
     /* [previous][next][first][last][top][bottom][index][help]  */
 867 {
 868     gint64 tim;
 869 
 870     if (me->flush)
 871     {
 872         me->flush = FALSE;
 873         return 0;
 874     }
 875 
 876     tim = g_get_monotonic_time ();
 877 
 878     return (tim < ino->timestamp);
 879 }
 880 
 881 /* --------------------------------------------------------------------------------------------- */
 882 /*** public functions ****************************************************************************/
 883 /* --------------------------------------------------------------------------------------------- */
 884 
 885 struct vfs_s_inode *
 886 vfs_s_new_inode (struct vfs_class *me, struct vfs_s_super *super, struct stat *initstat)
     /* [previous][next][first][last][top][bottom][index][help]  */
 887 {
 888     struct vfs_s_inode *ino;
 889 
 890     ino = g_try_new0 (struct vfs_s_inode, 1);
 891     if (ino == NULL)
 892         return NULL;
 893 
 894     if (initstat != NULL)
 895         ino->st = *initstat;
 896     ino->super = super;
 897     ino->subdir = g_queue_new ();
 898     ino->st.st_nlink = 0;
 899     ino->st.st_ino = VFS_SUBCLASS (me)->inode_counter++;
 900     ino->st.st_dev = VFS_SUBCLASS (me)->rdev;
 901 
 902     super->ino_usage++;
 903 
 904     CALL (init_inode) (me, ino);
 905 
 906     return ino;
 907 }
 908 
 909 /* --------------------------------------------------------------------------------------------- */
 910 
 911 void
 912 vfs_s_free_inode (struct vfs_class *me, struct vfs_s_inode *ino)
     /* [previous][next][first][last][top][bottom][index][help]  */
 913 {
 914     if (ino == NULL)
 915         vfs_die ("Don't pass NULL to me");
 916 
 917     /* ==0 can happen if freshly created entry is deleted */
 918     if (ino->st.st_nlink > 1)
 919     {
 920         ino->st.st_nlink--;
 921         return;
 922     }
 923 
 924     while (g_queue_get_length (ino->subdir) != 0)
 925     {
 926         struct vfs_s_entry *entry;
 927 
 928         entry = VFS_ENTRY (g_queue_peek_head (ino->subdir));
 929         vfs_s_free_entry (me, entry);
 930     }
 931 
 932     g_queue_free (ino->subdir);
 933     ino->subdir = NULL;
 934 
 935     CALL (free_inode) (me, ino);
 936     g_free (ino->linkname);
 937     if ((me->flags & VFSF_USETMP) != 0 && ino->localname != NULL)
 938     {
 939         unlink (ino->localname);
 940         g_free (ino->localname);
 941     }
 942     ino->super->ino_usage--;
 943     g_free (ino);
 944 }
 945 
 946 /* --------------------------------------------------------------------------------------------- */
 947 
 948 struct vfs_s_entry *
 949 vfs_s_new_entry (struct vfs_class *me, const char *name, struct vfs_s_inode *inode)
     /* [previous][next][first][last][top][bottom][index][help]  */
 950 {
 951     struct vfs_s_entry *entry;
 952 
 953     entry = g_new0 (struct vfs_s_entry, 1);
 954 
 955     entry->name = g_strdup (name);
 956     entry->ino = inode;
 957     entry->ino->ent = entry;
 958     CALL (init_entry) (me, entry);
 959 
 960     return entry;
 961 }
 962 
 963 /* --------------------------------------------------------------------------------------------- */
 964 
 965 void
 966 vfs_s_free_entry (struct vfs_class *me, struct vfs_s_entry *ent)
     /* [previous][next][first][last][top][bottom][index][help]  */
 967 {
 968     if (ent->dir != NULL)
 969         g_queue_remove (ent->dir->subdir, ent);
 970 
 971     MC_PTR_FREE (ent->name);
 972 
 973     if (ent->ino != NULL)
 974     {
 975         ent->ino->ent = NULL;
 976         vfs_s_free_inode (me, ent->ino);
 977     }
 978 
 979     g_free (ent);
 980 }
 981 
 982 /* --------------------------------------------------------------------------------------------- */
 983 
 984 void
 985 vfs_s_insert_entry (struct vfs_class *me, struct vfs_s_inode *dir, struct vfs_s_entry *ent)
     /* [previous][next][first][last][top][bottom][index][help]  */
 986 {
 987     (void) me;
 988 
 989     ent->dir = dir;
 990 
 991     ent->ino->st.st_nlink++;
 992     g_queue_push_tail (dir->subdir, ent);
 993 }
 994 
 995 /* --------------------------------------------------------------------------------------------- */
 996 
 997 int
 998 vfs_s_entry_compare (const void *a, const void *b)
     /* [previous][next][first][last][top][bottom][index][help]  */
 999 {
1000     const struct vfs_s_entry *e = (const struct vfs_s_entry *) a;
1001     const char *name = (const char *) b;
1002 
1003     return strcmp (e->name, name);
1004 }
1005 
1006 /* --------------------------------------------------------------------------------------------- */
1007 
1008 struct stat *
1009 vfs_s_default_stat (struct vfs_class *me, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1010 {
1011     static struct stat st;
1012     mode_t myumask;
1013 
1014     (void) me;
1015 
1016     myumask = umask (022);
1017     umask (myumask);
1018     mode &= ~myumask;
1019 
1020     st.st_mode = mode;
1021     st.st_ino = 0;
1022     st.st_dev = 0;
1023 #ifdef HAVE_STRUCT_STAT_ST_RDEV
1024     st.st_rdev = 0;
1025 #endif
1026     st.st_uid = getuid ();
1027     st.st_gid = getgid ();
1028 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1029     st.st_blksize = 512;
1030 #endif
1031     st.st_size = 0;
1032 
1033     st.st_mtime = st.st_atime = st.st_ctime = time (NULL);
1034 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1035     st.st_atim.tv_nsec = st.st_mtim.tv_nsec = st.st_ctim.tv_nsec = 0;
1036 #endif
1037 
1038     vfs_adjust_stat (&st);
1039 
1040     return &st;
1041 }
1042 
1043 /* --------------------------------------------------------------------------------------------- */
1044 /**
1045  * Calculate number of st_blocks using st_size and st_blksize.
1046  * In according to stat(2), st_blocks is the size in 512-byte units.
1047  *
1048  * @param s stat info
1049  */
1050 
1051 void
1052 vfs_adjust_stat (struct stat *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
1053 {
1054 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1055     if (s->st_size == 0)
1056         s->st_blocks = 0;
1057     else
1058     {
1059 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1060         blkcnt_t ioblocks;
1061         blksize_t ioblock_size;
1062 
1063         /* 1. Calculate how many IO blocks are occupied */
1064         ioblocks = 1 + (s->st_size - 1) / s->st_blksize;
1065         /* 2. Calculate size of st_blksize in 512-byte units */
1066         ioblock_size = 1 + (s->st_blksize - 1) / 512;
1067         /* 3. Calculate number of blocks */
1068         s->st_blocks = ioblocks * ioblock_size;
1069 #else
1070         /* Let IO block size is 512 bytes */
1071         s->st_blocks = 1 + (s->st_size - 1) / 512;
1072 #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
1073     }
1074 #endif /* HAVE_STRUCT_STAT_ST_BLOCKS */
1075 }
1076 
1077 /* --------------------------------------------------------------------------------------------- */
1078 
1079 struct vfs_s_entry *
1080 vfs_s_generate_entry (struct vfs_class *me, const char *name, struct vfs_s_inode *parent,
     /* [previous][next][first][last][top][bottom][index][help]  */
1081                       mode_t mode)
1082 {
1083     struct vfs_s_inode *inode;
1084     struct stat *st;
1085 
1086     st = vfs_s_default_stat (me, mode);
1087     inode = vfs_s_new_inode (me, parent->super, st);
1088 
1089     return vfs_s_new_entry (me, name, inode);
1090 }
1091 
1092 /* --------------------------------------------------------------------------------------------- */
1093 
1094 struct vfs_s_inode *
1095 vfs_s_find_inode (struct vfs_class *me, const struct vfs_s_super *super,
     /* [previous][next][first][last][top][bottom][index][help]  */
1096                   const char *path, int follow, int flags)
1097 {
1098     struct vfs_s_entry *ent;
1099 
1100     if (((me->flags & VFSF_REMOTE) == 0) && (*path == '\0'))
1101         return super->root;
1102 
1103     ent = VFS_SUBCLASS (me)->find_entry (me, super->root, path, follow, flags);
1104     return (ent != NULL ? ent->ino : NULL);
1105 }
1106 
1107 /* --------------------------------------------------------------------------------------------- */
1108 /* Ook, these were functions around directory entries / inodes */
1109 /* -------------------------------- superblock games -------------------------- */
1110 /**
1111  * get superlock object by vpath
1112  *
1113  * @param vpath path
1114  * @return superlock object or NULL if not found
1115  */
1116 
1117 struct vfs_s_super *
1118 vfs_get_super_by_vpath (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1119 {
1120     GList *iter;
1121     void *cookie = NULL;
1122     const vfs_path_element_t *path_element;
1123     struct vfs_s_subclass *subclass;
1124     struct vfs_s_super *super = NULL;
1125     vfs_path_t *vpath_archive;
1126 
1127     path_element = vfs_path_get_by_index (vpath, -1);
1128     subclass = VFS_SUBCLASS (path_element->class);
1129 
1130     vpath_archive = vfs_path_clone (vpath);
1131     vfs_path_remove_element_by_index (vpath_archive, -1);
1132 
1133     if (subclass->archive_check != NULL)
1134     {
1135         cookie = subclass->archive_check (vpath_archive);
1136         if (cookie == NULL)
1137             goto ret;
1138     }
1139 
1140     if (subclass->archive_same == NULL)
1141         goto ret;
1142 
1143     for (iter = subclass->supers; iter != NULL; iter = g_list_next (iter))
1144     {
1145         int i;
1146 
1147         super = VFS_SUPER (iter->data);
1148 
1149         /* 0 == other, 1 == same, return it, 2 == other but stop scanning */
1150         i = subclass->archive_same (path_element, super, vpath_archive, cookie);
1151         if (i == 1)
1152             goto ret;
1153         if (i != 0)
1154             break;
1155 
1156         super = NULL;
1157     }
1158 
1159   ret:
1160     vfs_path_free (vpath_archive, TRUE);
1161     return super;
1162 }
1163 
1164 /* --------------------------------------------------------------------------------------------- */
1165 /**
1166  * get path from last VFS-element and create corresponding superblock
1167  *
1168  * @param vpath source path object
1169  * @param archive pointer to object for store newly created superblock
1170  * @param flags flags
1171  *
1172  * @return path from last VFS-element
1173  */
1174 const char *
1175 vfs_s_get_path (const vfs_path_t * vpath, struct vfs_s_super **archive, int flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
1176 {
1177     const char *retval = "";
1178     int result = -1;
1179     struct vfs_s_super *super;
1180     const vfs_path_element_t *path_element;
1181     struct vfs_s_subclass *subclass;
1182 
1183     path_element = vfs_path_get_by_index (vpath, -1);
1184 
1185     if (path_element->path != NULL)
1186         retval = path_element->path;
1187 
1188     super = vfs_get_super_by_vpath (vpath);
1189     if (super != NULL)
1190         goto return_success;
1191 
1192     if ((flags & FL_NO_OPEN) != 0)
1193     {
1194         path_element->class->verrno = EIO;
1195         return NULL;
1196     }
1197 
1198     subclass = VFS_SUBCLASS (path_element->class);
1199 
1200     super = subclass->new_archive != NULL ?
1201         subclass->new_archive (path_element->class) : vfs_s_new_super (path_element->class);
1202 
1203     if (subclass->open_archive != NULL)
1204     {
1205         vfs_path_t *vpath_archive;
1206 
1207         vpath_archive = vfs_path_clone (vpath);
1208         vfs_path_remove_element_by_index (vpath_archive, -1);
1209 
1210         result = subclass->open_archive (super, vpath_archive, path_element);
1211         vfs_path_free (vpath_archive, TRUE);
1212     }
1213     if (result == -1)
1214     {
1215         vfs_s_free_super (path_element->class, super);
1216         path_element->class->verrno = EIO;
1217         return NULL;
1218     }
1219     if (super->name == NULL)
1220         vfs_die ("You have to fill name\n");
1221     if (super->root == NULL)
1222         vfs_die ("You have to fill root inode\n");
1223 
1224     vfs_s_insert_super (path_element->class, super);
1225     vfs_stamp_create (path_element->class, super);
1226 
1227   return_success:
1228     *archive = super;
1229     return retval;
1230 }
1231 
1232 /* --------------------------------------------------------------------------------------------- */
1233 
1234 void
1235 vfs_s_invalidate (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
1236 {
1237     if (!super->want_stale)
1238     {
1239         vfs_s_free_inode (me, super->root);
1240         super->root = vfs_s_new_inode (me, super, vfs_s_default_stat (me, S_IFDIR | 0755));
1241     }
1242 }
1243 
1244 /* --------------------------------------------------------------------------------------------- */
1245 
1246 char *
1247 vfs_s_fullpath (struct vfs_class *me, struct vfs_s_inode *ino)
     /* [previous][next][first][last][top][bottom][index][help]  */
1248 {
1249     if (ino->ent == NULL)
1250         ERRNOR (EAGAIN, NULL);
1251 
1252     if ((me->flags & VFSF_USETMP) == 0)
1253     {
1254         /* archives */
1255         char *path;
1256 
1257         path = g_strdup (ino->ent->name);
1258 
1259         while (TRUE)
1260         {
1261             char *newpath;
1262 
1263             ino = ino->ent->dir;
1264             if (ino == ino->super->root)
1265                 break;
1266 
1267             newpath = g_strconcat (ino->ent->name, PATH_SEP_STR, path, (char *) NULL);
1268             g_free (path);
1269             path = newpath;
1270         }
1271         return path;
1272     }
1273 
1274     /* remote systems */
1275     if (ino->ent->dir == NULL || ino->ent->dir->ent == NULL)
1276         return g_strdup (ino->ent->name);
1277 
1278     return g_strconcat (ino->ent->dir->ent->name, PATH_SEP_STR, ino->ent->name, (char *) NULL);
1279 }
1280 
1281 /* --------------------------------------------------------------------------------------------- */
1282 
1283 void
1284 vfs_s_init_fh (vfs_file_handler_t * fh, struct vfs_s_inode *ino, gboolean changed)
     /* [previous][next][first][last][top][bottom][index][help]  */
1285 {
1286     fh->ino = ino;
1287     fh->handle = -1;
1288     fh->changed = changed;
1289     fh->linear = LS_NOT_LINEAR;
1290 }
1291 
1292 /* --------------------------------------------------------------------------------------------- */
1293 /* --------------------------- stat and friends ---------------------------- */
1294 
1295 void *
1296 vfs_s_open (const vfs_path_t * vpath, int flags, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1297 {
1298     gboolean was_changed = FALSE;
1299     vfs_file_handler_t *fh;
1300     struct vfs_s_super *super;
1301     const char *q;
1302     struct vfs_s_inode *ino;
1303     struct vfs_class *me;
1304     struct vfs_s_subclass *s;
1305 
1306     q = vfs_s_get_path (vpath, &super, 0);
1307     if (q == NULL)
1308         return NULL;
1309 
1310     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1311 
1312     ino = vfs_s_find_inode (me, super, q, LINK_FOLLOW, FL_NONE);
1313     if (ino != NULL && (flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
1314     {
1315         me->verrno = EEXIST;
1316         return NULL;
1317     }
1318 
1319     s = VFS_SUBCLASS (me);
1320 
1321     if (ino == NULL)
1322     {
1323         char *name;
1324         struct vfs_s_entry *ent;
1325         struct vfs_s_inode *dir;
1326 
1327         /* If the filesystem is read-only, disable file creation */
1328         if ((flags & O_CREAT) == 0 || me->write == NULL)
1329             return NULL;
1330 
1331         name = g_path_get_dirname (q);
1332         dir = vfs_s_find_inode (me, super, name, LINK_FOLLOW, FL_DIR);
1333         g_free (name);
1334         if (dir == NULL)
1335             return NULL;
1336 
1337         name = g_path_get_basename (q);
1338         ent = vfs_s_generate_entry (me, name, dir, 0755);
1339         ino = ent->ino;
1340         vfs_s_insert_entry (me, dir, ent);
1341         if ((VFS_CLASS (s)->flags & VFSF_USETMP) != 0)
1342         {
1343             int tmp_handle;
1344             vfs_path_t *tmp_vpath;
1345 
1346             tmp_handle = vfs_mkstemps (&tmp_vpath, me->name, name);
1347             ino->localname = vfs_path_free (tmp_vpath, FALSE);
1348             if (tmp_handle == -1)
1349             {
1350                 g_free (name);
1351                 return NULL;
1352             }
1353 
1354             close (tmp_handle);
1355         }
1356 
1357         g_free (name);
1358         was_changed = TRUE;
1359     }
1360 
1361     if (S_ISDIR (ino->st.st_mode))
1362     {
1363         me->verrno = EISDIR;
1364         return NULL;
1365     }
1366 
1367     fh = s->fh_new != NULL ? s->fh_new (ino, was_changed) : vfs_s_new_fh (ino, was_changed);
1368 
1369     if (IS_LINEAR (flags))
1370     {
1371         if (s->linear_start != NULL)
1372         {
1373             vfs_print_message ("%s", _("Starting linear transfer..."));
1374             fh->linear = LS_LINEAR_PREOPEN;
1375         }
1376     }
1377     else
1378     {
1379         if (s->fh_open != NULL && s->fh_open (me, fh, flags, mode) != 0)
1380         {
1381             vfs_s_free_fh (s, fh);
1382             return NULL;
1383         }
1384     }
1385 
1386     if ((VFS_CLASS (s)->flags & VFSF_USETMP) != 0 && fh->ino->localname != NULL)
1387     {
1388         fh->handle = open (fh->ino->localname, NO_LINEAR (flags), mode);
1389         if (fh->handle == -1)
1390         {
1391             vfs_s_free_fh (s, fh);
1392             me->verrno = errno;
1393             return NULL;
1394         }
1395     }
1396 
1397     /* i.e. we had no open files and now we have one */
1398     vfs_rmstamp (me, (vfsid) super);
1399     super->fd_usage++;
1400     fh->ino->st.st_nlink++;
1401     return fh;
1402 }
1403 
1404 /* --------------------------------------------------------------------------------------------- */
1405 
1406 int
1407 vfs_s_stat (const vfs_path_t * vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
1408 {
1409     return vfs_s_internal_stat (vpath, buf, FL_FOLLOW);
1410 }
1411 
1412 /* --------------------------------------------------------------------------------------------- */
1413 
1414 int
1415 vfs_s_lstat (const vfs_path_t * vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
1416 {
1417     return vfs_s_internal_stat (vpath, buf, FL_NONE);
1418 }
1419 
1420 /* --------------------------------------------------------------------------------------------- */
1421 
1422 int
1423 vfs_s_fstat (void *fh, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
1424 {
1425     *buf = VFS_FILE_HANDLER (fh)->ino->st;
1426     return 0;
1427 }
1428 
1429 /* --------------------------------------------------------------------------------------------- */
1430 
1431 int
1432 vfs_s_retrieve_file (struct vfs_class *me, struct vfs_s_inode *ino)
     /* [previous][next][first][last][top][bottom][index][help]  */
1433 {
1434     /* If you want reget, you'll have to open file with O_LINEAR */
1435     off_t total = 0;
1436     char buffer[BUF_8K];
1437     int handle;
1438     ssize_t n;
1439     off_t stat_size = ino->st.st_size;
1440     vfs_file_handler_t *fh = NULL;
1441     vfs_path_t *tmp_vpath;
1442     struct vfs_s_subclass *s = VFS_SUBCLASS (me);
1443 
1444     if ((me->flags & VFSF_USETMP) == 0)
1445         return (-1);
1446 
1447     handle = vfs_mkstemps (&tmp_vpath, me->name, ino->ent->name);
1448     ino->localname = vfs_path_free (tmp_vpath, FALSE);
1449     if (handle == -1)
1450     {
1451         me->verrno = errno;
1452         goto error_4;
1453     }
1454 
1455     fh = s->fh_new != NULL ? s->fh_new (ino, FALSE) : vfs_s_new_fh (ino, FALSE);
1456 
1457     if (s->linear_start (me, fh, 0) == 0)
1458         goto error_3;
1459 
1460     /* Clear the interrupt status */
1461     tty_got_interrupt ();
1462     tty_enable_interrupt_key ();
1463 
1464     while ((n = s->linear_read (me, fh, buffer, sizeof (buffer))) != 0)
1465     {
1466         int t;
1467 
1468         if (n < 0)
1469             goto error_1;
1470 
1471         total += n;
1472         vfs_s_print_stats (me->name, _("Getting file"), ino->ent->name, total, stat_size);
1473 
1474         if (tty_got_interrupt ())
1475             goto error_1;
1476 
1477         t = write (handle, buffer, n);
1478         if (t != n)
1479         {
1480             if (t == -1)
1481                 me->verrno = errno;
1482             goto error_1;
1483         }
1484     }
1485     s->linear_close (me, fh);
1486     close (handle);
1487 
1488     tty_disable_interrupt_key ();
1489     vfs_s_free_fh (s, fh);
1490     return 0;
1491 
1492   error_1:
1493     s->linear_close (me, fh);
1494   error_3:
1495     tty_disable_interrupt_key ();
1496     close (handle);
1497     unlink (ino->localname);
1498   error_4:
1499     MC_PTR_FREE (ino->localname);
1500     if (fh != NULL)
1501         vfs_s_free_fh (s, fh);
1502     return (-1);
1503 }
1504 
1505 /* --------------------------------------------------------------------------------------------- */
1506 /* ----------------------------- Stamping support -------------------------- */
1507 
1508 /* Initialize one of our subclasses - fill common functions */
1509 void
1510 vfs_init_class (struct vfs_class *vclass, const char *name, vfs_flags_t flags, const char *prefix)
     /* [previous][next][first][last][top][bottom][index][help]  */
1511 {
1512     memset (vclass, 0, sizeof (struct vfs_class));
1513 
1514     vclass->name = name;
1515     vclass->flags = flags;
1516     vclass->prefix = prefix;
1517 
1518     vclass->fill_names = vfs_s_fill_names;
1519     vclass->open = vfs_s_open;
1520     vclass->close = vfs_s_close;
1521     vclass->read = vfs_s_read;
1522     if ((vclass->flags & VFSF_READONLY) == 0)
1523         vclass->write = vfs_s_write;
1524     vclass->opendir = vfs_s_opendir;
1525     vclass->readdir = vfs_s_readdir;
1526     vclass->closedir = vfs_s_closedir;
1527     vclass->stat = vfs_s_stat;
1528     vclass->lstat = vfs_s_lstat;
1529     vclass->fstat = vfs_s_fstat;
1530     vclass->readlink = vfs_s_readlink;
1531     vclass->chdir = vfs_s_chdir;
1532     vclass->ferrno = vfs_s_ferrno;
1533     vclass->lseek = vfs_s_lseek;
1534     vclass->getid = vfs_s_getid;
1535     vclass->nothingisopen = vfs_s_nothingisopen;
1536     vclass->free = vfs_s_free;
1537     vclass->setctl = vfs_s_setctl;
1538     if ((vclass->flags & VFSF_USETMP) != 0)
1539     {
1540         vclass->getlocalcopy = vfs_s_getlocalcopy;
1541         vclass->ungetlocalcopy = vfs_s_ungetlocalcopy;
1542     }
1543 }
1544 
1545 /* --------------------------------------------------------------------------------------------- */
1546 
1547 void
1548 vfs_init_subclass (struct vfs_s_subclass *sub, const char *name, vfs_flags_t flags,
     /* [previous][next][first][last][top][bottom][index][help]  */
1549                    const char *prefix)
1550 {
1551     struct vfs_class *vclass = VFS_CLASS (sub);
1552     size_t len;
1553     char *start;
1554 
1555     vfs_init_class (vclass, name, flags, prefix);
1556 
1557     len = sizeof (struct vfs_s_subclass) - sizeof (struct vfs_class);
1558     start = (char *) sub + sizeof (struct vfs_class);
1559     memset (start, 0, len);
1560 
1561     if ((vclass->flags & VFSF_USETMP) != 0)
1562         sub->find_entry = vfs_s_find_entry_linear;
1563     else if ((vclass->flags & VFSF_REMOTE) != 0)
1564         sub->find_entry = vfs_s_find_entry_linear;
1565     else
1566         sub->find_entry = vfs_s_find_entry_tree;
1567     sub->dir_uptodate = vfs_s_dir_uptodate;
1568 }
1569 
1570 /* --------------------------------------------------------------------------------------------- */
1571 /** Find VFS id for given directory name */
1572 
1573 vfsid
1574 vfs_getid (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1575 {
1576     const struct vfs_class *me;
1577 
1578     me = vfs_path_get_last_path_vfs (vpath);
1579     if (me == NULL || me->getid == NULL)
1580         return NULL;
1581 
1582     return me->getid (vpath);
1583 }
1584 
1585 /* --------------------------------------------------------------------------------------------- */
1586 /* ----------- Utility functions for networked filesystems  -------------- */
1587 
1588 #ifdef ENABLE_VFS_NET
1589 int
1590 vfs_s_select_on_two (int fd1, int fd2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1591 {
1592     fd_set set;
1593     struct timeval time_out;
1594     int v;
1595     int maxfd = MAX (fd1, fd2) + 1;
1596 
1597     time_out.tv_sec = 1;
1598     time_out.tv_usec = 0;
1599     FD_ZERO (&set);
1600     FD_SET (fd1, &set);
1601     FD_SET (fd2, &set);
1602 
1603     v = select (maxfd, &set, 0, 0, &time_out);
1604     if (v <= 0)
1605         return v;
1606     if (FD_ISSET (fd1, &set))
1607         return 1;
1608     if (FD_ISSET (fd2, &set))
1609         return 2;
1610     return (-1);
1611 }
1612 
1613 /* --------------------------------------------------------------------------------------------- */
1614 
1615 int
1616 vfs_s_get_line (struct vfs_class *me, int sock, char *buf, int buf_len, char term)
     /* [previous][next][first][last][top][bottom][index][help]  */
1617 {
1618     FILE *logfile = me->logfile;
1619     int i;
1620     char c;
1621 
1622     for (i = 0; i < buf_len - 1; i++, buf++)
1623     {
1624         if (read (sock, buf, sizeof (char)) <= 0)
1625             return 0;
1626 
1627         if (logfile != NULL)
1628         {
1629             size_t ret1;
1630             int ret2;
1631 
1632             ret1 = fwrite (buf, 1, 1, logfile);
1633             ret2 = fflush (logfile);
1634             (void) ret1;
1635             (void) ret2;
1636         }
1637 
1638         if (*buf == term)
1639         {
1640             *buf = '\0';
1641             return 1;
1642         }
1643     }
1644 
1645     /* Line is too long - terminate buffer and discard the rest of line */
1646     *buf = '\0';
1647     while (read (sock, &c, sizeof (c)) > 0)
1648     {
1649         if (logfile != NULL)
1650         {
1651             size_t ret1;
1652             int ret2;
1653 
1654             ret1 = fwrite (&c, 1, 1, logfile);
1655             ret2 = fflush (logfile);
1656             (void) ret1;
1657             (void) ret2;
1658         }
1659         if (c == '\n')
1660             return 1;
1661     }
1662     return 0;
1663 }
1664 
1665 /* --------------------------------------------------------------------------------------------- */
1666 
1667 int
1668 vfs_s_get_line_interruptible (struct vfs_class *me, char *buffer, int size, int fd)
     /* [previous][next][first][last][top][bottom][index][help]  */
1669 {
1670     int i;
1671     int res = 0;
1672 
1673     (void) me;
1674 
1675     tty_enable_interrupt_key ();
1676 
1677     for (i = 0; i < size - 1; i++)
1678     {
1679         ssize_t n;
1680 
1681         n = read (fd, &buffer[i], 1);
1682         if (n == -1 && errno == EINTR)
1683         {
1684             buffer[i] = '\0';
1685             res = EINTR;
1686             goto ret;
1687         }
1688         if (n == 0)
1689         {
1690             buffer[i] = '\0';
1691             goto ret;
1692         }
1693         if (buffer[i] == '\n')
1694         {
1695             buffer[i] = '\0';
1696             res = 1;
1697             goto ret;
1698         }
1699     }
1700 
1701     buffer[size - 1] = '\0';
1702 
1703   ret:
1704     tty_disable_interrupt_key ();
1705 
1706     return res;
1707 }
1708 #endif /* ENABLE_VFS_NET */
1709 
1710 /* --------------------------------------------------------------------------------------------- */
1711 /**
1712  * Normalize filenames start position
1713  */
1714 
1715 void
1716 vfs_s_normalize_filename_leading_spaces (struct vfs_s_inode *root_inode, size_t final_num_spaces)
     /* [previous][next][first][last][top][bottom][index][help]  */
1717 {
1718     GList *iter;
1719 
1720     for (iter = g_queue_peek_head_link (root_inode->subdir); iter != NULL;
1721          iter = g_list_next (iter))
1722     {
1723         struct vfs_s_entry *entry = VFS_ENTRY (iter->data);
1724 
1725         if ((size_t) entry->leading_spaces > final_num_spaces)
1726         {
1727             char *source_name, *spacer;
1728 
1729             source_name = entry->name;
1730             spacer = g_strnfill ((size_t) entry->leading_spaces - final_num_spaces, ' ');
1731             entry->name = g_strconcat (spacer, source_name, (char *) NULL);
1732             g_free (spacer);
1733             g_free (source_name);
1734         }
1735 
1736         entry->leading_spaces = -1;
1737     }
1738 }
1739 
1740 /* --------------------------------------------------------------------------------------------- */

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