Ticket #3485: extfs.c

File extfs.c, 48.7 KB (added by Nicolas Rybkin, 9 years ago)
Line 
1/*
2   Virtual File System: External file system.
3
4   Copyright (C) 1995-2015
5   Free Software Foundation, Inc.
6
7   Written by:
8   Jakub Jelinek, 1995
9   Pavel Machek, 1998
10   Andrew T. Veliath, 1999
11   Slava Zanko <slavazanko@gmail.com>, 2013
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 <http://www.gnu.org/licenses/>.
27 */
28
29/**
30 * \file
31 * \brief Source: Virtual File System: External file system
32 * \author Jakub Jelinek
33 * \author Pavel Machek
34 * \author Andrew T. Veliath
35 * \date 1995, 1998, 1999
36 */
37
38/* Namespace: init_extfs */
39
40#include <config.h>
41
42#include <stdio.h>
43#include <ctype.h>
44#include <string.h>
45#include <stdlib.h>
46#include <sys/types.h>
47#include <sys/stat.h>
48#include <unistd.h>
49#include <signal.h>
50#include <fcntl.h>
51#include <errno.h>
52#include <sys/wait.h>
53
54#include "lib/global.h"
55#include "lib/fileloc.h"
56#include "lib/mcconfig.h"
57#include "lib/util.h"
58#include "lib/widget.h"         /* message() */
59
60#include "src/execute.h"        /* For shell_execute */
61
62#include "lib/vfs/vfs.h"
63#include "lib/vfs/utilvfs.h"
64#include "lib/vfs/gc.h"         /* vfs_rmstamp */
65
66#include "extfs.h"
67
68/*** global variables ****************************************************************************/
69
70GArray *extfs_plugins = NULL;
71
72/*** file scope macro definitions ****************************************************************/
73
74#undef ERRNOR
75#define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
76
77#define RECORDSIZE 512
78
79/*** file scope type declarations ****************************************************************/
80
81struct inode
82{
83    nlink_t nlink;
84    struct entry *first_in_subdir;      /* only used if this is a directory */
85    struct entry *last_in_subdir;
86    ino_t inode;                /* This is inode # */
87    dev_t dev;                  /* This is an internal identification of the extfs archive */
88    struct archive *archive;    /* And this is an archive structure */
89    dev_t rdev;
90    mode_t mode;
91    uid_t uid;
92    gid_t gid;
93    off_t size;
94    time_t mtime;
95    char *linkname;
96    time_t atime;
97    time_t ctime;
98    char *local_filename;
99};
100
101struct entry
102{
103    struct entry *next_in_dir;
104    struct entry *dir;
105    char *name;
106    struct inode *inode;
107};
108
109struct pseudofile
110{
111    struct archive *archive;
112    gboolean has_changed;
113    int local_handle;
114    struct entry *entry;
115};
116
117struct archive
118{
119    int fstype;
120    char *name;
121    char *local_name;
122    struct stat local_stat;
123    dev_t rdev;
124    int fd_usage;
125    ino_t inode_counter;
126    struct entry *root_entry;
127    struct archive *next;
128};
129
130typedef struct
131{
132    char *path;
133    char *prefix;
134    gboolean need_archive;
135} extfs_plugin_info_t;
136
137/*** file scope variables ************************************************************************/
138
139static gboolean errloop;
140static gboolean notadir;
141
142static struct vfs_class vfs_extfs_ops;
143static struct archive *first_archive = NULL;
144static int my_errno = 0;
145
146/*** file scope functions ************************************************************************/
147/* --------------------------------------------------------------------------------------------- */
148
149static void extfs_remove_entry (struct entry *e);
150static void extfs_free (vfsid id);
151static void extfs_free_entry (struct entry *e);
152static struct entry *extfs_resolve_symlinks_int (struct entry *entry, GSList * list);
153
154/* --------------------------------------------------------------------------------------------- */
155
156static void
157extfs_make_dots (struct entry *ent)
158{
159    struct entry *entry = g_new (struct entry, 1);
160    struct entry *parentry = ent->dir;
161    struct inode *inode = ent->inode, *parent;
162
163    parent = (parentry != NULL) ? parentry->inode : NULL;
164    entry->name = g_strdup (".");
165    entry->inode = inode;
166    entry->dir = ent;
167    inode->local_filename = NULL;
168    inode->first_in_subdir = entry;
169    inode->nlink++;
170
171    entry->next_in_dir = g_new (struct entry, 1);
172    entry = entry->next_in_dir;
173    entry->name = g_strdup ("..");
174    inode->last_in_subdir = entry;
175    entry->next_in_dir = NULL;
176    if (parent != NULL)
177    {
178        entry->inode = parent;
179        entry->dir = parentry;
180        parent->nlink++;
181    }
182    else
183    {
184        entry->inode = inode;
185        entry->dir = ent;
186        inode->nlink++;
187    }
188}
189
190/* --------------------------------------------------------------------------------------------- */
191
192static struct entry *
193extfs_generate_entry (struct archive *archive,
194                      const char *name, struct entry *parentry, mode_t mode)
195{
196    mode_t myumask;
197    struct inode *inode, *parent;
198    struct entry *entry;
199
200    parent = (parentry != NULL) ? parentry->inode : NULL;
201    entry = g_new (struct entry, 1);
202
203    entry->name = g_strdup (name);
204    entry->next_in_dir = NULL;
205    entry->dir = parentry;
206    if (parent != NULL)
207    {
208        parent->last_in_subdir->next_in_dir = entry;
209        parent->last_in_subdir = entry;
210    }
211    inode = g_new (struct inode, 1);
212    entry->inode = inode;
213    inode->local_filename = NULL;
214    inode->linkname = NULL;
215    inode->last_in_subdir = NULL;
216    inode->inode = (archive->inode_counter)++;
217    inode->dev = archive->rdev;
218    inode->archive = archive;
219    myumask = umask (022);
220    umask (myumask);
221    inode->mode = mode & ~myumask;
222    mode = inode->mode;
223    inode->rdev = 0;
224    inode->uid = getuid ();
225    inode->gid = getgid ();
226    inode->size = 0;
227    inode->mtime = time (NULL);
228    inode->atime = inode->mtime;
229    inode->ctime = inode->mtime;
230    inode->nlink = 1;
231    if (S_ISDIR (mode))
232        extfs_make_dots (entry);
233    return entry;
234}
235
236/* --------------------------------------------------------------------------------------------- */
237
238static struct entry *
239extfs_find_entry_int (struct entry *dir, const char *name, GSList * list,
240                      gboolean make_dirs, gboolean make_file)
241{
242    struct entry *pent, *pdir;
243    const char *p, *name_end;
244    char *q;
245    char c = PATH_SEP;
246
247    if (g_path_is_absolute (name))
248    {
249        /* Handle absolute paths */
250        name = (char *) g_path_skip_root (name);
251        dir = dir->inode->archive->root_entry;
252    }
253
254    pent = dir;
255    p = name;
256    name_end = name + strlen (name);
257
258    q = strchr (p, PATH_SEP);
259    if (q == '\0')
260        q = strchr (p, '\0');
261
262    while ((pent != NULL) && (c != '\0') && (*p != '\0'))
263    {
264        c = *q;
265        *q = '\0';
266
267        if (!DIR_IS_DOT (p))
268        {
269            if (DIR_IS_DOTDOT (p))
270                pent = pent->dir;
271            else
272            {
273                pent = extfs_resolve_symlinks_int (pent, list);
274                if (pent == NULL)
275                {
276                    *q = c;
277                    return NULL;
278                }
279                if (!S_ISDIR (pent->inode->mode))
280                {
281                    *q = c;
282                    notadir = TRUE;
283                    return NULL;
284                }
285
286                pdir = pent;
287                for (pent = pent->inode->first_in_subdir; pent != NULL; pent = pent->next_in_dir)
288                    /* Hack: I keep the original semanthic unless
289                       q+1 would break in the strchr */
290                    if (strcmp (pent->name, p) == 0)
291                    {
292                        if (q + 1 > name_end)
293                        {
294                            *q = c;
295                            notadir = !S_ISDIR (pent->inode->mode);
296                            return pent;
297                        }
298                        break;
299                    }
300
301                /* When we load archive, we create automagically
302                 * non-existent directories
303                 */
304                if (pent == NULL && make_dirs)
305                    pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
306                if (pent == NULL && make_file)
307                    pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
308            }
309        }
310        /* Next iteration */
311        *q = c;
312        p = q + 1;
313        q = strchr (p, PATH_SEP);
314        if (q == '\0')
315            q = strchr (p, '\0');
316    }
317    if (pent == NULL)
318        my_errno = ENOENT;
319    return pent;
320}
321
322/* --------------------------------------------------------------------------------------------- */
323
324static struct entry *
325extfs_find_entry (struct entry *dir, const char *name, gboolean make_dirs, gboolean make_file)
326{
327    struct entry *res;
328
329    errloop = FALSE;
330    notadir = FALSE;
331
332    res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
333    if (res == NULL)
334    {
335        if (errloop)
336            my_errno = ELOOP;
337        else if (notadir)
338            my_errno = ENOTDIR;
339    }
340    return res;
341}
342
343/* --------------------------------------------------------------------------------------------- */
344
345static void
346extfs_fill_names (struct vfs_class *me, fill_names_f func)
347{
348    struct archive *a = first_archive;
349
350    (void) me;
351
352    while (a != NULL)
353    {
354        extfs_plugin_info_t *info;
355        char *name;
356
357        info = &g_array_index (extfs_plugins, extfs_plugin_info_t, a->fstype);
358        name =
359            g_strconcat (a->name ? a->name : "", PATH_SEP_STR, info->prefix, VFS_PATH_URL_DELIMITER,
360                         (char *) NULL);
361        func (name);
362        g_free (name);
363        a = a->next;
364    }
365}
366
367/* --------------------------------------------------------------------------------------------- */
368
369static void
370extfs_free_archive (struct archive *archive)
371{
372    extfs_free_entry (archive->root_entry);
373    if (archive->local_name != NULL)
374    {
375        struct stat my;
376        vfs_path_t *local_name_vpath, *name_vpath;
377
378        local_name_vpath = vfs_path_from_str (archive->local_name);
379        name_vpath = vfs_path_from_str (archive->name);
380        mc_stat (local_name_vpath, &my);
381        mc_ungetlocalcopy (name_vpath, local_name_vpath,
382                           archive->local_stat.st_mtime != my.st_mtime);
383        vfs_path_free (local_name_vpath);
384        vfs_path_free (name_vpath);
385        g_free (archive->local_name);
386    }
387    g_free (archive->name);
388    g_free (archive);
389}
390
391/* --------------------------------------------------------------------------------------------- */
392
393static FILE *
394extfs_open_archive (int fstype, const char *name, struct archive **pparc)
395{
396    const extfs_plugin_info_t *info;
397    static dev_t archive_counter = 0;
398    FILE *result = NULL;
399    mode_t mode;
400    char *cmd;
401    struct stat mystat;
402    struct archive *current_archive;
403    struct entry *root_entry;
404    char *tmp = NULL;
405    vfs_path_t *local_name_vpath = NULL;
406    vfs_path_t *name_vpath;
407
408    name_vpath = vfs_path_from_str (name);
409    info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
410
411    if (info->need_archive)
412    {
413        if (mc_stat (name_vpath, &mystat) == -1)
414            goto ret;
415
416        if (!vfs_file_is_local (name_vpath))
417        {
418            local_name_vpath = mc_getlocalcopy (name_vpath);
419            if (local_name_vpath == NULL)
420                goto ret;
421        }
422
423        tmp = name_quote (vfs_path_get_last_path_str (name_vpath), FALSE);
424    }
425
426    cmd = g_strconcat (info->path, info->prefix, " list ",
427                       vfs_path_get_last_path_str (local_name_vpath) != NULL ?
428                       vfs_path_get_last_path_str (local_name_vpath) : tmp, (char *) NULL);
429    g_free (tmp);
430
431    open_error_pipe ();
432    result = popen (cmd, "r");
433    g_free (cmd);
434    if (result == NULL)
435    {
436        close_error_pipe (D_ERROR, NULL);
437        if (local_name_vpath != NULL)
438        {
439            mc_ungetlocalcopy (name_vpath, local_name_vpath, FALSE);
440            vfs_path_free (local_name_vpath);
441        }
442        goto ret;
443    }
444
445#ifdef ___QNXNTO__
446    setvbuf (result, NULL, _IONBF, 0);
447#endif
448
449    current_archive = g_new (struct archive, 1);
450    current_archive->fstype = fstype;
451    current_archive->name = g_strdup (name);
452    current_archive->local_name = g_strdup (vfs_path_get_last_path_str (local_name_vpath));
453
454    if (local_name_vpath != NULL)
455    {
456        mc_stat (local_name_vpath, &current_archive->local_stat);
457        vfs_path_free (local_name_vpath);
458    }
459    current_archive->inode_counter = 0;
460    current_archive->fd_usage = 0;
461    current_archive->rdev = archive_counter++;
462    current_archive->next = first_archive;
463    first_archive = current_archive;
464    mode = mystat.st_mode & 07777;
465    if (mode & 0400)
466        mode |= 0100;
467    if (mode & 0040)
468        mode |= 0010;
469    if (mode & 0004)
470        mode |= 0001;
471    mode |= S_IFDIR;
472    root_entry = extfs_generate_entry (current_archive, PATH_SEP_STR, NULL, mode);
473    root_entry->inode->uid = mystat.st_uid;
474    root_entry->inode->gid = mystat.st_gid;
475    root_entry->inode->atime = mystat.st_atime;
476    root_entry->inode->ctime = mystat.st_ctime;
477    root_entry->inode->mtime = mystat.st_mtime;
478    current_archive->root_entry = root_entry;
479
480    *pparc = current_archive;
481
482  ret:
483    vfs_path_free (name_vpath);
484    return result;
485}
486
487/* --------------------------------------------------------------------------------------------- */
488/**
489 * Main loop for reading an archive.
490 * Return 0 on success, -1 on error.
491 */
492
493static int
494extfs_read_archive (int fstype, const char *name, struct archive **pparc)
495{
496    FILE *extfsd;
497    const extfs_plugin_info_t *info;
498    char *buffer;
499    struct archive *current_archive;
500    char *current_file_name, *current_link_name;
501
502    info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
503
504    extfsd = extfs_open_archive (fstype, name, &current_archive);
505
506    if (extfsd == NULL)
507    {
508        message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s"), info->prefix, name);
509        return -1;
510    }
511
512    buffer = g_malloc (BUF_4K);
513    while (fgets (buffer, BUF_4K, extfsd) != NULL)
514    {
515        struct stat hstat;
516
517        current_link_name = NULL;
518        if (vfs_parse_ls_lga (buffer, &hstat, &current_file_name, &current_link_name, NULL))
519        {
520            struct entry *entry, *pent;
521            struct inode *inode;
522            char *p, *q, *cfn = current_file_name;
523
524            if (*cfn != '\0')
525            {
526                if (IS_PATH_SEP (*cfn))
527                    cfn++;
528                p = strchr (cfn, '\0');
529                if (p != cfn && IS_PATH_SEP (p[-1]))
530                    p[-1] = '\0';
531                p = strrchr (cfn, PATH_SEP);
532                if (p == NULL)
533                {
534                    p = cfn;
535                    q = strchr (cfn, '\0');
536                }
537                else
538                {
539                    *(p++) = '\0';
540                    q = cfn;
541                }
542                if (S_ISDIR (hstat.st_mode) && (DIR_IS_DOT (p) || DIR_IS_DOTDOT (p)))
543                    goto read_extfs_continue;
544                pent = extfs_find_entry (current_archive->root_entry, q, TRUE, FALSE);
545                if (pent == NULL)
546                {
547                    /* FIXME: Should clean everything one day */
548                    g_free (buffer);
549                    pclose (extfsd);
550                    close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
551                    return -1;
552                }
553                entry = g_new (struct entry, 1);
554                entry->name = g_strdup (p);
555                entry->next_in_dir = NULL;
556                entry->dir = pent;
557                if (pent->inode->last_in_subdir)
558                {
559                    pent->inode->last_in_subdir->next_in_dir = entry;
560                    pent->inode->last_in_subdir = entry;
561                }
562                if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL))
563                {
564                    pent = extfs_find_entry (current_archive->root_entry,
565                                             current_link_name, FALSE, FALSE);
566                    if (pent == NULL)
567                    {
568                        /* FIXME: Should clean everything one day */
569                        g_free (buffer);
570                        pclose (extfsd);
571                        close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
572                        return -1;
573                    }
574
575                    entry->inode = pent->inode;
576                    pent->inode->nlink++;
577                }
578                else
579                {
580                    inode = g_new (struct inode, 1);
581                    entry->inode = inode;
582                    inode->local_filename = NULL;
583                    inode->inode = (current_archive->inode_counter)++;
584                    inode->nlink = 1;
585                    inode->dev = current_archive->rdev;
586                    inode->archive = current_archive;
587                    inode->mode = hstat.st_mode;
588#ifdef HAVE_STRUCT_STAT_ST_RDEV
589                    inode->rdev = hstat.st_rdev;
590#else
591                    inode->rdev = 0;
592#endif
593                    inode->uid = hstat.st_uid;
594                    inode->gid = hstat.st_gid;
595                    inode->size = hstat.st_size;
596                    inode->mtime = hstat.st_mtime;
597                    inode->atime = hstat.st_atime;
598                    inode->ctime = hstat.st_ctime;
599                    inode->first_in_subdir = NULL;
600                    inode->last_in_subdir = NULL;
601                    if (current_link_name != NULL && S_ISLNK (hstat.st_mode))
602                    {
603                        inode->linkname = current_link_name;
604                        current_link_name = NULL;
605                    }
606                    else
607                    {
608                        if (S_ISLNK (hstat.st_mode))
609                            inode->mode &= ~S_IFLNK;    /* You *DON'T* want to do this always */
610                        inode->linkname = NULL;
611                    }
612                    if (S_ISDIR (hstat.st_mode))
613                        extfs_make_dots (entry);
614                }
615            }
616          read_extfs_continue:
617            g_free (current_file_name);
618            g_free (current_link_name);
619        }
620    }
621    g_free (buffer);
622
623    /* Check if extfs 'list' returned 0 */
624    if (pclose (extfsd) != 0)
625    {
626        extfs_free (current_archive);
627        close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
628        return -1;
629    }
630
631    close_error_pipe (D_ERROR, NULL);
632    *pparc = current_archive;
633    return 0;
634}
635
636/* --------------------------------------------------------------------------------------------- */
637
638static int
639extfs_which (struct vfs_class *me, const char *path)
640{
641    size_t path_len;
642    size_t i;
643
644    (void) me;
645
646    path_len = strlen (path);
647
648    for (i = 0; i < extfs_plugins->len; i++)
649    {
650        extfs_plugin_info_t *info;
651
652        info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
653
654        if ((strncmp (path, info->prefix, path_len) == 0)
655            && ((info->prefix[path_len] == '\0') || (info->prefix[path_len] == '+')))
656            return i;
657    }
658    return -1;
659}
660
661/* --------------------------------------------------------------------------------------------- */
662/**
663 * Dissect the path and create corresponding superblock.
664 */
665static const char *
666extfs_get_path_int (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
667{
668    char *archive_name;
669    int result = -1;
670    struct archive *parc;
671    int fstype;
672    const vfs_path_element_t *path_element;
673
674    path_element = vfs_path_get_by_index (vpath, -1);
675
676    fstype = extfs_which (path_element->class, path_element->vfs_prefix);
677    if (fstype == -1)
678        return NULL;
679
680    archive_name = vfs_path_to_str_elements_count (vpath, -1);
681
682    /*
683     * All filesystems should have some local archive, at least
684     * it can be PATH_SEP ('/').
685     */
686    for (parc = first_archive; parc != NULL; parc = parc->next)
687        if (parc->name != NULL)
688        {
689            if (strcmp (parc->name, archive_name) == 0)
690            {
691                vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
692                g_free (archive_name);
693                goto return_success;
694            }
695        }
696
697    result = do_not_open ? -1 : extfs_read_archive (fstype, archive_name, &parc);
698    g_free (archive_name);
699    if (result == -1)
700    {
701        path_element->class->verrno = EIO;
702        return NULL;
703    }
704
705  return_success:
706    *archive = parc;
707    return path_element->path;
708}
709
710/* --------------------------------------------------------------------------------------------- */
711/**
712 * Dissect the path and create corresponding superblock.
713 * The result should be freed.
714 */
715
716static char *
717extfs_get_path (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
718{
719    return g_strdup (extfs_get_path_int (vpath, archive, do_not_open));
720}
721
722/* --------------------------------------------------------------------------------------------- */
723/* Return allocated path (without leading slash) inside the archive  */
724
725static char *
726extfs_get_path_from_entry (struct entry *entry)
727{
728    GString *localpath;
729
730    localpath = g_string_new ("");
731
732    while (entry->dir != NULL)
733    {
734        g_string_prepend (localpath, entry->name);
735        if (entry->dir->dir != NULL)
736            g_string_prepend_c (localpath, PATH_SEP);
737        entry = entry->dir;
738    }
739
740    return g_string_free (localpath, FALSE);
741}
742
743/* --------------------------------------------------------------------------------------------- */
744
745static struct entry *
746extfs_resolve_symlinks_int (struct entry *entry, GSList * list)
747{
748    struct entry *pent = NULL;
749
750    if (!S_ISLNK (entry->inode->mode))
751        return entry;
752
753    if (g_slist_find (list, entry) != NULL)
754    {
755        /* Here we protect us against symlink looping */
756        errloop = TRUE;
757    }
758    else
759    {
760        GSList *looping;
761
762        looping = g_slist_prepend (list, entry);
763        pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, FALSE, FALSE);
764        looping = g_slist_delete_link (looping, looping);
765
766        if (pent == NULL)
767            my_errno = ENOENT;
768    }
769
770    return pent;
771}
772
773/* --------------------------------------------------------------------------------------------- */
774
775static struct entry *
776extfs_resolve_symlinks (struct entry *entry)
777{
778    struct entry *res;
779
780    errloop = FALSE;
781    notadir = FALSE;
782    res = extfs_resolve_symlinks_int (entry, NULL);
783    if (res == NULL)
784    {
785        if (errloop)
786            my_errno = ELOOP;
787        else if (notadir)
788            my_errno = ENOTDIR;
789    }
790    return res;
791}
792
793/* --------------------------------------------------------------------------------------------- */
794
795static char *
796extfs_get_archive_name (struct archive *archive)
797{
798    const char *archive_name;
799
800    if (archive->local_name)
801        archive_name = archive->local_name;
802    else
803        archive_name = archive->name;
804
805    if (!archive_name || !*archive_name)
806        return g_strdup ("no_archive_name");
807    else
808    {
809        char *ret_str;
810        vfs_path_t *vpath;
811        const vfs_path_element_t *path_element;
812
813        vpath = vfs_path_from_str (archive_name);
814        path_element = vfs_path_get_by_index (vpath, -1);
815        ret_str = g_strdup (path_element->path);
816        vfs_path_free (vpath);
817        return ret_str;
818    }
819}
820
821/* --------------------------------------------------------------------------------------------- */
822/** Don't pass localname as NULL */
823
824static int
825extfs_cmd (const char *str_extfs_cmd, struct archive *archive,
826           struct entry *entry, const char *localname)
827{
828    char *file;
829    char *quoted_file;
830    char *quoted_localname;
831    char *archive_name, *quoted_archive_name;
832    const extfs_plugin_info_t *info;
833    char *cmd;
834    int retval;
835
836    file = extfs_get_path_from_entry (entry);
837    quoted_file = name_quote (file, FALSE);
838    g_free (file);
839
840    archive_name = extfs_get_archive_name (archive);
841    quoted_archive_name = name_quote (archive_name, FALSE);
842    g_free (archive_name);
843    quoted_localname = name_quote (localname, FALSE);
844    info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
845    cmd = g_strconcat (info->path, info->prefix, str_extfs_cmd,
846                       quoted_archive_name, " ", quoted_file, " ", quoted_localname, (char *) NULL);
847    g_free (quoted_file);
848    g_free (quoted_localname);
849    g_free (quoted_archive_name);
850
851    open_error_pipe ();
852    retval = my_system (EXECUTE_AS_SHELL, mc_global.tty.shell, cmd);
853    g_free (cmd);
854    close_error_pipe (D_ERROR, NULL);
855    return retval;
856}
857
858/* --------------------------------------------------------------------------------------------- */
859
860static void
861extfs_run (const vfs_path_t * vpath)
862{
863    struct archive *archive = NULL;
864    char *p, *q, *archive_name, *quoted_archive_name;
865    char *cmd;
866    const extfs_plugin_info_t *info;
867
868    p = extfs_get_path (vpath, &archive, FALSE);
869    if (p == NULL)
870        return;
871    q = name_quote (p, FALSE);
872    g_free (p);
873
874    archive_name = extfs_get_archive_name (archive);
875    quoted_archive_name = name_quote (archive_name, FALSE);
876    g_free (archive_name);
877    info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
878    cmd =
879        g_strconcat (info->path, info->prefix, " run ", quoted_archive_name, " ", q, (char *) NULL);
880    g_free (quoted_archive_name);
881    g_free (q);
882    shell_execute (cmd, 0);
883    g_free (cmd);
884}
885
886/* --------------------------------------------------------------------------------------------- */
887
888static void *
889extfs_open (const vfs_path_t * vpath, int flags, mode_t mode)
890{
891    struct pseudofile *extfs_info;
892    struct archive *archive = NULL;
893    char *q;
894    struct entry *entry;
895    int local_handle;
896    gboolean created = FALSE;
897
898    q = extfs_get_path (vpath, &archive, FALSE);
899    if (q == NULL)
900        return NULL;
901    entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
902    if ((entry == NULL) && ((flags & O_CREAT) != 0))
903    {
904        /* Create new entry */
905        entry = extfs_find_entry (archive->root_entry, q, FALSE, TRUE);
906        created = (entry != NULL);
907    }
908
909    g_free (q);
910    if (entry == NULL)
911        return NULL;
912    entry = extfs_resolve_symlinks (entry);
913    if (entry == NULL)
914        return NULL;
915
916    if (S_ISDIR (entry->inode->mode))
917        ERRNOR (EISDIR, NULL);
918
919    if (entry->inode->local_filename == NULL)
920    {
921        vfs_path_t *local_filename_vpath;
922        const char *local_filename;
923
924        local_handle = vfs_mkstemps (&local_filename_vpath, "extfs", entry->name);
925
926        if (local_handle == -1)
927            return NULL;
928        close (local_handle);
929        local_filename = vfs_path_get_by_index (local_filename_vpath, -1)->path;
930
931        if (!created && ((flags & O_TRUNC) == 0)
932            && extfs_cmd (" copyout ", archive, entry, local_filename))
933        {
934            unlink (local_filename);
935            vfs_path_free (local_filename_vpath);
936            my_errno = EIO;
937            return NULL;
938        }
939        entry->inode->local_filename = g_strdup (local_filename);
940        vfs_path_free (local_filename_vpath);
941    }
942
943    local_handle = open (entry->inode->local_filename, NO_LINEAR (flags), mode);
944
945    if (local_handle == -1)
946    {
947        /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
948        flags = ~O_CREAT & (NO_LINEAR (flags) | O_TRUNC);
949        local_handle = open (entry->inode->local_filename, flags, mode);
950    }
951
952    if (local_handle == -1)
953        ERRNOR (EIO, NULL);
954
955    extfs_info = g_new (struct pseudofile, 1);
956    extfs_info->archive = archive;
957    extfs_info->entry = entry;
958    extfs_info->has_changed = created;
959    extfs_info->local_handle = local_handle;
960
961    /* i.e. we had no open files and now we have one */
962    vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
963    archive->fd_usage++;
964    return extfs_info;
965}
966
967/* --------------------------------------------------------------------------------------------- */
968
969static ssize_t
970extfs_read (void *data, char *buffer, size_t count)
971{
972    struct pseudofile *file = (struct pseudofile *) data;
973
974    return read (file->local_handle, buffer, count);
975}
976
977/* --------------------------------------------------------------------------------------------- */
978
979static int
980extfs_close (void *data)
981{
982    struct pseudofile *file;
983    int errno_code = 0;
984    file = (struct pseudofile *) data;
985
986    close (file->local_handle);
987
988    /* Commit the file if it has changed */
989    if (file->has_changed)
990    {
991        struct stat file_status;
992
993        if (extfs_cmd (" copyin ", file->archive, file->entry, file->entry->inode->local_filename))
994            errno_code = EIO;
995
996        if (stat (file->entry->inode->local_filename, &file_status) != 0)
997            errno_code = EIO;
998        else
999            file->entry->inode->size = file_status.st_size;
1000
1001        file->entry->inode->mtime = time (NULL);
1002    }
1003
1004    if (--file->archive->fd_usage == 0)
1005        vfs_stamp_create (&vfs_extfs_ops, file->archive);
1006
1007    g_free (data);
1008    if (errno_code != 0)
1009        ERRNOR (EIO, -1);
1010    return 0;
1011}
1012
1013/* --------------------------------------------------------------------------------------------- */
1014
1015static int
1016extfs_errno (struct vfs_class *me)
1017{
1018    (void) me;
1019    return my_errno;
1020}
1021
1022/* --------------------------------------------------------------------------------------------- */
1023
1024static void *
1025extfs_opendir (const vfs_path_t * vpath)
1026{
1027    struct archive *archive = NULL;
1028    char *q;
1029    struct entry *entry;
1030    struct entry **info;
1031
1032    q = extfs_get_path (vpath, &archive, FALSE);
1033    if (q == NULL)
1034        return NULL;
1035    entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1036    g_free (q);
1037    if (entry == NULL)
1038        return NULL;
1039    entry = extfs_resolve_symlinks (entry);
1040    if (entry == NULL)
1041        return NULL;
1042    if (!S_ISDIR (entry->inode->mode))
1043        ERRNOR (ENOTDIR, NULL);
1044
1045    info = g_new (struct entry *, 2);
1046    info[0] = entry->inode->first_in_subdir;
1047    info[1] = entry->inode->first_in_subdir;
1048
1049    return info;
1050}
1051
1052/* --------------------------------------------------------------------------------------------- */
1053
1054static void *
1055extfs_readdir (void *data)
1056{
1057    static union vfs_dirent dir;
1058    struct entry **info = (struct entry **) data;
1059
1060    if (*info == NULL)
1061        return NULL;
1062
1063    g_strlcpy (dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
1064
1065    compute_namelen (&dir.dent);
1066    *info = (*info)->next_in_dir;
1067
1068    return (void *) &dir;
1069}
1070
1071/* --------------------------------------------------------------------------------------------- */
1072
1073static int
1074extfs_closedir (void *data)
1075{
1076    g_free (data);
1077    return 0;
1078}
1079
1080/* --------------------------------------------------------------------------------------------- */
1081
1082static void
1083extfs_stat_move (struct stat *buf, const struct inode *inode)
1084{
1085    buf->st_dev = inode->dev;
1086    buf->st_ino = inode->inode;
1087    buf->st_mode = inode->mode;
1088    buf->st_nlink = inode->nlink;
1089    buf->st_uid = inode->uid;
1090    buf->st_gid = inode->gid;
1091#ifdef HAVE_STRUCT_STAT_ST_RDEV
1092    buf->st_rdev = inode->rdev;
1093#endif
1094    buf->st_size = inode->size;
1095#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1096    buf->st_blksize = RECORDSIZE;
1097#endif
1098#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
1099    buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
1100#endif
1101    buf->st_atime = inode->atime;
1102    buf->st_mtime = inode->mtime;
1103    buf->st_ctime = inode->ctime;
1104}
1105
1106/* --------------------------------------------------------------------------------------------- */
1107
1108static int
1109extfs_internal_stat (const vfs_path_t * vpath, struct stat *buf, gboolean resolve)
1110{
1111    struct archive *archive;
1112    const char *q;
1113    struct entry *entry;
1114    int result = -1;
1115
1116    q = extfs_get_path_int (vpath, &archive, FALSE);
1117    if (q == NULL)
1118        goto cleanup;
1119    entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1120    if (entry == NULL)
1121        goto cleanup;
1122    if (resolve)
1123    {
1124        entry = extfs_resolve_symlinks (entry);
1125        if (entry == NULL)
1126            goto cleanup;
1127    }
1128    extfs_stat_move (buf, entry->inode);
1129    result = 0;
1130  cleanup:
1131    return result;
1132}
1133
1134/* --------------------------------------------------------------------------------------------- */
1135
1136static int
1137extfs_stat (const vfs_path_t * vpath, struct stat *buf)
1138{
1139    return extfs_internal_stat (vpath, buf, TRUE);
1140}
1141
1142/* --------------------------------------------------------------------------------------------- */
1143
1144static int
1145extfs_lstat (const vfs_path_t * vpath, struct stat *buf)
1146{
1147    return extfs_internal_stat (vpath, buf, FALSE);
1148}
1149
1150/* --------------------------------------------------------------------------------------------- */
1151
1152static int
1153extfs_fstat (void *data, struct stat *buf)
1154{
1155    struct pseudofile *file = (struct pseudofile *) data;
1156    struct stat tmp_stat;
1157
1158    if (!stat(file->entry->inode->local_filename,&tmp_stat))
1159        {
1160            file->entry->inode->mtime = tmp_stat.st_mtime;
1161            file->entry->inode->atime = tmp_stat.st_atime;
1162        }
1163    extfs_stat_move (buf, file->entry->inode);
1164    return 0;
1165}
1166
1167/* --------------------------------------------------------------------------------------------- */
1168
1169static int
1170extfs_readlink (const vfs_path_t * vpath, char *buf, size_t size)
1171{
1172    struct archive *archive;
1173    const char *q;
1174    size_t len;
1175    struct entry *entry;
1176    int result = -1;
1177
1178    q = extfs_get_path_int (vpath, &archive, FALSE);
1179    if (q == NULL)
1180        goto cleanup;
1181    entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1182    if (entry == NULL)
1183        goto cleanup;
1184    if (!S_ISLNK (entry->inode->mode))
1185    {
1186        const vfs_path_element_t *path_element;
1187
1188        path_element = vfs_path_get_by_index (vpath, -1);
1189        path_element->class->verrno = EINVAL;
1190        goto cleanup;
1191    }
1192    len = strlen (entry->inode->linkname);
1193    if (size < len)
1194        len = size;
1195    /* readlink() does not append a NUL character to buf */
1196    result = len;
1197    memcpy (buf, entry->inode->linkname, result);
1198  cleanup:
1199    return result;
1200}
1201
1202/* --------------------------------------------------------------------------------------------- */
1203
1204static int
1205extfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
1206{
1207    (void) vpath;
1208    (void) owner;
1209    (void) group;
1210    return 0;
1211}
1212
1213/* --------------------------------------------------------------------------------------------- */
1214
1215static int
1216extfs_chmod (const vfs_path_t * vpath, mode_t mode)
1217{
1218    (void) vpath;
1219    (void) mode;
1220    return 0;
1221}
1222
1223/* --------------------------------------------------------------------------------------------- */
1224
1225static ssize_t
1226extfs_write (void *data, const char *buf, size_t nbyte)
1227{
1228    struct pseudofile *file = (struct pseudofile *) data;
1229
1230    file->has_changed = TRUE;
1231    return write (file->local_handle, buf, nbyte);
1232}
1233
1234/* --------------------------------------------------------------------------------------------- */
1235
1236static int
1237extfs_unlink (const vfs_path_t * vpath)
1238{
1239    struct archive *archive;
1240    const char *q;
1241    struct entry *entry;
1242    int result = -1;
1243
1244    q = extfs_get_path_int (vpath, &archive, FALSE);
1245    if (q == NULL)
1246        goto cleanup;
1247    entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1248    if (entry == NULL)
1249        goto cleanup;
1250    entry = extfs_resolve_symlinks (entry);
1251    if (entry == NULL)
1252        goto cleanup;
1253    if (S_ISDIR (entry->inode->mode))
1254    {
1255        const vfs_path_element_t *path_element;
1256
1257        path_element = vfs_path_get_by_index (vpath, -1);
1258        path_element->class->verrno = EISDIR;
1259        goto cleanup;
1260    }
1261    if (extfs_cmd (" rm ", archive, entry, ""))
1262    {
1263        my_errno = EIO;
1264        goto cleanup;
1265    }
1266    extfs_remove_entry (entry);
1267    result = 0;
1268  cleanup:
1269    return result;
1270}
1271
1272/* --------------------------------------------------------------------------------------------- */
1273
1274static int
1275extfs_mkdir (const vfs_path_t * vpath, mode_t mode)
1276{
1277    struct archive *archive;
1278    const char *q;
1279    struct entry *entry;
1280    int result = -1;
1281    const vfs_path_element_t *path_element;
1282
1283    (void) mode;
1284
1285    path_element = vfs_path_get_by_index (vpath, -1);
1286    q = extfs_get_path_int (vpath, &archive, FALSE);
1287    if (q == NULL)
1288        goto cleanup;
1289    entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1290    if (entry != NULL)
1291    {
1292        path_element->class->verrno = EEXIST;
1293        goto cleanup;
1294    }
1295    entry = extfs_find_entry (archive->root_entry, q, TRUE, FALSE);
1296    if (entry == NULL)
1297        goto cleanup;
1298    entry = extfs_resolve_symlinks (entry);
1299    if (entry == NULL)
1300        goto cleanup;
1301    if (!S_ISDIR (entry->inode->mode))
1302    {
1303        path_element->class->verrno = ENOTDIR;
1304        goto cleanup;
1305    }
1306
1307    if (extfs_cmd (" mkdir ", archive, entry, ""))
1308    {
1309        my_errno = EIO;
1310        extfs_remove_entry (entry);
1311        goto cleanup;
1312    }
1313    result = 0;
1314  cleanup:
1315    return result;
1316}
1317
1318/* --------------------------------------------------------------------------------------------- */
1319
1320static int
1321extfs_rmdir (const vfs_path_t * vpath)
1322{
1323    struct archive *archive;
1324    const char *q;
1325    struct entry *entry;
1326    int result = -1;
1327
1328    q = extfs_get_path_int (vpath, &archive, FALSE);
1329    if (q == NULL)
1330        goto cleanup;
1331    entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1332    if (entry == NULL)
1333        goto cleanup;
1334    entry = extfs_resolve_symlinks (entry);
1335    if (entry == NULL)
1336        goto cleanup;
1337    if (!S_ISDIR (entry->inode->mode))
1338    {
1339        const vfs_path_element_t *path_element;
1340
1341        path_element = vfs_path_get_by_index (vpath, -1);
1342        path_element->class->verrno = ENOTDIR;
1343        goto cleanup;
1344    }
1345
1346    if (extfs_cmd (" rmdir ", archive, entry, ""))
1347    {
1348        my_errno = EIO;
1349        goto cleanup;
1350    }
1351    extfs_remove_entry (entry);
1352    result = 0;
1353  cleanup:
1354    return result;
1355}
1356
1357/* --------------------------------------------------------------------------------------------- */
1358
1359static int
1360extfs_chdir (const vfs_path_t * vpath)
1361{
1362    struct archive *archive = NULL;
1363    char *q;
1364    struct entry *entry;
1365
1366    my_errno = ENOTDIR;
1367    q = extfs_get_path (vpath, &archive, FALSE);
1368    if (q == NULL)
1369        return -1;
1370    entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
1371    g_free (q);
1372    if (entry == NULL)
1373        return -1;
1374    entry = extfs_resolve_symlinks (entry);
1375    if ((entry == NULL) || (!S_ISDIR (entry->inode->mode)))
1376        return -1;
1377    my_errno = 0;
1378    return 0;
1379}
1380
1381/* --------------------------------------------------------------------------------------------- */
1382
1383static off_t
1384extfs_lseek (void *data, off_t offset, int whence)
1385{
1386    struct pseudofile *file = (struct pseudofile *) data;
1387
1388    return lseek (file->local_handle, offset, whence);
1389}
1390
1391/* --------------------------------------------------------------------------------------------- */
1392
1393static vfsid
1394extfs_getid (const vfs_path_t * vpath)
1395{
1396    struct archive *archive = NULL;
1397    char *p;
1398
1399    p = extfs_get_path (vpath, &archive, TRUE);
1400    if (p == NULL)
1401        return NULL;
1402    g_free (p);
1403    return (vfsid) archive;
1404}
1405
1406/* --------------------------------------------------------------------------------------------- */
1407
1408static int
1409extfs_nothingisopen (vfsid id)
1410{
1411    return (((struct archive *) id)->fd_usage <= 0);
1412}
1413
1414/* --------------------------------------------------------------------------------------------- */
1415
1416static void
1417extfs_remove_entry (struct entry *e)
1418{
1419    int i = --e->inode->nlink;
1420    struct entry *pe, *ent, *prev;
1421
1422    if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1423    {
1424        struct entry *f = e->inode->first_in_subdir;
1425        e->inode->first_in_subdir = NULL;
1426        extfs_remove_entry (f);
1427    }
1428    pe = e->dir;
1429    if (e == pe->inode->first_in_subdir)
1430        pe->inode->first_in_subdir = e->next_in_dir;
1431
1432    prev = NULL;
1433    for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir; ent = ent->next_in_dir)
1434        if (e == ent->next_in_dir)
1435        {
1436            prev = ent;
1437            break;
1438        }
1439    if (prev)
1440        prev->next_in_dir = e->next_in_dir;
1441    if (e == pe->inode->last_in_subdir)
1442        pe->inode->last_in_subdir = prev;
1443
1444    if (i <= 0)
1445    {
1446        if (e->inode->local_filename != NULL)
1447        {
1448            unlink (e->inode->local_filename);
1449            g_free (e->inode->local_filename);
1450        }
1451        g_free (e->inode->linkname);
1452        g_free (e->inode);
1453    }
1454
1455    g_free (e->name);
1456    g_free (e);
1457}
1458
1459/* --------------------------------------------------------------------------------------------- */
1460
1461static void
1462extfs_free_entry (struct entry *e)
1463{
1464    int i = --e->inode->nlink;
1465
1466    if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
1467    {
1468        struct entry *f = e->inode->first_in_subdir;
1469
1470        e->inode->first_in_subdir = NULL;
1471        extfs_free_entry (f);
1472    }
1473    if (i <= 0)
1474    {
1475        if (e->inode->local_filename != NULL)
1476        {
1477            unlink (e->inode->local_filename);
1478            g_free (e->inode->local_filename);
1479        }
1480        g_free (e->inode->linkname);
1481        g_free (e->inode);
1482    }
1483    if (e->next_in_dir != NULL)
1484        extfs_free_entry (e->next_in_dir);
1485    g_free (e->name);
1486    g_free (e);
1487}
1488
1489/* --------------------------------------------------------------------------------------------- */
1490
1491static void
1492extfs_free (vfsid id)
1493{
1494    struct archive *archive = (struct archive *) id;
1495
1496    if (archive == first_archive)
1497    {
1498        first_archive = archive->next;
1499    }
1500    else
1501    {
1502        struct archive *parc;
1503        for (parc = first_archive; parc != NULL; parc = parc->next)
1504            if (parc->next == archive)
1505            {
1506                parc->next = archive->next;
1507                break;
1508            }
1509    }
1510    extfs_free_archive (archive);
1511}
1512
1513/* --------------------------------------------------------------------------------------------- */
1514
1515static vfs_path_t *
1516extfs_getlocalcopy (const vfs_path_t * vpath)
1517{
1518    struct pseudofile *fp;
1519    vfs_path_t *p;
1520
1521    fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1522    if (fp == NULL)
1523        return NULL;
1524    if (fp->entry->inode->local_filename == NULL)
1525    {
1526        extfs_close ((void *) fp);
1527        return NULL;
1528    }
1529    p = vfs_path_from_str (fp->entry->inode->local_filename);
1530    fp->archive->fd_usage++;
1531    extfs_close ((void *) fp);
1532    return p;
1533}
1534
1535/* --------------------------------------------------------------------------------------------- */
1536
1537static int
1538extfs_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed)
1539{
1540    struct pseudofile *fp;
1541
1542    fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
1543    if (fp == NULL)
1544        return 0;
1545
1546    if (strcmp (fp->entry->inode->local_filename, vfs_path_get_last_path_str (local)) == 0)
1547    {
1548        fp->archive->fd_usage--;
1549        if (has_changed)
1550            fp->has_changed = TRUE;
1551        extfs_close ((void *) fp);
1552        return 0;
1553    }
1554    else
1555    {
1556        /* Should not happen */
1557        extfs_close ((void *) fp);
1558        return 0;
1559    }
1560}
1561
1562/* --------------------------------------------------------------------------------------------- */
1563
1564static gboolean
1565extfs_get_plugins (const char *where, gboolean silent)
1566{
1567    char *dirname;
1568    GDir *dir;
1569    const char *filename;
1570
1571    dirname = g_build_path (PATH_SEP_STR, where, MC_EXTFS_DIR, (char *) NULL);
1572    dir = g_dir_open (dirname, 0, NULL);
1573
1574    /* We may not use vfs_die() message or message or similar,
1575     * UI is not initialized at this time and message would not
1576     * appear on screen. */
1577    if (dir == NULL)
1578    {
1579        if (!silent)
1580            fprintf (stderr, _("Warning: cannot open %s directory\n"), dirname);
1581        g_free (dirname);
1582        return FALSE;
1583    }
1584
1585    if (extfs_plugins == NULL)
1586        extfs_plugins = g_array_sized_new (FALSE, TRUE, sizeof (extfs_plugin_info_t), 32);
1587
1588    while ((filename = g_dir_read_name (dir)) != NULL)
1589    {
1590        char fullname[MC_MAXPATHLEN];
1591        struct stat s;
1592
1593        g_snprintf (fullname, sizeof (fullname), "%s" PATH_SEP_STR "%s", dirname, filename);
1594
1595        if ((stat (fullname, &s) == 0)
1596            && S_ISREG (s.st_mode) && !S_ISDIR (s.st_mode)
1597            && (((s.st_mode & S_IXOTH) != 0) ||
1598                ((s.st_mode & S_IXUSR) != 0) || ((s.st_mode & S_IXGRP) != 0)))
1599        {
1600            int f;
1601
1602            f = open (fullname, O_RDONLY);
1603
1604            if (f >= 0)
1605            {
1606                size_t len, i;
1607                extfs_plugin_info_t info;
1608                gboolean found = FALSE;
1609
1610                close (f);
1611
1612                /* Handle those with a trailing '+', those flag that the
1613                 * file system does not require an archive to work
1614                 */
1615                len = strlen (filename);
1616                info.need_archive = (filename[len - 1] != '+');
1617                info.path = g_strconcat (dirname, PATH_SEP_STR, (char *) NULL);
1618                info.prefix = g_strdup (filename);
1619
1620                /* prepare to compare file names without trailing '+' */
1621                if (!info.need_archive)
1622                    info.prefix[len - 1] = '\0';
1623
1624                /* don't overload already found plugin */
1625                for (i = 0; i < extfs_plugins->len; i++)
1626                {
1627                    extfs_plugin_info_t *p;
1628
1629                    p = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1630
1631                    /* 2 files with same names cannot be in a directory */
1632                    if ((strcmp (info.path, p->path) != 0)
1633                        && (strcmp (info.prefix, p->prefix) == 0))
1634                    {
1635                        found = TRUE;
1636                        break;
1637                    }
1638                }
1639
1640                if (found)
1641                {
1642                    g_free (info.path);
1643                    g_free (info.prefix);
1644                }
1645                else
1646                {
1647                    /* restore file name */
1648                    if (!info.need_archive)
1649                        info.prefix[len - 1] = '+';
1650                    g_array_append_val (extfs_plugins, info);
1651                }
1652            }
1653        }
1654    }
1655
1656    g_dir_close (dir);
1657    g_free (dirname);
1658
1659    return TRUE;
1660}
1661
1662/* --------------------------------------------------------------------------------------------- */
1663
1664static int
1665extfs_init (struct vfs_class *me)
1666{
1667    gboolean d1, d2;
1668
1669    (void) me;
1670
1671    /* 1st: scan user directory */
1672    d1 = extfs_get_plugins (mc_config_get_data_path (), TRUE);  /* silent about user dir */
1673    /* 2nd: scan system dir */
1674    d2 = extfs_get_plugins (LIBEXECDIR, d1);
1675
1676    return (d1 || d2 ? 1 : 0);
1677}
1678
1679/* --------------------------------------------------------------------------------------------- */
1680
1681static void
1682extfs_done (struct vfs_class *me)
1683{
1684    size_t i;
1685    struct archive *ar;
1686
1687    (void) me;
1688
1689    for (ar = first_archive; ar != NULL;)
1690    {
1691        extfs_free ((vfsid) ar);
1692        ar = first_archive;
1693    }
1694
1695    for (i = 0; i < extfs_plugins->len; i++)
1696    {
1697        extfs_plugin_info_t *info;
1698
1699        info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
1700        g_free (info->path);
1701        g_free (info->prefix);
1702    }
1703
1704    if (extfs_plugins != NULL)
1705        g_array_free (extfs_plugins, TRUE);
1706}
1707
1708/* --------------------------------------------------------------------------------------------- */
1709
1710static int
1711extfs_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
1712{
1713    (void) arg;
1714
1715    if (ctlop == VFS_SETCTL_RUN)
1716    {
1717        extfs_run (vpath);
1718        return 1;
1719    }
1720    return 0;
1721}
1722
1723/* --------------------------------------------------------------------------------------------- */
1724/*** public functions ****************************************************************************/
1725/* --------------------------------------------------------------------------------------------- */
1726
1727void
1728init_extfs (void)
1729{
1730    vfs_extfs_ops.name = "extfs";
1731    vfs_extfs_ops.init = extfs_init;
1732    vfs_extfs_ops.done = extfs_done;
1733    vfs_extfs_ops.fill_names = extfs_fill_names;
1734    vfs_extfs_ops.which = extfs_which;
1735    vfs_extfs_ops.open = extfs_open;
1736    vfs_extfs_ops.close = extfs_close;
1737    vfs_extfs_ops.read = extfs_read;
1738    vfs_extfs_ops.write = extfs_write;
1739    vfs_extfs_ops.opendir = extfs_opendir;
1740    vfs_extfs_ops.readdir = extfs_readdir;
1741    vfs_extfs_ops.closedir = extfs_closedir;
1742    vfs_extfs_ops.stat = extfs_stat;
1743    vfs_extfs_ops.lstat = extfs_lstat;
1744    vfs_extfs_ops.fstat = extfs_fstat;
1745    vfs_extfs_ops.chmod = extfs_chmod;
1746    vfs_extfs_ops.chown = extfs_chown;
1747    vfs_extfs_ops.readlink = extfs_readlink;
1748    vfs_extfs_ops.unlink = extfs_unlink;
1749    vfs_extfs_ops.chdir = extfs_chdir;
1750    vfs_extfs_ops.ferrno = extfs_errno;
1751    vfs_extfs_ops.lseek = extfs_lseek;
1752    vfs_extfs_ops.getid = extfs_getid;
1753    vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
1754    vfs_extfs_ops.free = extfs_free;
1755    vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
1756    vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
1757    vfs_extfs_ops.mkdir = extfs_mkdir;
1758    vfs_extfs_ops.rmdir = extfs_rmdir;
1759    vfs_extfs_ops.setctl = extfs_setctl;
1760    vfs_register_class (&vfs_extfs_ops);
1761}
1762
1763/* --------------------------------------------------------------------------------------------- */