mc/vfs/shared_ftp_fish.c

869 lines
19 KiB
C
Raw Normal View History

Tue Sep 15 20:51:42 1998 Norbert Warmuth <k3190@fh-sw.de> * setup.c: save and restore new global variable/option ftp_use_unix_list_options Tue Sep 15 20:31:32 1998 Norbert Warmuth <k3190@fh-sw.de> * ftpfs.c (ftp_use_unix_list_options): New global variable/option. If true we try to use 'LIST -la <path>'. When it fails we use the two commands 'CWD <path>' and 'LIST' instead. (resolve_symlink): rewritten. Don't get a second directory listing with `LIST -lLa'. Instead use the cache to get the file stat of symbolic links. If the directory the symlink points to isn't already in the cache the directory listing will be fetched and stored in the directory cache (without resolving symlinks recursively). The new method to resolve symlinks is faster if symlinks the same directory or the directory the symlink points to is already in the cache. This function was small and nice until I discovered that it was broken for symlinks to symlinks. Now it looks ugly and perhaps I will revert it to use "LIST -lLa" again. With a fast connection it doesn't matter which methode we use but with a slow connection I wouldn't hesitate to burn more cpu cycles on the client side. (retrieve_dir): Added parameter to tell whether to resolve symlinks (don't resolve symlinks in directory listings retrieved while resolving symlinks). When we don't get a directory listing with 'LIST -la <path>' then try to get it with `CWD <path>; LIST'. Tue Sep 15 20:27:29 1998 Norbert Warmuth <k3190@fh-sw.de * ftpfs.c (login_server): s/ftpfs_get_host/my_get_host/ (retrieve_file_start2): Don't create target file O_EXCL, in copy_file_file we check existance of the target file and know that we want to truncate it (this change was already done a while back but it was reverted with the vfs-split). Tue Sep 15 20:15:42 1998 Norbert Warmuth <k3190@fh-sw.de> * ftpfs.h (struct connection): added boolean which indicates that the ftp server doesn't unterstand Unix ls options * ftpfs.h (struct dir): added enum to store symlink status of the in memory directory cache (directory has no symbolic links; symbolic links but not yet resolved; symbolic links which are resolved) Tue Sep 15 20:02:08 1998 Norbert Warmuth <k3190@fh-sw.de> * shared_ftp_fish.c, fish.c: updated references to retrieve_dir to honour the additional boolean parameter
1998-09-15 23:41:22 +04:00
static struct dir *retrieve_dir(struct connection *bucket, char *remote_path, int resolve_symlinks);
static int store_file(struct direntry *fe);
static int retrieve_file_start(struct direntry *fe);
static int retrieve_file(struct direntry *fe);
static int remove_temp_file (char *file_name);
static int
select_on_two (int fd1, int fd2)
{
fd_set set;
struct timeval timeout;
int v;
int maxfd = (fd1 > fd2 ? fd1 : fd2) + 1;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
FD_ZERO(&set);
FD_SET(fd1, &set);
FD_SET(fd2, &set);
v = select (maxfd, &set, 0, 0, &timeout);
if (v <= 0)
return v;
if (FD_ISSET (fd1, &set))
return 1;
if (FD_ISSET (fd2, &set))
return 2;
return -1;
}
static int
get_line (int sock, char *buf, int buf_len, char term)
{
int i, status;
char c;
for (i = 0; i < buf_len; i++, buf++) {
if (read(sock, buf, sizeof(char)) <= 0)
return 0;
if (logfile){
fwrite (buf, 1, 1, logfile);
fflush (logfile);
}
if (*buf == term) {
*buf = 0;
return 1;
}
}
*buf = 0;
while ((status = read(sock, &c, sizeof(c))) > 0){
if (logfile){
fwrite (&c, 1, 1, logfile);
fflush (logfile);
}
if (c == '\n')
return 1;
}
return 0;
}
static void
direntry_destructor (void *data)
{
struct direntry *fe = data;
fe->count--;
if ((fe->tmp_reget == 1 && fe->local_filename)){
unlink (fe->local_filename);
fe->tmp_reget = 0;
}
if (fe->count > 0)
return;
free(fe->name);
if (fe->linkname)
free(fe->linkname);
if (fe->local_filename) {
if (fe->local_is_temp) {
if (!fe->local_stat.st_mtime)
unlink(fe->local_filename);
else {
struct stat sb;
if (stat (fe->local_filename, &sb) >=0 &&
fe->local_stat.st_mtime == sb.st_mtime)
unlink (fe->local_filename); /* Delete only if it hasn't changed */
}
}
free(fe->local_filename);
fe->local_filename = NULL;
}
if (fe->remote_filename)
free(fe->remote_filename);
if (fe->l_stat)
free(fe->l_stat);
free(fe);
}
static void
dir_destructor(void *data)
{
struct dir *fd = data;
fd->count--;
if (fd->count > 0)
return;
free(fd->remote_path);
linklist_destroy(fd->file_list, direntry_destructor);
free(fd);
}
static int
get_line_interruptible (char *buffer, int size, int fd)
{
int n;
int i = 0;
for (i = 0; i < size-1; i++) {
n = read (fd, buffer+i, 1);
if (n == -1 && errno == EINTR){
buffer [i] = 0;
return EINTR;
}
if (n == 0){
buffer [i] = 0;
return 0;
}
if (buffer [i] == '\n'){
buffer [i] = 0;
return 1;
}
}
buffer [size-1] = 0;
return 0;
}
static void
free_bucket (void *data)
{
struct connection *bucket = data;
free(qhost(bucket));
free(quser(bucket));
if (qcdir(bucket))
free(qcdir(bucket));
if (qhome(bucket))
free(qhome(bucket));
if (qupdir(bucket))
free(qupdir(bucket));
if (bucket->password)
wipe_password (bucket->password);
linklist_destroy(qdcache(bucket), dir_destructor);
free(bucket);
}
static void
connection_destructor(void *data)
{
connection_close (data);
free_bucket (data);
}
static void
flush_all_directory(struct connection *bucket)
{
linklist_delete_all(qdcache(bucket), dir_destructor);
}
void X_fill_names (void (*func)(char *))
{
struct linklist *lptr;
char *path_name;
struct connection *bucket;
if (!connections_list)
return;
lptr = connections_list;
do {
if ((bucket = lptr->data) != 0){
path_name = copy_strings ( X_myname, quser (bucket),
"@", qhost (bucket),
qcdir(bucket), 0);
(*func)(path_name);
free (path_name);
}
lptr = lptr->next;
} while (lptr != connections_list);
}
/* get_path:
* makes BUCKET point to the connection bucket descriptor for PATH
* returns a malloced string with the pathname relative to BUCKET.
*
* path must _not_ contain initial /bla/#ftp:
*/
static char*
s_get_path (struct connection **bucket, char *path, char *name)
{
char *user, *host, *remote_path, *pass;
int port;
#ifndef BROKEN_PATHS
if (strncmp (path, name, strlen (name)))
return NULL; /* Normal: consider cd /bla/#ftp */
#else
if (!(path = strstr (path, name)))
return NULL;
#endif
path += strlen (name);
if (!(remote_path = my_get_host_and_username (path, &host, &user, &port, &pass)))
my_errno = ENOENT;
else {
if ((*bucket = open_link (host, user, port, pass)) == NULL) {
free (remote_path);
remote_path = NULL;
}
}
free (host);
free (user);
if (pass)
wipe_password (pass);
if (!remote_path)
return NULL;
/* NOTE: Usage of tildes is deprecated, consider:
* cd /#ftp:pavel@hobit
* cd ~
* And now: what do I want to do? Do I want to go to /home/pavel or to
* /#ftp:hobit/home/pavel? I think first has better sense...
*/
{
int f = !strcmp( remote_path, "/~" );
if (f || !strncmp( remote_path, "/~/", 3 )) {
char *s;
s = concat_dir_and_file( qhome (*bucket), remote_path +3-f );
free (remote_path);
remote_path = s;
}
}
return remote_path;
}
void
X_flushdir (void)
{
force_expiration = 1;
}
static int
s_setctl (char *path, int ctlop, char *arg)
{
switch (ctlop) {
case MCCTL_REMOVELOCALCOPY:
return remove_temp_file (path);
return 0;
}
}
static struct direntry *
_get_file_entry(struct connection *bucket, char *file_name,
int op, int flags)
{
char *p, q;
struct direntry *ent;
struct linklist *file_list, *lptr;
struct dir *dcache;
struct stat sb;
p = strrchr(file_name, '/');
q = *p;
*p = '\0';
Tue Sep 15 20:51:42 1998 Norbert Warmuth <k3190@fh-sw.de> * setup.c: save and restore new global variable/option ftp_use_unix_list_options Tue Sep 15 20:31:32 1998 Norbert Warmuth <k3190@fh-sw.de> * ftpfs.c (ftp_use_unix_list_options): New global variable/option. If true we try to use 'LIST -la <path>'. When it fails we use the two commands 'CWD <path>' and 'LIST' instead. (resolve_symlink): rewritten. Don't get a second directory listing with `LIST -lLa'. Instead use the cache to get the file stat of symbolic links. If the directory the symlink points to isn't already in the cache the directory listing will be fetched and stored in the directory cache (without resolving symlinks recursively). The new method to resolve symlinks is faster if symlinks the same directory or the directory the symlink points to is already in the cache. This function was small and nice until I discovered that it was broken for symlinks to symlinks. Now it looks ugly and perhaps I will revert it to use "LIST -lLa" again. With a fast connection it doesn't matter which methode we use but with a slow connection I wouldn't hesitate to burn more cpu cycles on the client side. (retrieve_dir): Added parameter to tell whether to resolve symlinks (don't resolve symlinks in directory listings retrieved while resolving symlinks). When we don't get a directory listing with 'LIST -la <path>' then try to get it with `CWD <path>; LIST'. Tue Sep 15 20:27:29 1998 Norbert Warmuth <k3190@fh-sw.de * ftpfs.c (login_server): s/ftpfs_get_host/my_get_host/ (retrieve_file_start2): Don't create target file O_EXCL, in copy_file_file we check existance of the target file and know that we want to truncate it (this change was already done a while back but it was reverted with the vfs-split). Tue Sep 15 20:15:42 1998 Norbert Warmuth <k3190@fh-sw.de> * ftpfs.h (struct connection): added boolean which indicates that the ftp server doesn't unterstand Unix ls options * ftpfs.h (struct dir): added enum to store symlink status of the in memory directory cache (directory has no symbolic links; symbolic links but not yet resolved; symbolic links which are resolved) Tue Sep 15 20:02:08 1998 Norbert Warmuth <k3190@fh-sw.de> * shared_ftp_fish.c, fish.c: updated references to retrieve_dir to honour the additional boolean parameter
1998-09-15 23:41:22 +04:00
dcache = retrieve_dir(bucket, *file_name ? file_name : "/", op & DO_RESOLVE_SYMLINK);
if (dcache == NULL)
return NULL;
file_list = dcache->file_list;
*p++ = q;
if (!*p)
p = ".";
for (lptr = file_list->next; lptr != file_list; lptr = lptr->next) {
ent = lptr->data;
if (strcmp(p, ent->name) == 0) {
if (S_ISLNK(ent->s.st_mode) && (op & DO_RESOLVE_SYMLINK)) {
if (ent->l_stat == NULL) ERRNOR (ENOENT, NULL);
if (S_ISLNK(ent->l_stat->st_mode)) ERRNOR (ELOOP, NULL);
}
if (ent && (op & DO_OPEN)) {
mode_t fmode;
fmode = S_ISLNK(ent->s.st_mode)
? ent->l_stat->st_mode
: ent->s.st_mode;
if (S_ISDIR(fmode)) ERRNOR (EISDIR, NULL);
if (!S_ISREG(fmode)) ERRNOR (EPERM, NULL);
if ((flags & O_EXCL) && (flags & O_CREAT)) ERRNOR (EEXIST, NULL);
if (ent->remote_filename == NULL)
if (!(ent->remote_filename = strdup(file_name))) ERRNOR (ENOMEM, NULL);
if (ent->local_filename == NULL ||
!ent->local_stat.st_mtime ||
stat (ent->local_filename, &sb) < 0 ||
sb.st_mtime != ent->local_stat.st_mtime) {
int handle;
if (ent->local_filename){
free (ent->local_filename);
ent->local_filename = NULL;
}
if (flags & O_TRUNC) {
ent->local_filename = tempnam (NULL, X "fs");
if (ent->local_filename == NULL) ERRNOR (ENOMEM, NULL);
handle = open(ent->local_filename, O_CREAT | O_TRUNC | O_RDWR | O_EXCL, 0600);
if (handle < 0) ERRNOR (EIO, NULL);
close(handle);
if (stat (ent->local_filename, &ent->local_stat) < 0)
ent->local_stat.st_mtime = 0;
}
else {
if (IS_LINEAR(flags)) {
ent->local_is_temp = 0;
ent->local_filename = NULL;
if (!linear_start (ent))
return NULL;
return ent;
}
if (!retrieve_file(ent))
return NULL;
}
}
else if (flags & O_TRUNC) {
truncate(ent->local_filename, 0);
}
}
return ent;
}
}
if ((op & DO_OPEN) && (flags & O_CREAT)) {
int handle;
ent = xmalloc(sizeof(struct direntry), "struct direntry");
ent->freshly_created = 0;
ent->tmp_reget = 0;
if (ent == NULL) ERRNOR (ENOMEM, NULL);
ent->count = 1;
ent->linkname = NULL;
ent->l_stat = NULL;
ent->bucket = bucket;
ent->name = strdup(p);
ent->remote_filename = strdup(file_name);
ent->local_filename = tempnam (NULL, X "fs");
if (!ent->name && !ent->remote_filename && !ent->local_filename) {
direntry_destructor(ent);
ERRNOR (ENOMEM, NULL);
}
handle = creat(ent->local_filename, 0700);
if (handle == -1) {
my_errno = EIO;
goto error;
}
fstat(handle, &ent->s);
close(handle);
#if 0
/* This is very wrong - like this a zero length file will be always created
and usually preclude uploading anything more desirable */
#if defined(UPLOAD_ZERO_LENGTH_FILE)
if (!store_file(ent)) goto error;
#endif
#endif
if (!linklist_insert(file_list, ent)) {
my_errno = ENOMEM;
goto error;
}
ent->freshly_created = 1;
return ent;
}
else ERRNOR (ENOENT, NULL);
error:
direntry_destructor(ent);
return NULL;
}
/* this just free's the local temp file. I don't know if the
remote file can be used after this without crashing - paul
psheer@obsidian.co.za psheer@icon.co.za */
static int
remove_temp_file (char *file_name)
{
char *p, q;
struct connection *bucket;
struct direntry *ent;
struct linklist *file_list, *lptr;
struct dir *dcache;
if (!(file_name = get_path (&bucket, file_name)))
return -1;
p = strrchr (file_name, '/');
q = *p;
*p = '\0';
Tue Sep 15 20:51:42 1998 Norbert Warmuth <k3190@fh-sw.de> * setup.c: save and restore new global variable/option ftp_use_unix_list_options Tue Sep 15 20:31:32 1998 Norbert Warmuth <k3190@fh-sw.de> * ftpfs.c (ftp_use_unix_list_options): New global variable/option. If true we try to use 'LIST -la <path>'. When it fails we use the two commands 'CWD <path>' and 'LIST' instead. (resolve_symlink): rewritten. Don't get a second directory listing with `LIST -lLa'. Instead use the cache to get the file stat of symbolic links. If the directory the symlink points to isn't already in the cache the directory listing will be fetched and stored in the directory cache (without resolving symlinks recursively). The new method to resolve symlinks is faster if symlinks the same directory or the directory the symlink points to is already in the cache. This function was small and nice until I discovered that it was broken for symlinks to symlinks. Now it looks ugly and perhaps I will revert it to use "LIST -lLa" again. With a fast connection it doesn't matter which methode we use but with a slow connection I wouldn't hesitate to burn more cpu cycles on the client side. (retrieve_dir): Added parameter to tell whether to resolve symlinks (don't resolve symlinks in directory listings retrieved while resolving symlinks). When we don't get a directory listing with 'LIST -la <path>' then try to get it with `CWD <path>; LIST'. Tue Sep 15 20:27:29 1998 Norbert Warmuth <k3190@fh-sw.de * ftpfs.c (login_server): s/ftpfs_get_host/my_get_host/ (retrieve_file_start2): Don't create target file O_EXCL, in copy_file_file we check existance of the target file and know that we want to truncate it (this change was already done a while back but it was reverted with the vfs-split). Tue Sep 15 20:15:42 1998 Norbert Warmuth <k3190@fh-sw.de> * ftpfs.h (struct connection): added boolean which indicates that the ftp server doesn't unterstand Unix ls options * ftpfs.h (struct dir): added enum to store symlink status of the in memory directory cache (directory has no symbolic links; symbolic links but not yet resolved; symbolic links which are resolved) Tue Sep 15 20:02:08 1998 Norbert Warmuth <k3190@fh-sw.de> * shared_ftp_fish.c, fish.c: updated references to retrieve_dir to honour the additional boolean parameter
1998-09-15 23:41:22 +04:00
dcache = retrieve_dir (bucket, *file_name ? file_name : "/", 0);
if (dcache == NULL)
return -1;
file_list = dcache->file_list;
*p++ = q;
if (!*p)
p = ".";
for (lptr = file_list->next; lptr != file_list; lptr = lptr->next) {
ent = lptr->data;
if (strcmp (p, ent->name) == 0) {
if (ent->local_filename) {
unlink (ent->local_filename);
free (ent->local_filename);
ent->local_filename = 0;
return 0;
}
}
}
return -1;
}
static struct direntry *
get_file_entry(char *path, int op, int flags)
{
struct connection *bucket;
struct direntry *fe;
char *remote_path;
if (!(remote_path = get_path (&bucket, path)))
return NULL;
fe = _get_file_entry(bucket, remote_path, op,
flags);
free(remote_path);
#if 0
if (op & DO_FREE_RESOURCE)
vfs_add_noncurrent_stamps (&X_vfs_ops, (vfsid) bucket, NULL);
#endif
return fe;
}
#define OPT_FLUSH 1
#define OPT_IGNORE_ERROR 2
static int normal_flush = 1;
void X_hint_reread(int reread)
{
if (reread)
normal_flush++;
else
normal_flush--;
}
/* The callbacks */
struct filp {
unsigned int has_changed:1;
struct direntry *fe;
int local_handle;
};
static void *s_open (char *file, int flags, int mode)
{
struct filp *fp;
struct direntry *fe;
fp = xmalloc(sizeof(struct filp), "struct filp");
if (fp == NULL) ERRNOR (ENOMEM, NULL);
fe = get_file_entry(file, DO_OPEN | DO_RESOLVE_SYMLINK, flags);
if (!fe) {
free(fp);
return NULL;
}
fe->linear = IS_LINEAR(flags);
if (!fe->linear) {
fp->local_handle = open(fe->local_filename, flags, mode);
if (fp->local_handle < 0) {
free(fp);
ERRNOR (errno, NULL);
}
} else fp->local_handle = -1;
#ifdef UPLOAD_ZERO_LENGTH_FILE
fp->has_changed = fe->freshly_created;
#else
fp->has_changed = 0;
#endif
fp->fe = fe;
qlock(fe->bucket)++;
fe->count++;
return fp;
}
static int s_read (void *data, char *buffer, int count)
{
struct filp *fp;
int n;
fp = data;
if (fp->fe->linear)
return linear_read (fp->fe, buffer, count);
n = read (fp->local_handle, buffer, count);
if (n < 0)
my_errno = errno;
return n;
}
static int s_write (void *data, char *buf, int nbyte)
{
struct filp *fp;
int n;
fp = data;
n = write (fp->local_handle, buf, nbyte);
if (n < 0)
my_errno = errno;
fp->has_changed = 1;
return n;
}
static int s_close (void *data)
{
struct filp *fp = data;
int result = 0;
if (fp->has_changed) {
if (!store_file(fp->fe))
result = -1;
if (normal_flush)
flush_all_directory(fp->fe->bucket);
}
if (fp->fe->linear)
linear_close(fp->fe);
if (fp->local_handle >= 0)
close(fp->local_handle);
qlock(fp->fe->bucket)--;
direntry_destructor(fp->fe);
free(fp);
return result;
}
static int s_errno (void)
{
return my_errno;
}
/* Explanation:
* On some operating systems (Slowaris 2 for example)
* the d_name member is just a char long (nice trick that break everything),
* so we need to set up some space for the filename.
*/
struct my_dirent {
struct dirent dent;
#ifdef NEED_EXTRA_DIRENT_BUFFER
char extra_buffer [MC_MAXPATHLEN];
#endif
struct linklist *pos;
struct dir *dcache;
};
/* Possible FIXME: what happens if one directory is opened twice ? */
static void *s_opendir (char *dirname)
{
struct connection *bucket;
char *remote_path;
struct my_dirent *dirp;
if (!(remote_path = get_path (&bucket, dirname)))
return NULL;
dirp = xmalloc(sizeof(struct my_dirent), "struct my_dirent");
if (dirp == NULL) {
my_errno = ENOMEM;
goto error_return;
}
Tue Sep 15 20:51:42 1998 Norbert Warmuth <k3190@fh-sw.de> * setup.c: save and restore new global variable/option ftp_use_unix_list_options Tue Sep 15 20:31:32 1998 Norbert Warmuth <k3190@fh-sw.de> * ftpfs.c (ftp_use_unix_list_options): New global variable/option. If true we try to use 'LIST -la <path>'. When it fails we use the two commands 'CWD <path>' and 'LIST' instead. (resolve_symlink): rewritten. Don't get a second directory listing with `LIST -lLa'. Instead use the cache to get the file stat of symbolic links. If the directory the symlink points to isn't already in the cache the directory listing will be fetched and stored in the directory cache (without resolving symlinks recursively). The new method to resolve symlinks is faster if symlinks the same directory or the directory the symlink points to is already in the cache. This function was small and nice until I discovered that it was broken for symlinks to symlinks. Now it looks ugly and perhaps I will revert it to use "LIST -lLa" again. With a fast connection it doesn't matter which methode we use but with a slow connection I wouldn't hesitate to burn more cpu cycles on the client side. (retrieve_dir): Added parameter to tell whether to resolve symlinks (don't resolve symlinks in directory listings retrieved while resolving symlinks). When we don't get a directory listing with 'LIST -la <path>' then try to get it with `CWD <path>; LIST'. Tue Sep 15 20:27:29 1998 Norbert Warmuth <k3190@fh-sw.de * ftpfs.c (login_server): s/ftpfs_get_host/my_get_host/ (retrieve_file_start2): Don't create target file O_EXCL, in copy_file_file we check existance of the target file and know that we want to truncate it (this change was already done a while back but it was reverted with the vfs-split). Tue Sep 15 20:15:42 1998 Norbert Warmuth <k3190@fh-sw.de> * ftpfs.h (struct connection): added boolean which indicates that the ftp server doesn't unterstand Unix ls options * ftpfs.h (struct dir): added enum to store symlink status of the in memory directory cache (directory has no symbolic links; symbolic links but not yet resolved; symbolic links which are resolved) Tue Sep 15 20:02:08 1998 Norbert Warmuth <k3190@fh-sw.de> * shared_ftp_fish.c, fish.c: updated references to retrieve_dir to honour the additional boolean parameter
1998-09-15 23:41:22 +04:00
dirp->dcache = retrieve_dir(bucket, remote_path, 1);
if (dirp->dcache == NULL)
goto error_return;
dirp->pos = dirp->dcache->file_list->next;
free(remote_path);
dirp->dcache->count++;
return (void *)dirp;
error_return:
vfs_add_noncurrent_stamps (&X_vfs_ops, (vfsid) bucket, NULL);
free(remote_path);
free(dirp);
return NULL;
}
static void *s_readdir (void *data)
{
struct direntry *fe;
struct my_dirent *dirp = data;
if (dirp->pos == dirp->dcache->file_list)
return NULL;
fe = dirp->pos->data;
strcpy (&(dirp->dent.d_name [0]), fe->name);
#ifndef DIRENT_LENGTH_COMPUTED
dirp->d_namlen = strlen (dirp->d_name);
#endif
dirp->pos = dirp->pos->next;
return (void *) &dirp->dent;
}
static int s_telldir (void *data)
{
struct my_dirent *dirp = data;
struct linklist *pos;
int i = 0;
pos = dirp->dcache->file_list->next;
while( pos!=dirp->dcache->file_list) {
if (pos == dirp->pos)
return i;
pos = pos->next;
i++;
}
return -1;
}
static void s_seekdir (void *data, int pos)
{
struct my_dirent *dirp = data;
int i;
dirp->pos = dirp->dcache->file_list->next;
for (i=0; i<pos; i++)
s_readdir(data);
}
static int s_closedir (void *info)
{
struct my_dirent *dirp = info;
dir_destructor(dirp->dcache);
free(dirp);
return 0;
}
static int s_lstat (char *path, struct stat *buf)
{
struct direntry *fe;
fe = get_file_entry(path, DO_FREE_RESOURCE, 0);
if (fe) {
*buf = fe->s;
return 0;
}
else
return -1;
}
static int s_stat (char *path, struct stat *buf)
{
struct direntry *fe;
fe = get_file_entry(path, DO_RESOLVE_SYMLINK | DO_FREE_RESOURCE, 0);
if (fe) {
if (!S_ISLNK(fe->s.st_mode))
*buf = fe->s;
else
*buf = *fe->l_stat;
return 0;
}
else
return -1;
}
static int s_fstat (void *data, struct stat *buf)
{
struct filp *fp = data;
if (!S_ISLNK(fp->fe->s.st_mode))
*buf = fp->fe->s;
else
*buf = *fp->fe->l_stat;
return 0;
}
static int s_readlink (char *path, char *buf, int size)
{
struct direntry *fe;
fe = get_file_entry(path, DO_FREE_RESOURCE, 0);
if (!fe)
return -1;
if (!S_ISLNK(fe->s.st_mode)) ERRNOR (EINVAL, -1);
if (fe->linkname == NULL) ERRNOR (EACCES, -1);
if (strlen(fe->linkname) >= size) ERRNOR (ERANGE, -1);
strncpy(buf, fe->linkname, size);
return strlen(fe->linkname);
}
static int s_chdir (char *path)
{
char *remote_path;
struct connection *bucket;
if (!(remote_path = get_path(&bucket, path)))
return -1;
if (qcdir(bucket))
free(qcdir(bucket));
qcdir(bucket) = remote_path;
bucket->cwd_defered = 1;
vfs_add_noncurrent_stamps (&X_vfs_ops, (vfsid) bucket, NULL);
return 0;
}
static int s_lseek (void *data, off_t offset, int whence)
{
struct filp *fp = data;
return lseek(fp->local_handle, offset, whence);
}
static vfsid s_getid (char *p, struct vfs_stamping **parent)
{
struct connection *bucket;
char *remote_path;
*parent = NULL; /* We are not enclosed in any other fs */
if (!(remote_path = get_path (&bucket, p)))
return (vfsid) -1;
else {
free(remote_path);
return (vfsid) bucket;
}
}
static int s_nothingisopen (vfsid id)
{
return qlock((struct connection *)id) == 0;
}
static void s_free (vfsid id)
{
struct connection *bucket = (struct connection *) id;
connection_destructor(bucket);
linklist_delete(connections_list, bucket);
}
static char *s_getlocalcopy (char *path)
{
struct filp *fp = (struct filp *) s_open (path, O_RDONLY, 0);
char *p;
if (fp == NULL)
return NULL;
if (fp->fe->local_filename == NULL) {
s_close ((void *) fp);
return NULL;
}
p = strdup (fp->fe->local_filename);
qlock(fp->fe->bucket)++;
fp->fe->count++;
s_close ((void *) fp);
return p;
}
static void s_ungetlocalcopy (char *path, char *local, int has_changed)
{
struct filp *fp = (struct filp *) s_open (path, O_WRONLY, 0);
if (fp == NULL)
return;
if (!strcmp (fp->fe->local_filename, local)) {
fp->has_changed = has_changed;
qlock(fp->fe->bucket)--;
direntry_destructor(fp->fe);
s_close ((void *) fp);
} else {
/* Should not happen */
s_close ((void *) fp);
mc_def_ungetlocalcopy (path, local, has_changed);
}
}
void
X_done(void)
{
linklist_destroy(connections_list, connection_destructor);
connections_list = NULL;
if (logfile)
fclose (logfile);
logfile = NULL;
}
static int retrieve_file(struct direntry *fe)
{
int total, tmp_reget = 0; /* do_reget; -- I think it can not work: pavel@ucw.cz */
char buffer[8192];
int local_handle, n;
int stat_size = fe->s.st_size;
if (fe->local_filename)
return 1;
if (!(fe->local_filename = tempnam (NULL, X))) ERRNOR (ENOMEM, 0);
fe->local_is_temp = 1;
local_handle = open(fe->local_filename, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600);
if (local_handle == -1) {
my_errno = EIO;
goto error_4;
}
if (!linear_start (fe))
goto error_3;
/* Clear the interrupt status */
enable_interrupt_key ();
total = (tmp_reget > 0) ? tmp_reget : 0;
while (1) {
if ((n = linear_read(fe, buffer, sizeof(buffer))) < 0)
goto error_1;
if (!n)
break;
total += n;
print_vfs_stats (X, "Getting file", fe->remote_filename, total, stat_size);
while (write(local_handle, buffer, n) < 0) {
if (errno == EINTR) {
if (got_interrupt()) {
my_errno = EINTR;
goto error_2;
}
else
continue;
}
my_errno = errno;
goto error_1;
}
}
linear_close(fe);
disable_interrupt_key();
close(local_handle);
if (stat (fe->local_filename, &fe->local_stat) < 0)
fe->local_stat.st_mtime = 0;
if (tmp_reget > 0)
fe->tmp_reget = 1;
return 1;
error_1:
error_2:
linear_close(fe);
error_3:
disable_interrupt_key();
close(local_handle);
unlink(fe->local_filename);
error_4:
free(fe->local_filename);
fe->local_filename = NULL;
return 0;
}