Merge branch '2372_mcedit_utf8_fix'
[midnight-commander.git] / lib / vfs / interface.c
blobf68e08706dc7115a1f3c411f24d6951c8865d5b0
1 /*
2 Virtual File System: interface functions
4 Copyright (C) 2011
5 The Free Software Foundation, Inc.
7 Written by:
8 Slava Zanko <slavazanko@gmail.com>, 2011
10 This file is part of the Midnight Commander.
12 The Midnight Commander is free software: you can redistribute it
13 and/or modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation, either version 3 of the License,
15 or (at your option) any later version.
17 The Midnight Commander is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 /**
27 * \file
28 * \brief Source: Virtual File System: path handlers
29 * \author Slava Zanko
30 * \date 2011
34 #include <config.h>
36 #include <stdio.h>
37 #include <stdlib.h> /* For atol() */
38 #include <stdarg.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <sys/types.h>
42 #include <signal.h>
43 #include <ctype.h> /* is_digit() */
44 #include <fcntl.h>
45 #include <sys/stat.h>
46 #include <unistd.h>
47 #include <dirent.h>
49 #include "lib/global.h"
51 #include "lib/widget.h" /* message() */
52 #include "lib/strutil.h" /* str_crt_conv_from() */
54 #include "vfs.h"
55 #include "utilvfs.h"
56 #include "path.h"
57 #include "gc.h"
59 extern GString *vfs_str_buffer;
60 extern struct vfs_class *current_vfs;
62 /*** global variables ****************************************************************************/
64 struct dirent *mc_readdir_result = NULL;
66 /*** file scope macro definitions ****************************************************************/
68 /*** file scope type declarations ****************************************************************/
70 /*** file scope variables ************************************************************************/
72 /*** file scope functions ************************************************************************/
73 /* --------------------------------------------------------------------------------------------- */
75 static char *
76 mc_def_getlocalcopy (const char *filename)
78 char *tmp;
79 int fdin, fdout;
80 ssize_t i;
81 char buffer[8192];
82 struct stat mystat;
84 fdin = mc_open (filename, O_RDONLY | O_LINEAR);
85 if (fdin == -1)
86 return NULL;
88 fdout = vfs_mkstemps (&tmp, "vfs", filename);
90 if (fdout == -1)
91 goto fail;
92 while ((i = mc_read (fdin, buffer, sizeof (buffer))) > 0)
94 if (write (fdout, buffer, i) != i)
95 goto fail;
97 if (i == -1)
98 goto fail;
99 i = mc_close (fdin);
100 fdin = -1;
101 if (i == -1)
102 goto fail;
103 if (close (fdout) == -1)
105 fdout = -1;
106 goto fail;
109 if (mc_stat (filename, &mystat) != -1)
110 chmod (tmp, mystat.st_mode);
112 return tmp;
114 fail:
115 if (fdout != -1)
116 close (fdout);
117 if (fdin != -1)
118 mc_close (fdin);
119 g_free (tmp);
120 return NULL;
123 /* --------------------------------------------------------------------------------------------- */
125 static int
126 mc_def_ungetlocalcopy (struct vfs_class *vfs, const char *filename,
127 const char *local, int has_changed)
129 int fdin = -1, fdout = -1;
130 if (has_changed)
132 char buffer[8192];
133 ssize_t i;
135 if (!vfs->write)
136 goto failed;
138 fdin = open (local, O_RDONLY);
139 if (fdin == -1)
140 goto failed;
141 fdout = mc_open (filename, O_WRONLY | O_TRUNC);
142 if (fdout == -1)
143 goto failed;
144 while ((i = read (fdin, buffer, sizeof (buffer))) > 0)
145 if (mc_write (fdout, buffer, (size_t) i) != i)
146 goto failed;
147 if (i == -1)
148 goto failed;
150 if (close (fdin) == -1)
152 fdin = -1;
153 goto failed;
155 fdin = -1;
156 if (mc_close (fdout) == -1)
158 fdout = -1;
159 goto failed;
162 unlink (local);
163 return 0;
165 failed:
166 message (D_ERROR, _("Changes to file lost"), "%s", filename);
167 if (fdout != -1)
168 mc_close (fdout);
169 if (fdin != -1)
170 close (fdin);
171 unlink (local);
172 return -1;
175 /* --------------------------------------------------------------------------------------------- */
176 /*** public functions ****************************************************************************/
177 /* --------------------------------------------------------------------------------------------- */
180 mc_open (const char *filename, int flags, ...)
182 int mode = 0, result = -1;
183 vfs_path_t *vpath;
184 vfs_path_element_t *path_element;
186 vpath = vfs_path_from_str (filename);
187 if (vpath == NULL)
188 return -1;
190 /* Get the mode flag */
191 if (flags & O_CREAT)
193 va_list ap;
194 va_start (ap, flags);
195 mode = va_arg (ap, int);
196 va_end (ap);
199 path_element = vfs_path_get_by_index (vpath, -1);
200 if (vfs_path_element_valid (path_element) && path_element->class->open != NULL)
202 void *info;
203 /* open must be supported */
204 info = path_element->class->open (vpath, flags, mode);
205 if (info == NULL)
206 errno = vfs_ferrno (path_element->class);
207 else
208 result = vfs_new_handle (path_element->class, info);
210 else
211 errno = -EOPNOTSUPP;
213 vfs_path_free (vpath);
214 return result;
217 /* --------------------------------------------------------------------------------------------- */
219 /* *INDENT-OFF* */
221 #define MC_NAMEOP(name, inarg, callarg) \
222 int mc_##name inarg \
224 int result; \
225 vfs_path_t *vpath; \
226 vfs_path_element_t *path_element; \
228 vpath = vfs_path_from_str (path); \
229 if (vpath == NULL) \
230 return -1; \
232 path_element = vfs_path_get_by_index (vpath, -1); \
233 if (!vfs_path_element_valid (path_element)) \
235 vfs_path_free(vpath); \
236 return -1; \
239 result = path_element->class->name != NULL ? path_element->class->name callarg : -1; \
240 if (result == -1) \
241 errno = path_element->class->name != NULL ? vfs_ferrno (path_element->class) : E_NOTSUPP; \
242 vfs_path_free(vpath); \
243 return result; \
246 MC_NAMEOP (chmod, (const char *path, mode_t mode), (vpath, mode))
247 MC_NAMEOP (chown, (const char *path, uid_t owner, gid_t group), (vpath, owner, group))
248 MC_NAMEOP (utime, (const char *path, struct utimbuf * times), (vpath, times))
249 MC_NAMEOP (readlink, (const char *path, char *buf, size_t bufsiz), (vpath, buf, bufsiz))
250 MC_NAMEOP (unlink, (const char *path), (vpath))
251 MC_NAMEOP (mkdir, (const char *path, mode_t mode), (vpath, mode))
252 MC_NAMEOP (rmdir, (const char *path), (vpath))
253 MC_NAMEOP (mknod, (const char *path, mode_t mode, dev_t dev), (vpath, mode, dev))
255 /* *INDENT-ON* */
257 /* --------------------------------------------------------------------------------------------- */
260 mc_symlink (const char *name1, const char *path)
262 int result = -1;
263 vfs_path_t *vpath1, *vpath2;
265 vpath1 = vfs_path_from_str (path);
266 if (vpath1 == NULL)
267 return -1;
269 vpath2 = vfs_path_from_str_flags (name1, VPF_NO_CANON);
271 if (vpath2 != NULL)
273 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath1, -1);
274 if (vfs_path_element_valid (path_element))
276 result =
277 path_element->class->symlink !=
278 NULL ? path_element->class->symlink (vpath2, vpath1) : -1;
280 if (result == -1)
281 errno =
282 path_element->class->symlink !=
283 NULL ? vfs_ferrno (path_element->class) : E_NOTSUPP;
286 vfs_path_free (vpath1);
287 vfs_path_free (vpath2);
288 return result;
291 /* --------------------------------------------------------------------------------------------- */
293 /* *INDENT-OFF* */
295 #define MC_HANDLEOP(name, inarg, callarg) \
296 ssize_t mc_##name inarg \
298 struct vfs_class *vfs; \
299 int result; \
300 if (handle == -1) \
301 return -1; \
302 vfs = vfs_class_find_by_handle (handle); \
303 if (vfs == NULL) \
304 return -1; \
305 result = vfs->name != NULL ? vfs->name callarg : -1; \
306 if (result == -1) \
307 errno = vfs->name != NULL ? vfs_ferrno (vfs) : E_NOTSUPP; \
308 return result; \
311 MC_HANDLEOP (read, (int handle, void *buffer, size_t count), (vfs_class_data_find_by_handle (handle), buffer, count))
312 MC_HANDLEOP (write, (int handle, const void *buf, size_t nbyte), (vfs_class_data_find_by_handle (handle), buf, nbyte))
314 /* --------------------------------------------------------------------------------------------- */
316 #define MC_RENAMEOP(name) \
317 int mc_##name (const char *fname1, const char *fname2) \
319 int result; \
320 vfs_path_t *vpath1, *vpath2; \
321 vfs_path_element_t *path_element1, *path_element2; \
323 vpath1 = vfs_path_from_str (fname1); \
324 if (vpath1 == NULL) \
325 return -1; \
327 vpath2 = vfs_path_from_str (fname2); \
328 if (vpath2 == NULL) \
330 vfs_path_free(vpath1); \
331 return -1; \
333 path_element1 = vfs_path_get_by_index (vpath1, - 1); \
334 path_element2 = vfs_path_get_by_index (vpath2, - 1); \
336 if (!vfs_path_element_valid (path_element1) || !vfs_path_element_valid (path_element2) || \
337 path_element1->class != path_element2->class) \
339 errno = EXDEV; \
340 vfs_path_free(vpath1); \
341 vfs_path_free(vpath2); \
342 return -1; \
345 result = path_element1->class->name != NULL \
346 ? path_element1->class->name (vpath1, vpath2) \
347 : -1; \
348 if (result == -1) \
349 errno = path_element1->class->name != NULL ? vfs_ferrno (path_element1->class) : E_NOTSUPP; \
350 vfs_path_free(vpath1); \
351 vfs_path_free(vpath2); \
352 return result; \
355 MC_RENAMEOP (link)
356 MC_RENAMEOP (rename)
358 /* *INDENT-ON* */
360 /* --------------------------------------------------------------------------------------------- */
363 mc_ctl (int handle, int ctlop, void *arg)
365 struct vfs_class *vfs = vfs_class_find_by_handle (handle);
367 if (vfs == NULL)
368 return 0;
370 return vfs->ctl ? (*vfs->ctl) (vfs_class_data_find_by_handle (handle), ctlop, arg) : 0;
373 /* --------------------------------------------------------------------------------------------- */
376 mc_setctl (const char *path, int ctlop, void *arg)
378 int result = -1;
380 vfs_path_t *vpath;
381 vfs_path_element_t *path_element;
383 vpath = vfs_path_from_str (path);
384 if (vpath == NULL)
385 vfs_die ("You don't want to pass NULL to mc_setctl.");
387 path_element = vfs_path_get_by_index (vpath, -1);
388 if (vfs_path_element_valid (path_element))
389 result =
390 path_element->class->setctl != NULL ? path_element->class->setctl (vpath,
391 ctlop, arg) : 0;
393 vfs_path_free (vpath);
394 return result;
397 /* --------------------------------------------------------------------------------------------- */
400 mc_close (int handle)
402 struct vfs_class *vfs;
403 int result;
405 if (handle == -1 || !vfs_class_data_find_by_handle (handle))
406 return -1;
408 vfs = vfs_class_find_by_handle (handle);
409 if (vfs == NULL)
410 return -1;
412 if (handle < 3)
413 return close (handle);
415 if (!vfs->close)
416 vfs_die ("VFS must support close.\n");
417 result = (*vfs->close) (vfs_class_data_find_by_handle (handle));
418 vfs_free_handle (handle);
419 if (result == -1)
420 errno = vfs_ferrno (vfs);
422 return result;
425 /* --------------------------------------------------------------------------------------------- */
427 DIR *
428 mc_opendir (const char *dirname)
430 int handle, *handlep;
431 void *info;
432 vfs_path_t *vpath;
433 vfs_path_element_t *path_element;
435 vpath = vfs_path_from_str (dirname);
437 if (vpath == NULL)
438 return NULL;
440 path_element = vfs_path_get_by_index (vpath, -1);
442 if (!vfs_path_element_valid (path_element))
444 errno = E_NOTSUPP;
445 vfs_path_free (vpath);
446 return NULL;
449 info = path_element->class->opendir ? (*path_element->class->opendir) (vpath) : NULL;
451 if (info == NULL)
453 errno = path_element->class->opendir ? vfs_ferrno (path_element->class) : E_NOTSUPP;
454 vfs_path_free (vpath);
455 return NULL;
458 path_element->dir.info = info;
460 path_element->dir.converter =
461 (path_element->encoding !=
462 NULL) ? str_crt_conv_from (path_element->encoding) : str_cnv_from_term;
463 if (path_element->dir.converter == INVALID_CONV)
464 path_element->dir.converter = str_cnv_from_term;
466 handle = vfs_new_handle (path_element->class, vfs_path_element_clone (path_element));
468 handlep = g_new (int, 1);
469 *handlep = handle;
470 vfs_path_free (vpath);
471 return (DIR *) handlep;
474 /* --------------------------------------------------------------------------------------------- */
476 struct dirent *
477 mc_readdir (DIR * dirp)
479 int handle;
480 struct vfs_class *vfs;
481 struct dirent *entry = NULL;
482 vfs_path_element_t *vfs_path_element;
483 estr_t state;
485 if (!mc_readdir_result)
487 /* We can't just allocate struct dirent as (see man dirent.h)
488 * struct dirent has VERY nonnaive semantics of allocating
489 * d_name in it. Moreover, linux's glibc-2.9 allocates dirents _less_,
490 * than 'sizeof (struct dirent)' making full bitwise (sizeof dirent) copy
491 * heap corrupter. So, allocate longliving dirent with at least
492 * (MAXNAMLEN + 1) for d_name in it.
493 * Strictly saying resulting dirent is unusable as we don't adjust internal
494 * structures, holding dirent size. But we don't use it in libc infrastructure.
495 * TODO: to make simpler homemade dirent-alike structure.
497 mc_readdir_result = (struct dirent *) g_malloc (sizeof (struct dirent) + MAXNAMLEN + 1);
500 if (!dirp)
502 errno = EFAULT;
503 return NULL;
505 handle = *(int *) dirp;
507 vfs = vfs_class_find_by_handle (handle);
508 if (vfs == NULL)
509 return NULL;
511 vfs_path_element = vfs_class_data_find_by_handle (handle);
512 if (vfs->readdir)
514 entry = (*vfs->readdir) (vfs_path_element->dir.info);
515 if (entry == NULL)
516 return NULL;
517 g_string_set_size (vfs_str_buffer, 0);
518 state =
519 str_vfs_convert_from (vfs_path_element->dir.converter, entry->d_name, vfs_str_buffer);
520 mc_readdir_result->d_ino = entry->d_ino;
521 g_strlcpy (mc_readdir_result->d_name, vfs_str_buffer->str, MAXNAMLEN + 1);
523 if (entry == NULL)
524 errno = vfs->readdir ? vfs_ferrno (vfs) : E_NOTSUPP;
525 return (entry != NULL) ? mc_readdir_result : NULL;
528 /* --------------------------------------------------------------------------------------------- */
531 mc_closedir (DIR * dirp)
533 int handle = *(int *) dirp;
534 struct vfs_class *vfs;
535 int result = -1;
537 vfs = vfs_class_find_by_handle (handle);
538 if (vfs != NULL)
540 vfs_path_element_t *vfs_path_element;
541 vfs_path_element = vfs_class_data_find_by_handle (handle);
542 if (vfs_path_element->dir.converter != str_cnv_from_term)
544 str_close_conv (vfs_path_element->dir.converter);
545 vfs_path_element->dir.converter = INVALID_CONV;
548 result = vfs->closedir ? (*vfs->closedir) (vfs_path_element->dir.info) : -1;
549 vfs_free_handle (handle);
550 vfs_path_element_free (vfs_path_element);
552 g_free (dirp);
553 return result;
556 /* --------------------------------------------------------------------------------------------- */
559 mc_stat (const char *filename, struct stat *buf)
561 int result = -1;
562 vfs_path_t *vpath;
563 vfs_path_element_t *path_element;
565 vpath = vfs_path_from_str (filename);
566 if (vpath == NULL)
567 return -1;
569 path_element = vfs_path_get_by_index (vpath, -1);
571 if (vfs_path_element_valid (path_element))
573 result = path_element->class->stat ? (*path_element->class->stat) (vpath, buf) : -1;
574 if (result == -1)
575 errno = path_element->class->name ? vfs_ferrno (path_element->class) : E_NOTSUPP;
578 vfs_path_free (vpath);
579 return result;
582 /* --------------------------------------------------------------------------------------------- */
585 mc_lstat (const char *filename, struct stat *buf)
587 int result = -1;
588 vfs_path_t *vpath;
589 vfs_path_element_t *path_element;
591 vpath = vfs_path_from_str (filename);
592 if (vpath == NULL)
593 return -1;
595 path_element = vfs_path_get_by_index (vpath, -1);
597 if (vfs_path_element_valid (path_element))
599 result = path_element->class->lstat ? (*path_element->class->lstat) (vpath, buf) : -1;
600 if (result == -1)
601 errno = path_element->class->name ? vfs_ferrno (path_element->class) : E_NOTSUPP;
604 vfs_path_free (vpath);
605 return result;
608 /* --------------------------------------------------------------------------------------------- */
611 mc_fstat (int handle, struct stat *buf)
613 struct vfs_class *vfs;
614 int result;
616 if (handle == -1)
617 return -1;
619 vfs = vfs_class_find_by_handle (handle);
620 if (vfs == NULL)
621 return -1;
623 result = vfs->fstat ? (*vfs->fstat) (vfs_class_data_find_by_handle (handle), buf) : -1;
624 if (result == -1)
625 errno = vfs->name ? vfs_ferrno (vfs) : E_NOTSUPP;
626 return result;
629 /* --------------------------------------------------------------------------------------------- */
631 * Return current directory. If it's local, reread the current directory
632 * from the OS. Put directory to the provided buffer.
635 char *
636 mc_get_current_wd (char *buffer, size_t size)
638 char *cwd = _vfs_get_cwd ();
640 g_strlcpy (buffer, cwd, size);
641 g_free (cwd);
643 return buffer;
646 /* --------------------------------------------------------------------------------------------- */
648 char *
649 mc_getlocalcopy (const char *pathname)
651 char *result = NULL;
652 vfs_path_t *vpath;
653 vfs_path_element_t *path_element;
655 vpath = vfs_path_from_str (pathname);
656 if (vpath == NULL)
657 return NULL;
659 path_element = vfs_path_get_by_index (vpath, -1);
661 if (vfs_path_element_valid (path_element))
663 result = path_element->class->getlocalcopy != NULL ?
664 path_element->class->getlocalcopy (vpath) : mc_def_getlocalcopy (pathname);
665 if (result == NULL)
666 errno = vfs_ferrno (path_element->class);
668 vfs_path_free (vpath);
669 return result;
672 /* --------------------------------------------------------------------------------------------- */
675 mc_ungetlocalcopy (const char *pathname, const char *local, int has_changed)
677 int return_value = -1;
678 vfs_path_t *vpath;
679 vfs_path_element_t *path_element;
681 vpath = vfs_path_from_str (pathname);
682 if (vpath == NULL)
683 return -1;
685 path_element = vfs_path_get_by_index (vpath, -1);
687 if (vfs_path_element_valid (path_element))
689 return_value = path_element->class->ungetlocalcopy != NULL ?
690 path_element->class->ungetlocalcopy (vpath, local,
691 has_changed) :
692 mc_def_ungetlocalcopy (path_element->class, path_element->path, local, has_changed);
694 vfs_path_free (vpath);
695 return return_value;
698 /* --------------------------------------------------------------------------------------------- */
700 * VFS chdir.
701 * Return 0 on success, -1 on failure.
705 mc_chdir (const char *path)
707 struct vfs_class *old_vfs;
708 vfsid old_vfsid;
709 int result;
710 vfs_path_t *vpath;
711 vfs_path_element_t *path_element;
713 vpath = vfs_path_from_str (path);
715 if (vpath == NULL)
716 return -1;
718 path_element = vfs_path_get_by_index (vpath, -1);
720 if (!vfs_path_element_valid (path_element) || path_element->class->chdir == NULL)
722 vfs_path_free (vpath);
723 return -1;
726 result = (*path_element->class->chdir) (vpath);
728 if (result == -1)
730 errno = vfs_ferrno (path_element->class);
731 vfs_path_free (vpath);
732 return -1;
735 old_vfsid = vfs_getid (vfs_get_raw_current_dir ());
736 old_vfs = current_vfs;
738 /* Actually change directory */
739 vfs_set_raw_current_dir (vpath);
740 current_vfs = path_element->class;
742 /* This function uses the new current_dir implicitly */
743 vfs_stamp_create (old_vfs, old_vfsid);
745 /* Sometimes we assume no trailing slash on cwd */
746 path_element = vfs_path_get_by_index (vfs_get_raw_current_dir (), -1);
747 if (vfs_path_element_valid (path_element) && (*path_element->path != '\0'))
749 char *p;
750 p = strchr (path_element->path, 0) - 1;
751 if (*p == PATH_SEP && p > path_element->path)
752 *p = 0;
754 return 0;
757 /* --------------------------------------------------------------------------------------------- */
759 off_t
760 mc_lseek (int fd, off_t offset, int whence)
762 struct vfs_class *vfs;
763 off_t result;
765 if (fd == -1)
766 return -1;
768 vfs = vfs_class_find_by_handle (fd);
769 if (vfs == NULL)
770 return -1;
772 result = vfs->lseek ? (*vfs->lseek) (vfs_class_data_find_by_handle (fd), offset, whence) : -1;
773 if (result == -1)
774 errno = vfs->lseek ? vfs_ferrno (vfs) : E_NOTSUPP;
775 return result;
778 /* --------------------------------------------------------------------------------------------- */