Manual pages: mcmcdiffmceditmcview

root/lib/vfs/vfs.c

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

DEFINITIONS

This source file includes following definitions.
  1. _vfs_translate_path
  2. vfs_get_openfile
  3. vfs_test_current_dir
  4. vfs_free_handle
  5. vfs_class_find_by_handle
  6. vfs_new_handle
  7. vfs_ferrno
  8. vfs_register_class
  9. vfs_unregister_class
  10. vfs_strip_suffix_from_filename
  11. vfs_translate_path
  12. vfs_translate_path_n
  13. vfs_get_current_dir
  14. vfs_get_current_dir_n
  15. vfs_get_raw_current_dir
  16. vfs_set_raw_current_dir
  17. vfs_current_is_local
  18. vfs_file_class_flags
  19. vfs_init
  20. vfs_setup_work_dir
  21. vfs_shut
  22. vfs_dirent_init
  23. vfs_dirent_assign
  24. vfs_dirent_free
  25. vfs_fill_names
  26. vfs_file_is_local
  27. vfs_print_message
  28. vfs_setup_cwd
  29. vfs_get_cwd
  30. vfs_preallocate
  31. vfs_clone_file
  32. vfs_clone_file_by_path

   1 /*
   2    Virtual File System switch code
   3 
   4    Copyright (C) 1995-2026
   5    Free Software Foundation, Inc.
   6 
   7    Written by: 1995 Miguel de Icaza
   8    Jakub Jelinek, 1995
   9    Pavel Machek, 1998
  10    Slava Zanko <slavazanko@gmail.com>, 2011-2013
  11    Andrew Borodin <aborodin@vmail.ru>, 2011-2022
  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 <https://www.gnu.org/licenses/>.
  27  */
  28 
  29 /**
  30  * \file
  31  * \brief Source: Virtual File System switch code
  32  * \author Miguel de Icaza
  33  * \author Jakub Jelinek
  34  * \author Pavel Machek
  35  * \date 1995, 1998
  36  * \warning functions like extfs_lstat() have right to destroy any
  37  * strings you pass to them. This is actually ok as you g_strdup what
  38  * you are passing to them, anyway; still, beware.
  39  *
  40  * Namespace: exports *many* functions with vfs_ prefix; exports
  41  * parse_ls_lga and friends which do not have that prefix.
  42  */
  43 
  44 #include <config.h>
  45 
  46 #include <errno.h>
  47 #include <stdlib.h>
  48 
  49 #ifdef HAVE_FICLONERANGE
  50 #include <linux/fs.h>   // FICLONERANGE
  51 #include <sys/ioctl.h>  // ioctl()
  52 #elif defined(HAVE_COPY_FILE_RANGE)
  53 #include <unistd.h>  // COPY_FILE_RANGE_CLONE
  54 #elif defined(HAVE_SYS_CLONEFILE_H)
  55 #include <sys/clonefile.h>  // CLONE_NOOWNERCOPY
  56 #elif defined(HAVE_REFLINK)
  57 #include <unistd.h>  // reflink()
  58 #endif
  59 
  60 #include "lib/global.h"
  61 #include "lib/strutil.h"
  62 #include "lib/util.h"
  63 #include "lib/widget.h"  // message()
  64 #include "lib/event.h"
  65 #include "lib/charsets.h"
  66 
  67 #include "vfs.h"
  68 #include "utilvfs.h"
  69 #include "gc.h"
  70 
  71 /* TODO: move it to the separate .h */
  72 extern struct vfs_dirent *mc_readdir_result;
  73 extern GPtrArray *vfs__classes_list;
  74 extern GString *vfs_str_buffer;
  75 extern vfs_class *current_vfs;
  76 
  77 /*** global variables ****************************************************************************/
  78 
  79 MC_MOCKABLE struct vfs_dirent *mc_readdir_result = NULL;
  80 MC_MOCKABLE GPtrArray *vfs__classes_list = NULL;
  81 MC_MOCKABLE GString *vfs_str_buffer = NULL;
  82 MC_MOCKABLE vfs_class *current_vfs = NULL;
  83 
  84 /*** file scope macro definitions ****************************************************************/
  85 
  86 #define VFS_FIRST_HANDLE 100
  87 
  88 /*** file scope type declarations ****************************************************************/
  89 
  90 struct vfs_openfile
  91 {
  92     int handle;
  93     vfs_class *vclass;
  94     void *fsinfo;
  95 };
  96 
  97 /*** forward declarations (file scope functions) *************************************************/
  98 
  99 /*** file scope variables ************************************************************************/
 100 
 101 /** They keep track of the current directory */
 102 static vfs_path_t *current_path = NULL;
 103 
 104 static GPtrArray *vfs_openfiles = NULL;
 105 static long vfs_free_handle_list = -1;
 106 
 107 /* --------------------------------------------------------------------------------------------- */
 108 /*** file scope functions ************************************************************************/
 109 /* --------------------------------------------------------------------------------------------- */
 110 /* now used only by vfs_translate_path, but could be used in other vfs
 111  * plugin to automatic detect encoding
 112  * path - path to translate
 113  * size - how many bytes from path translate
 114  * defcnv - converter, that is used as default, when path does not contain any
 115  *          #enc: substring
 116  * buffer - used to store result of translation
 117  */
 118 
 119 static estr_t
 120 _vfs_translate_path (const char *path, const long size, GIConv defcnv, GString *buffer)
     /* [previous][next][first][last][top][bottom][index][help]  */
 121 {
 122     estr_t state = ESTR_SUCCESS;
 123     const char *semi;
 124 
 125     if (size == 0)
 126         return ESTR_SUCCESS;
 127 
 128     const long sz = size > 0 ? size : (long) strlen (path);
 129 
 130     // try to find /#enc:
 131     semi = g_strrstr_len (path, sz, VFS_ENCODING_PREFIX);
 132     if (semi != NULL && (semi == path || IS_PATH_SEP (semi[-1])))
 133     {
 134         char encoding[16];
 135         const char *slash;
 136         GIConv coder = INVALID_CONV;
 137         long ms;
 138 
 139         // first must be translated part before #enc:
 140         ms = semi - path;
 141         state = _vfs_translate_path (path, ms, defcnv, buffer);
 142         if (state != ESTR_SUCCESS)
 143             return state;
 144 
 145         // now can be translated part after #enc:
 146         semi += strlen (VFS_ENCODING_PREFIX);  // skip "#enc:"
 147         slash = strchr (semi, PATH_SEP);
 148         // ignore slashes after sz
 149         if (slash - path >= sz)
 150             slash = NULL;
 151 
 152         ms = slash != NULL ? slash - semi : (long) strlen (semi);
 153         ms = MIN (ms, (long) sizeof (encoding) - 1);
 154         // limit encoding size (ms) to path size (sz)
 155         if (semi + ms > path + sz)
 156             ms = path + sz - semi;
 157         memcpy (encoding, semi, ms);
 158         encoding[ms] = '\0';
 159 
 160         if (is_supported_encoding (encoding))
 161             coder = str_crt_conv_to (encoding);
 162 
 163         if (coder == INVALID_CONV)
 164         {
 165             errno = EINVAL;
 166             state = ESTR_FAILURE;
 167         }
 168         else
 169         {
 170             if (slash != NULL)
 171                 state = str_vfs_convert_to (coder, slash + 1, path + sz - slash - 1, buffer);
 172             str_close_conv (coder);
 173         }
 174     }
 175     else
 176     {
 177         // path can be translated whole at once
 178         state = str_vfs_convert_to (defcnv, path, sz, buffer);
 179     }
 180 
 181     return state;
 182 }
 183 
 184 /* --------------------------------------------------------------------------------------------- */
 185 
 186 static struct vfs_openfile *
 187 vfs_get_openfile (int handle)
     /* [previous][next][first][last][top][bottom][index][help]  */
 188 {
 189     struct vfs_openfile *h;
 190 
 191     if (handle < VFS_FIRST_HANDLE || (guint) (handle - VFS_FIRST_HANDLE) >= vfs_openfiles->len)
 192         return NULL;
 193 
 194     h = (struct vfs_openfile *) g_ptr_array_index (vfs_openfiles, handle - VFS_FIRST_HANDLE);
 195     if (h != NULL)
 196         g_assert (h->handle == handle);
 197 
 198     return h;
 199 }
 200 
 201 /* --------------------------------------------------------------------------------------------- */
 202 
 203 static gboolean
 204 vfs_test_current_dir (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 205 {
 206     struct stat my_stat, my_stat2;
 207 
 208     return (mc_global.vfs.cd_symlinks && mc_stat (vpath, &my_stat) == 0
 209             && mc_stat (vfs_get_raw_current_dir (), &my_stat2) == 0
 210             && my_stat.st_ino == my_stat2.st_ino && my_stat.st_dev == my_stat2.st_dev);
 211 }
 212 
 213 /* --------------------------------------------------------------------------------------------- */
 214 /*** public functions ****************************************************************************/
 215 /* --------------------------------------------------------------------------------------------- */
 216 /** Free open file data for given file handle */
 217 
 218 void
 219 vfs_free_handle (int handle)
     /* [previous][next][first][last][top][bottom][index][help]  */
 220 {
 221     const int idx = handle - VFS_FIRST_HANDLE;
 222 
 223     if (handle >= VFS_FIRST_HANDLE && (guint) idx < vfs_openfiles->len)
 224     {
 225         struct vfs_openfile *h;
 226 
 227         h = (struct vfs_openfile *) g_ptr_array_index (vfs_openfiles, idx);
 228         g_free (h);
 229         g_ptr_array_index (vfs_openfiles, idx) = (void *) vfs_free_handle_list;
 230         vfs_free_handle_list = idx;
 231     }
 232 }
 233 
 234 /* --------------------------------------------------------------------------------------------- */
 235 /** Find VFS class by file handle */
 236 
 237 struct vfs_class *
 238 vfs_class_find_by_handle (int handle, void **fsinfo)
     /* [previous][next][first][last][top][bottom][index][help]  */
 239 {
 240     struct vfs_openfile *h;
 241 
 242     h = vfs_get_openfile (handle);
 243 
 244     if (h == NULL)
 245         return NULL;
 246 
 247     if (fsinfo != NULL)
 248         *fsinfo = h->fsinfo;
 249 
 250     return h->vclass;
 251 }
 252 
 253 /* --------------------------------------------------------------------------------------------- */
 254 
 255 /**
 256  * Create new VFS handle and put it to the list
 257  */
 258 
 259 int
 260 vfs_new_handle (struct vfs_class *vclass, void *fsinfo)
     /* [previous][next][first][last][top][bottom][index][help]  */
 261 {
 262     struct vfs_openfile *h;
 263 
 264     h = g_new (struct vfs_openfile, 1);
 265     h->fsinfo = fsinfo;
 266     h->vclass = vclass;
 267 
 268     // Allocate the first free handle
 269     h->handle = vfs_free_handle_list;
 270     if (h->handle == -1)
 271     {
 272         // No free allocated handles, allocate one
 273         h->handle = vfs_openfiles->len;
 274         g_ptr_array_add (vfs_openfiles, h);
 275     }
 276     else
 277     {
 278         vfs_free_handle_list = (long) g_ptr_array_index (vfs_openfiles, vfs_free_handle_list);
 279         g_ptr_array_index (vfs_openfiles, h->handle) = h;
 280     }
 281 
 282     h->handle += VFS_FIRST_HANDLE;
 283     return h->handle;
 284 }
 285 
 286 /* --------------------------------------------------------------------------------------------- */
 287 
 288 int
 289 vfs_ferrno (struct vfs_class *vfs)
     /* [previous][next][first][last][top][bottom][index][help]  */
 290 {
 291     return vfs->ferrno != NULL ? vfs->ferrno (vfs) : E_UNKNOWN;
 292     // Hope that error message is obscure enough ;-)
 293 }
 294 
 295 /* --------------------------------------------------------------------------------------------- */
 296 
 297 gboolean
 298 vfs_register_class (struct vfs_class *vfs)
     /* [previous][next][first][last][top][bottom][index][help]  */
 299 {
 300     if (vfs->init != NULL)         // vfs has own initialization function
 301         if (vfs->init (vfs) == 0)  // but it failed
 302             return FALSE;
 303 
 304     g_ptr_array_add (vfs__classes_list, vfs);
 305 
 306     return TRUE;
 307 }
 308 
 309 /* --------------------------------------------------------------------------------------------- */
 310 
 311 void
 312 vfs_unregister_class (struct vfs_class *vfs)
     /* [previous][next][first][last][top][bottom][index][help]  */
 313 {
 314     if (vfs->done != NULL)
 315         vfs->done (vfs);
 316 
 317     g_ptr_array_remove (vfs__classes_list, vfs);
 318 }
 319 
 320 /* --------------------------------------------------------------------------------------------- */
 321 /** Strip known vfs suffixes from a filename (possible improvement: strip
 322  *  suffix from last path component).
 323  *  \return a malloced string which has to be freed.
 324  */
 325 
 326 char *
 327 vfs_strip_suffix_from_filename (const char *filename)
     /* [previous][next][first][last][top][bottom][index][help]  */
 328 {
 329     char *semi, *p;
 330 
 331     if (filename == NULL)
 332         vfs_die ("vfs_strip_suffix_from_path got NULL: impossible");
 333 
 334     p = g_strdup (filename);
 335     semi = g_strrstr (p, VFS_PATH_URL_DELIMITER);
 336     if (semi != NULL)
 337     {
 338         char *vfs_prefix;
 339 
 340         *semi = '\0';
 341         vfs_prefix = strrchr (p, PATH_SEP);
 342         if (vfs_prefix == NULL)
 343             *semi = *VFS_PATH_URL_DELIMITER;
 344         else
 345             *vfs_prefix = '\0';
 346     }
 347 
 348     return p;
 349 }
 350 
 351 /* --------------------------------------------------------------------------------------------- */
 352 
 353 const char *
 354 vfs_translate_path (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 355 {
 356     estr_t state;
 357 
 358     g_string_set_size (vfs_str_buffer, 0);
 359     state = _vfs_translate_path (path, -1, str_cnv_from_term, vfs_str_buffer);
 360     return (state != ESTR_FAILURE) ? vfs_str_buffer->str : NULL;
 361 }
 362 
 363 /* --------------------------------------------------------------------------------------------- */
 364 
 365 char *
 366 vfs_translate_path_n (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 367 {
 368     const char *result;
 369 
 370     result = vfs_translate_path (path);
 371     return g_strdup (result);
 372 }
 373 
 374 /* --------------------------------------------------------------------------------------------- */
 375 /**
 376  * Get current directory without any OS calls.
 377  *
 378  * @return string contains current path
 379  */
 380 
 381 const char *
 382 vfs_get_current_dir (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 383 {
 384     return current_path->str;
 385 }
 386 
 387 /* --------------------------------------------------------------------------------------------- */
 388 /**
 389  * Get current directory without any OS calls.
 390  *
 391  * @return newly allocated string contains current path
 392  */
 393 
 394 char *
 395 vfs_get_current_dir_n (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 396 {
 397     return g_strdup (current_path->str);
 398 }
 399 
 400 /* --------------------------------------------------------------------------------------------- */
 401 /**
 402  * Get raw current directory object without any OS calls.
 403  *
 404  * @return object contain current path
 405  */
 406 
 407 const vfs_path_t *
 408 vfs_get_raw_current_dir (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 409 {
 410     return current_path;
 411 }
 412 
 413 /* --------------------------------------------------------------------------------------------- */
 414 /**
 415  * Set current directory object.
 416  *
 417  * @param vpath new path
 418  */
 419 void
 420 vfs_set_raw_current_dir (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 421 {
 422     vfs_path_free (current_path, TRUE);
 423     current_path = (vfs_path_t *) vpath;
 424 }
 425 
 426 /* --------------------------------------------------------------------------------------------- */
 427 /* Return TRUE is the current VFS class is local */
 428 
 429 gboolean
 430 vfs_current_is_local (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 431 {
 432     return (current_vfs->flags & VFSF_LOCAL) != 0;
 433 }
 434 
 435 /* --------------------------------------------------------------------------------------------- */
 436 /* Return flags of the VFS class of the given filename */
 437 
 438 vfs_flags_t
 439 vfs_file_class_flags (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 440 {
 441     const vfs_path_element_t *path_element;
 442 
 443     path_element = vfs_path_get_by_index (vpath, -1);
 444     if (!vfs_path_element_valid (path_element))
 445         return VFSF_UNKNOWN;
 446 
 447     return path_element->class->flags;
 448 }
 449 
 450 /* --------------------------------------------------------------------------------------------- */
 451 
 452 void
 453 vfs_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 454 {
 455     // create the VFS handle arrays
 456     vfs__classes_list = g_ptr_array_new ();
 457 
 458     // create the VFS handle array
 459     vfs_openfiles = g_ptr_array_new ();
 460 
 461     vfs_str_buffer = g_string_new ("");
 462 
 463     mc_readdir_result = vfs_dirent_init (NULL, "", -1, DT_UNKNOWN);
 464 }
 465 
 466 /* --------------------------------------------------------------------------------------------- */
 467 
 468 void
 469 vfs_setup_work_dir (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 470 {
 471     vfs_setup_cwd ();
 472 
 473     // FIXME: is we really need for this check?
 474     /*
 475        if (strlen (current_dir) > MC_MAXPATHLEN - 2)
 476        vfs_die ("Current dir too long.\n");
 477      */
 478 
 479     current_vfs = VFS_CLASS (vfs_path_get_last_path_vfs (current_path));
 480 }
 481 
 482 /* --------------------------------------------------------------------------------------------- */
 483 
 484 void
 485 vfs_shut (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 486 {
 487     guint i;
 488 
 489     vfs_gc_done ();
 490 
 491     vfs_set_raw_current_dir (NULL);
 492 
 493     for (i = 0; i < vfs__classes_list->len; i++)
 494     {
 495         struct vfs_class *vfs = VFS_CLASS (g_ptr_array_index (vfs__classes_list, i));
 496 
 497         if (vfs->done != NULL)
 498             vfs->done (vfs);
 499     }
 500 
 501     // NULL-ize pointers to make unit tests happy
 502     g_ptr_array_free (vfs_openfiles, TRUE);
 503     vfs_openfiles = NULL;
 504     g_ptr_array_free (vfs__classes_list, TRUE);
 505     vfs__classes_list = NULL;
 506     g_string_free (vfs_str_buffer, TRUE);
 507     vfs_str_buffer = NULL;
 508     current_vfs = NULL;
 509     vfs_free_handle_list = -1;
 510     vfs_dirent_free (mc_readdir_result);
 511     mc_readdir_result = NULL;
 512 }
 513 
 514 /* --------------------------------------------------------------------------------------------- */
 515 /**
 516  * Init or create vfs_dirent structure
 517  *
 518  * @d vfs_dirent structure to init. If NULL, new structure is created.
 519  * @fname file name
 520  * @ino file inode number
 521  *
 522  * @return pointer to d if d isn't NULL, or pointer to newly created structure.
 523  */
 524 
 525 struct vfs_dirent *
 526 vfs_dirent_init (struct vfs_dirent *d, const char *fname, ino_t ino, unsigned char type)
     /* [previous][next][first][last][top][bottom][index][help]  */
 527 {
 528     struct vfs_dirent *ret = d;
 529 
 530     if (ret == NULL)
 531         ret = g_new0 (struct vfs_dirent, 1);
 532 
 533     if (ret->d_name_str == NULL)
 534         ret->d_name_str = g_string_sized_new (MC_MAXFILENAMELEN);
 535 
 536     vfs_dirent_assign (ret, fname, ino, type);
 537 
 538     return ret;
 539 }
 540 
 541 /* --------------------------------------------------------------------------------------------- */
 542 /**
 543  * Assign members of vfs_dirent structure
 544  *
 545  * @d vfs_dirent structure for assignment
 546  * @fname file name
 547  * @ino file inode number
 548  */
 549 
 550 void
 551 vfs_dirent_assign (struct vfs_dirent *d, const char *fname, ino_t ino, unsigned char type)
     /* [previous][next][first][last][top][bottom][index][help]  */
 552 {
 553     g_string_assign (d->d_name_str, fname);
 554     d->d_name = d->d_name_str->str;
 555     d->d_len = d->d_name_str->len;
 556     d->d_ino = ino;
 557     d->d_type = type;
 558 }
 559 
 560 /* --------------------------------------------------------------------------------------------- */
 561 /**
 562  * Destroy vfs_dirent structure
 563  *
 564  * @d vfs_dirent structure to destroy.
 565  */
 566 
 567 void
 568 vfs_dirent_free (struct vfs_dirent *d)
     /* [previous][next][first][last][top][bottom][index][help]  */
 569 {
 570     g_string_free (d->d_name_str, TRUE);
 571     g_free (d);
 572 }
 573 
 574 /* --------------------------------------------------------------------------------------------- */
 575 
 576 /**
 577  * These ones grab information from the VFS
 578  *  and handles them to an upper layer
 579  */
 580 
 581 void
 582 vfs_fill_names (fill_names_f func)
     /* [previous][next][first][last][top][bottom][index][help]  */
 583 {
 584     guint i;
 585 
 586     for (i = 0; i < vfs__classes_list->len; i++)
 587     {
 588         struct vfs_class *vfs = VFS_CLASS (g_ptr_array_index (vfs__classes_list, i));
 589 
 590         if (vfs->fill_names != NULL)
 591             vfs->fill_names (vfs, func);
 592     }
 593 }
 594 
 595 /* --------------------------------------------------------------------------------------------- */
 596 
 597 gboolean
 598 vfs_file_is_local (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 599 {
 600     return (vfs_file_class_flags (vpath) & VFSF_LOCAL) != 0;
 601 }
 602 
 603 /* --------------------------------------------------------------------------------------------- */
 604 
 605 void
 606 vfs_print_message (const char *msg, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
 607 {
 608     ev_vfs_print_message_t event_data;
 609     va_list ap;
 610 
 611     va_start (ap, msg);
 612     event_data.msg = g_strdup_vprintf (msg, ap);
 613     va_end (ap);
 614 
 615     mc_event_raise (MCEVENT_GROUP_CORE, "vfs_print_message", (gpointer) &event_data);
 616 }
 617 
 618 /* --------------------------------------------------------------------------------------------- */
 619 /**
 620  * If it's local, reread the current directory
 621  * from the OS.
 622  */
 623 
 624 void
 625 vfs_setup_cwd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 626 {
 627     char *current_dir;
 628     vfs_path_t *tmp_vpath;
 629     const struct vfs_class *me;
 630 
 631     if (vfs_get_raw_current_dir () == NULL)
 632     {
 633         current_dir = my_get_current_dir ();
 634         vfs_set_raw_current_dir (vfs_path_from_str (current_dir));
 635         g_free (current_dir);
 636 
 637         current_dir = getenv ("PWD");
 638         tmp_vpath = vfs_path_from_str (current_dir);
 639 
 640         if (tmp_vpath != NULL)
 641         {
 642             if (vfs_test_current_dir (tmp_vpath))
 643                 vfs_set_raw_current_dir (tmp_vpath);
 644             else
 645                 vfs_path_free (tmp_vpath, TRUE);
 646         }
 647     }
 648 
 649     me = vfs_path_get_last_path_vfs (vfs_get_raw_current_dir ());
 650     if ((me->flags & VFSF_LOCAL) != 0)
 651     {
 652         current_dir = my_get_current_dir ();
 653         tmp_vpath = vfs_path_from_str (current_dir);
 654         g_free (current_dir);
 655 
 656         if (tmp_vpath != NULL)
 657         {
 658             // One of directories in the path is not readable
 659 
 660             // Check if it is O.K. to use the current_dir
 661             if (!vfs_test_current_dir (tmp_vpath))
 662                 vfs_set_raw_current_dir (tmp_vpath);
 663             else
 664                 vfs_path_free (tmp_vpath, TRUE);
 665         }
 666     }
 667 }
 668 
 669 /* --------------------------------------------------------------------------------------------- */
 670 /**
 671  * Return current directory.  If it's local, reread the current directory
 672  * from the OS.
 673  */
 674 
 675 char *
 676 vfs_get_cwd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 677 {
 678     vfs_setup_cwd ();
 679     return vfs_get_current_dir_n ();
 680 }
 681 
 682 /* --------------------------------------------------------------------------------------------- */
 683 /**
 684  * Preallocate space for file in new place for ensure that file
 685  * will be fully copied with less fragmentation.
 686  *
 687  * @param dest_vfs_fd mc VFS file handler
 688  * @param src_fsize source file size
 689  * @param dest_fsize destination file size (if destination exists, otherwise should be 0)
 690  *
 691  * @return 0 if success and non-zero otherwise.
 692  * Note: function doesn't touch errno global variable.
 693  */
 694 
 695 int
 696 vfs_preallocate (int dest_vfs_fd, off_t src_fsize, off_t dest_fsize)
     /* [previous][next][first][last][top][bottom][index][help]  */
 697 {
 698 #ifndef HAVE_POSIX_FALLOCATE
 699     (void) dest_vfs_fd;
 700     (void) src_fsize;
 701     (void) dest_fsize;
 702     return 0;
 703 
 704 #else  // HAVE_POSIX_FALLOCATE
 705     void *dest_fd = NULL;
 706     struct vfs_class *dest_class;
 707 
 708     if (src_fsize == 0)
 709         return 0;
 710 
 711     dest_class = vfs_class_find_by_handle (dest_vfs_fd, &dest_fd);
 712     if ((dest_class->flags & VFSF_LOCAL) == 0 || dest_fd == NULL)
 713         return 0;
 714 
 715     return posix_fallocate (*(int *) dest_fd, dest_fsize, src_fsize - dest_fsize);
 716 
 717 #endif
 718 }
 719 
 720 /* --------------------------------------------------------------------------------------------- */
 721 
 722 int
 723 vfs_clone_file (int dest_vfs_fd, int src_vfs_fd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 724 {
 725 #ifdef HAVE_FILE_CLONING_BY_RANGE
 726     void *dest_fd = NULL;
 727     void *src_fd = NULL;
 728     struct vfs_class *dest_class;
 729     struct vfs_class *src_class;
 730     off_t in_offset, out_offset;
 731 
 732     dest_class = vfs_class_find_by_handle (dest_vfs_fd, &dest_fd);
 733     if ((dest_class->flags & VFSF_LOCAL) == 0)
 734     {
 735         errno = ENOTSUP;
 736         return (-1);
 737     }
 738     if (dest_fd == NULL)
 739     {
 740         errno = EBADF;
 741         return (-1);
 742     }
 743 
 744     src_class = vfs_class_find_by_handle (src_vfs_fd, &src_fd);
 745     if ((src_class->flags & VFSF_LOCAL) == 0)
 746     {
 747         errno = ENOTSUP;
 748         return (-1);
 749     }
 750     if (src_fd == NULL)
 751     {
 752         errno = EBADF;
 753         return (-1);
 754     }
 755 
 756     in_offset = mc_lseek (src_vfs_fd, 0, SEEK_CUR);
 757     if (in_offset < 0)
 758         return (-1);
 759     out_offset = mc_lseek (dest_vfs_fd, 0, SEEK_CUR);
 760     if (out_offset < 0)
 761         return (-1);
 762 
 763 #if defined(FICLONERANGE)
 764     {
 765         struct file_clone_range fcr = {
 766             .src_fd = *(int *) src_fd,
 767             .src_offset = in_offset,
 768             .src_length = 0,
 769             .dest_offset = out_offset,
 770         };
 771 
 772         return ioctl (*(int *) dest_fd, FICLONERANGE, &fcr);
 773     }
 774 #elif defined(COPY_FILE_RANGE_CLONE)
 775     {
 776         ssize_t result;
 777 
 778         do
 779         {
 780             result = copy_file_range (*(int *) src_fd, &in_offset, *(int *) dest_fd, &out_offset,
 781                                       SSIZE_MAX, COPY_FILE_RANGE_CLONE);
 782         }
 783         while (result > 0);
 784         return result;
 785     }
 786 #endif
 787 
 788 #else
 789     (void) dest_vfs_fd;
 790     (void) src_vfs_fd;
 791     errno = ENOTSUP;
 792     return (-1);
 793 #endif
 794 }
 795 
 796 /* --------------------------------------------------------------------------------------------- */
 797 
 798 int
 799 vfs_clone_file_by_path (const vfs_path_t *dest_vpath, const vfs_path_t *src_vpath,
     /* [previous][next][first][last][top][bottom][index][help]  */
 800                         gboolean preserve_uidgid)
 801 {
 802 #ifdef HAVE_FILE_CLONING_BY_PATH
 803     const char *src_path;
 804     const char *dest_path;
 805 
 806     if (!vfs_file_is_local (dest_vpath) || !vfs_file_is_local (src_vpath))
 807     {
 808         errno = ENOTSUP;
 809         return (-1);
 810     }
 811 
 812     src_path = vfs_path_get_last_path_str (src_vpath);
 813     dest_path = vfs_path_get_last_path_str (dest_vpath);
 814 
 815 #if defined(HAVE_SYS_CLONEFILE_H)
 816 #ifndef CLONE_NOOWNERCOPY  // macOS 10.13+
 817 #define CLONE_NOOWNERCOPY 0
 818 #endif
 819     return my_clonefile (src_path, dest_path, preserve_uidgid ? 0 : CLONE_NOOWNERCOPY);
 820 #elif defined(HAVE_REFLINK)
 821     return reflink (src_path, dest_path, preserve_uidgid);
 822 #endif
 823 
 824 #else
 825     (void) dest_vpath;
 826     (void) src_vpath;
 827     (void) preserve_uidgid;
 828     errno = ENOTSUP;
 829     return (-1);
 830 #endif
 831 }
 832 
 833 /* --------------------------------------------------------------------------------------------- */

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