Changed interface of function mc_symlink()
[midnight-commander.git] / lib / vfs / interface.c
blob0b74d7a398d25925d4c0cf8dc0539f1a4ad9a334
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;
83 vfs_path_t *vpath = vfs_path_from_str (filename);
85 fdin = mc_open (filename, O_RDONLY | O_LINEAR);
86 if (fdin == -1)
87 return NULL;
89 fdout = vfs_mkstemps (&tmp, "vfs", filename);
91 if (fdout == -1)
92 goto fail;
93 while ((i = mc_read (fdin, buffer, sizeof (buffer))) > 0)
95 if (write (fdout, buffer, i) != i)
96 goto fail;
98 if (i == -1)
99 goto fail;
100 i = mc_close (fdin);
101 fdin = -1;
102 if (i == -1)
103 goto fail;
104 if (close (fdout) == -1)
106 fdout = -1;
107 goto fail;
110 if (mc_stat (vpath, &mystat) != -1)
111 chmod (tmp, mystat.st_mode);
112 vfs_path_free (vpath);
114 return tmp;
116 fail:
117 vfs_path_free (vpath);
118 if (fdout != -1)
119 close (fdout);
120 if (fdin != -1)
121 mc_close (fdin);
122 g_free (tmp);
123 return NULL;
126 /* --------------------------------------------------------------------------------------------- */
128 static int
129 mc_def_ungetlocalcopy (struct vfs_class *vfs, const char *filename,
130 const char *local, int has_changed)
132 int fdin = -1, fdout = -1;
133 if (has_changed)
135 char buffer[8192];
136 ssize_t i;
138 if (!vfs->write)
139 goto failed;
141 fdin = open (local, O_RDONLY);
142 if (fdin == -1)
143 goto failed;
144 fdout = mc_open (filename, O_WRONLY | O_TRUNC);
145 if (fdout == -1)
146 goto failed;
147 while ((i = read (fdin, buffer, sizeof (buffer))) > 0)
148 if (mc_write (fdout, buffer, (size_t) i) != i)
149 goto failed;
150 if (i == -1)
151 goto failed;
153 if (close (fdin) == -1)
155 fdin = -1;
156 goto failed;
158 fdin = -1;
159 if (mc_close (fdout) == -1)
161 fdout = -1;
162 goto failed;
165 unlink (local);
166 return 0;
168 failed:
169 message (D_ERROR, _("Changes to file lost"), "%s", filename);
170 if (fdout != -1)
171 mc_close (fdout);
172 if (fdin != -1)
173 close (fdin);
174 unlink (local);
175 return -1;
178 /* --------------------------------------------------------------------------------------------- */
179 /*** public functions ****************************************************************************/
180 /* --------------------------------------------------------------------------------------------- */
183 mc_open (const char *filename, int flags, ...)
185 int mode = 0, result = -1;
186 vfs_path_t *vpath;
187 vfs_path_element_t *path_element;
189 vpath = vfs_path_from_str (filename);
190 if (vpath == NULL)
191 return -1;
193 /* Get the mode flag */
194 if (flags & O_CREAT)
196 va_list ap;
197 va_start (ap, flags);
198 mode = va_arg (ap, int);
199 va_end (ap);
202 path_element = vfs_path_get_by_index (vpath, -1);
203 if (vfs_path_element_valid (path_element) && path_element->class->open != NULL)
205 void *info;
206 /* open must be supported */
207 info = path_element->class->open (vpath, flags, mode);
208 if (info == NULL)
209 errno = vfs_ferrno (path_element->class);
210 else
211 result = vfs_new_handle (path_element->class, info);
213 else
214 errno = -EOPNOTSUPP;
216 vfs_path_free (vpath);
217 return result;
220 /* --------------------------------------------------------------------------------------------- */
222 /* *INDENT-OFF* */
224 #define MC_NAMEOP(name, inarg, callarg) \
225 int mc_##name inarg \
227 int result; \
228 vfs_path_element_t *path_element; \
230 if (vpath == NULL) \
231 return -1; \
233 path_element = vfs_path_get_by_index (vpath, -1); \
234 if (!vfs_path_element_valid (path_element)) \
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 return result; \
245 MC_NAMEOP (chmod, (const vfs_path_t *vpath, mode_t mode), (vpath, mode))
246 MC_NAMEOP (chown, (const vfs_path_t *vpath, uid_t owner, gid_t group), (vpath, owner, group))
247 MC_NAMEOP (utime, (const vfs_path_t *vpath, struct utimbuf * times), (vpath, times))
248 MC_NAMEOP (readlink, (const vfs_path_t *vpath, char *buf, size_t bufsiz), (vpath, buf, bufsiz))
249 MC_NAMEOP (unlink, (const vfs_path_t *vpath), (vpath))
250 MC_NAMEOP (mkdir, (const vfs_path_t *vpath, mode_t mode), (vpath, mode))
251 MC_NAMEOP (rmdir, (const vfs_path_t *vpath), (vpath))
252 MC_NAMEOP (mknod, (const vfs_path_t *vpath, mode_t mode, dev_t dev), (vpath, mode, dev))
254 /* *INDENT-ON* */
256 /* --------------------------------------------------------------------------------------------- */
259 mc_symlink (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
261 int result = -1;
263 if (vpath1 == NULL)
264 return -1;
266 if (vpath1 != NULL)
268 vfs_path_element_t *path_element;
270 path_element = vfs_path_get_by_index (vpath2, -1);
271 if (vfs_path_element_valid (path_element))
273 result =
274 path_element->class->symlink != NULL ?
275 path_element->class->symlink (vpath1, vpath2) : -1;
277 if (result == -1)
278 errno =
279 path_element->class->symlink != NULL ?
280 vfs_ferrno (path_element->class) : E_NOTSUPP;
283 return result;
286 /* --------------------------------------------------------------------------------------------- */
288 /* *INDENT-OFF* */
290 #define MC_HANDLEOP(name, inarg, callarg) \
291 ssize_t mc_##name inarg \
293 struct vfs_class *vfs; \
294 int result; \
295 if (handle == -1) \
296 return -1; \
297 vfs = vfs_class_find_by_handle (handle); \
298 if (vfs == NULL) \
299 return -1; \
300 result = vfs->name != NULL ? vfs->name callarg : -1; \
301 if (result == -1) \
302 errno = vfs->name != NULL ? vfs_ferrno (vfs) : E_NOTSUPP; \
303 return result; \
306 MC_HANDLEOP (read, (int handle, void *buffer, size_t count), (vfs_class_data_find_by_handle (handle), buffer, count))
307 MC_HANDLEOP (write, (int handle, const void *buf, size_t nbyte), (vfs_class_data_find_by_handle (handle), buf, nbyte))
309 /* --------------------------------------------------------------------------------------------- */
311 #define MC_RENAMEOP(name) \
312 int mc_##name (const vfs_path_t *vpath1, const vfs_path_t *vpath2) \
314 int result; \
315 vfs_path_element_t *path_element1, *path_element2; \
317 if (vpath1 == NULL || vpath2 == NULL) \
318 return -1; \
320 path_element1 = vfs_path_get_by_index (vpath1, - 1); \
321 path_element2 = vfs_path_get_by_index (vpath2, - 1); \
323 if (!vfs_path_element_valid (path_element1) || !vfs_path_element_valid (path_element2) || \
324 path_element1->class != path_element2->class) \
326 errno = EXDEV; \
327 return -1; \
330 result = path_element1->class->name != NULL \
331 ? path_element1->class->name (vpath1, vpath2) \
332 : -1; \
333 if (result == -1) \
334 errno = path_element1->class->name != NULL ? vfs_ferrno (path_element1->class) : E_NOTSUPP; \
335 return result; \
338 MC_RENAMEOP (link)
339 MC_RENAMEOP (rename)
341 /* *INDENT-ON* */
343 /* --------------------------------------------------------------------------------------------- */
346 mc_ctl (int handle, int ctlop, void *arg)
348 struct vfs_class *vfs = vfs_class_find_by_handle (handle);
350 if (vfs == NULL)
351 return 0;
353 return vfs->ctl ? (*vfs->ctl) (vfs_class_data_find_by_handle (handle), ctlop, arg) : 0;
356 /* --------------------------------------------------------------------------------------------- */
359 mc_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
361 int result = -1;
363 vfs_path_element_t *path_element;
365 if (vpath == NULL)
366 vfs_die ("You don't want to pass NULL to mc_setctl.");
368 path_element = vfs_path_get_by_index (vpath, -1);
369 if (vfs_path_element_valid (path_element))
370 result =
371 path_element->class->setctl != NULL ? path_element->class->setctl (vpath,
372 ctlop, arg) : 0;
374 return result;
377 /* --------------------------------------------------------------------------------------------- */
380 mc_close (int handle)
382 struct vfs_class *vfs;
383 int result;
385 if (handle == -1 || !vfs_class_data_find_by_handle (handle))
386 return -1;
388 vfs = vfs_class_find_by_handle (handle);
389 if (vfs == NULL)
390 return -1;
392 if (handle < 3)
393 return close (handle);
395 if (!vfs->close)
396 vfs_die ("VFS must support close.\n");
397 result = (*vfs->close) (vfs_class_data_find_by_handle (handle));
398 vfs_free_handle (handle);
399 if (result == -1)
400 errno = vfs_ferrno (vfs);
402 return result;
405 /* --------------------------------------------------------------------------------------------- */
407 DIR *
408 mc_opendir (const vfs_path_t * vpath)
410 int handle, *handlep;
411 void *info;
412 vfs_path_element_t *path_element;
414 if (vpath == NULL)
415 return NULL;
417 path_element = vfs_path_get_by_index (vpath, -1);
419 if (!vfs_path_element_valid (path_element))
421 errno = E_NOTSUPP;
422 return NULL;
425 info = path_element->class->opendir ? (*path_element->class->opendir) (vpath) : NULL;
427 if (info == NULL)
429 errno = path_element->class->opendir ? vfs_ferrno (path_element->class) : E_NOTSUPP;
430 return NULL;
433 path_element->dir.info = info;
435 path_element->dir.converter =
436 (path_element->encoding !=
437 NULL) ? str_crt_conv_from (path_element->encoding) : str_cnv_from_term;
438 if (path_element->dir.converter == INVALID_CONV)
439 path_element->dir.converter = str_cnv_from_term;
441 handle = vfs_new_handle (path_element->class, vfs_path_element_clone (path_element));
443 handlep = g_new (int, 1);
444 *handlep = handle;
445 return (DIR *) handlep;
448 /* --------------------------------------------------------------------------------------------- */
450 struct dirent *
451 mc_readdir (DIR * dirp)
453 int handle;
454 struct vfs_class *vfs;
455 struct dirent *entry = NULL;
456 vfs_path_element_t *vfs_path_element;
457 estr_t state;
459 if (!mc_readdir_result)
461 /* We can't just allocate struct dirent as (see man dirent.h)
462 * struct dirent has VERY nonnaive semantics of allocating
463 * d_name in it. Moreover, linux's glibc-2.9 allocates dirents _less_,
464 * than 'sizeof (struct dirent)' making full bitwise (sizeof dirent) copy
465 * heap corrupter. So, allocate longliving dirent with at least
466 * (MAXNAMLEN + 1) for d_name in it.
467 * Strictly saying resulting dirent is unusable as we don't adjust internal
468 * structures, holding dirent size. But we don't use it in libc infrastructure.
469 * TODO: to make simpler homemade dirent-alike structure.
471 mc_readdir_result = (struct dirent *) g_malloc (sizeof (struct dirent) + MAXNAMLEN + 1);
474 if (!dirp)
476 errno = EFAULT;
477 return NULL;
479 handle = *(int *) dirp;
481 vfs = vfs_class_find_by_handle (handle);
482 if (vfs == NULL)
483 return NULL;
485 vfs_path_element = vfs_class_data_find_by_handle (handle);
486 if (vfs->readdir)
488 entry = (*vfs->readdir) (vfs_path_element->dir.info);
489 if (entry == NULL)
490 return NULL;
491 g_string_set_size (vfs_str_buffer, 0);
492 state =
493 str_vfs_convert_from (vfs_path_element->dir.converter, entry->d_name, vfs_str_buffer);
494 mc_readdir_result->d_ino = entry->d_ino;
495 g_strlcpy (mc_readdir_result->d_name, vfs_str_buffer->str, MAXNAMLEN + 1);
497 if (entry == NULL)
498 errno = vfs->readdir ? vfs_ferrno (vfs) : E_NOTSUPP;
499 return (entry != NULL) ? mc_readdir_result : NULL;
502 /* --------------------------------------------------------------------------------------------- */
505 mc_closedir (DIR * dirp)
507 int handle = *(int *) dirp;
508 struct vfs_class *vfs;
509 int result = -1;
511 vfs = vfs_class_find_by_handle (handle);
512 if (vfs != NULL)
514 vfs_path_element_t *vfs_path_element;
515 vfs_path_element = vfs_class_data_find_by_handle (handle);
516 if (vfs_path_element->dir.converter != str_cnv_from_term)
518 str_close_conv (vfs_path_element->dir.converter);
519 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);
526 g_free (dirp);
527 return result;
530 /* --------------------------------------------------------------------------------------------- */
533 mc_stat (const vfs_path_t * vpath, struct stat *buf)
535 int result = -1;
536 vfs_path_element_t *path_element;
538 if (vpath == NULL)
539 return -1;
541 path_element = vfs_path_get_by_index (vpath, -1);
543 if (vfs_path_element_valid (path_element))
545 result = path_element->class->stat ? (*path_element->class->stat) (vpath, buf) : -1;
546 if (result == -1)
547 errno = path_element->class->name ? vfs_ferrno (path_element->class) : E_NOTSUPP;
550 return result;
553 /* --------------------------------------------------------------------------------------------- */
556 mc_lstat (const vfs_path_t * vpath, struct stat *buf)
558 int result = -1;
559 vfs_path_element_t *path_element;
561 if (vpath == NULL)
562 return -1;
564 path_element = vfs_path_get_by_index (vpath, -1);
566 if (vfs_path_element_valid (path_element))
568 result = path_element->class->lstat ? (*path_element->class->lstat) (vpath, buf) : -1;
569 if (result == -1)
570 errno = path_element->class->name ? vfs_ferrno (path_element->class) : E_NOTSUPP;
573 return result;
576 /* --------------------------------------------------------------------------------------------- */
579 mc_fstat (int handle, struct stat *buf)
581 struct vfs_class *vfs;
582 int result;
584 if (handle == -1)
585 return -1;
587 vfs = vfs_class_find_by_handle (handle);
588 if (vfs == NULL)
589 return -1;
591 result = vfs->fstat ? (*vfs->fstat) (vfs_class_data_find_by_handle (handle), buf) : -1;
592 if (result == -1)
593 errno = vfs->name ? vfs_ferrno (vfs) : E_NOTSUPP;
594 return result;
597 /* --------------------------------------------------------------------------------------------- */
599 * Return current directory. If it's local, reread the current directory
600 * from the OS. Put directory to the provided buffer.
603 char *
604 mc_get_current_wd (char *buffer, size_t size)
606 char *cwd = _vfs_get_cwd ();
608 g_strlcpy (buffer, cwd, size);
609 g_free (cwd);
611 return buffer;
614 /* --------------------------------------------------------------------------------------------- */
616 char *
617 mc_getlocalcopy (const char *pathname)
619 char *result = NULL;
620 vfs_path_t *vpath;
621 vfs_path_element_t *path_element;
623 vpath = vfs_path_from_str (pathname);
624 if (vpath == NULL)
625 return NULL;
627 path_element = vfs_path_get_by_index (vpath, -1);
629 if (vfs_path_element_valid (path_element))
631 result = path_element->class->getlocalcopy != NULL ?
632 path_element->class->getlocalcopy (vpath) : mc_def_getlocalcopy (pathname);
633 if (result == NULL)
634 errno = vfs_ferrno (path_element->class);
636 vfs_path_free (vpath);
637 return result;
640 /* --------------------------------------------------------------------------------------------- */
643 mc_ungetlocalcopy (const char *pathname, const char *local, int has_changed)
645 int return_value = -1;
646 vfs_path_t *vpath;
647 vfs_path_element_t *path_element;
649 vpath = vfs_path_from_str (pathname);
650 if (vpath == NULL)
651 return -1;
653 path_element = vfs_path_get_by_index (vpath, -1);
655 if (vfs_path_element_valid (path_element))
657 return_value = path_element->class->ungetlocalcopy != NULL ?
658 path_element->class->ungetlocalcopy (vpath, local,
659 has_changed) :
660 mc_def_ungetlocalcopy (path_element->class, path_element->path, local, has_changed);
662 vfs_path_free (vpath);
663 return return_value;
666 /* --------------------------------------------------------------------------------------------- */
668 * VFS chdir.
670 * @param vpath VFS-path
672 * @return 0 on success, -1 on failure.
676 mc_chdir (const vfs_path_t * vpath)
678 struct vfs_class *old_vfs;
679 vfsid old_vfsid;
680 int result;
681 vfs_path_element_t *path_element;
683 if (vpath == NULL)
684 return -1;
686 path_element = vfs_path_get_by_index (vpath, -1);
688 if (!vfs_path_element_valid (path_element) || path_element->class->chdir == NULL)
690 return -1;
693 result = (*path_element->class->chdir) (vpath);
695 if (result == -1)
697 errno = vfs_ferrno (path_element->class);
698 return -1;
701 old_vfsid = vfs_getid (vfs_get_raw_current_dir ());
702 old_vfs = current_vfs;
704 /* Actually change directory */
705 vfs_set_raw_current_dir (vfs_path_clone (vpath));
706 current_vfs = path_element->class;
708 /* This function uses the new current_dir implicitly */
709 vfs_stamp_create (old_vfs, old_vfsid);
711 /* Sometimes we assume no trailing slash on cwd */
712 path_element = vfs_path_get_by_index (vfs_get_raw_current_dir (), -1);
713 if (vfs_path_element_valid (path_element) && (*path_element->path != '\0'))
715 char *p;
716 p = strchr (path_element->path, 0) - 1;
717 if (*p == PATH_SEP && p > path_element->path)
718 *p = 0;
720 return 0;
723 /* --------------------------------------------------------------------------------------------- */
725 off_t
726 mc_lseek (int fd, off_t offset, int whence)
728 struct vfs_class *vfs;
729 off_t result;
731 if (fd == -1)
732 return -1;
734 vfs = vfs_class_find_by_handle (fd);
735 if (vfs == NULL)
736 return -1;
738 result = vfs->lseek ? (*vfs->lseek) (vfs_class_data_find_by_handle (fd), offset, whence) : -1;
739 if (result == -1)
740 errno = vfs->lseek ? vfs_ferrno (vfs) : E_NOTSUPP;
741 return result;
744 /* --------------------------------------------------------------------------------------------- */