2 Virtual File System: interface functions
4 Copyright (C) 2011-2024
5 Free Software Foundation, Inc.
8 Slava Zanko <slavazanko@gmail.com>, 2011, 2013
9 Andrew Borodin <aborodin@vmail.ru>, 2011-2022
11 This file is part of the Midnight Commander.
13 The Midnight Commander is free software: you can redistribute it
14 and/or modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation, either version 3 of the License,
16 or (at your option) any later version.
18 The Midnight Commander is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
29 * \brief Source: Virtual File System: path handlers
38 #include <stdlib.h> /* For atol() */
42 #include <sys/types.h>
44 #include <ctype.h> /* is_digit() */
51 #include "lib/global.h"
53 #include "lib/widget.h" /* message() */
54 #include "lib/strutil.h" /* str_crt_conv_from() */
61 #include "xdirentry.h"
63 /* TODO: move it to separate private .h */
64 extern GString
*vfs_str_buffer
;
65 extern vfs_class
*current_vfs
;
66 extern struct vfs_dirent
*mc_readdir_result
;
68 /*** global variables ****************************************************************************/
70 /*** file scope macro definitions ****************************************************************/
72 /*** file scope type declarations ****************************************************************/
74 /*** forward declarations (file scope functions) *************************************************/
76 /*** file scope variables ************************************************************************/
78 /* --------------------------------------------------------------------------------------------- */
79 /*** file scope functions ************************************************************************/
80 /* --------------------------------------------------------------------------------------------- */
83 mc_def_getlocalcopy (const vfs_path_t
*filename_vpath
)
85 vfs_path_t
*tmp_vpath
= NULL
;
88 char buffer
[BUF_1K
* 8];
91 fdin
= mc_open (filename_vpath
, O_RDONLY
| O_LINEAR
);
95 fdout
= vfs_mkstemps (&tmp_vpath
, "vfs", vfs_path_get_last_path_str (filename_vpath
));
99 while ((i
= mc_read (fdin
, buffer
, sizeof (buffer
))) > 0)
101 if (write (fdout
, buffer
, i
) != i
)
116 if (mc_stat (filename_vpath
, &mystat
) != -1)
117 mc_chmod (tmp_vpath
, mystat
.st_mode
);
122 vfs_path_free (tmp_vpath
, TRUE
);
130 /* --------------------------------------------------------------------------------------------- */
133 mc_def_ungetlocalcopy (const vfs_path_t
*filename_vpath
,
134 const vfs_path_t
*local_vpath
, gboolean has_changed
)
136 int fdin
= -1, fdout
= -1;
139 local
= vfs_path_get_last_path_str (local_vpath
);
143 char buffer
[BUF_1K
* 8];
146 if (vfs_path_get_last_path_vfs (filename_vpath
)->write
== NULL
)
149 fdin
= open (local
, O_RDONLY
);
152 fdout
= mc_open (filename_vpath
, O_WRONLY
| O_TRUNC
);
155 while ((i
= read (fdin
, buffer
, sizeof (buffer
))) > 0)
156 if (mc_write (fdout
, buffer
, (size_t) i
) != i
)
161 if (close (fdin
) == -1)
167 if (mc_close (fdout
) == -1)
177 message (D_ERROR
, _("Changes to file lost"), "%s", vfs_path_get_last_path_str (filename_vpath
));
186 /* --------------------------------------------------------------------------------------------- */
187 /*** public functions ****************************************************************************/
188 /* --------------------------------------------------------------------------------------------- */
191 mc_open (const vfs_path_t
*vpath
, int flags
, ...)
195 struct vfs_class
*me
;
200 /* Get the mode flag */
201 if ((flags
& O_CREAT
) != 0)
205 va_start (ap
, flags
);
206 /* We have to use PROMOTED_MODE_T instead of mode_t. Doing 'va_arg (ap, mode_t)'
207 * fails on systems where 'mode_t' is smaller than 'int' because of C's "default
208 * argument promotions". */
209 mode
= va_arg (ap
, PROMOTED_MODE_T
);
213 me
= VFS_CLASS (vfs_path_get_last_path_vfs (vpath
));
214 if (me
!= NULL
&& me
->open
!= NULL
)
218 /* open must be supported */
219 info
= me
->open (vpath
, flags
, mode
);
221 errno
= vfs_ferrno (me
);
223 result
= vfs_new_handle (me
, info
);
231 /* --------------------------------------------------------------------------------------------- */
235 #define MC_NAMEOP(name, inarg, callarg) \
236 int mc_##name inarg \
239 struct vfs_class *me; \
244 me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); \
248 result = me->name != NULL ? me->name callarg : -1; \
250 errno = me->name != NULL ? vfs_ferrno (me) : ENOTSUP; \
254 MC_NAMEOP (chmod
, (const vfs_path_t
*vpath
, mode_t mode
), (vpath
, mode
))
255 MC_NAMEOP (chown
, (const vfs_path_t
*vpath
, uid_t owner
, gid_t group
), (vpath
, owner
, group
))
256 MC_NAMEOP (fgetflags
, (const vfs_path_t
*vpath
, unsigned long *flags
), (vpath
, flags
))
257 MC_NAMEOP (fsetflags
, (const vfs_path_t
*vpath
, unsigned long flags
), (vpath
, flags
))
258 MC_NAMEOP (utime
, (const vfs_path_t
*vpath
, mc_timesbuf_t
* times
), (vpath
, times
))
259 MC_NAMEOP (readlink
, (const vfs_path_t
*vpath
, char *buf
, size_t bufsiz
), (vpath
, buf
, bufsiz
))
260 MC_NAMEOP (unlink
, (const vfs_path_t
*vpath
), (vpath
))
261 MC_NAMEOP (mkdir
, (const vfs_path_t
*vpath
, mode_t mode
), (vpath
, mode
))
262 MC_NAMEOP (rmdir
, (const vfs_path_t
*vpath
), (vpath
))
263 MC_NAMEOP (mknod
, (const vfs_path_t
*vpath
, mode_t mode
, dev_t dev
), (vpath
, mode
, dev
))
267 /* --------------------------------------------------------------------------------------------- */
270 mc_symlink (const vfs_path_t
*vpath1
, const vfs_path_t
*vpath2
)
274 if (vpath1
!= NULL
&& vpath2
!= NULL
)
276 struct vfs_class
*me
;
278 me
= VFS_CLASS (vfs_path_get_last_path_vfs (vpath2
));
281 result
= me
->symlink
!= NULL
? me
->symlink (vpath1
, vpath2
) : -1;
283 errno
= me
->symlink
!= NULL
? vfs_ferrno (me
) : ENOTSUP
;
289 /* --------------------------------------------------------------------------------------------- */
293 #define MC_HANDLEOP(rettype, name, inarg, callarg) \
294 rettype mc_##name inarg \
296 struct vfs_class *vfs; \
297 void *fsinfo = NULL; \
303 vfs = vfs_class_find_by_handle (handle, &fsinfo); \
307 result = vfs->name != NULL ? vfs->name callarg : -1; \
309 errno = vfs->name != NULL ? vfs_ferrno (vfs) : ENOTSUP; \
313 MC_HANDLEOP (ssize_t
, read
, (int handle
, void *buf
, size_t count
), (fsinfo
, buf
, count
))
314 MC_HANDLEOP (ssize_t
, write
, (int handle
, const void *buf
, size_t count
), (fsinfo
, buf
, count
))
315 MC_HANDLEOP (int, fstat
, (int handle
, struct stat
*buf
), (fsinfo
, buf
))
317 /* --------------------------------------------------------------------------------------------- */
319 #define MC_RENAMEOP(name) \
320 int mc_##name (const vfs_path_t *vpath1, const vfs_path_t *vpath2) \
323 struct vfs_class *me1, *me2; \
325 if (vpath1 == NULL || vpath2 == NULL) \
328 me1 = VFS_CLASS (vfs_path_get_last_path_vfs (vpath1)); \
329 me2 = VFS_CLASS (vfs_path_get_last_path_vfs (vpath2)); \
331 if (me1 == NULL || me2 == NULL || me1 != me2) \
337 result = me1->name != NULL ? me1->name (vpath1, vpath2) : -1; \
339 errno = me1->name != NULL ? vfs_ferrno (me1) : ENOTSUP; \
348 /* --------------------------------------------------------------------------------------------- */
351 mc_ctl (int handle
, int ctlop
, void *arg
)
353 struct vfs_class
*vfs
;
356 vfs
= vfs_class_find_by_handle (handle
, &fsinfo
);
358 return (vfs
== NULL
|| vfs
->ctl
== NULL
) ? 0 : vfs
->ctl (fsinfo
, ctlop
, arg
);
361 /* --------------------------------------------------------------------------------------------- */
364 mc_setctl (const vfs_path_t
*vpath
, int ctlop
, void *arg
)
367 struct vfs_class
*me
;
370 vfs_die ("You don't want to pass NULL to mc_setctl.");
372 me
= VFS_CLASS (vfs_path_get_last_path_vfs (vpath
));
374 result
= me
->setctl
!= NULL
? me
->setctl (vpath
, ctlop
, arg
) : 0;
379 /* --------------------------------------------------------------------------------------------- */
382 mc_close (int handle
)
384 struct vfs_class
*vfs
;
391 vfs
= vfs_class_find_by_handle (handle
, &fsinfo
);
392 if (vfs
== NULL
|| fsinfo
== NULL
)
396 return close (handle
);
398 if (vfs
->close
== NULL
)
399 vfs_die ("VFS must support close.\n");
400 result
= vfs
->close (fsinfo
);
401 vfs_free_handle (handle
);
403 errno
= vfs_ferrno (vfs
);
408 /* --------------------------------------------------------------------------------------------- */
411 mc_opendir (const vfs_path_t
*vpath
)
413 int handle
, *handlep
;
415 vfs_path_element_t
*path_element
;
420 path_element
= (vfs_path_element_t
*) vfs_path_get_by_index (vpath
, -1);
421 if (!vfs_path_element_valid (path_element
))
427 info
= path_element
->class->opendir
? path_element
->class->opendir (vpath
) : NULL
;
430 errno
= path_element
->class->opendir
? vfs_ferrno (path_element
->class) : ENOTSUP
;
434 path_element
->dir
.info
= info
;
437 path_element
->dir
.converter
= (path_element
->encoding
!= NULL
) ?
438 str_crt_conv_from (path_element
->encoding
) : str_cnv_from_term
;
439 if (path_element
->dir
.converter
== INVALID_CONV
)
440 path_element
->dir
.converter
= str_cnv_from_term
;
443 handle
= vfs_new_handle (path_element
->class, vfs_path_element_clone (path_element
));
445 handlep
= g_new (int, 1);
447 return (DIR *) handlep
;
450 /* --------------------------------------------------------------------------------------------- */
453 mc_readdir (DIR *dirp
)
456 struct vfs_class
*vfs
;
458 struct vfs_dirent
*entry
= NULL
;
459 vfs_path_element_t
*vfs_path_element
;
467 handle
= *(int *) dirp
;
469 vfs
= vfs_class_find_by_handle (handle
, &fsinfo
);
470 if (vfs
== NULL
|| fsinfo
== NULL
)
473 vfs_path_element
= (vfs_path_element_t
*) fsinfo
;
474 if (vfs
->readdir
!= NULL
)
476 entry
= vfs
->readdir (vfs_path_element
->dir
.info
);
480 g_string_set_size (vfs_str_buffer
, 0);
482 str_vfs_convert_from (vfs_path_element
->dir
.converter
, entry
->d_name
, vfs_str_buffer
);
484 g_string_append_len (vfs_str_buffer
, entry
->d_name
, entry
->d_len
);
486 vfs_dirent_assign (mc_readdir_result
, vfs_str_buffer
->str
, entry
->d_ino
);
487 vfs_dirent_free (entry
);
490 errno
= vfs
->readdir
? vfs_ferrno (vfs
) : ENOTSUP
;
491 return (entry
!= NULL
) ? mc_readdir_result
: NULL
;
494 /* --------------------------------------------------------------------------------------------- */
497 mc_closedir (DIR *dirp
)
500 struct vfs_class
*vfs
;
507 handle
= *(int *) dirp
;
509 vfs
= vfs_class_find_by_handle (handle
, &fsinfo
);
510 if (vfs
!= NULL
&& fsinfo
!= NULL
)
512 vfs_path_element_t
*vfs_path_element
= (vfs_path_element_t
*) fsinfo
;
515 if (vfs_path_element
->dir
.converter
!= str_cnv_from_term
)
517 str_close_conv (vfs_path_element
->dir
.converter
);
518 vfs_path_element
->dir
.converter
= INVALID_CONV
;
522 result
= vfs
->closedir
? (*vfs
->closedir
) (vfs_path_element
->dir
.info
) : -1;
523 vfs_free_handle (handle
);
524 vfs_path_element_free (vfs_path_element
);
530 /* --------------------------------------------------------------------------------------------- */
534 #define MC_STATOP(name) \
535 int mc_##name (const vfs_path_t *vpath, struct stat *buf) \
538 struct vfs_class *me; \
543 me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath)); \
546 result = me->name ? me->name (vpath, buf) : -1; \
548 errno = me->name ? vfs_ferrno (me) : ENOTSUP; \
559 /* --------------------------------------------------------------------------------------------- */
562 mc_getlocalcopy (const vfs_path_t
*pathname_vpath
)
564 vfs_path_t
*result
= NULL
;
565 struct vfs_class
*me
;
567 if (pathname_vpath
== NULL
)
570 me
= VFS_CLASS (vfs_path_get_last_path_vfs (pathname_vpath
));
573 result
= me
->getlocalcopy
!= NULL
?
574 me
->getlocalcopy (pathname_vpath
) : mc_def_getlocalcopy (pathname_vpath
);
576 errno
= vfs_ferrno (me
);
581 /* --------------------------------------------------------------------------------------------- */
584 mc_ungetlocalcopy (const vfs_path_t
*pathname_vpath
, const vfs_path_t
*local_vpath
,
585 gboolean has_changed
)
588 const struct vfs_class
*me
;
590 if (pathname_vpath
== NULL
)
593 me
= vfs_path_get_last_path_vfs (pathname_vpath
);
595 result
= me
->ungetlocalcopy
!= NULL
?
596 me
->ungetlocalcopy (pathname_vpath
, local_vpath
, has_changed
) :
597 mc_def_ungetlocalcopy (pathname_vpath
, local_vpath
, has_changed
);
602 /* --------------------------------------------------------------------------------------------- */
606 * @param vpath VFS path.
607 * May be NULL. In this case NULL is returned and errno set to 0.
609 * @return 0 on success, -1 on failure.
613 mc_chdir (const vfs_path_t
*vpath
)
615 struct vfs_class
*old_vfs
;
618 struct vfs_class
*me
;
619 const vfs_path_element_t
*path_element
;
620 vfs_path_t
*cd_vpath
;
629 cd_vpath
= vfs_path_to_absolute (vpath
);
631 cd_vpath
= vfs_path_clone (vpath
);
633 me
= VFS_CLASS (vfs_path_get_last_path_vfs (cd_vpath
));
640 if (me
->chdir
== NULL
)
646 result
= me
->chdir (cd_vpath
);
649 errno
= vfs_ferrno (me
);
653 old_vfsid
= vfs_getid (vfs_get_raw_current_dir ());
654 old_vfs
= current_vfs
;
656 /* Actually change directory */
657 vfs_set_raw_current_dir (cd_vpath
);
660 /* This function uses the new current_dir implicitly */
661 vfs_stamp_create (old_vfs
, old_vfsid
);
663 /* Sometimes we assume no trailing slash on cwd */
664 path_element
= vfs_path_get_by_index (vfs_get_raw_current_dir (), -1);
665 if (vfs_path_element_valid (path_element
))
667 if (*path_element
->path
!= '\0')
671 p
= strchr (path_element
->path
, 0) - 1;
672 if (IS_PATH_SEP (*p
) && p
> path_element
->path
)
676 #ifdef ENABLE_VFS_NET
678 struct vfs_s_super
*super
;
680 super
= vfs_get_super_by_vpath (vpath
);
681 if (super
!= NULL
&& super
->path_element
!= NULL
)
683 g_free (super
->path_element
->path
);
684 super
->path_element
->path
= g_strdup (path_element
->path
);
687 #endif /* ENABLE_VFS_NET */
693 vfs_path_free (cd_vpath
, TRUE
);
697 /* --------------------------------------------------------------------------------------------- */
700 mc_lseek (int fd
, off_t offset
, int whence
)
702 struct vfs_class
*vfs
;
709 vfs
= vfs_class_find_by_handle (fd
, &fsinfo
);
713 result
= vfs
->lseek
? vfs
->lseek (fsinfo
, offset
, whence
) : -1;
715 errno
= vfs
->lseek
? vfs_ferrno (vfs
) : ENOTSUP
;
719 /* --------------------------------------------------------------------------------------------- */
720 /* Following code heavily borrows from libiberty, mkstemps.c */
723 * pname (output) - pointer to the name of the temp file (needs g_free).
724 * NULL if the function fails.
725 * prefix - part of the filename before the random part.
726 * Prepend $TMPDIR or /tmp if there are no path separators.
727 * suffix - if not NULL, part of the filename after the random part.
730 * handle of the open file or -1 if couldn't open any.
734 mc_mkstemps (vfs_path_t
**pname_vpath
, const char *prefix
, const char *suffix
)
739 if (strchr (prefix
, PATH_SEP
) != NULL
)
740 p1
= g_strdup (prefix
);
743 /* Add prefix first to find the position of XXXXXX */
744 p1
= g_build_filename (mc_tmpdir (), prefix
, (char *) NULL
);
747 p2
= g_strconcat (p1
, "XXXXXX", suffix
, (char *) NULL
);
752 *pname_vpath
= vfs_path_from_str (p2
);
764 /* --------------------------------------------------------------------------------------------- */
766 * Return the directory where mc should keep its temporary files.
767 * This directory is (in Bourne shell terms) "${TMPDIR=/tmp}/mc-XXXXXX"
768 * When called the first time, the directory is created if needed.
769 * The first call should be done early, since we are using fprintf()
770 * and not message() to report possible problems.
776 static char buffer
[PATH_MAX
];
777 static const char *tmpdir
= NULL
;
781 /* Check if already correctly initialized */
786 if (lstat (tmpdir
, &st
) == 0 && S_ISDIR (st
.st_mode
) && st
.st_uid
== getuid ()
787 && (st
.st_mode
& 0777) == 0700)
791 sys_tmp
= getenv ("MC_TMPDIR");
792 if (sys_tmp
== NULL
|| !IS_PATH_SEP (sys_tmp
[0]))
794 sys_tmp
= getenv ("TMPDIR");
795 if (sys_tmp
== NULL
|| !IS_PATH_SEP (sys_tmp
[0]))
796 sys_tmp
= TMPDIR_DEFAULT
;
799 template = g_build_filename (sys_tmp
, "mc-XXXXXX", (char *) NULL
);
800 g_strlcpy (buffer
, template, sizeof (buffer
));
803 tmpdir
= g_mkdtemp (buffer
);
805 g_setenv ("MC_TMPDIR", tmpdir
, TRUE
);
808 fprintf (stderr
, _("Cannot create temporary directory %s: %s.\n"
809 "Temporary files will not be created\n"), buffer
,
810 unix_error_string (errno
));
811 g_snprintf (buffer
, sizeof (buffer
), "%s", "/dev/null/");
812 fprintf (stderr
, "%s\n", _("Press any key to continue..."));
819 /* --------------------------------------------------------------------------------------------- */