mc/vfs/fish.c
Roland Illig 9558c7178f * src/info.c (info_show_info): Enabled display of the device number,
for the case that the column in the directory listing is too
	short. This had been disabled since 1994, for no obvious reason
	and without any comment.
	* src/screen.c (format_device_number): Added. Converts a device number
	into a bounded string.
	* screen.c (string_file_size): Support for larger device numbers
	(and for non-Linux systems).
	* vfs/cpio.c (cpio_read_crc_head): Fixed device number handling for
	non-Linux systems.
	* vfs/fish.c (fish_dir_load): Likewise.
	* vfs/utilvfs.c (vfs_parse_ls_lga): Likewise.
2005-01-27 23:32:13 +00:00

933 lines
24 KiB
C

/* Virtual File System: FISH implementation for transfering files over
shell connections.
Copyright (C) 1998 The Free Software Foundation
Written by: 1998 Pavel Machek
Spaces fix: 2000 Michal Svec
Derived from ftpfs.c.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Library 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 Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/*
* Read README.fish for protocol specification.
*
* Syntax of path is: /#sh:user@host[:Cr]/path
* where C means you want compressed connection,
* and r means you want to use rsh
*
* Namespace: fish_vfs_ops exported.
*/
/* Define this if your ssh can take -I option */
#include <config.h>
#include <errno.h>
#undef HAVE_HACKED_SSH
#include "utilvfs.h"
#include "xdirentry.h"
#include "vfs.h"
#include "vfs-impl.h"
#include "gc.h" /* vfs_stamp_create */
#include "tcputil.h"
#include "../src/unixcompat.h"
#define FISH_DIRECTORY_TIMEOUT 30 * 60
#define DO_RESOLVE_SYMLINK 1
#define DO_OPEN 2
#define DO_FREE_RESOURCE 4
#define FISH_FLAG_COMPRESSED 1
#define FISH_FLAG_RSH 2
#define OPT_FLUSH 1
#define OPT_IGNORE_ERROR 2
/*
* Reply codes.
*/
#define PRELIM 1 /* positive preliminary */
#define COMPLETE 2 /* positive completion */
#define CONTINUE 3 /* positive intermediate */
#define TRANSIENT 4 /* transient negative completion */
#define ERROR 5 /* permanent negative completion */
/* command wait_flag: */
#define NONE 0x00
#define WAIT_REPLY 0x01
#define WANT_STRING 0x02
static char reply_str [80];
static struct vfs_class vfs_fish_ops;
static int
fish_command (struct vfs_class *me, struct vfs_s_super *super,
int wait_reply, const char *fmt, ...)
__attribute__ ((format (printf, 4, 5)));
static int fish_decode_reply (char *s, int was_garbage)
{
int code;
if (!sscanf(s, "%d", &code)) {
code = 500;
return 5;
}
if (code<100) return was_garbage ? ERROR : (!code ? COMPLETE : PRELIM);
return code / 100;
}
/* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
static int fish_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
{
char answer[1024];
int was_garbage = 0;
for (;;) {
if (!vfs_s_get_line(me, sock, answer, sizeof(answer), '\n')) {
if (string_buf)
*string_buf = 0;
return 4;
}
if (strncmp(answer, "### ", 4)) {
was_garbage = 1;
if (string_buf)
g_strlcpy(string_buf, answer, string_len);
} else return fish_decode_reply(answer+4, was_garbage);
}
}
#define SUP super->u.fish
static int
fish_command (struct vfs_class *me, struct vfs_s_super *super,
int wait_reply, const char *fmt, ...)
{
va_list ap;
char *str;
int status;
FILE *logfile = MEDATA->logfile;
va_start (ap, fmt);
str = g_strdup_vprintf (fmt, ap);
va_end (ap);
if (logfile) {
fwrite (str, strlen (str), 1, logfile);
fflush (logfile);
}
enable_interrupt_key ();
status = write (SUP.sockw, str, strlen (str));
g_free (str);
disable_interrupt_key ();
if (status < 0)
return TRANSIENT;
if (wait_reply)
return fish_get_reply (me, SUP.sockr,
(wait_reply & WANT_STRING) ? reply_str :
NULL, sizeof (reply_str) - 1);
return COMPLETE;
}
static void
fish_free_archive (struct vfs_class *me, struct vfs_s_super *super)
{
if ((SUP.sockw != -1) || (SUP.sockr != -1)) {
print_vfs_message (_("fish: Disconnecting from %s"),
super->name ? super->name : "???");
fish_command (me, super, NONE, "#BYE\nexit\n");
close (SUP.sockw);
close (SUP.sockr);
SUP.sockw = SUP.sockr = -1;
}
g_free (SUP.host);
g_free (SUP.user);
g_free (SUP.cwdir);
g_free (SUP.password);
}
static void
fish_pipeopen(struct vfs_s_super *super, const char *path, const char *argv[])
{
int fileset1[2], fileset2[2];
int res;
if ((pipe(fileset1)<0) || (pipe(fileset2)<0))
vfs_die("Cannot pipe(): %m.");
if ((res = fork())) {
if (res<0) vfs_die("Cannot fork(): %m.");
/* We are the parent */
close(fileset1[0]);
SUP.sockw = fileset1[1];
close(fileset2[1]);
SUP.sockr = fileset2[0];
} else {
close(0);
dup(fileset1[0]);
close(fileset1[0]); close(fileset1[1]);
close(1); close(2);
dup(fileset2[1]);
/* stderr to /dev/null */
open ("/dev/null", O_WRONLY);
close(fileset2[0]); close(fileset2[1]);
execvp(path, const_cast(char **, argv));
_exit(3);
}
}
/* The returned directory should always contain a trailing slash */
static char *fish_getcwd(struct vfs_class *me, struct vfs_s_super *super)
{
if (fish_command (me, super, WANT_STRING, "#PWD\npwd; echo '### 200'\n") == COMPLETE)
return g_strconcat (reply_str, "/", (char *) NULL);
ERRNOR (EIO, NULL);
}
static int
fish_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
{
{
const char *argv[10];
const char *xsh = (SUP.flags == FISH_FLAG_RSH ? "rsh" : "ssh");
int i = 0;
argv[i++] = xsh;
#ifdef HAVE_HACKED_SSH
argv[i++] = "-I";
#endif
if (SUP.flags == FISH_FLAG_COMPRESSED)
argv[i++] = "-C";
argv[i++] = "-l";
argv[i++] = SUP.user;
argv[i++] = SUP.host;
argv[i++] = "echo FISH:; /bin/sh";
argv[i++] = NULL;
fish_pipeopen (super, xsh, argv);
}
{
char answer[2048];
print_vfs_message (_("fish: Waiting for initial line..."));
if (!vfs_s_get_line (me, SUP.sockr, answer, sizeof (answer), ':'))
ERRNOR (E_PROTO, -1);
print_vfs_message ("%s", answer);
if (strstr (answer, "assword")) {
/* Currently, this does not work. ssh reads passwords from
/dev/tty, not from stdin :-(. */
#ifndef HAVE_HACKED_SSH
message (1, MSG_ERROR,
_
("Sorry, we cannot do password authenticated connections for now."));
ERRNOR (EPERM, -1);
#endif
if (!SUP.password) {
char *p, *op;
p = g_strconcat (_(" fish: Password required for "),
SUP.user, " ", (char *) NULL);
op = vfs_get_password (p);
g_free (p);
if (op == NULL)
ERRNOR (EPERM, -1);
SUP.password = op;
}
print_vfs_message (_("fish: Sending password..."));
write (SUP.sockw, SUP.password, strlen (SUP.password));
write (SUP.sockw, "\n", 1);
}
}
print_vfs_message (_("fish: Sending initial line..."));
/*
* Run `start_fish_server'. If it doesn't exist - no problem,
* we'll talk directly to the shell.
*/
if (fish_command
(me, super, WAIT_REPLY,
"#FISH\necho; start_fish_server 2>&1; echo '### 200'\n") !=
COMPLETE)
ERRNOR (E_PROTO, -1);
print_vfs_message (_("fish: Handshaking version..."));
if (fish_command
(me, super, WAIT_REPLY,
"#VER 0.0.0\necho '### 000'\n") != COMPLETE)
ERRNOR (E_PROTO, -1);
/* Set up remote locale to C, otherwise dates cannot be recognized */
if (fish_command
(me, super, WAIT_REPLY,
"LANG=C; LC_ALL=C; LC_TIME=C\n"
"export LANG; export LC_ALL; export LC_TIME\n" "echo '### 200'\n")
!= COMPLETE)
ERRNOR (E_PROTO, -1);
print_vfs_message (_("fish: Setting up current directory..."));
SUP.cwdir = fish_getcwd (me, super);
print_vfs_message (_("fish: Connected, home %s."), SUP.cwdir);
#if 0
super->name =
g_strconcat ("/#sh:", SUP.user, "@", SUP.host, "/", (char *) NULL);
#endif
super->name = g_strdup (PATH_SEP_STR);
super->root =
vfs_s_new_inode (me, super,
vfs_s_default_stat (me, S_IFDIR | 0755));
return 0;
}
static int
fish_open_archive (struct vfs_class *me, struct vfs_s_super *super,
const char *archive_name, char *op)
{
char *host, *user, *password, *p;
int flags;
p = vfs_split_url (strchr (op, ':') + 1, &host, &user, &flags,
&password, 0, URL_NOSLASH);
g_free (p);
SUP.host = host;
SUP.user = user;
SUP.flags = flags;
if (!strncmp (op, "rsh:", 4))
SUP.flags |= FISH_FLAG_RSH;
SUP.cwdir = NULL;
if (password)
SUP.password = password;
return fish_open_archive_int (me, super);
}
static int
fish_archive_same (struct vfs_class *me, struct vfs_s_super *super,
const char *archive_name, char *op, void *cookie)
{
char *host, *user;
int flags;
op = vfs_split_url (strchr (op, ':') + 1, &host, &user, &flags, 0, 0,
URL_NOSLASH);
g_free (op);
flags = ((strcmp (host, SUP.host) == 0)
&& (strcmp (user, SUP.user) == 0) && (flags == SUP.flags));
g_free (host);
g_free (user);
return flags;
}
static int
fish_dir_uptodate(struct vfs_class *me, struct vfs_s_inode *ino)
{
struct timeval tim;
gettimeofday(&tim, NULL);
if (MEDATA->flush) {
MEDATA->flush = 0;
return 0;
}
if (tim.tv_sec < ino->timestamp.tv_sec)
return 1;
return 0;
}
static int
fish_dir_load(struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
{
struct vfs_s_super *super = dir->super;
char buffer[8192];
struct vfs_s_entry *ent = NULL;
FILE *logfile;
char *quoted_path;
logfile = MEDATA->logfile;
print_vfs_message(_("fish: Reading directory %s..."), remote_path);
gettimeofday(&dir->timestamp, NULL);
dir->timestamp.tv_sec += 10; /* was 360: 10 is good for
stressing direntry layer a bit */
quoted_path = name_quote (remote_path, 0);
fish_command (me, super, NONE,
"#LIST /%s\n"
"ls -lLa /%s 2>/dev/null | grep '^[^cbt]' | (\n"
"while read p x u g s m d y n; do\n"
"echo \"P$p $u.$g\nS$s\nd$m $d $y\n:$n\n\"\n"
"done\n"
")\n"
"ls -lLa /%s 2>/dev/null | grep '^[cb]' | (\n"
"while read p x u g a i m d y n; do\n"
"echo \"P$p $u.$g\nE$a$i\nd$m $d $y\n:$n\n\"\n"
"done\n"
")\n"
"echo '### 200'\n",
remote_path, quoted_path, quoted_path);
g_free (quoted_path);
ent = vfs_s_generate_entry(me, NULL, dir, 0);
while (1) {
int res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), SUP.sockr);
if ((!res) || (res == EINTR)) {
vfs_s_free_entry(me, ent);
me->verrno = ECONNRESET;
goto error;
}
if (logfile) {
fputs (buffer, logfile);
fputs ("\n", logfile);
fflush (logfile);
}
if (!strncmp(buffer, "### ", 4))
break;
if ((!buffer[0])) {
if (ent->name) {
vfs_s_insert_entry(me, dir, ent);
ent = vfs_s_generate_entry(me, NULL, dir, 0);
}
continue;
}
#define ST ent->ino->st
switch(buffer[0]) {
case ':': {
if (!strcmp(buffer+1, ".") || !strcmp(buffer+1, ".."))
break; /* We'll do . and .. ourself */
ent->name = g_strdup(buffer+1);
break;
}
case 'S':
#ifdef HAVE_ATOLL
ST.st_size = (off_t) atoll (buffer+1);
#else
ST.st_size = (off_t) atof (buffer+1);
#endif
break;
case 'P': {
int i;
if ((i = vfs_parse_filetype(buffer[1])) ==-1)
break;
ST.st_mode = i;
if ((i = vfs_parse_filemode(buffer+2)) ==-1)
break;
ST.st_mode |= i;
if (S_ISLNK(ST.st_mode))
ST.st_mode = 0;
}
break;
case 'd': {
vfs_split_text(buffer+1);
if (!vfs_parse_filedate(0, &ST.st_ctime))
break;
ST.st_atime = ST.st_mtime = ST.st_ctime;
}
break;
case 'D': {
struct tm tim;
if (sscanf(buffer+1, "%d %d %d %d %d %d", &tim.tm_year, &tim.tm_mon,
&tim.tm_mday, &tim.tm_hour, &tim.tm_min, &tim.tm_sec) != 6)
break;
ST.st_atime = ST.st_mtime = ST.st_ctime = mktime(&tim);
}
break;
case 'E': {
int maj, min;
if (sscanf(buffer+1, "%d,%d", &maj, &min) != 2)
break;
#ifdef HAVE_STRUCT_STAT_ST_RDEV
ST.st_rdev = makedev (maj, min);
#endif
}
case 'L': ent->ino->linkname = g_strdup(buffer+1);
break;
}
}
vfs_s_free_entry (me, ent);
me->verrno = E_REMOTE;
if (fish_decode_reply(buffer+4, 0) == COMPLETE) {
g_free (SUP.cwdir);
SUP.cwdir = g_strdup (remote_path);
print_vfs_message (_("%s: done."), me->name);
return 0;
}
error:
print_vfs_message (_("%s: failure"), me->name);
return 1;
}
static int
fish_file_store(struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *localname)
{
struct vfs_s_super *super = FH_SUPER;
int n, total;
char buffer[8192];
struct stat s;
int was_error = 0;
int h;
char *quoted_name;
h = open (localname, O_RDONLY);
if (h == -1)
ERRNOR (EIO, -1);
if (fstat(h, &s)<0) {
close (h);
ERRNOR (EIO, -1);
}
/* Use this as stor: ( dd block ; dd smallblock ) | ( cat > file; cat > /dev/null ) */
print_vfs_message(_("fish: store %s: sending command..."), name );
quoted_name = name_quote (name, 0);
/* FIXME: File size is limited to ULONG_MAX */
if (!fh->u.fish.append)
n = fish_command (me, super, WAIT_REPLY,
"#STOR %lu /%s\n"
"> /%s\n"
"echo '### 001'\n"
"(\n"
"dd bs=1 count=%lu\n"
") 2>/dev/null | (\n"
"cat > /%s\n"
"cat > /dev/null\n"
"); echo '### 200'\n",
(unsigned long) s.st_size, name, quoted_name,
(unsigned long) s.st_size, quoted_name);
else
n = fish_command (me, super, WAIT_REPLY,
"#STOR %lu /%s\n"
"echo '### 001'\n"
"(\n"
"dd bs=1 count=%lu\n"
") 2>/dev/null | (\n"
"cat >> /%s\n"
"cat > /dev/null\n"
"); echo '### 200'\n",
(unsigned long) s.st_size, name,
(unsigned long) s.st_size, quoted_name);
g_free (quoted_name);
if (n != PRELIM) {
close (h);
ERRNOR(E_REMOTE, -1);
}
total = 0;
while (1) {
int t;
while ((n = read(h, buffer, sizeof(buffer))) < 0) {
if ((errno == EINTR) && got_interrupt())
continue;
print_vfs_message(_("fish: Local read failed, sending zeros") );
close(h);
h = open( "/dev/zero", O_RDONLY );
}
if (n == 0)
break;
if ((t = write (SUP.sockw, buffer, n)) != n) {
if (t == -1) {
me->verrno = errno;
} else {
me->verrno = EIO;
}
goto error_return;
}
disable_interrupt_key();
total += n;
print_vfs_message(_("fish: storing %s %d (%lu)"),
was_error ? _("zeros") : _("file"), total,
(unsigned long) s.st_size);
}
close(h);
if ((fish_get_reply (me, SUP.sockr, NULL, 0) != COMPLETE) || was_error)
ERRNOR (E_REMOTE, -1);
return 0;
error_return:
close(h);
fish_get_reply(me, SUP.sockr, NULL, 0);
return -1;
}
static int
fish_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
{
char *name;
char *quoted_name;
if (offset)
ERRNOR (E_NOTSUPP, 0);
name = vfs_s_fullpath (me, fh->ino);
if (!name)
return 0;
quoted_name = name_quote (name, 0);
g_free (name);
name = quoted_name;
fh->u.fish.append = 0;
offset = fish_command (me, FH_SUPER, WANT_STRING,
"#RETR /%s\n"
"ls -l /%s 2>/dev/null | (\n"
"read var1 var2 var3 var4 var5 var6\n"
"echo \"$var5\"\n"
")\n"
"echo '### 100'\n"
"cat /%s\n"
"echo '### 200'\n",
name, name, name );
g_free (name);
if (offset != PRELIM) ERRNOR (E_REMOTE, 0);
fh->linear = LS_LINEAR_OPEN;
fh->u.fish.got = 0;
if (sscanf( reply_str, "%d", &fh->u.fish.total )!=1)
ERRNOR (E_REMOTE, 0);
return 1;
}
static void
fish_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
{
struct vfs_s_super *super = FH_SUPER;
char buffer[8192];
int n;
print_vfs_message( _("Aborting transfer...") );
do {
n = MIN(8192, fh->u.fish.total - fh->u.fish.got);
if (n) {
if ((n = read(SUP.sockr, buffer, n)) < 0)
return;
fh->u.fish.got += n;
}
} while (n);
if (fish_get_reply (me, SUP.sockr, NULL, 0) != COMPLETE)
print_vfs_message( _("Error reported after abort.") );
else
print_vfs_message( _("Aborted transfer would be successful.") );
}
static int
fish_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
{
struct vfs_s_super *super = FH_SUPER;
int n = 0;
len = MIN( fh->u.fish.total - fh->u.fish.got, len );
disable_interrupt_key();
while (len && ((n = read (SUP.sockr, buf, len))<0)) {
if ((errno == EINTR) && !got_interrupt())
continue;
break;
}
enable_interrupt_key();
if (n>0) fh->u.fish.got += n;
if (n<0) fish_linear_abort(me, fh);
if ((!n) && ((fish_get_reply (me, SUP.sockr, NULL, 0) != COMPLETE)))
ERRNOR (E_REMOTE, -1);
ERRNOR (errno, n);
}
static void
fish_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
{
if (fh->u.fish.total != fh->u.fish.got)
fish_linear_abort(me, fh);
}
static int
fish_ctl (void *fh, int ctlop, void *arg)
{
return 0;
switch (ctlop) {
case VFS_CTL_IS_NOTREADY:
{
int v;
if (!FH->linear)
vfs_die ("You may not do this");
if (FH->linear == LS_LINEAR_CLOSED)
return 0;
v = vfs_s_select_on_two (FH_SUPER->u.fish.sockr, 0);
if (((v < 0) && (errno == EINTR)) || v == 0)
return 1;
return 0;
}
default:
return 0;
}
}
static int
fish_send_command(struct vfs_class *me, struct vfs_s_super *super, const char *cmd, int flags)
{
int r;
r = fish_command (me, super, WAIT_REPLY, "%s", cmd);
vfs_stamp_create (&vfs_fish_ops, super);
if (r != COMPLETE) ERRNOR (E_REMOTE, -1);
if (flags & OPT_FLUSH)
vfs_s_invalidate(me, super);
return 0;
}
#define PREFIX \
char buf[BUF_LARGE]; \
char *rpath, *mpath = g_strdup (path); \
struct vfs_s_super *super; \
if (!(rpath = vfs_s_get_path_mangle (me, mpath, &super, 0))) { \
g_free (mpath); \
return -1; \
} \
rpath = name_quote (rpath, 0); \
g_free (mpath);
#define POSTFIX(flags) \
g_free (rpath); \
return fish_send_command(me, super, buf, flags);
static int
fish_chmod (struct vfs_class *me, const char *path, int mode)
{
PREFIX
g_snprintf(buf, sizeof(buf), "#CHMOD %4.4o /%s\n"
"chmod %4.4o \"/%s\" 2>/dev/null\n"
"echo '### 000'\n",
mode & 07777, rpath,
mode & 07777, rpath);
POSTFIX(OPT_FLUSH);
}
#define FISH_OP(name, chk, string) \
static int fish_##name (struct vfs_class *me, const char *path1, const char *path2) \
{ \
char buf[BUF_LARGE]; \
char *rpath1, *rpath2, *mpath1, *mpath2; \
struct vfs_s_super *super1, *super2; \
if (!(rpath1 = vfs_s_get_path_mangle (me, mpath1 = g_strdup(path1), &super1, 0))) { \
g_free (mpath1); \
return -1; \
} \
if (!(rpath2 = vfs_s_get_path_mangle (me, mpath2 = g_strdup(path2), &super2, 0))) { \
g_free (mpath1); \
g_free (mpath2); \
return -1; \
} \
rpath1 = name_quote (rpath1, 0); \
g_free (mpath1); \
rpath2 = name_quote (rpath2, 0); \
g_free (mpath2); \
g_snprintf(buf, sizeof(buf), string "\n", rpath1, rpath2, rpath1, rpath2); \
g_free (rpath1); \
g_free (rpath2); \
return fish_send_command(me, super2, buf, OPT_FLUSH); \
}
#define XTEST if (bucket1 != bucket2) { ERRNOR (EXDEV, -1); }
FISH_OP(rename, XTEST, "#RENAME /%s /%s\n"
"mv /%s /%s 2>/dev/null\n"
"echo '### 000'" )
FISH_OP(link, XTEST, "#LINK /%s /%s\n"
"ln /%s /%s 2>/dev/null\n"
"echo '### 000'" )
static int fish_symlink (struct vfs_class *me, const char *setto, const char *path)
{
char *qsetto;
PREFIX
qsetto = name_quote (setto, 0);
g_snprintf(buf, sizeof(buf),
"#SYMLINK %s /%s\n"
"ln -s %s /%s 2>/dev/null\n"
"echo '### 000'\n",
qsetto, rpath, qsetto, rpath);
g_free (qsetto);
POSTFIX(OPT_FLUSH);
}
static int
fish_chown (struct vfs_class *me, const char *path, int owner, int group)
{
char *sowner, *sgroup;
struct passwd *pw;
struct group *gr;
if ((pw = getpwuid (owner)) == NULL)
return 0;
if ((gr = getgrgid (group)) == NULL)
return 0;
sowner = pw->pw_name;
sgroup = gr->gr_name;
{
PREFIX
g_snprintf (buf, sizeof(buf),
"#CHOWN /%s /%s\n"
"chown %s /%s 2>/dev/null\n"
"echo '### 000'\n",
sowner, rpath,
sowner, rpath);
fish_send_command (me, super, buf, OPT_FLUSH);
/* FIXME: what should we report if chgrp succeeds but chown fails? */
g_snprintf (buf, sizeof(buf),
"#CHGRP /%s /%s\n"
"chgrp %s /%s 2>/dev/null\n"
"echo '### 000'\n",
sgroup, rpath,
sgroup, rpath);
/* fish_send_command(me, super, buf, OPT_FLUSH); */
POSTFIX (OPT_FLUSH)
}
}
static int fish_unlink (struct vfs_class *me, const char *path)
{
PREFIX
g_snprintf(buf, sizeof(buf),
"#DELE /%s\n"
"rm -f /%s 2>/dev/null\n"
"echo '### 000'\n",
rpath, rpath);
POSTFIX(OPT_FLUSH);
}
static int fish_mkdir (struct vfs_class *me, const char *path, mode_t mode)
{
PREFIX
g_snprintf(buf, sizeof(buf),
"#MKD /%s\n"
"mkdir /%s 2>/dev/null\n"
"echo '### 000'\n",
rpath, rpath);
POSTFIX(OPT_FLUSH);
}
static int fish_rmdir (struct vfs_class *me, const char *path)
{
PREFIX
g_snprintf(buf, sizeof(buf),
"#RMD /%s\n"
"rmdir /%s 2>/dev/null\n"
"echo '### 000'\n",
rpath, rpath);
POSTFIX(OPT_FLUSH);
}
static int
fish_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags,
int mode)
{
fh->u.fish.append = 0;
/* File will be written only, so no need to retrieve it */
if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR))) {
fh->u.fish.append = flags & O_APPEND;
if (!fh->ino->localname) {
int tmp_handle =
vfs_mkstemps (&fh->ino->localname, me->name,
fh->ino->ent->name);
if (tmp_handle == -1)
return -1;
close (tmp_handle);
}
return 0;
}
if (!fh->ino->localname)
if (vfs_s_retrieve_file (me, fh->ino) == -1)
return -1;
if (!fh->ino->localname)
vfs_die ("retrieve_file failed to fill in localname");
return 0;
}
static void
fish_fill_names (struct vfs_class *me, fill_names_f func)
{
struct vfs_s_super *super = MEDATA->supers;
const char *flags;
char *name;
while (super){
switch (SUP.flags & (FISH_FLAG_RSH | FISH_FLAG_COMPRESSED)) {
case FISH_FLAG_RSH:
flags = ":r";
break;
case FISH_FLAG_COMPRESSED:
flags = ":C";
break;
case FISH_FLAG_RSH | FISH_FLAG_COMPRESSED:
flags = "";
break;
default:
flags = "";
break;
}
name = g_strconcat ("/#sh:", SUP.user, "@", SUP.host, flags,
"/", SUP.cwdir, (char *) NULL);
(*func)(name);
g_free (name);
super = super->next;
}
}
void
init_fish (void)
{
static struct vfs_s_subclass fish_subclass;
fish_subclass.flags = VFS_S_REMOTE;
fish_subclass.archive_same = fish_archive_same;
fish_subclass.open_archive = fish_open_archive;
fish_subclass.free_archive = fish_free_archive;
fish_subclass.fh_open = fish_fh_open;
fish_subclass.dir_load = fish_dir_load;
fish_subclass.dir_uptodate = fish_dir_uptodate;
fish_subclass.file_store = fish_file_store;
fish_subclass.linear_start = fish_linear_start;
fish_subclass.linear_read = fish_linear_read;
fish_subclass.linear_close = fish_linear_close;
vfs_s_init_class (&vfs_fish_ops, &fish_subclass);
vfs_fish_ops.name = "fish";
vfs_fish_ops.prefix = "sh:";
vfs_fish_ops.fill_names = fish_fill_names;
vfs_fish_ops.chmod = fish_chmod;
vfs_fish_ops.chown = fish_chown;
vfs_fish_ops.symlink = fish_symlink;
vfs_fish_ops.link = fish_link;
vfs_fish_ops.unlink = fish_unlink;
vfs_fish_ops.rename = fish_rename;
vfs_fish_ops.mkdir = fish_mkdir;
vfs_fish_ops.rmdir = fish_rmdir;
vfs_fish_ops.ctl = fish_ctl;
vfs_register_class (&vfs_fish_ops);
}