2 Virtual File System switch code
4 Copyright (C) 1995-2024
5 Free Software Foundation, Inc.
7 Written by: 1995 Miguel de Icaza
10 Slava Zanko <slavazanko@gmail.com>, 2011-2013
11 Andrew Borodin <aborodin@vmail.ru>, 2011-2022
13 This file is part of the Midnight Commander.
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.
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.
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
31 * \brief Source: Virtual File System switch code
32 * \author Miguel de Icaza
33 * \author Jakub Jelinek
34 * \author Pavel Machek
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.
40 * Namespace: exports *many* functions with vfs_ prefix; exports
41 * parse_ls_lga and friends which do not have that prefix.
50 #ifdef HAVE_LINUX_FS_H
52 #endif /* HAVE_LINUX_FS_H */
53 #ifdef HAVE_SYS_IOCTL_H
54 #include <sys/ioctl.h>
55 #endif /* HAVE_SYS_IOCTL_H */
56 #endif /* __linux__ */
58 #include "lib/global.h"
59 #include "lib/strutil.h"
61 #include "lib/widget.h" /* message() */
62 #include "lib/event.h"
65 #include "lib/charsets.h"
72 /* TODO: move it to the separate .h */
73 extern struct vfs_dirent
*mc_readdir_result
;
74 extern GPtrArray
*vfs__classes_list
;
75 extern GString
*vfs_str_buffer
;
76 extern vfs_class
*current_vfs
;
78 /*** global variables ****************************************************************************/
80 struct vfs_dirent
*mc_readdir_result
= NULL
;
81 GPtrArray
*vfs__classes_list
= NULL
;
82 GString
*vfs_str_buffer
= NULL
;
83 vfs_class
*current_vfs
= NULL
;
85 /*** file scope macro definitions ****************************************************************/
87 #define VFS_FIRST_HANDLE 100
89 /*** file scope type declarations ****************************************************************/
98 /*** forward declarations (file scope functions) *************************************************/
100 /*** file scope variables ************************************************************************/
102 /** They keep track of the current directory */
103 static vfs_path_t
*current_path
= NULL
;
105 static GPtrArray
*vfs_openfiles
= NULL
;
106 static long vfs_free_handle_list
= -1;
108 /* --------------------------------------------------------------------------------------------- */
109 /*** file scope functions ************************************************************************/
110 /* --------------------------------------------------------------------------------------------- */
111 /* now used only by vfs_translate_path, but could be used in other vfs
112 * plugin to automatic detect encoding
113 * path - path to translate
114 * size - how many bytes from path translate
115 * defcnv - converter, that is used as default, when path does not contain any
117 * buffer - used to store result of translation
121 _vfs_translate_path (const char *path
, int size
, GIConv defcnv
, GString
* buffer
)
123 estr_t state
= ESTR_SUCCESS
;
130 size
= (size
> 0) ? size
: (signed int) strlen (path
);
132 /* try found /#enc: */
133 semi
= g_strrstr_len (path
, size
, VFS_ENCODING_PREFIX
);
134 if (semi
!= NULL
&& (semi
== path
|| IS_PATH_SEP (semi
[-1])))
138 GIConv coder
= INVALID_CONV
;
141 /* first must be translated part before #enc: */
144 state
= _vfs_translate_path (path
, ms
, defcnv
, buffer
);
146 if (state
!= ESTR_SUCCESS
)
149 /* now can be translated part after #enc: */
150 semi
+= strlen (VFS_ENCODING_PREFIX
); /* skip "#enc:" */
151 slash
= strchr (semi
, PATH_SEP
);
152 /* ignore slashes after size; */
153 if (slash
- path
>= size
)
156 ms
= (slash
!= NULL
) ? slash
- semi
: (int) strlen (semi
);
157 ms
= MIN ((unsigned int) ms
, sizeof (encoding
) - 1);
158 /* limit encoding size (ms) to path size (size) */
159 if (semi
+ ms
> path
+ size
)
160 ms
= path
+ size
- semi
;
161 memcpy (encoding
, semi
, ms
);
164 if (is_supported_encoding (encoding
))
165 coder
= str_crt_conv_to (encoding
);
167 if (coder
!= INVALID_CONV
)
170 state
= str_vfs_convert_to (coder
, slash
+ 1, path
+ size
- slash
- 1, buffer
);
171 str_close_conv (coder
);
176 state
= ESTR_FAILURE
;
180 /* path can be translated whole at once */
181 state
= str_vfs_convert_to (defcnv
, path
, size
, buffer
);
187 g_string_assign (buffer
, path
);
188 #endif /* HAVE_CHARSET */
193 /* --------------------------------------------------------------------------------------------- */
195 static struct vfs_openfile
*
196 vfs_get_openfile (int handle
)
198 struct vfs_openfile
*h
;
200 if (handle
< VFS_FIRST_HANDLE
|| (guint
) (handle
- VFS_FIRST_HANDLE
) >= vfs_openfiles
->len
)
203 h
= (struct vfs_openfile
*) g_ptr_array_index (vfs_openfiles
, handle
- VFS_FIRST_HANDLE
);
207 g_assert (h
->handle
== handle
);
212 /* --------------------------------------------------------------------------------------------- */
215 vfs_test_current_dir (const vfs_path_t
* vpath
)
217 struct stat my_stat
, my_stat2
;
219 return (mc_global
.vfs
.cd_symlinks
&& mc_stat (vpath
, &my_stat
) == 0
220 && mc_stat (vfs_get_raw_current_dir (), &my_stat2
) == 0
221 && my_stat
.st_ino
== my_stat2
.st_ino
&& my_stat
.st_dev
== my_stat2
.st_dev
);
225 /* --------------------------------------------------------------------------------------------- */
226 /*** public functions ****************************************************************************/
227 /* --------------------------------------------------------------------------------------------- */
228 /** Free open file data for given file handle */
231 vfs_free_handle (int handle
)
233 const int idx
= handle
- VFS_FIRST_HANDLE
;
235 if (handle
>= VFS_FIRST_HANDLE
&& (guint
) idx
< vfs_openfiles
->len
)
237 struct vfs_openfile
*h
;
239 h
= (struct vfs_openfile
*) g_ptr_array_index (vfs_openfiles
, idx
);
241 g_ptr_array_index (vfs_openfiles
, idx
) = (void *) vfs_free_handle_list
;
242 vfs_free_handle_list
= idx
;
247 /* --------------------------------------------------------------------------------------------- */
248 /** Find VFS class by file handle */
251 vfs_class_find_by_handle (int handle
, void **fsinfo
)
253 struct vfs_openfile
*h
;
255 h
= vfs_get_openfile (handle
);
266 /* --------------------------------------------------------------------------------------------- */
269 * Create new VFS handle and put it to the list
273 vfs_new_handle (struct vfs_class
*vclass
, void *fsinfo
)
275 struct vfs_openfile
*h
;
277 h
= g_new (struct vfs_openfile
, 1);
281 /* Allocate the first free handle */
282 h
->handle
= vfs_free_handle_list
;
285 /* No free allocated handles, allocate one */
286 h
->handle
= vfs_openfiles
->len
;
287 g_ptr_array_add (vfs_openfiles
, h
);
291 vfs_free_handle_list
= (long) g_ptr_array_index (vfs_openfiles
, vfs_free_handle_list
);
292 g_ptr_array_index (vfs_openfiles
, h
->handle
) = h
;
295 h
->handle
+= VFS_FIRST_HANDLE
;
299 /* --------------------------------------------------------------------------------------------- */
302 vfs_ferrno (struct vfs_class
*vfs
)
304 return vfs
->ferrno
? (*vfs
->ferrno
) (vfs
) : E_UNKNOWN
;
305 /* Hope that error message is obscure enough ;-) */
308 /* --------------------------------------------------------------------------------------------- */
311 vfs_register_class (struct vfs_class
* vfs
)
313 if (vfs
->init
!= NULL
) /* vfs has own initialization function */
314 if (!vfs
->init (vfs
)) /* but it failed */
317 g_ptr_array_add (vfs__classes_list
, vfs
);
322 /* --------------------------------------------------------------------------------------------- */
325 vfs_unregister_class (struct vfs_class
*vfs
)
327 if (vfs
->done
!= NULL
)
330 g_ptr_array_remove (vfs__classes_list
, vfs
);
333 /* --------------------------------------------------------------------------------------------- */
334 /** Strip known vfs suffixes from a filename (possible improvement: strip
335 * suffix from last path component).
336 * \return a malloced string which has to be freed.
340 vfs_strip_suffix_from_filename (const char *filename
)
344 if (filename
== NULL
)
345 vfs_die ("vfs_strip_suffix_from_path got NULL: impossible");
347 p
= g_strdup (filename
);
348 semi
= g_strrstr (p
, VFS_PATH_URL_DELIMITER
);
354 vfs_prefix
= strrchr (p
, PATH_SEP
);
355 if (vfs_prefix
== NULL
)
356 *semi
= *VFS_PATH_URL_DELIMITER
;
364 /* --------------------------------------------------------------------------------------------- */
367 vfs_translate_path (const char *path
)
371 g_string_set_size (vfs_str_buffer
, 0);
372 state
= _vfs_translate_path (path
, -1, str_cnv_from_term
, vfs_str_buffer
);
373 return (state
!= ESTR_FAILURE
) ? vfs_str_buffer
->str
: NULL
;
376 /* --------------------------------------------------------------------------------------------- */
379 vfs_translate_path_n (const char *path
)
383 result
= vfs_translate_path (path
);
384 return g_strdup (result
);
387 /* --------------------------------------------------------------------------------------------- */
389 * Get current directory without any OS calls.
391 * @return string contains current path
395 vfs_get_current_dir (void)
397 return current_path
->str
;
400 /* --------------------------------------------------------------------------------------------- */
402 * Get current directory without any OS calls.
404 * @return newly allocated string contains current path
408 vfs_get_current_dir_n (void)
410 return g_strdup (current_path
->str
);
413 /* --------------------------------------------------------------------------------------------- */
415 * Get raw current directory object without any OS calls.
417 * @return object contain current path
421 vfs_get_raw_current_dir (void)
426 /* --------------------------------------------------------------------------------------------- */
428 * Set current directory object.
430 * @param vpath new path
433 vfs_set_raw_current_dir (const vfs_path_t
* vpath
)
435 vfs_path_free (current_path
, TRUE
);
436 current_path
= (vfs_path_t
*) vpath
;
439 /* --------------------------------------------------------------------------------------------- */
440 /* Return TRUE is the current VFS class is local */
443 vfs_current_is_local (void)
445 return (current_vfs
->flags
& VFSF_LOCAL
) != 0;
448 /* --------------------------------------------------------------------------------------------- */
449 /* Return flags of the VFS class of the given filename */
452 vfs_file_class_flags (const vfs_path_t
* vpath
)
454 const vfs_path_element_t
*path_element
;
456 path_element
= vfs_path_get_by_index (vpath
, -1);
457 if (!vfs_path_element_valid (path_element
))
460 return path_element
->class->flags
;
463 /* --------------------------------------------------------------------------------------------- */
468 /* create the VFS handle arrays */
469 vfs__classes_list
= g_ptr_array_new ();
471 /* create the VFS handle array */
472 vfs_openfiles
= g_ptr_array_new ();
474 vfs_str_buffer
= g_string_new ("");
476 mc_readdir_result
= vfs_dirent_init (NULL
, "", -1);
479 /* --------------------------------------------------------------------------------------------- */
482 vfs_setup_work_dir (void)
486 /* FIXME: is we really need for this check? */
488 if (strlen (current_dir) > MC_MAXPATHLEN - 2)
489 vfs_die ("Current dir too long.\n");
492 current_vfs
= VFS_CLASS (vfs_path_get_last_path_vfs (current_path
));
495 /* --------------------------------------------------------------------------------------------- */
504 vfs_set_raw_current_dir (NULL
);
506 for (i
= 0; i
< vfs__classes_list
->len
; i
++)
508 struct vfs_class
*vfs
= VFS_CLASS (g_ptr_array_index (vfs__classes_list
, i
));
510 if (vfs
->done
!= NULL
)
514 /* NULL-ize pointers to make unit tests happy */
515 g_ptr_array_free (vfs_openfiles
, TRUE
);
516 vfs_openfiles
= NULL
;
517 g_ptr_array_free (vfs__classes_list
, TRUE
);
518 vfs__classes_list
= NULL
;
519 g_string_free (vfs_str_buffer
, TRUE
);
520 vfs_str_buffer
= NULL
;
522 vfs_free_handle_list
= -1;
523 vfs_dirent_free (mc_readdir_result
);
524 mc_readdir_result
= NULL
;
527 /* --------------------------------------------------------------------------------------------- */
529 * Init or create vfs_dirent structure
531 * @d vfs_dirent structure to init. If NULL, new structure is created.
533 * @ino file inode number
535 * @return pointer to d if d isn't NULL, or pointer to newly created structure.
539 vfs_dirent_init (struct vfs_dirent
*d
, const char *fname
, ino_t ino
)
541 struct vfs_dirent
*ret
= d
;
544 ret
= g_new0 (struct vfs_dirent
, 1);
546 if (ret
->d_name_str
== NULL
)
547 ret
->d_name_str
= g_string_sized_new (MC_MAXFILENAMELEN
);
549 vfs_dirent_assign (ret
, fname
, ino
);
554 /* --------------------------------------------------------------------------------------------- */
556 * Assign members of vfs_dirent structure
558 * @d vfs_dirent structure for assignment
560 * @ino file inode number
564 vfs_dirent_assign (struct vfs_dirent
*d
, const char *fname
, ino_t ino
)
566 g_string_assign (d
->d_name_str
, fname
);
567 d
->d_name
= d
->d_name_str
->str
;
571 /* --------------------------------------------------------------------------------------------- */
573 * Destroy vfs_dirent structure
575 * @d vfs_dirent structure to destroy.
579 vfs_dirent_free (struct vfs_dirent
*d
)
581 g_string_free (d
->d_name_str
, TRUE
);
585 /* --------------------------------------------------------------------------------------------- */
588 * These ones grab information from the VFS
589 * and handles them to an upper layer
593 vfs_fill_names (fill_names_f func
)
597 for (i
= 0; i
< vfs__classes_list
->len
; i
++)
599 struct vfs_class
*vfs
= VFS_CLASS (g_ptr_array_index (vfs__classes_list
, i
));
601 if (vfs
->fill_names
!= NULL
)
602 vfs
->fill_names (vfs
, func
);
606 /* --------------------------------------------------------------------------------------------- */
609 vfs_file_is_local (const vfs_path_t
* vpath
)
611 return (vfs_file_class_flags (vpath
) & VFSF_LOCAL
) != 0;
614 /* --------------------------------------------------------------------------------------------- */
617 vfs_print_message (const char *msg
, ...)
619 ev_vfs_print_message_t event_data
;
623 event_data
.msg
= g_strdup_vprintf (msg
, ap
);
626 mc_event_raise (MCEVENT_GROUP_CORE
, "vfs_print_message", (gpointer
) & event_data
);
629 /* --------------------------------------------------------------------------------------------- */
631 * If it's local, reread the current directory
639 vfs_path_t
*tmp_vpath
;
640 const struct vfs_class
*me
;
642 if (vfs_get_raw_current_dir () == NULL
)
644 current_dir
= g_get_current_dir ();
645 vfs_set_raw_current_dir (vfs_path_from_str (current_dir
));
646 g_free (current_dir
);
648 current_dir
= getenv ("PWD");
649 tmp_vpath
= vfs_path_from_str (current_dir
);
651 if (tmp_vpath
!= NULL
)
653 if (vfs_test_current_dir (tmp_vpath
))
654 vfs_set_raw_current_dir (tmp_vpath
);
656 vfs_path_free (tmp_vpath
, TRUE
);
660 me
= vfs_path_get_last_path_vfs (vfs_get_raw_current_dir ());
661 if ((me
->flags
& VFSF_LOCAL
) != 0)
663 current_dir
= g_get_current_dir ();
664 tmp_vpath
= vfs_path_from_str (current_dir
);
665 g_free (current_dir
);
667 if (tmp_vpath
!= NULL
)
669 /* One of directories in the path is not readable */
671 /* Check if it is O.K. to use the current_dir */
672 if (!vfs_test_current_dir (tmp_vpath
))
673 vfs_set_raw_current_dir (tmp_vpath
);
675 vfs_path_free (tmp_vpath
, TRUE
);
680 /* --------------------------------------------------------------------------------------------- */
682 * Return current directory. If it's local, reread the current directory
690 return vfs_get_current_dir_n ();
693 /* --------------------------------------------------------------------------------------------- */
695 * Preallocate space for file in new place for ensure that file
696 * will be fully copied with less fragmentation.
698 * @param dest_vfs_fd mc VFS file handler
699 * @param src_fsize source file size
700 * @param dest_fsize destination file size (if destination exists, otherwise should be 0)
702 * @return 0 if success and non-zero otherwise.
703 * Note: function doesn't touch errno global variable.
707 vfs_preallocate (int dest_vfs_fd
, off_t src_fsize
, off_t dest_fsize
)
709 #ifndef HAVE_POSIX_FALLOCATE
715 #else /* HAVE_POSIX_FALLOCATE */
716 void *dest_fd
= NULL
;
717 struct vfs_class
*dest_class
;
722 dest_class
= vfs_class_find_by_handle (dest_vfs_fd
, &dest_fd
);
723 if ((dest_class
->flags
& VFSF_LOCAL
) == 0 || dest_fd
== NULL
)
726 return posix_fallocate (*(int *) dest_fd
, dest_fsize
, src_fsize
- dest_fsize
);
728 #endif /* HAVE_POSIX_FALLOCATE */
731 /* --------------------------------------------------------------------------------------------- */
734 vfs_clone_file (int dest_vfs_fd
, int src_vfs_fd
)
737 void *dest_fd
= NULL
;
739 struct vfs_class
*dest_class
;
740 struct vfs_class
*src_class
;
742 dest_class
= vfs_class_find_by_handle (dest_vfs_fd
, &dest_fd
);
743 if ((dest_class
->flags
& VFSF_LOCAL
) == 0)
754 src_class
= vfs_class_find_by_handle (src_vfs_fd
, &src_fd
);
755 if ((src_class
->flags
& VFSF_LOCAL
) == 0)
766 return ioctl (*(int *) dest_fd
, FICLONE
, *(int *) src_fd
);
775 /* --------------------------------------------------------------------------------------------- */