mirror of
https://github.com/MidnightCommander/mc
synced 2024-12-23 04:46:55 +03:00
39ad0b489a
* extfs.c, shared.c, tar.c: I changed semantics in my previous patch - _get_path() now modifies strings passed to it. I thought that I were carefull enough not to introduce bugs. You guessed it - I was not. [Many tar_ and extfs_ functions modify strings passed to them, but as they are only called from vfs layer, it does not matter much.] So I renamed function to alert everyone that semantics changed. Fixed bugs in timestamping (and segfaults) when you entered tar inside zip file or similar. * vfs.c (mc_open): Die if the filesystem doesn't support open
1528 lines
36 KiB
C
1528 lines
36 KiB
C
/* Virtual File System switch code
|
|
Copyright (C) 1995 The Free Software Foundation
|
|
|
|
Written by: 1995 Miguel de Icaza
|
|
1995 Jakub Jelinek
|
|
1998 Pavel Machek
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
#define _VFS_VFS_C 1
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h> /* For atol() */
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <malloc.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#ifdef SCO_FLAVOR
|
|
#include <sys/timeb.h> /* alex: for struct timeb definition */
|
|
#endif /* SCO_FLAVOR */
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include "../src/fs.h"
|
|
#include "../src/mad.h"
|
|
#include "../src/dir.h"
|
|
#include "../src/util.h"
|
|
#include "../src/main.h"
|
|
#ifndef VFS_STANDALONE
|
|
#include "../src/panel.h"
|
|
#include "../src/key.h" /* Required for the async alarm handler */
|
|
#include "../src/layout.h" /* For get_panel_widget and get_other_index */
|
|
#endif
|
|
#include "vfs.h"
|
|
#include "mcfs.h"
|
|
#include "names.h"
|
|
#include "extfs.h"
|
|
#ifdef USE_NETCODE
|
|
# include "tcputil.h"
|
|
#endif
|
|
|
|
extern int get_other_type (void);
|
|
extern int extfs_which (char *path);
|
|
|
|
int vfs_timeout = 60; /* VFS timeout in seconds */
|
|
|
|
extern int cd_symlinks; /* Defined in main.c */
|
|
|
|
/* They keep track of the current directory */
|
|
static vfs *current_vfs = &local_vfs_ops;
|
|
char *current_dir = NULL;
|
|
|
|
static int current_mon;
|
|
static int current_year;
|
|
|
|
/* Open files managed by the vfs layer */
|
|
#define MAX_VFS_FILES 100
|
|
static struct {
|
|
void *fs_info;
|
|
vfs *operations;
|
|
} vfs_file_table [MAX_VFS_FILES];
|
|
|
|
static int get_bucket ()
|
|
{
|
|
int i;
|
|
|
|
/* 0, 1, 2 are reserved file descriptors, while (DIR *) 0 means error */
|
|
for (i = 3; i < MAX_VFS_FILES; i++){
|
|
if (!vfs_file_table [i].fs_info)
|
|
return i;
|
|
}
|
|
fprintf (stderr, "No more virtual file handles\n");
|
|
exit (1);
|
|
}
|
|
|
|
vfs *vfs_type_from_op (char *path)
|
|
{
|
|
#ifdef USE_NETCODE
|
|
if (!strncmp (path, "mc:", 3))
|
|
return &mcfs_vfs_ops;
|
|
if (!strncmp (path, "ftp:", 4))
|
|
return &ftpfs_vfs_ops;
|
|
#endif
|
|
#ifdef USE_EXT2FSLIB
|
|
if (!strcmp (path, "undel"))
|
|
return &undelfs_vfs_ops;
|
|
#endif
|
|
if (!strcmp (path, "utar"))
|
|
return &tarfs_vfs_ops;
|
|
if (extfs_which (path) != -1)
|
|
return &extfs_vfs_ops;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Splits path '/p1:op/inpath' into inpath,op; returns which vfs it is.
|
|
* What is left in path is p1. You still want to free(path), you DON'T
|
|
* want to free neither *inpath nor *op
|
|
*/
|
|
vfs *vfs_split (char *path, char **inpath, char **op)
|
|
{
|
|
char *semi = strrchr (path, '#');
|
|
char *slash;
|
|
vfs *ret;
|
|
|
|
if (!semi)
|
|
return NULL;
|
|
slash = strchr (semi, '/');
|
|
*semi = 0;
|
|
if (op)
|
|
*op = NULL;
|
|
if (inpath)
|
|
*inpath = NULL;
|
|
if (slash)
|
|
*slash = 0;
|
|
if ((ret = vfs_type_from_op (semi+1))){
|
|
if (op)
|
|
*op = semi+1;
|
|
if (inpath)
|
|
*inpath = slash?slash+1:NULL;
|
|
return ret;
|
|
}
|
|
if (slash)
|
|
*slash = '/';
|
|
ret = vfs_split (path, inpath, op);
|
|
*semi = '#';
|
|
return ret;
|
|
}
|
|
|
|
vfs*
|
|
vfs_rosplit (char *path)
|
|
{
|
|
char *semi = strrchr (path, '#');
|
|
char *slash;
|
|
vfs *ret;
|
|
|
|
if (!semi)
|
|
return NULL;
|
|
slash = strchr (semi, '/');
|
|
*semi = 0;
|
|
if (slash)
|
|
*slash = 0;
|
|
|
|
ret = vfs_type_from_op (semi+1);
|
|
if (slash)
|
|
*slash = '/';
|
|
if (!ret)
|
|
ret = vfs_rosplit (path);
|
|
*semi = '#';
|
|
return ret;
|
|
}
|
|
|
|
vfs *vfs_type (char *path)
|
|
{
|
|
vfs *vfs = vfs_rosplit(path);
|
|
if (!vfs)
|
|
vfs = &local_vfs_ops;
|
|
return vfs;
|
|
}
|
|
|
|
static struct vfs_stamping *stamps;
|
|
|
|
/* Returns the number of seconds remaining to the vfs timeout
|
|
*
|
|
* FIXME: currently this is set to 10 seconds. We should compute this.
|
|
*/
|
|
int vfs_timeouts ()
|
|
{
|
|
return stamps ? 10 : 0;
|
|
}
|
|
|
|
void vfs_addstamp (vfs *v, vfsid id, struct vfs_stamping *parent)
|
|
{
|
|
if (v != &local_vfs_ops && id != (vfsid)-1){
|
|
struct vfs_stamping *stamp, *st1;
|
|
|
|
for (stamp = stamps; stamp != NULL; st1 = stamp, stamp = stamp->next)
|
|
if (stamp->v == v && stamp->id == id){
|
|
gettimeofday(&(stamp->time), NULL);
|
|
return;
|
|
}
|
|
stamp = xmalloc (sizeof (struct vfs_stamping), "vfs stamping");
|
|
stamp->v = v;
|
|
stamp->id = id;
|
|
if (parent){
|
|
struct vfs_stamping *st = stamp;
|
|
for (; parent;){
|
|
st->parent = xmalloc (sizeof (struct vfs_stamping), "vfs stamping");
|
|
*st->parent = *parent;
|
|
parent = parent->parent;
|
|
st = st->parent;
|
|
}
|
|
st->parent = 0;
|
|
}
|
|
else
|
|
stamp->parent = 0;
|
|
gettimeofday (&(stamp->time), NULL);
|
|
stamp->next = 0;
|
|
if (stamps)
|
|
st1->next = stamp;
|
|
else
|
|
stamps = stamp;
|
|
}
|
|
}
|
|
|
|
void vfs_stamp (vfs *v, vfsid id)
|
|
{
|
|
struct vfs_stamping *stamp;
|
|
|
|
for (stamp = stamps; stamp != NULL; stamp = stamp->next)
|
|
if (stamp->v == v && stamp->id == id){
|
|
gettimeofday (&(stamp->time), NULL);
|
|
if (stamp->parent != NULL)
|
|
vfs_stamp (stamp->parent->v, stamp->parent->id);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void vfs_rm_parents (struct vfs_stamping *stamp)
|
|
{
|
|
struct vfs_stamping *st2, *st3;
|
|
if (stamp){
|
|
for (st2 = stamp, st3 = st2->parent; st3 != NULL; st2 = st3, st3 = st3->parent)
|
|
free (st2);
|
|
free (st2);
|
|
}
|
|
}
|
|
|
|
void vfs_rmstamp (vfs *v, vfsid id, int removeparents)
|
|
{
|
|
struct vfs_stamping *stamp, *st1;
|
|
|
|
for (stamp = stamps, st1 = NULL; stamp != NULL; st1 = stamp, stamp = stamp->next)
|
|
if (stamp->v == v && stamp->id == id){
|
|
if (stamp->parent != NULL){
|
|
if (removeparents)
|
|
vfs_rmstamp (stamp->parent->v, stamp->parent->id, 1);
|
|
vfs_rm_parents (stamp->parent);
|
|
}
|
|
if (st1 == NULL){
|
|
stamps = stamp->next;
|
|
} else {
|
|
st1->next = stamp->next;
|
|
}
|
|
free (stamp);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static int
|
|
ferrno (vfs *vfs)
|
|
{
|
|
return vfs->ferrno ? (*vfs->ferrno)() : EOPNOTSUPP;
|
|
/* Hope that error message is obscure enough ;-) */
|
|
}
|
|
|
|
int mc_open (char *file, int flags, ...)
|
|
{
|
|
int handle;
|
|
int mode;
|
|
void *info;
|
|
vfs *vfs;
|
|
va_list ap;
|
|
|
|
file = vfs_canon (file);
|
|
vfs = vfs_type (file);
|
|
|
|
/* Get the mode flag */ /* FIXME: should look if O_CREAT is present */
|
|
va_start (ap, flags);
|
|
mode = va_arg (ap, int);
|
|
va_end (ap);
|
|
|
|
if (!vfs->open)
|
|
vfs_die( "VFS must support open\n" );
|
|
info = (*vfs->open) (file, flags, mode); /* open must be supported */
|
|
free (file);
|
|
if (!info){
|
|
errno = ferrno (vfs);
|
|
return -1;
|
|
}
|
|
handle = get_bucket ();
|
|
vfs_file_table [handle].fs_info = info;
|
|
vfs_file_table [handle].operations = vfs;
|
|
|
|
return handle;
|
|
}
|
|
|
|
#define vfs_op(handle) vfs_file_table [handle].operations
|
|
#define vfs_info(handle) vfs_file_table [handle].fs_info
|
|
#define vfs_free_bucket(handle) vfs_info(handle) = 0;
|
|
|
|
#define MC_OP(name, inarg, callarg, pre, post) \
|
|
int mc_##name inarg \
|
|
{ \
|
|
vfs *vfs; \
|
|
int result; \
|
|
\
|
|
pre \
|
|
result = vfs->name ? (*vfs->name)callarg : -1; \
|
|
post \
|
|
if (result == -1) \
|
|
errno = ferrno (vfs); \
|
|
return result; \
|
|
}
|
|
|
|
#define MC_NAMEOP(name, inarg, callarg) MC_OP (name, inarg, callarg, path = vfs_canon (path); vfs = vfs_type (path);, free (path); )
|
|
#define MC_HANDLEOP(name, inarg, callarg) MC_OP (name, inarg, callarg, if (handle == -1) return -1; vfs = vfs_op (handle);, )
|
|
|
|
MC_HANDLEOP(read, (int handle, char *buffer, int count), (vfs_info (handle), buffer, count) );
|
|
|
|
int mc_ctl (int handle, int ctlop, int arg)
|
|
{
|
|
vfs *vfs;
|
|
int result;
|
|
|
|
vfs = vfs_op (handle);
|
|
result = vfs->ctl ? (*vfs->ctl)(vfs_info (handle), ctlop, arg) : 0;
|
|
return result;
|
|
}
|
|
|
|
int mc_setctl (char *path, int ctlop, char *arg)
|
|
{
|
|
vfs *vfs;
|
|
int result;
|
|
|
|
path = vfs_canon (path);
|
|
vfs = vfs_type (path);
|
|
result = vfs->setctl ? (*vfs->setctl)(path, ctlop, arg) : 0;
|
|
free (path);
|
|
return result;
|
|
}
|
|
|
|
int mc_close (int handle)
|
|
{
|
|
vfs *vfs;
|
|
int result;
|
|
|
|
if (handle == -1 || !vfs_info (handle))
|
|
return -1;
|
|
|
|
vfs = vfs_op (handle);
|
|
if (handle < 3)
|
|
return close (handle);
|
|
|
|
result = (*vfs->close)(vfs_info (handle)); /* close must be supported */
|
|
vfs_free_bucket (handle);
|
|
if (result == -1)
|
|
errno = ferrno (vfs);
|
|
|
|
return result;
|
|
}
|
|
|
|
DIR *mc_opendir (char *dirname)
|
|
{
|
|
int handle, *handlep;
|
|
void *info;
|
|
vfs *vfs;
|
|
char *p = NULL;
|
|
int i = strlen (dirname);
|
|
|
|
if (dirname [i - 1] != '/'){
|
|
/* We should make possible reading of the root directory in a tar file */
|
|
p = xmalloc (i + 2, "slash");
|
|
strcpy (p, dirname);
|
|
strcpy (p + i, "/");
|
|
dirname = p;
|
|
}
|
|
dirname = vfs_canon (dirname);
|
|
vfs = vfs_type (dirname);
|
|
|
|
info = vfs->opendir ? (*vfs->opendir)(dirname) : NULL;
|
|
free (dirname);
|
|
if (!info){
|
|
errno = ferrno (vfs);
|
|
if (p)
|
|
free (p);
|
|
return NULL;
|
|
}
|
|
handle = get_bucket ();
|
|
vfs_file_table [handle].fs_info = info;
|
|
vfs_file_table [handle].operations = vfs;
|
|
|
|
if (p)
|
|
free (p);
|
|
|
|
handlep = (int *) xmalloc (sizeof (int), "opendir handle");
|
|
*handlep = handle;
|
|
return (DIR *) handlep;
|
|
}
|
|
|
|
/* This should strip the non needed part of a path name */
|
|
#define vfs_name(x) x
|
|
|
|
#define MC_DIROP(name, type, inarg, callarg, onerr ) \
|
|
type mc_##name inarg \
|
|
{ \
|
|
int handle; \
|
|
vfs *vfs; \
|
|
\
|
|
if (!dirp){ \
|
|
errno = EFAULT; \
|
|
return onerr; \
|
|
} \
|
|
handle = *(int *) dirp; \
|
|
vfs = vfs_op (handle); \
|
|
return vfs->name ? (*vfs->name) callarg : onerr; \
|
|
}
|
|
|
|
#define MC_DIROP_VOID(name, inarg, callarg ) \
|
|
void mc_##name inarg \
|
|
{ \
|
|
int handle; \
|
|
vfs *vfs; \
|
|
\
|
|
if (!dirp){ \
|
|
errno = EFAULT; \
|
|
return; \
|
|
} \
|
|
handle = *(int *) dirp; \
|
|
vfs = vfs_op (handle); \
|
|
if (vfs->name) \
|
|
(*vfs->name) callarg; \
|
|
}
|
|
|
|
MC_DIROP (readdir, struct dirent *, (DIR *dirp), (vfs_info (handle)), NULL)
|
|
MC_DIROP_VOID (seekdir, (DIR *dirp, int offset), (vfs_info (handle), offset))
|
|
MC_DIROP (telldir, int, (DIR *dirp), (vfs_info (handle)), -1)
|
|
|
|
int mc_closedir (DIR *dirp)
|
|
{
|
|
int handle = *(int *) dirp;
|
|
vfs *vfs = vfs_op (handle);
|
|
int result;
|
|
|
|
result = vfs->closedir ? (*vfs->closedir)(vfs_info (handle)) : -1;
|
|
vfs_free_bucket (handle);
|
|
free (dirp);
|
|
return result;
|
|
}
|
|
|
|
MC_NAMEOP (stat, (char *path, struct stat *buf), (vfs_name (path), buf))
|
|
MC_NAMEOP (lstat, (char *path, struct stat *buf), (vfs_name (path), buf))
|
|
MC_HANDLEOP (fstat, (int handle, struct stat *buf), (vfs_info (handle), buf))
|
|
|
|
/*
|
|
* You must strdup whatever this function returns, static buffers are in use
|
|
*/
|
|
|
|
char *mc_return_cwd (void)
|
|
{
|
|
char *p;
|
|
struct stat my_stat, my_stat2;
|
|
|
|
if (!vfs_rosplit (current_dir)){
|
|
static char buffer[MC_MAXPATHLEN];
|
|
|
|
p = get_current_wd (buffer, MC_MAXPATHLEN);
|
|
if (!p) /* One of the directories in the path is not readable */
|
|
return current_dir;
|
|
|
|
/* Otherwise check if it is O.K. to use the current_dir */
|
|
mc_stat (p, &my_stat);
|
|
mc_stat (current_dir, &my_stat2);
|
|
if (my_stat.st_ino != my_stat2.st_ino ||
|
|
my_stat.st_dev != my_stat2.st_dev ||
|
|
!cd_symlinks){
|
|
free (current_dir);
|
|
current_dir = strdup (p);
|
|
return p;
|
|
} /* Otherwise we return current_dir below */
|
|
}
|
|
return current_dir;
|
|
}
|
|
|
|
char *mc_get_current_wd (char *buffer, int size)
|
|
{
|
|
char *cwd = mc_return_cwd();
|
|
if (strlen (cwd) > size){
|
|
vfs_die ("Current_dir size overflow\n");
|
|
}
|
|
strcpy (buffer, cwd);
|
|
return buffer;
|
|
}
|
|
|
|
MC_NAMEOP (chmod, (char *path, int mode), (vfs_name (path), mode))
|
|
MC_NAMEOP (chown, (char *path, int owner, int group), (vfs_name (path), owner, group))
|
|
MC_NAMEOP (utime, (char *path, struct utimbuf *times), (vfs_name (path), times))
|
|
MC_NAMEOP (readlink, (char *path, char *buf, int bufsiz), (vfs_name (path), buf, bufsiz))
|
|
MC_NAMEOP (unlink, (char *path), (vfs_name (path)))
|
|
MC_NAMEOP (symlink, (char *name1, char *path), (vfs_name (name1), vfs_name (path)))
|
|
|
|
int mc_link (char *name1, char *name2)
|
|
{
|
|
vfs *vfs1;
|
|
vfs *vfs2;
|
|
int result;
|
|
|
|
name1 = vfs_canon (name1);
|
|
vfs1 = vfs_type (name1);
|
|
name2 = vfs_canon (name2);
|
|
vfs2 = vfs_type (name2);
|
|
|
|
if (vfs1 != vfs2){
|
|
errno = EXDEV;
|
|
free (name1);
|
|
free (name2);
|
|
return -1;
|
|
}
|
|
|
|
result = (*vfs1->link)(vfs_name (name1), vfs_name (name2));
|
|
free (name1);
|
|
free (name2);
|
|
if (result == -1){
|
|
errno = (*vfs1->ferrno)();
|
|
return -1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
MC_HANDLEOP (write, (int handle, char *buf, int nbyte), (vfs_info (handle), buf, nbyte));
|
|
|
|
int mc_rename (char *path1, char *path2)
|
|
{
|
|
vfs *vfs1;
|
|
vfs *vfs2;
|
|
int result;
|
|
|
|
path1 = vfs_canon (path1);
|
|
vfs1 = vfs_type (path1);
|
|
path2 = vfs_canon (path2);
|
|
vfs2 = vfs_type (path2);
|
|
|
|
if (vfs1 != vfs2){
|
|
errno = EXDEV;
|
|
free (path1);
|
|
free (path2);
|
|
return -1;
|
|
}
|
|
|
|
result = (*vfs1->rename)(vfs_name (path1), vfs_name (path2));
|
|
free (path1);
|
|
free (path2);
|
|
if (result == -1){
|
|
errno = (*vfs1->ferrno)();
|
|
return -1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
off_t mc_lseek (int fd, off_t offset, int whence)
|
|
{
|
|
vfs *vfs;
|
|
|
|
if (fd == -1)
|
|
return -1;
|
|
|
|
vfs = vfs_op (fd);
|
|
return (*vfs->lseek)(vfs_info (fd), offset, whence);
|
|
}
|
|
|
|
/*
|
|
* remove //, /./ and /../, local should point to big enough buffer
|
|
*/
|
|
|
|
#define ISSLASH(a) (!a || (a == '/'))
|
|
|
|
void
|
|
vfs_kill_dots (char *path, char *local)
|
|
{
|
|
char *s = path, *t = local;
|
|
char *last_slash = path;
|
|
|
|
*t++ = *s++;
|
|
while (1){
|
|
if (ISSLASH (*s)){
|
|
if (*last_slash != '/')
|
|
vfs_die ("/ not there\n");
|
|
|
|
if (ISSLASH (last_slash [1]))
|
|
t--;
|
|
|
|
if (last_slash [1] == '.'){
|
|
if (ISSLASH(last_slash [2]))
|
|
t -= 2;
|
|
if ((last_slash [2] == '.') && (ISSLASH (last_slash [3]))){
|
|
t -= 4;
|
|
while ((t >= local) && (*t != '/'))
|
|
t--;
|
|
}
|
|
}
|
|
last_slash = s;
|
|
}
|
|
|
|
if (t <= local){
|
|
*local = '/';
|
|
t = local;
|
|
}
|
|
if (!*s)
|
|
break;
|
|
*t++ = *s++;
|
|
*t = 0;
|
|
}
|
|
*t = 0;
|
|
if (!(*local)){
|
|
local [0] = '/';
|
|
local [1] = 0;
|
|
}
|
|
}
|
|
|
|
|
|
char *vfs_canon (char *path)
|
|
{
|
|
if (*path == '~'){ /* Tilde expansion */
|
|
char *local, *result;
|
|
|
|
local = tilde_expand (path);
|
|
if (local){
|
|
result = vfs_canon (local);
|
|
free (local);
|
|
return result;
|
|
} else
|
|
return strdup (path);
|
|
}
|
|
|
|
if (*path != '/'){ /* Relative to current directory */
|
|
char *local, *result;
|
|
|
|
if (current_dir [strlen (current_dir) - 1] == '/')
|
|
local = copy_strings (current_dir, path, NULL);
|
|
else
|
|
local = copy_strings (current_dir, "/", path, NULL);
|
|
|
|
result = vfs_canon (local);
|
|
free (local);
|
|
return result;
|
|
}
|
|
/* So we have path of following form:
|
|
* /p1/p2:op/.././././p3:op/p4. Good luck.
|
|
*/
|
|
canonicalize_pathname (path);
|
|
|
|
return strdup (path);
|
|
}
|
|
|
|
vfsid vfs_ncs_getid (vfs *nvfs, char *dir, struct vfs_stamping **par)
|
|
{
|
|
vfsid nvfsid;
|
|
int freeit = 0;
|
|
|
|
if (dir [strlen (dir) - 1] != '/'){
|
|
dir = copy_strings (dir, "/", NULL);
|
|
freeit = 1;
|
|
}
|
|
nvfsid = (*nvfs->getid)(dir, par);
|
|
if (freeit)
|
|
free (dir);
|
|
return nvfsid;
|
|
}
|
|
|
|
#ifndef VFS_STANDALONE
|
|
static int is_parent (vfs * nvfs, vfsid nvfsid, struct vfs_stamping *parent)
|
|
{
|
|
struct vfs_stamping *stamp;
|
|
for (stamp = parent; stamp; stamp = stamp->parent)
|
|
if (stamp->v == nvfs && stamp->id == nvfsid)
|
|
break;
|
|
return (stamp ? 1 : 0);
|
|
}
|
|
#endif
|
|
|
|
void vfs_add_noncurrent_stamps (vfs * oldvfs, vfsid oldvfsid, struct vfs_stamping *parent)
|
|
{
|
|
#ifndef VFS_STANDALONE
|
|
vfs *nvfs, *n2vfs, *n3vfs;
|
|
vfsid nvfsid, n2vfsid, n3vfsid;
|
|
struct vfs_stamping *par, *stamp;
|
|
int f;
|
|
|
|
/* FIXME: As soon as we convert to multiple panels, this stuff
|
|
has to change. It works like this: We do not time out the
|
|
vfs's which are current in any panel and on the other
|
|
side we add the old directory with all its parents which
|
|
are not in any panel (if we find such one, we stop adding
|
|
parents to the time-outing structure. */
|
|
|
|
/* There are three directories we have to take care of: current_dir,
|
|
cpanel->cwd and opanel->cwd. Athough most of the time either
|
|
current_dir and cpanel->cwd or current_dir and opanel->cwd are the
|
|
same, it's possible that all three are different -- Norbert */
|
|
|
|
if (!cpanel)
|
|
return;
|
|
|
|
nvfs = vfs_type (current_dir);
|
|
nvfsid = vfs_ncs_getid (nvfs, current_dir, &par);
|
|
vfs_rmstamp (nvfs, nvfsid, 1);
|
|
|
|
f = is_parent (oldvfs, oldvfsid, par);
|
|
vfs_rm_parents (par);
|
|
if ((nvfs == oldvfs && nvfs == oldvfsid) || oldvfsid == (vfsid *)-1 || f){
|
|
return;
|
|
}
|
|
|
|
if (get_current_type () == view_listing){
|
|
n2vfs = vfs_type (cpanel->cwd);
|
|
n2vfsid = vfs_ncs_getid (n2vfs, cpanel->cwd, &par);
|
|
f = is_parent (oldvfs, oldvfsid, par);
|
|
vfs_rm_parents (par);
|
|
if ((n2vfs == oldvfs && n2vfsid == oldvfsid) || f)
|
|
return;
|
|
} else {
|
|
n2vfs = (vfs *) -1;
|
|
n2vfsid = (vfs *) -1;
|
|
}
|
|
|
|
if (get_other_type () == view_listing){
|
|
n3vfs = vfs_type (opanel->cwd);
|
|
n3vfsid = vfs_ncs_getid (n3vfs, opanel->cwd, &par);
|
|
f = is_parent (oldvfs, oldvfsid, par);
|
|
vfs_rm_parents (par);
|
|
if ((n3vfs == oldvfs && n3vfsid == oldvfsid) || f)
|
|
return;
|
|
} else {
|
|
n3vfs = (vfs *)-1;
|
|
n3vfsid = (vfs *)-1;
|
|
}
|
|
|
|
if ((*oldvfs->nothingisopen) (oldvfsid)){
|
|
if (oldvfs == &extfs_vfs_ops && ((struct extfs_archive *) oldvfsid)->name == 0){
|
|
/* Free the resources immediatly when we leave a mtools fs
|
|
('cd a:') instead of waiting for the vfs-timeout */
|
|
(oldvfs->free) (oldvfsid);
|
|
} else
|
|
vfs_addstamp (oldvfs, oldvfsid, parent);
|
|
for (stamp = parent; stamp != NULL; stamp = stamp->parent){
|
|
if ((stamp->v == nvfs && stamp->id == nvfsid) ||
|
|
(stamp->v == n2vfs && stamp->id == n2vfsid) ||
|
|
(stamp->v == n3vfs && stamp->id == n3vfsid) ||
|
|
stamp->id == (vfsid) - 1 ||
|
|
!(*stamp->v->nothingisopen) (stamp->id))
|
|
break;
|
|
if (stamp->v == &extfs_vfs_ops && ((struct extfs_archive *) stamp->id)->name == 0){
|
|
(stamp->v->free) (stamp->id);
|
|
vfs_rmstamp (stamp->v, stamp->id, 0);
|
|
} else
|
|
vfs_addstamp (stamp->v, stamp->id, stamp->parent);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifndef VFS_STANDALONE
|
|
static void vfs_stamp_path (char *path)
|
|
{
|
|
vfs *vfs;
|
|
vfsid id;
|
|
struct vfs_stamping *par, *stamp;
|
|
|
|
vfs = vfs_type (path);
|
|
id = vfs_ncs_getid (vfs, path, &par);
|
|
vfs_addstamp (vfs, id, par);
|
|
|
|
for (stamp = par; stamp != NULL; stamp = stamp->parent)
|
|
vfs_addstamp (stamp->v, stamp->id, stamp->parent);
|
|
vfs_rm_parents (par);
|
|
}
|
|
#endif
|
|
|
|
#ifndef VFS_STANDALONE
|
|
void vfs_add_current_stamps (void)
|
|
{
|
|
vfs_stamp_path (current_dir);
|
|
if (get_current_type () == view_listing)
|
|
vfs_stamp_path (cpanel->cwd);
|
|
if (get_other_type () == view_listing)
|
|
vfs_stamp_path (opanel->cwd);
|
|
}
|
|
#endif
|
|
|
|
/* This function is really broken */
|
|
int mc_chdir (char *path)
|
|
{
|
|
char *a, *b;
|
|
int result;
|
|
char *p = NULL;
|
|
int i = strlen (path);
|
|
vfs *oldvfs;
|
|
vfsid oldvfsid;
|
|
struct vfs_stamping *parent;
|
|
|
|
if (path [i - 1] != '/'){
|
|
/* We should make possible reading of the root directory in a tar archive */
|
|
p = xmalloc (i + 2, "slash");
|
|
strcpy (p, path);
|
|
strcpy (p + i, "/");
|
|
path = p;
|
|
}
|
|
|
|
a = current_dir; /* Save a copy for case of failure */
|
|
current_dir = vfs_canon (path);
|
|
current_vfs = vfs_type (current_dir);
|
|
b = strdup (current_dir);
|
|
result = (*current_vfs->chdir)(vfs_name (b));
|
|
free (b);
|
|
if (result == -1){
|
|
errno = (*current_vfs->ferrno)();
|
|
free (current_dir);
|
|
current_vfs = vfs_type (a);
|
|
current_dir = a;
|
|
} else {
|
|
oldvfs = vfs_type (a);
|
|
oldvfsid = vfs_ncs_getid (oldvfs, a, &parent);
|
|
free (a);
|
|
vfs_add_noncurrent_stamps (oldvfs, oldvfsid, parent);
|
|
vfs_rm_parents (parent);
|
|
}
|
|
if (p)
|
|
free (p);
|
|
|
|
if (*current_dir){
|
|
p = strchr (current_dir, 0) - 1;
|
|
if (*p == '/' && p > current_dir)
|
|
*p = 0; /* Sometimes we assume no trailing slash on cwd */
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int vfs_current_is_local (void)
|
|
{
|
|
return current_vfs == &local_vfs_ops;
|
|
}
|
|
|
|
int vfs_current_is_extfs (void)
|
|
{
|
|
return current_vfs == &extfs_vfs_ops;
|
|
}
|
|
|
|
int vfs_current_is_tarfs (void)
|
|
{
|
|
return current_vfs == &tarfs_vfs_ops;
|
|
}
|
|
|
|
int vfs_file_is_local (char *filename)
|
|
{
|
|
vfs *vfs;
|
|
|
|
filename = vfs_canon (filename);
|
|
vfs = vfs_type (filename);
|
|
free (filename);
|
|
return vfs == &local_vfs_ops;
|
|
}
|
|
|
|
int vfs_file_is_ftp (char *filename)
|
|
{
|
|
#ifdef USE_NETCODE
|
|
vfs *vfs;
|
|
|
|
filename = vfs_canon (filename);
|
|
vfs = vfs_type (filename);
|
|
free (filename);
|
|
return vfs == &ftpfs_vfs_ops;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
char *vfs_get_current_dir (void)
|
|
{
|
|
return current_dir;
|
|
}
|
|
|
|
void vfs_setup_wd (void)
|
|
{
|
|
current_dir = strdup ("/");
|
|
mc_return_cwd(); /* mc_return_cwd will fixup current_dir for us */
|
|
|
|
if (strlen(current_dir)>MC_MAXPATHLEN-2)
|
|
vfs_die ("Current dir too long\n");
|
|
|
|
return;
|
|
}
|
|
|
|
MC_NAMEOP (mkdir, (char *path, mode_t mode), (vfs_name (path), mode))
|
|
MC_NAMEOP (rmdir, (char *path), (vfs_name (path)))
|
|
MC_NAMEOP (mknod, (char *path, int mode, int dev), (vfs_name (path), mode, dev))
|
|
|
|
#ifdef HAVE_MMAP
|
|
struct mc_mmapping {
|
|
caddr_t addr;
|
|
void *vfs_info;
|
|
vfs *vfs;
|
|
struct mc_mmapping *next;
|
|
} *mc_mmaparray = NULL;
|
|
|
|
caddr_t
|
|
mc_mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
|
|
{
|
|
vfs *vfs;
|
|
caddr_t result;
|
|
struct mc_mmapping *mcm;
|
|
|
|
if (fd == -1)
|
|
return (caddr_t) -1;
|
|
|
|
vfs = vfs_op (fd);
|
|
result = (*vfs->mmap)(addr, len, prot, flags, vfs_info (fd), offset);
|
|
if (result == (caddr_t)-1){
|
|
errno = (*vfs->ferrno)();
|
|
return (caddr_t)-1;
|
|
}
|
|
mcm = (struct mc_mmapping *) xmalloc (sizeof (struct mc_mmapping), "vfs: mmap handling");
|
|
mcm->addr = result;
|
|
mcm->vfs_info = vfs_info (fd);
|
|
mcm->vfs = vfs;
|
|
mcm->next = mc_mmaparray;
|
|
mc_mmaparray = mcm;
|
|
return result;
|
|
}
|
|
|
|
int mc_munmap (caddr_t addr, size_t len)
|
|
{
|
|
struct mc_mmapping *mcm, *mcm2 = NULL;
|
|
|
|
for (mcm = mc_mmaparray; mcm != NULL; mcm2 = mcm, mcm = mcm->next){
|
|
if (mcm->addr == addr){
|
|
if (mcm2 == NULL)
|
|
mc_mmaparray = mcm->next;
|
|
else
|
|
mcm2->next = mcm->next;
|
|
(*mcm->vfs->munmap)(addr, len, mcm->vfs_info);
|
|
free (mcm);
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
#endif
|
|
|
|
char *mc_def_getlocalcopy (char *filename)
|
|
{
|
|
char *tmp;
|
|
int fdin, fdout, i;
|
|
char buffer[8192];
|
|
struct stat mystat;
|
|
|
|
fdin = mc_open (filename, O_RDONLY);
|
|
if (fdin == -1)
|
|
return NULL;
|
|
tmp = tmpnam(NULL);
|
|
fdout = creat (tmp, 0600); /* FIXME: What about symlink attack ? */
|
|
if (fdout == -1){
|
|
mc_close (fdin);
|
|
return NULL;
|
|
}
|
|
tmp = strdup (tmp);
|
|
while ((i = mc_read (fdin, buffer, sizeof (buffer))) == sizeof (buffer)){
|
|
write (fdout, buffer, i);
|
|
}
|
|
if (i != -1)
|
|
write (fdout, buffer, i);
|
|
mc_close (fdin);
|
|
close (fdout);
|
|
if (mc_stat (filename, &mystat) != -1){
|
|
chmod (tmp, mystat.st_mode);
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
char *mc_getlocalcopy (char *path)
|
|
{
|
|
vfs *vfs;
|
|
char *result;
|
|
|
|
path = vfs_canon (path);
|
|
vfs = vfs_type (path);
|
|
result = vfs->getlocalcopy ? (*vfs->getlocalcopy)(vfs_name (path)) :
|
|
mc_def_getlocalcopy (vfs_name (path));
|
|
free (path);
|
|
if (result == NULL){
|
|
errno = ferrno (vfs);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void mc_def_ungetlocalcopy (char *filename, char *local, int has_changed)
|
|
{
|
|
if (has_changed){
|
|
int fdin, fdout, i;
|
|
char buffer [8192];
|
|
|
|
fdin = open (local, O_RDONLY);
|
|
if (fdin == -1){
|
|
unlink (local);
|
|
free (local);
|
|
return;
|
|
}
|
|
fdout = mc_open (filename, O_WRONLY | O_TRUNC);
|
|
if (fdout == -1){
|
|
close (fdin);
|
|
unlink (local);
|
|
free (local);
|
|
return;
|
|
}
|
|
while ((i = read (fdin, buffer, sizeof (buffer))) == sizeof (buffer)){
|
|
mc_write (fdout, buffer, i);
|
|
}
|
|
if (i != -1)
|
|
mc_write (fdout, buffer, i);
|
|
close (fdin);
|
|
mc_close (fdout);
|
|
}
|
|
unlink (local);
|
|
free (local);
|
|
}
|
|
|
|
void mc_ungetlocalcopy (char *path, char *local, int has_changed)
|
|
{
|
|
vfs *vfs;
|
|
|
|
path = vfs_canon (path);
|
|
vfs = vfs_type (path);
|
|
vfs->ungetlocalcopy ? (*vfs->ungetlocalcopy)(vfs_name (path), local, has_changed) :
|
|
mc_def_ungetlocalcopy (vfs_name (path), local, has_changed);
|
|
free (path);
|
|
}
|
|
|
|
inline int timeoutcmp (struct timeval *t1, struct timeval *t2)
|
|
{
|
|
return ((t1->tv_sec < t2->tv_sec)
|
|
|| ((t1->tv_sec == t2->tv_sec) && (t1->tv_usec <= t2->tv_usec)));
|
|
}
|
|
|
|
void vfs_timeout_handler (void)
|
|
{
|
|
struct timeval time;
|
|
struct vfs_stamping *stamp, *st;
|
|
|
|
gettimeofday (&time, NULL);
|
|
time.tv_sec -= vfs_timeout;
|
|
for (stamp = stamps; stamp != NULL;){
|
|
if (timeoutcmp (&stamp->time, &time)){
|
|
st = stamp->next;
|
|
(*stamp->v->free) (stamp->id);
|
|
vfs_rmstamp (stamp->v, stamp->id, 0);
|
|
stamp = st;
|
|
} else
|
|
stamp = stamp->next;
|
|
}
|
|
}
|
|
|
|
void vfs_init (void)
|
|
{
|
|
time_t current_time;
|
|
struct tm *t;
|
|
|
|
memset (vfs_file_table, 0, sizeof (vfs_file_table));
|
|
current_time = time (NULL);
|
|
t = localtime (¤t_time);
|
|
current_mon = t->tm_mon;
|
|
current_year = t->tm_year;
|
|
|
|
#ifdef USE_NETCODE
|
|
tcp_init();
|
|
ftpfs_init();
|
|
#endif
|
|
extfs_init ();
|
|
vfs_setup_wd ();
|
|
}
|
|
|
|
void vfs_free_resources (char *path)
|
|
{
|
|
vfs *vfs;
|
|
vfsid vid;
|
|
struct vfs_stamping *parent;
|
|
|
|
vfs = vfs_type (path);
|
|
vid = vfs_ncs_getid (vfs, path, &parent);
|
|
if (vid != (vfsid) -1)
|
|
(*vfs->free)(vid);
|
|
vfs_rm_parents (parent);
|
|
}
|
|
|
|
#if 0
|
|
/* Shutdown a vfs given a path name */
|
|
void vfs_shut_path (char *p)
|
|
{
|
|
vfs *the_vfs;
|
|
struct vfs_stamping *par;
|
|
|
|
the_vfs = vfs_type (p);
|
|
vfs_ncs_getid (the_vfs, p, &par);
|
|
(*par->v->free)(par->id);
|
|
vfs_rm_parents (par);
|
|
}
|
|
#endif
|
|
|
|
void vfs_shut (void)
|
|
{
|
|
struct vfs_stamping *stamp, *st;
|
|
|
|
for (stamp = stamps, stamps = 0; stamp != NULL;){
|
|
(*stamp->v->free)(stamp->id);
|
|
st = stamp->next;
|
|
free (stamp);
|
|
stamp = st;
|
|
}
|
|
|
|
if (stamps)
|
|
vfs_rmstamp (stamps->v, stamps->id, 1);
|
|
|
|
if (current_dir)
|
|
free (current_dir);
|
|
|
|
extfs_done ();
|
|
|
|
#ifdef USE_NETCODE
|
|
ftpfs_done();
|
|
#endif
|
|
|
|
}
|
|
|
|
/* These ones grab information from the VFS
|
|
* and handles them to an upper layer
|
|
*/
|
|
void vfs_fill_names (void (*func)(char *))
|
|
{
|
|
#ifdef USE_NETCODE
|
|
mcfs_fill_names (func);
|
|
ftpfs_fill_names (func);
|
|
#endif
|
|
tarfs_fill_names (func);
|
|
extfs_fill_names (func);
|
|
}
|
|
|
|
/* Following stuff (parse_ls_lga) is used by ftpfs and extfs */
|
|
#define MAXCOLS 30
|
|
|
|
static char *columns [MAXCOLS]; /* Points to the string in column n */
|
|
static int column_ptr [MAXCOLS]; /* Index from 0 to the starting positions of the columns */
|
|
|
|
static int split_text (char *p)
|
|
{
|
|
char *original = p;
|
|
int numcols;
|
|
|
|
|
|
for (numcols = 0; *p && numcols < MAXCOLS; numcols++){
|
|
while (*p == ' ' || *p == '\r' || *p == '\n'){
|
|
*p = 0;
|
|
p++;
|
|
}
|
|
columns [numcols] = p;
|
|
column_ptr [numcols] = p - original;
|
|
while (*p && *p != ' ' && *p != '\r' && *p != '\n')
|
|
p++;
|
|
}
|
|
return numcols;
|
|
}
|
|
|
|
static int is_num (int idx)
|
|
{
|
|
if (columns [idx][0] < '0' || columns [idx][0] > '9')
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
#define free_and_return(x) { free (p_copy); return (x); }
|
|
int parse_ls_lga (char *p, struct stat *s, char **filename, char **linkname)
|
|
{
|
|
int idx, idx2, num_cols, isconc = 0;
|
|
int i;
|
|
long l;
|
|
struct tm tim;
|
|
int extfs_format_date = 0;
|
|
int year_supplied = 0;
|
|
char *p_copy;
|
|
|
|
s->st_mode = 0;
|
|
if (strncmp (p, "total", 5) == 0){
|
|
return 0;
|
|
}
|
|
switch (*(p++)){
|
|
case 'd': s->st_mode |= S_IFDIR; break;
|
|
case 'b': s->st_mode |= S_IFBLK; break;
|
|
case 'c': s->st_mode |= S_IFCHR; break;
|
|
case 'm': s->st_mode |= S_IFREG; break; /* Don't know what it is :-) */
|
|
case 'n': s->st_mode |= S_IFREG; break; /* and this as well */
|
|
case 'l': s->st_mode |= S_IFLNK; break;
|
|
#ifdef IS_IFSOCK
|
|
case 's': s->st_mode |= S_IFSOCK; break;
|
|
#endif
|
|
case 'p': s->st_mode |= S_IFIFO; break;
|
|
case '-': s->st_mode |= S_IFREG; break;
|
|
case '?': s->st_mode |= S_IFREG; break;
|
|
default: return 0;
|
|
}
|
|
if (*p == '['){
|
|
if (strlen (p) <= 8 || p [8] != ']')
|
|
return 0;
|
|
/* Should parse here the Notwell permissions :) */
|
|
if (S_ISDIR (s->st_mode))
|
|
s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IXUSR | S_IXGRP | S_IXOTH);
|
|
else
|
|
s->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR);
|
|
p += 9;
|
|
} else {
|
|
switch (*(p++)){
|
|
case 'r': s->st_mode |= 0400; break;
|
|
case '-': break;
|
|
default: return 0;
|
|
}
|
|
switch (*(p++)){
|
|
case 'w': s->st_mode |= 0200; break;
|
|
case '-': break;
|
|
default: return 0;
|
|
}
|
|
switch (*(p++)){
|
|
case 'x': s->st_mode |= 0100; break;
|
|
case 's': s->st_mode |= 0100 | S_ISUID; break;
|
|
case 'S': s->st_mode |= S_ISUID; break;
|
|
case '-': break;
|
|
default: return 0;
|
|
}
|
|
switch (*(p++)){
|
|
case 'r': s->st_mode |= 0040; break;
|
|
case '-': break;
|
|
default: return 0;
|
|
}
|
|
switch (*(p++)){
|
|
case 'w': s->st_mode |= 0020; break;
|
|
case '-': break;
|
|
default: return 0;
|
|
}
|
|
switch (*(p++)){
|
|
case 'x': s->st_mode |= 0010; break;
|
|
case 's': s->st_mode |= 0010 | S_ISGID; break;
|
|
case 'S': s->st_mode |= S_ISGID; break;
|
|
case '-': break;
|
|
default: return 0;
|
|
}
|
|
switch (*(p++)){
|
|
case 'r': s->st_mode |= 0004; break;
|
|
case '-': break;
|
|
default: return 0;
|
|
}
|
|
switch (*(p++)){
|
|
case 'w': s->st_mode |= 0002; break;
|
|
case '-': break;
|
|
default: return 0;
|
|
}
|
|
switch (*(p++)){
|
|
case 'x': s->st_mode |= 0001; break;
|
|
case 't': s->st_mode |= 0001 | S_ISVTX; break;
|
|
case 'T': s->st_mode |= S_ISVTX; break;
|
|
case '-': break;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
p_copy = strdup (p);
|
|
num_cols = split_text (p);
|
|
|
|
s->st_nlink = atol (columns [0]);
|
|
if (s->st_nlink <= 0)
|
|
free_and_return (0);
|
|
|
|
if (!is_num (1))
|
|
s->st_uid = finduid (columns [1]);
|
|
else
|
|
s->st_uid = (uid_t) atol (columns [1]);
|
|
|
|
/* Mhm, the ls -lg did not produce a group field */
|
|
for (idx = 3; idx <= 5; idx++)
|
|
if ((*columns [idx] >= 'A' && *columns [idx] <= 'S' &&
|
|
strlen (columns[idx]) == 3) || (strlen (columns[idx])==8 &&
|
|
columns [idx][2] == '-' && columns [idx][5] == '-'))
|
|
break;
|
|
if (idx == 6 || (idx == 5 && !S_ISCHR (s->st_mode) && !S_ISBLK (s->st_mode)))
|
|
free_and_return (0);
|
|
if (idx < 5){
|
|
char *p = strchr(columns [idx - 1], ',');
|
|
if (p && p[1] >= '0' && p[1] <= '9')
|
|
isconc = 1;
|
|
}
|
|
if (idx == 3 || (idx == 4 && !isconc && (S_ISCHR(s->st_mode) || S_ISBLK (s->st_mode))))
|
|
idx2 = 2;
|
|
else {
|
|
if (is_num (2))
|
|
s->st_gid = (gid_t) atol (columns [2]);
|
|
else
|
|
s->st_gid = findgid (columns [2]);
|
|
idx2 = 3;
|
|
}
|
|
|
|
if (S_ISCHR (s->st_mode) || S_ISBLK (s->st_mode)){
|
|
char *p;
|
|
if (!is_num (idx2))
|
|
free_and_return (0);
|
|
#ifdef HAVE_ST_RDEV
|
|
s->st_rdev = (atol (columns [idx2]) & 0xff) << 8;
|
|
#endif
|
|
if (isconc){
|
|
p = strchr (columns [idx2], ',');
|
|
if (!p || p [1] < '0' || p [1] > '9')
|
|
free_and_return (0);
|
|
p++;
|
|
} else {
|
|
p = columns [idx2 + 1];
|
|
if (!is_num (idx2+1))
|
|
free_and_return (0);
|
|
}
|
|
|
|
#ifdef HAVE_ST_RDEV
|
|
s->st_rdev |= (atol (p) & 0xff);
|
|
#endif
|
|
s->st_size = 0;
|
|
} else {
|
|
if (!is_num (idx2))
|
|
free_and_return (0);
|
|
|
|
s->st_size = (size_t) atol (columns [idx2]);
|
|
#ifdef HAVE_ST_RDEV
|
|
s->st_rdev = 0;
|
|
#endif
|
|
}
|
|
|
|
p = columns [idx++];
|
|
|
|
if (!strcmp (p, "Jan"))
|
|
tim.tm_mon = 0;
|
|
else if (!strcmp (p, "Feb"))
|
|
tim.tm_mon = 1;
|
|
else if (!strcmp (p, "Mar"))
|
|
tim.tm_mon = 2;
|
|
else if (!strcmp (p, "Apr"))
|
|
tim.tm_mon = 3;
|
|
else if (!strcmp (p, "May"))
|
|
tim.tm_mon = 4;
|
|
else if (!strcmp (p, "Jun"))
|
|
tim.tm_mon = 5;
|
|
else if (!strcmp (p, "Jul"))
|
|
tim.tm_mon = 6;
|
|
else if (!strcmp (p, "Aug"))
|
|
tim.tm_mon = 7;
|
|
else if (!strcmp (p, "Sep"))
|
|
tim.tm_mon = 8;
|
|
else if (!strcmp (p, "Oct"))
|
|
tim.tm_mon = 9;
|
|
else if (!strcmp (p, "Nov"))
|
|
tim.tm_mon = 10;
|
|
else if (!strcmp (p, "Dec"))
|
|
tim.tm_mon = 11;
|
|
else {
|
|
/* This case should not normaly happen, but in extfs we allow these
|
|
date formats:
|
|
Mon DD hh:mm
|
|
Mon DD YYYY
|
|
Mon DD YYYY hh:mm
|
|
MM-DD-YY hh:mm
|
|
where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
|
|
YYYY four digit year, hh, mm two digit hour and minute. */
|
|
if (strlen (p) == 8 && p [2] == '-' && p [5] == '-'){
|
|
p [2] = 0;
|
|
p [5] = 0;
|
|
tim.tm_mon = (int) atol (p);
|
|
if (!tim.tm_mon)
|
|
free_and_return (0)
|
|
else
|
|
tim.tm_mon--;
|
|
tim.tm_mday = (int) atol (p + 3);
|
|
tim.tm_year = (int) atol (p + 6);
|
|
if (tim.tm_year < 70)
|
|
tim.tm_year += 70;
|
|
extfs_format_date = 1;
|
|
} else
|
|
free_and_return (0);
|
|
}
|
|
|
|
if (!extfs_format_date){
|
|
if (!is_num (idx))
|
|
free_and_return (0);
|
|
tim.tm_mday = (int)atol (columns [idx++]);
|
|
}
|
|
|
|
/* Microsoft ftp server may send a non padded digit */
|
|
if (columns [idx][2] != ':' && columns [idx][1] != ':'){
|
|
/* There is a year */
|
|
l = atol (columns [idx++]);
|
|
if (l < 1900 || l > 3000)
|
|
free_and_return (0);
|
|
tim.tm_year = (int) (l - 1900);
|
|
tim.tm_hour = 0;
|
|
tim.tm_min = 0;
|
|
tim.tm_sec = 0;
|
|
year_supplied = 1;
|
|
}
|
|
if (columns [idx][2] == ':' || columns [idx][1] == ':'){
|
|
if (sscanf (columns [idx],
|
|
"%2d:%2d", &tim.tm_hour, &tim.tm_min) != 2){
|
|
if (!year_supplied)
|
|
free_and_return (0);
|
|
tim.tm_hour = 0;
|
|
tim.tm_min = 0;
|
|
tim.tm_sec = 0;
|
|
} else {
|
|
idx++;
|
|
tim.tm_sec = 0;
|
|
if (!extfs_format_date && !year_supplied){
|
|
tim.tm_year = current_year;
|
|
if (tim.tm_mon > current_mon)
|
|
tim.tm_year--;
|
|
}
|
|
}
|
|
} else if (!year_supplied)
|
|
free_and_return (0);
|
|
tim.tm_isdst = 0;
|
|
s->st_mtime = mktime (&tim);
|
|
if (s->st_mtime == -1)
|
|
s->st_mtime = 0;
|
|
s->st_atime = s->st_mtime;
|
|
s->st_ctime = s->st_mtime;
|
|
s->st_dev = 0;
|
|
s->st_ino = 0;
|
|
#ifdef HAVE_ST_BLKSIZE
|
|
s->st_blksize = 512;
|
|
#endif
|
|
#ifdef HAVE_ST_BLOCKS
|
|
s->st_blocks = (s->st_size + 511) / 512;
|
|
#endif
|
|
|
|
for (i = idx + 1, idx2 = 0; i < num_cols; i++ )
|
|
if (strcmp (columns [i], "->") == 0){
|
|
idx2 = i;
|
|
break;
|
|
}
|
|
|
|
if (((S_ISLNK (s->st_mode) ||
|
|
(num_cols == idx + 3 && s->st_nlink > 1))) /* Maybe a hardlink? (in extfs) */
|
|
&& idx2){
|
|
int p;
|
|
char *s;
|
|
|
|
if (filename){
|
|
p = column_ptr [idx2] - column_ptr [idx];
|
|
s = xmalloc (p, "filename");
|
|
strncpy (s, p_copy + column_ptr [idx], p - 1);
|
|
s[p - 1] = '\0';
|
|
*filename = s;
|
|
}
|
|
if (linkname){
|
|
s = strdup (p_copy + column_ptr [idx2+1]);
|
|
p = strlen (s);
|
|
if (s [p-1] == '\r' || s [p-1] == '\n')
|
|
s [p-1] = 0;
|
|
if (s [p-2] == '\r' || s [p-2] == '\n')
|
|
s [p-2] = 0;
|
|
|
|
*linkname = s;
|
|
}
|
|
} else {
|
|
/* Extract the filename from the string copy, not from the columns
|
|
* this way we have a chance of entering hidden directories like ". ."
|
|
*/
|
|
if (filename){
|
|
/*
|
|
*filename = strdup (columns [idx++]);
|
|
*/
|
|
int p;
|
|
char *s;
|
|
|
|
s = strdup (p_copy + column_ptr [idx++]);
|
|
p = strlen (s);
|
|
if (s [p-1] == '\r' || s [p-1] == '\n')
|
|
s [p-1] = 0;
|
|
if (s [p-2] == '\r' || s [p-2] == '\n')
|
|
s [p-2] = 0;
|
|
|
|
*filename = s;
|
|
}
|
|
if (linkname)
|
|
*linkname = NULL;
|
|
}
|
|
free_and_return (1);
|
|
}
|
|
|
|
void
|
|
vfs_force_expire (char *pathname)
|
|
{
|
|
vfs *vfs;
|
|
|
|
pathname = vfs_canon (pathname);
|
|
vfs = vfs_type (pathname);
|
|
if (vfs->forget_about)
|
|
(*vfs->forget_about) (pathname);
|
|
free (pathname);
|
|
}
|
|
|
|
void
|
|
vfs_die (char *m)
|
|
{
|
|
message_1s (1, "Internal error:", m);
|
|
exit (1);
|
|
}
|
|
|
|
#ifndef VFS_STANDALONE
|
|
char *
|
|
vfs_get_password (char *msg)
|
|
{
|
|
return (char *) input_dialog (msg, _("Password:"), "");
|
|
}
|
|
#endif
|