Apply code identation policy.
[midnight-commander.git] / lib / vfs / mc-vfs / fish.c
blob8fc6b49e1b6cc966ede2ad34e22055a9180f35d4
1 /* Virtual File System: FISH implementation for transfering files over
2 shell connections.
4 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
5 2007 Free Software Foundation, Inc.
7 Written by: 1998 Pavel Machek
8 Spaces fix: 2000 Michal Svec
9 2010 Andrew Borodin
10 2010 Slava Zanko
11 2010 Ilia Maslakov
13 Derived from ftpfs.c.
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU Library General Public License
17 as published by the Free Software Foundation; either version 2 of
18 the License, or (at your option) any later version.
20 This program 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 Library General Public License for more details.
25 You should have received a copy of the GNU Library General Public
26 License along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
29 /**
30 * \file
31 * \brief Source: Virtual File System: FISH implementation for transfering files over
32 * shell connections
33 * \author Pavel Machek
34 * \author Michal Svec
35 * \date 1998, 2000
37 * Derived from ftpfs.c
38 * Read README.fish for protocol specification.
40 * Syntax of path is: \verbatim /#sh:user@host[:Cr]/path \endverbatim
41 * where C means you want compressed connection,
42 * and r means you want to use rsh
44 * Namespace: fish_vfs_ops exported.
47 /* Define this if your ssh can take -I option */
49 #include <config.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <pwd.h>
53 #include <grp.h>
54 #include <sys/time.h> /* gettimeofday() */
55 #include <stdlib.h>
56 #include <string.h>
57 #include <stdint.h> /* uintmax_t */
59 #include "lib/global.h"
60 #include "lib/fs.h"
61 #include "lib/tty/tty.h" /* enable/disable interrupt key */
62 #include "lib/strescape.h"
63 #include "lib/unixcompat.h"
64 #include "lib/fileloc.h"
66 #include "src/wtools.h" /* message() */
67 #include "src/main.h" /* print_vfs_message */
69 #include "vfs-impl.h"
70 #include "utilvfs.h"
71 #include "netutil.h"
72 #include "xdirentry.h"
73 #include "gc.h" /* vfs_stamp_create */
75 #include "fish.h"
76 #include "fishdef.h"
77 #include "src/execute.h" /* pre_exec, post_exec */
79 int fish_directory_timeout = 900;
81 #define DO_RESOLVE_SYMLINK 1
82 #define DO_OPEN 2
83 #define DO_FREE_RESOURCE 4
85 #define FISH_FLAG_COMPRESSED 1
86 #define FISH_FLAG_RSH 2
88 #define OPT_FLUSH 1
89 #define OPT_IGNORE_ERROR 2
92 * Reply codes.
94 #define PRELIM 1 /* positive preliminary */
95 #define COMPLETE 2 /* positive completion */
96 #define CONTINUE 3 /* positive intermediate */
97 #define TRANSIENT 4 /* transient negative completion */
98 #define ERROR 5 /* permanent negative completion */
100 /* command wait_flag: */
101 #define NONE 0x00
102 #define WAIT_REPLY 0x01
103 #define WANT_STRING 0x02
105 /* environment flags */
106 #define FISH_HAVE_HEAD 1
107 #define FISH_HAVE_SED 2
108 #define FISH_HAVE_AWK 4
109 #define FISH_HAVE_PERL 8
110 #define FISH_HAVE_LSQ 16
111 #define FISH_HAVE_DATE_MDYT 32
112 #define FISH_HAVE_TAIL 64
114 static char reply_str[80];
116 static struct vfs_class vfs_fish_ops;
119 static char *
120 fish_load_script_from_file (const char *hostname, const char *script_name, const char *def_content)
122 char *scr_filename = NULL;
123 char *scr_content;
124 gsize scr_len = 0;
126 /* 1st: scan user directory */
127 scr_filename = g_build_path (PATH_SEP_STR, home_dir, MC_USERCONF_DIR, FISH_PREFIX, hostname,
128 script_name, (char *) NULL);
129 /* silent about user dir */
130 g_file_get_contents (scr_filename, &scr_content, &scr_len, NULL);
131 g_free (scr_filename);
132 /* 2nd: scan system dir */
133 if (scr_content == NULL)
135 scr_filename = g_build_path (PATH_SEP_STR, LIBEXECDIR, FISH_PREFIX, script_name, (char *) NULL);
136 g_file_get_contents (scr_filename, &scr_content, &scr_len, NULL);
137 g_free (scr_filename);
140 if (scr_content != NULL)
141 return scr_content;
143 return g_strdup (def_content);
146 static int
147 fish_decode_reply (char *s, int was_garbage)
149 int code;
150 if (!sscanf (s, "%d", &code))
152 code = 500;
153 return 5;
155 if (code < 100)
156 return was_garbage ? ERROR : (!code ? COMPLETE : PRELIM);
157 return code / 100;
160 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
161 static int
162 fish_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
164 char answer[1024];
165 int was_garbage = 0;
167 for (;;)
169 if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
171 if (string_buf)
172 *string_buf = 0;
173 return 4;
176 if (strncmp (answer, "### ", 4))
178 was_garbage = 1;
179 if (string_buf)
180 g_strlcpy (string_buf, answer, string_len);
182 else
183 return fish_decode_reply (answer + 4, was_garbage);
187 #define SUP super->u.fish
189 static int
190 fish_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *fmt, ...)
192 va_list ap;
193 char *str;
194 int status;
195 FILE *logfile = MEDATA->logfile;
197 va_start (ap, fmt);
199 str = g_strdup_vprintf (fmt, ap);
200 va_end (ap);
202 if (logfile)
204 size_t ret;
205 ret = fwrite (str, strlen (str), 1, logfile);
206 ret = fflush (logfile);
209 tty_enable_interrupt_key ();
211 status = write (SUP.sockw, str, strlen (str));
212 g_free (str);
214 tty_disable_interrupt_key ();
215 if (status < 0)
216 return TRANSIENT;
218 if (wait_reply)
219 return fish_get_reply (me, SUP.sockr,
220 (wait_reply & WANT_STRING) ? reply_str :
221 NULL, sizeof (reply_str) - 1);
222 return COMPLETE;
225 static void
226 fish_free_archive (struct vfs_class *me, struct vfs_s_super *super)
228 if ((SUP.sockw != -1) || (SUP.sockr != -1))
230 print_vfs_message (_("fish: Disconnecting from %s"), super->name ? super->name : "???");
231 fish_command (me, super, NONE, "#BYE\nexit\n");
232 close (SUP.sockw);
233 close (SUP.sockr);
234 SUP.sockw = SUP.sockr = -1;
236 g_free (SUP.host);
237 g_free (SUP.user);
238 g_free (SUP.cwdir);
239 g_free (SUP.password);
240 g_free (SUP.scr_ls);
241 g_free (SUP.scr_exists);
242 g_free (SUP.scr_mkdir);
243 g_free (SUP.scr_unlink);
244 g_free (SUP.scr_chown);
245 g_free (SUP.scr_chmod);
246 g_free (SUP.scr_rmdir);
247 g_free (SUP.scr_ln);
248 g_free (SUP.scr_mv);
249 g_free (SUP.scr_hardlink);
250 g_free (SUP.scr_get);
251 g_free (SUP.scr_send);
252 g_free (SUP.scr_append);
253 g_free (SUP.scr_info);
254 g_free (SUP.scr_env);
257 static void
258 fish_pipeopen (struct vfs_s_super *super, const char *path, const char *argv[])
260 int fileset1[2], fileset2[2];
261 int res;
263 if ((pipe (fileset1) < 0) || (pipe (fileset2) < 0))
264 vfs_die ("Cannot pipe(): %m.");
266 res = fork ();
268 if (res != 0)
270 if (res < 0)
271 vfs_die ("Cannot fork(): %m.");
272 /* We are the parent */
273 close (fileset1[0]);
274 SUP.sockw = fileset1[1];
275 close (fileset2[1]);
276 SUP.sockr = fileset2[0];
278 else
280 res = dup2 (fileset1[0], 0);
281 close (fileset1[0]);
282 close (fileset1[1]);
283 res = dup2 (fileset2[1], 1);
284 close (2);
285 /* stderr to /dev/null */
286 res = open ("/dev/null", O_WRONLY);
287 close (fileset2[0]);
288 close (fileset2[1]);
289 execvp (path, const_cast (char **, argv));
290 _exit (3);
294 static char *
295 fish_set_env (int flags)
297 GString *tmp;
299 tmp = g_string_sized_new (150);
300 g_string_assign (tmp, "export ");
302 if ((flags & FISH_HAVE_HEAD) != 0)
303 g_string_append (tmp, "FISH_HAVE_HEAD=1 ");
305 if ((flags & FISH_HAVE_SED) != 0)
306 g_string_append (tmp, "FISH_HAVE_SED=1 ");
308 if ((flags & FISH_HAVE_AWK) != 0)
309 g_string_append (tmp, "FISH_HAVE_AWK=1 ");
311 if ((flags & FISH_HAVE_PERL) != 0)
312 g_string_append (tmp, "FISH_HAVE_PERL=1 ");
314 if ((flags & FISH_HAVE_LSQ) != 0)
315 g_string_append (tmp, "FISH_HAVE_LSQ=1 ");
317 if ((flags & FISH_HAVE_DATE_MDYT) != 0)
318 g_string_append (tmp, "FISH_HAVE_DATE_MDYT=1 ");
320 if ((flags & FISH_HAVE_TAIL) != 0)
321 g_string_append (tmp, "FISH_HAVE_TAIL=1 ");
323 return g_string_free (tmp, FALSE);
326 static gboolean
327 fish_info (struct vfs_class *me, struct vfs_s_super *super)
329 char buffer[8192];
330 if (fish_command (me, super, NONE, SUP.scr_info) == COMPLETE)
332 while (1)
334 int res;
335 res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), SUP.sockr);
336 if ((!res) || (res == EINTR))
337 ERRNOR (ECONNRESET, FALSE);
338 if (!strncmp (buffer, "### ", 4))
339 break;
340 SUP.host_flags = atol (buffer);
342 return TRUE;
344 ERRNOR (E_PROTO, FALSE);
348 /* The returned directory should always contain a trailing slash */
349 static char *
350 fish_getcwd (struct vfs_class *me, struct vfs_s_super *super)
352 if (fish_command (me, super, WANT_STRING, "#PWD\npwd; echo '### 200'\n") == COMPLETE)
353 return g_strconcat (reply_str, "/", (char *) NULL);
354 ERRNOR (EIO, NULL);
358 static void
359 fish_open_archive_pipeopen (struct vfs_s_super *super)
361 char gbuf[10];
362 const char *argv[10]; /* All of 10 is used now */
363 const char *xsh = (SUP.flags == FISH_FLAG_RSH ? "rsh" : "ssh");
364 int i = 0;
366 argv[i++] = xsh;
367 if (SUP.flags == FISH_FLAG_COMPRESSED)
368 argv[i++] = "-C";
370 if (SUP.flags > FISH_FLAG_RSH)
372 argv[i++] = "-p";
373 g_snprintf (gbuf, sizeof (gbuf), "%d", SUP.flags);
374 argv[i++] = gbuf;
378 * Add the user name to the ssh command line only if it was explicitly
379 * set in vfs URL. rsh/ssh will get current user by default
380 * plus we can set convenient overrides in ~/.ssh/config (explicit -l
381 * option breaks it for some)
384 if (SUP.user)
386 argv[i++] = "-l";
387 argv[i++] = SUP.user;
389 else
391 /* The rest of the code assumes it to be a valid username */
392 SUP.user = vfs_get_local_username ();
395 argv[i++] = SUP.host;
396 argv[i++] = "echo FISH:; /bin/sh";
397 argv[i++] = NULL;
399 fish_pipeopen (super, xsh, argv);
402 static gboolean
403 fish_open_archive_talk (struct vfs_class *me, struct vfs_s_super *super)
405 char answer[2048];
407 print_vfs_message (_("fish: Waiting for initial line..."));
409 if (!vfs_s_get_line (me, SUP.sockr, answer, sizeof (answer), ':'))
410 return FALSE;
412 print_vfs_message ("%s", answer);
413 if (strstr (answer, "assword"))
415 /* Currently, this does not work. ssh reads passwords from
416 /dev/tty, not from stdin :-(. */
418 message (D_ERROR, MSG_ERROR,
419 _("Sorry, we cannot do password authenticated connections for now."));
420 return FALSE;
421 #if 0
422 if (!SUP.password)
424 char *p, *op;
425 p = g_strdup_printf (_("fish: Password is required for %s"), SUP.user);
426 op = vfs_get_password (p);
427 g_free (p);
428 if (op == NULL)
429 return FALSE;
430 SUP.password = op;
432 print_vfs_message (_("fish: Sending password..."));
434 size_t str_len;
435 str_len = strlen (SUP.password);
436 if ((write (SUP.sockw, SUP.password, str_len) != (ssize_t) str_len)
437 || (write (SUP.sockw, "\n", 1) != 1))
439 return FALSE;
442 #endif
444 return TRUE;
447 static int
448 fish_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
450 /* hide panels */
451 pre_exec ();
453 /* open pipe */
454 fish_open_archive_pipeopen (super);
456 /* Start talk with ssh-server (password prompt, etc ) */
457 if (!fish_open_archive_talk (me, super))
459 ERRNOR (E_PROTO, -1);
462 print_vfs_message (_("fish: Sending initial line..."));
464 * Run `start_fish_server'. If it doesn't exist - no problem,
465 * we'll talk directly to the shell.
468 /* show panels */
469 post_exec ();
471 if (fish_command
472 (me, super, WAIT_REPLY,
473 "#FISH\necho; start_fish_server 2>&1; echo '### 200'\n") != COMPLETE)
474 ERRNOR (E_PROTO, -1);
476 print_vfs_message (_("fish: Handshaking version..."));
477 if (fish_command (me, super, WAIT_REPLY, "#VER 0.0.3\necho '### 000'\n") != COMPLETE)
478 ERRNOR (E_PROTO, -1);
480 /* Set up remote locale to C, otherwise dates cannot be recognized */
481 if (fish_command
482 (me, super, WAIT_REPLY,
483 "export LANG=C LC_ALL=C LC_TIME=C\n" "echo '### 200'\n") != COMPLETE)
484 ERRNOR (E_PROTO, -1);
486 print_vfs_message (_("fish: Getting host info..."));
487 if (fish_info (me, super))
488 SUP.scr_env = fish_set_env (SUP.host_flags);
490 print_vfs_message (_("fish: Setting up current directory..."));
491 SUP.cwdir = fish_getcwd (me, super);
492 print_vfs_message (_("fish: Connected, home %s."), SUP.cwdir);
493 #if 0
494 super->name = g_strconcat ("/#sh:", SUP.user, "@", SUP.host, "/", (char *) NULL);
495 #endif
496 super->name = g_strdup (PATH_SEP_STR);
498 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat (me, S_IFDIR | 0755));
499 return 0;
502 static int
503 fish_open_archive (struct vfs_class *me, struct vfs_s_super *super,
504 const char *archive_name, char *op)
506 char *host, *user, *password, *p;
507 int flags;
509 (void) archive_name;
511 p = vfs_split_url (strchr (op, ':') + 1, &host, &user, &flags,
512 &password, 0, URL_NOSLASH | URL_USE_ANONYMOUS);
514 g_free (p);
516 SUP.host = host;
517 SUP.user = user;
518 SUP.flags = flags;
519 if (!strncmp (op, "rsh:", 4))
520 SUP.flags = FISH_FLAG_RSH;
521 SUP.cwdir = NULL;
522 if (password)
523 SUP.password = password;
524 SUP.scr_ls = fish_load_script_from_file (host, FISH_LS_FILE, FISH_LS_DEF_CONTENT);
525 SUP.scr_exists = fish_load_script_from_file (host, FISH_EXISTS_FILE, FISH_EXISTS_DEF_CONTENT);
526 SUP.scr_mkdir = fish_load_script_from_file (host, FISH_MKDIR_FILE, FISH_MKDIR_DEF_CONTENT);
527 SUP.scr_unlink = fish_load_script_from_file (host, FISH_UNLINK_FILE, FISH_UNLINK_DEF_CONTENT);
528 SUP.scr_chown = fish_load_script_from_file (host, FISH_CHOWN_FILE, FISH_CHOWN_DEF_CONTENT);
529 SUP.scr_chmod = fish_load_script_from_file (host, FISH_CHMOD_FILE, FISH_CHMOD_DEF_CONTENT);
530 SUP.scr_rmdir = fish_load_script_from_file (host, FISH_RMDIR_FILE, FISH_RMDIR_DEF_CONTENT);
531 SUP.scr_ln = fish_load_script_from_file (host, FISH_LN_FILE, FISH_LN_DEF_CONTENT);
532 SUP.scr_mv = fish_load_script_from_file (host, FISH_MV_FILE, FISH_MV_DEF_CONTENT);
533 SUP.scr_hardlink = fish_load_script_from_file (host, FISH_HARDLINK_FILE, FISH_HARDLINK_DEF_CONTENT);
534 SUP.scr_get = fish_load_script_from_file (host, FISH_GET_FILE, FISH_GET_DEF_CONTENT);
535 SUP.scr_send = fish_load_script_from_file (host, FISH_SEND_FILE, FISH_SEND_DEF_CONTENT);
536 SUP.scr_append = fish_load_script_from_file (host, FISH_APPEND_FILE, FISH_APPEND_DEF_CONTENT);
537 SUP.scr_info = fish_load_script_from_file (host, FISH_INFO_FILE, FISH_INFO_DEF_CONTENT);
538 return fish_open_archive_int (me, super);
541 static int
542 fish_archive_same (struct vfs_class *me, struct vfs_s_super *super,
543 const char *archive_name, char *op, void *cookie)
545 char *host, *user;
546 int flags;
547 int result;
549 (void) me;
550 (void) archive_name;
551 (void) cookie;
553 op = vfs_split_url (strchr (op, ':') + 1, &host, &user, &flags, 0, 0,
554 URL_NOSLASH | URL_USE_ANONYMOUS);
556 g_free (op);
558 if (user == NULL)
559 user = vfs_get_local_username ();
561 result = ((strcmp (host, SUP.host) == 0)
562 && (strcmp (user, SUP.user) == 0) && (flags == SUP.flags));
564 g_free (host);
565 g_free (user);
567 return result;
570 static int
571 fish_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
573 struct vfs_s_super *super = dir->super;
574 char buffer[8192];
575 struct vfs_s_entry *ent = NULL;
576 FILE *logfile;
577 char *quoted_path;
578 int reply_code;
579 gchar *shell_commands;
582 * Simple FISH debug interface :]
584 #if 0
585 if (!(MEDATA->logfile))
587 MEDATA->logfile = fopen ("/tmp/mc-FISH.sh", "w");
589 #endif
590 logfile = MEDATA->logfile;
592 print_vfs_message (_("fish: Reading directory %s..."), remote_path);
594 gettimeofday (&dir->timestamp, NULL);
595 dir->timestamp.tv_sec += fish_directory_timeout;
596 quoted_path = strutils_shell_escape (remote_path);
597 shell_commands = g_strconcat (SUP.scr_env, "FISH_FILENAME=%s;\n", SUP.scr_ls, (char *) NULL);
598 fish_command (me, super, NONE, shell_commands, quoted_path);
599 g_free (shell_commands);
600 g_free (quoted_path);
601 ent = vfs_s_generate_entry (me, NULL, dir, 0);
602 while (1)
604 int res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), SUP.sockr);
605 if ((!res) || (res == EINTR))
607 vfs_s_free_entry (me, ent);
608 me->verrno = ECONNRESET;
609 goto error;
611 if (logfile)
613 fputs (buffer, logfile);
614 fputs ("\n", logfile);
615 fflush (logfile);
617 if (!strncmp (buffer, "### ", 4))
618 break;
619 if ((!buffer[0]))
621 if (ent->name)
623 vfs_s_insert_entry (me, dir, ent);
624 ent = vfs_s_generate_entry (me, NULL, dir, 0);
626 continue;
629 #define ST ent->ino->st
631 switch (buffer[0])
633 case ':':
635 char *temp;
636 char *data_start = buffer + 1;
637 char *filename = data_start;
638 char *linkname = data_start;
639 char *filename_bound = filename + strlen (filename);
640 char *linkname_bound = filename_bound;
641 if (!strcmp (data_start, "\".\"") || !strcmp (data_start, "\"..\""))
642 break; /* We'll do "." and ".." ourselves */
644 if (S_ISLNK (ST.st_mode))
646 /* we expect: "escaped-name" -> "escaped-name"
647 // -> cannot occur in filenames,
648 // because it will be escaped to -\> */
650 if (*filename == '"')
651 ++filename;
653 linkname = strstr (filename, "\" -> \"");
654 if (!linkname)
656 /* broken client, or smth goes wrong */
657 linkname = filename_bound;
658 if (filename_bound > filename && *(filename_bound - 1) == '"')
659 --filename_bound; /* skip trailing " */
661 else
663 filename_bound = linkname;
664 linkname += 6; /* strlen ("\" -> \"") */
665 if (*(linkname_bound - 1) == '"')
666 --linkname_bound; /* skip trailing " */
669 ent->name = str_dup_range (filename, filename_bound);
670 temp = ent->name;
671 ent->name = strutils_shell_unescape (ent->name);
672 g_free (temp);
674 ent->ino->linkname = str_dup_range (linkname, linkname_bound);
675 temp = ent->ino->linkname;
676 ent->ino->linkname = strutils_shell_unescape (ent->ino->linkname);
677 g_free (temp);
679 else
681 /* we expect: "escaped-name" */
682 if (filename_bound - filename > 2)
685 there is at least 2 "
686 and we skip them
688 if (*filename == '"')
689 ++filename;
690 if (*(filename_bound - 1) == '"')
691 --filename_bound;
693 ent->name = str_dup_range (filename, filename_bound);
694 temp = ent->name;
695 ent->name = strutils_shell_unescape (ent->name);
696 g_free (temp);
698 break;
700 case 'S':
701 #ifdef HAVE_ATOLL
702 ST.st_size = (off_t) atoll (buffer + 1);
703 #else
704 ST.st_size = (off_t) atof (buffer + 1);
705 #endif
706 break;
707 case 'P':
709 size_t skipped;
710 vfs_parse_filemode (buffer + 1, &skipped, &ST.st_mode);
711 break;
713 case 'R':
716 raw filemode:
717 we expect: Roctal-filemode octal-filetype uid.gid
719 size_t skipped;
720 vfs_parse_raw_filemode (buffer + 1, &skipped, &ST.st_mode);
721 break;
723 case 'd':
725 vfs_split_text (buffer + 1);
726 if (!vfs_parse_filedate (0, &ST.st_ctime))
727 break;
728 ST.st_atime = ST.st_mtime = ST.st_ctime;
730 break;
731 case 'D':
733 struct tm tim;
734 if (sscanf (buffer + 1, "%d %d %d %d %d %d", &tim.tm_year, &tim.tm_mon,
735 &tim.tm_mday, &tim.tm_hour, &tim.tm_min, &tim.tm_sec) != 6)
736 break;
737 ST.st_atime = ST.st_mtime = ST.st_ctime = mktime (&tim);
739 break;
740 case 'E':
742 int maj, min;
743 if (sscanf (buffer + 1, "%d,%d", &maj, &min) != 2)
744 break;
745 #ifdef HAVE_STRUCT_STAT_ST_RDEV
746 ST.st_rdev = makedev (maj, min);
747 #endif
752 vfs_s_free_entry (me, ent);
753 reply_code = fish_decode_reply (buffer + 4, 0);
754 if (reply_code == COMPLETE)
756 g_free (SUP.cwdir);
757 SUP.cwdir = g_strdup (remote_path);
758 print_vfs_message (_("%s: done."), me->name);
759 return 0;
761 else if (reply_code == ERROR)
763 me->verrno = EACCES;
765 else
767 me->verrno = E_REMOTE;
770 error:
771 print_vfs_message (_("%s: failure"), me->name);
772 return -1;
775 static int
776 fish_file_store (struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *localname)
778 gchar *shell_commands = NULL;
779 struct vfs_s_super *super = FH_SUPER;
780 int n, total;
781 char buffer[8192];
782 struct stat s;
783 int was_error = 0;
784 int h;
785 char *quoted_name;
787 h = open (localname, O_RDONLY);
789 if (h == -1)
790 ERRNOR (EIO, -1);
791 if (fstat (h, &s) < 0)
793 close (h);
794 ERRNOR (EIO, -1);
797 /* First, try this as stor:
799 * ( head -c number ) | ( cat > file; cat >/dev/null )
801 * If `head' is not present on the remote system, `dd' will be used.
802 * Unfortunately, we cannot trust most non-GNU `head' implementations
803 * even if `-c' options is supported. Therefore, we separate GNU head
804 * (and other modern heads?) using `-q' and `-' . This causes another
805 * implementations to fail (because of "incorrect options").
807 * Fallback is:
809 * rest=<number>
810 * while [ $rest -gt 0 ]
811 * do
812 * cnt=`expr \( $rest + 255 \) / 256`
813 * n=`dd bs=256 count=$cnt | tee -a <target_file> | wc -c`
814 * rest=`expr $rest - $n`
815 * done
817 * `dd' was not designed for full filling of input buffers,
818 * and does not report exact number of bytes (not blocks).
819 * Therefore a more complex shell script is needed.
821 * On some systems non-GNU head writes "Usage:" error report to stdout
822 * instead of stderr. It makes impossible the use of "head || dd"
823 * algorithm for file appending case, therefore just "dd" is used for it.
826 quoted_name = strutils_shell_escape (name);
827 print_vfs_message (_("fish: store %s: sending command..."), quoted_name);
829 /* FIXME: File size is limited to ULONG_MAX */
830 if (!fh->u.fish.append)
832 shell_commands = g_strconcat (SUP.scr_env, "FISH_FILENAME=%s FISH_FILESIZE=%ju;\n",
833 SUP.scr_append, (char *) NULL);
834 n = fish_command (me, super, WAIT_REPLY, shell_commands, quoted_name,
835 (uintmax_t) s.st_size);
836 g_free (shell_commands);
838 else
840 shell_commands = g_strconcat (SUP.scr_env, "FISH_FILENAME=%s FISH_FILESIZE=%ju;\n",
841 SUP.scr_send, (char *) NULL);
842 n = fish_command (me, super, WAIT_REPLY, shell_commands, quoted_name,
843 (uintmax_t) s.st_size);
844 g_free (shell_commands);
846 if (n != PRELIM)
848 close (h);
849 ERRNOR (E_REMOTE, -1);
852 total = 0;
854 while (1)
856 int t;
857 while ((n = read (h, buffer, sizeof (buffer))) < 0)
859 if ((errno == EINTR) && tty_got_interrupt ())
860 continue;
861 print_vfs_message (_("fish: Local read failed, sending zeros"));
862 close (h);
863 h = open ("/dev/zero", O_RDONLY);
866 if (n == 0)
867 break;
869 t = write (SUP.sockw, buffer, n);
870 if (t != n)
872 if (t == -1)
873 me->verrno = errno;
874 else
875 me->verrno = EIO;
876 goto error_return;
878 tty_disable_interrupt_key ();
879 total += n;
880 print_vfs_message (_("fish: storing %s %d (%ju)"),
881 was_error ? _("zeros") : _("file"), total, (uintmax_t) s.st_size);
883 close (h);
884 g_free (quoted_name);
885 if ((fish_get_reply (me, SUP.sockr, NULL, 0) != COMPLETE) || was_error)
886 ERRNOR (E_REMOTE, -1);
887 return 0;
888 error_return:
889 close (h);
890 fish_get_reply (me, SUP.sockr, NULL, 0);
891 g_free (quoted_name);
892 return -1;
895 static int
896 fish_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
898 gchar *shell_commands = NULL;
899 struct vfs_s_super *super = FH_SUPER;
900 char *name;
901 char *quoted_name;
903 name = vfs_s_fullpath (me, fh->ino);
904 if (name == NULL)
905 return 0;
906 quoted_name = strutils_shell_escape (name);
907 g_free (name);
908 fh->u.fish.append = 0;
911 * Check whether the remote file is readable by using `dd' to copy
912 * a single byte from the remote file to /dev/null. If `dd' completes
913 * with exit status of 0 use `cat' to send the file contents to the
914 * standard output (i.e. over the network).
917 shell_commands = g_strconcat (SUP.scr_env, "FISH_FILENAME=%s FISH_START_OFFSET=%ju;\n",
918 SUP.scr_get, (char *) NULL);
919 offset = fish_command (me, super, WANT_STRING, shell_commands, quoted_name, (uintmax_t) offset);
920 g_free (shell_commands);
921 g_free (quoted_name);
922 if (offset != PRELIM)
923 ERRNOR (E_REMOTE, 0);
924 fh->linear = LS_LINEAR_OPEN;
925 fh->u.fish.got = 0;
926 errno = 0;
927 #if SIZEOF_OFF_T == SIZEOF_LONG
928 fh->u.fish.total = (off_t) strtol (reply_str, NULL, 10);
929 #else
930 fh->u.fish.total = (off_t) strtoll (reply_str, NULL, 10);
931 #endif
932 if (errno != 0)
933 ERRNOR (E_REMOTE, 0);
934 return 1;
937 static void
938 fish_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
940 struct vfs_s_super *super = FH_SUPER;
941 char buffer[8192];
942 int n;
944 print_vfs_message (_("Aborting transfer..."));
947 n = MIN (8192, fh->u.fish.total - fh->u.fish.got);
948 if (n != 0)
950 n = read (SUP.sockr, buffer, n);
951 if (n < 0)
952 return;
953 fh->u.fish.got += n;
956 while (n != 0);
958 if (fish_get_reply (me, SUP.sockr, NULL, 0) != COMPLETE)
959 print_vfs_message (_("Error reported after abort."));
960 else
961 print_vfs_message (_("Aborted transfer would be successful."));
964 static int
965 fish_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, size_t len)
967 struct vfs_s_super *super = FH_SUPER;
968 ssize_t n = 0;
969 len = MIN ((size_t) (fh->u.fish.total - fh->u.fish.got), len);
970 tty_disable_interrupt_key ();
971 while (len != 0 && ((n = read (SUP.sockr, buf, len)) < 0))
973 if ((errno == EINTR) && !tty_got_interrupt ())
974 continue;
975 break;
977 tty_enable_interrupt_key ();
979 if (n > 0)
980 fh->u.fish.got += n;
981 else if (n < 0)
982 fish_linear_abort (me, fh);
983 else if (fish_get_reply (me, SUP.sockr, NULL, 0) != COMPLETE)
984 ERRNOR (E_REMOTE, -1);
985 ERRNOR (errno, n);
988 static void
989 fish_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
991 if (fh->u.fish.total != fh->u.fish.got)
992 fish_linear_abort (me, fh);
995 static int
996 fish_ctl (void *fh, int ctlop, void *arg)
998 (void) arg;
999 (void) fh;
1000 (void) ctlop;
1001 return 0;
1002 #if 0
1003 switch (ctlop)
1005 case VFS_CTL_IS_NOTREADY:
1007 int v;
1009 if (!FH->linear)
1010 vfs_die ("You may not do this");
1011 if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
1012 return 0;
1014 v = vfs_s_select_on_two (FH_SUPER->u.fish.sockr, 0);
1015 if (((v < 0) && (errno == EINTR)) || v == 0)
1016 return 1;
1017 return 0;
1019 default:
1020 return 0;
1022 #endif
1025 static int
1026 fish_send_command (struct vfs_class *me, struct vfs_s_super *super, const char *cmd, int flags)
1028 int r;
1030 r = fish_command (me, super, WAIT_REPLY, "%s", cmd);
1031 vfs_stamp_create (&vfs_fish_ops, super);
1032 if (r != COMPLETE)
1033 ERRNOR (E_REMOTE, -1);
1034 if (flags & OPT_FLUSH)
1035 vfs_s_invalidate (me, super);
1036 return 0;
1039 #define PREFIX \
1040 char buf[BUF_LARGE]; \
1041 const char *crpath; \
1042 char *rpath, *mpath; \
1043 struct vfs_s_super *super; \
1044 mpath = g_strdup (path); \
1045 crpath = vfs_s_get_path_mangle (me, mpath, &super, 0); \
1046 if (crpath == NULL) \
1048 g_free (mpath); \
1049 return -1; \
1051 rpath = strutils_shell_escape (crpath); \
1052 g_free (mpath);
1054 static int
1055 fish_rename (struct vfs_class *me, const char *path1, const char *path2)
1057 gchar *shell_commands = NULL;
1058 char buf[BUF_LARGE];
1059 const char *crpath1, *crpath2;
1060 char *rpath1, *rpath2, *mpath1, *mpath2;
1061 struct vfs_s_super *super, *super2;
1063 mpath1 = g_strdup (path1);
1064 crpath1 = vfs_s_get_path_mangle (me, mpath1, &super, 0);
1065 if (crpath1 == NULL)
1067 g_free (mpath1);
1068 return -1;
1070 mpath2 = g_strdup (path2);
1071 crpath2 = vfs_s_get_path_mangle (me, mpath2, &super2, 0);
1072 if (crpath2 == NULL)
1074 g_free (mpath1);
1075 g_free (mpath2);
1076 return -1;
1078 rpath1 = strutils_shell_escape (crpath1);
1079 g_free (mpath1);
1080 rpath2 = strutils_shell_escape (crpath2);
1081 g_free (mpath2);
1082 shell_commands = g_strconcat (SUP.scr_env, "FISH_FILEFROM=%s FISH_FILETO=%s;\n",
1083 SUP.scr_mv, (char *) NULL);
1084 g_snprintf (buf, sizeof (buf), shell_commands, rpath1, rpath2);
1085 g_free (shell_commands);
1086 g_free (rpath1);
1087 g_free (rpath2);
1088 return fish_send_command(me, super2, buf, OPT_FLUSH);
1091 static int
1092 fish_link (struct vfs_class *me, const char *path1, const char *path2)
1094 gchar *shell_commands = NULL;
1095 char buf[BUF_LARGE];
1096 const char *crpath1, *crpath2;
1097 char *rpath1, *rpath2, *mpath1, *mpath2;
1098 struct vfs_s_super *super, *super2;
1100 mpath1 = g_strdup (path1);
1101 crpath1 = vfs_s_get_path_mangle (me, mpath1, &super, 0);
1102 if (crpath1 == NULL)
1104 g_free (mpath1);
1105 return -1;
1107 mpath2 = g_strdup (path2);
1108 crpath2 = vfs_s_get_path_mangle (me, mpath2, &super2, 0);
1109 if (crpath2 == NULL)
1111 g_free (mpath1);
1112 g_free (mpath2);
1113 return -1;
1115 rpath1 = strutils_shell_escape (crpath1);
1116 g_free (mpath1);
1117 rpath2 = strutils_shell_escape (crpath2);
1118 g_free (mpath2);
1119 shell_commands = g_strconcat (SUP.scr_env, "FISH_FILEFROM=%s FISH_FILETO=%s;\n",
1120 SUP.scr_hardlink, (char *) NULL);
1121 g_snprintf (buf, sizeof (buf), shell_commands, rpath1, rpath2);
1122 g_free (shell_commands);
1123 g_free (rpath1);
1124 g_free (rpath2);
1125 return fish_send_command (me, super2, buf, OPT_FLUSH);
1129 static int
1130 fish_symlink (struct vfs_class *me, const char *setto, const char *path)
1132 char *qsetto;
1133 gchar *shell_commands = NULL;
1134 char buf[BUF_LARGE];
1135 const char *crpath;
1136 char *rpath, *mpath;
1137 struct vfs_s_super *super;
1139 mpath = g_strdup (path);
1140 crpath = vfs_s_get_path_mangle (me, mpath, &super, 0);
1141 if (crpath == NULL)
1143 g_free (mpath);
1144 return -1;
1146 rpath = strutils_shell_escape (crpath);
1147 g_free (mpath);
1149 qsetto = strutils_shell_escape (setto);
1150 shell_commands = g_strconcat (SUP.scr_env, "FISH_FILEFROM=%s FISH_FILETO=%s;\n",
1151 SUP.scr_ln, (char *) NULL);
1152 g_snprintf (buf, sizeof (buf), shell_commands, qsetto, rpath);
1153 g_free (shell_commands);
1154 g_free (qsetto);
1155 g_free (rpath);
1156 return fish_send_command (me, super, buf, OPT_FLUSH);
1159 static int
1160 fish_chmod (struct vfs_class *me, const char *path, int mode)
1162 gchar *shell_commands = NULL;
1163 PREFIX
1164 shell_commands = g_strconcat (SUP.scr_env, "FISH_FILENAME=%s FISH_FILEMODE=%4.4o;\n",
1165 SUP.scr_chmod, (char *) NULL);
1166 g_snprintf (buf, sizeof (buf), shell_commands, rpath, mode & 07777);
1167 g_free (shell_commands);
1168 g_free (rpath);
1169 return fish_send_command (me, super, buf, OPT_FLUSH);
1172 static int
1173 fish_chown (struct vfs_class *me, const char *path, uid_t owner, gid_t group)
1175 char *sowner, *sgroup;
1176 struct passwd *pw;
1177 struct group *gr;
1179 pw = getpwuid (owner);
1180 if (pw == NULL)
1181 return 0;
1183 gr = getgrgid (group);
1184 if (gr == NULL)
1185 return 0;
1187 sowner = pw->pw_name;
1188 sgroup = gr->gr_name;
1190 gchar *shell_commands = NULL;
1192 PREFIX
1193 shell_commands = g_strconcat (SUP.scr_env,
1194 "FISH_FILENAME=%s FISH_FILEOWNER=%s FISH_FILEGROUP=%s;\n",
1195 SUP.scr_chown, (char *) NULL);
1196 g_snprintf (buf, sizeof (buf), shell_commands, rpath, sowner, sgroup);
1197 g_free (shell_commands);
1198 fish_send_command (me, super, buf, OPT_FLUSH);
1199 /* FIXME: what should we report if chgrp succeeds but chown fails? */
1200 /* fish_send_command(me, super, buf, OPT_FLUSH); */
1201 g_free (rpath);
1202 return fish_send_command (me, super, buf, OPT_FLUSH);
1206 static int
1207 fish_unlink (struct vfs_class *me, const char *path)
1209 gchar *shell_commands = NULL;
1210 PREFIX
1211 shell_commands =
1212 g_strconcat (SUP.scr_env, "FISH_FILENAME=%s;\n", SUP.scr_unlink, (char *) NULL);
1213 g_snprintf (buf, sizeof (buf), shell_commands, rpath);
1214 g_free (shell_commands);
1215 g_free (rpath);
1216 return fish_send_command (me, super, buf, OPT_FLUSH);
1219 static int
1220 fish_exists (struct vfs_class *me, const char *path)
1222 gchar *shell_commands = NULL;
1223 PREFIX
1224 shell_commands = g_strconcat (SUP.scr_env, "FISH_FILENAME=%s;\n", SUP.scr_exists, (char *) NULL);
1225 g_snprintf (buf, sizeof (buf), shell_commands, rpath);
1226 g_free (shell_commands);
1227 g_free (rpath);
1229 return (fish_send_command (me, super, buf, OPT_FLUSH) == 0) ? 1 : 0;
1233 static int
1234 fish_mkdir (struct vfs_class *me, const char *path, mode_t mode)
1236 gchar *shell_commands = NULL;
1237 int ret_code;
1239 PREFIX
1240 (void) mode;
1242 shell_commands = g_strconcat (SUP.scr_env, "FISH_FILENAME=%s;\n", SUP.scr_mkdir, (char *) NULL);
1243 g_snprintf (buf, sizeof (buf), shell_commands, rpath);
1244 g_free (shell_commands);
1246 g_free (rpath);
1247 ret_code = fish_send_command (me, super, buf, OPT_FLUSH);
1249 if (ret_code != 0)
1250 return ret_code;
1252 if (!fish_exists (me, path))
1254 ERRNOR (EACCES, -1);
1256 return 0;
1259 static int
1260 fish_rmdir (struct vfs_class *me, const char *path)
1262 gchar *shell_commands = NULL;
1263 PREFIX
1264 shell_commands = g_strconcat (SUP.scr_env, "FISH_FILENAME=%s;\n", SUP.scr_rmdir, (char *) NULL);
1265 g_snprintf (buf, sizeof (buf), shell_commands, rpath);
1266 g_free (shell_commands);
1267 g_free (rpath);
1268 return fish_send_command (me, super, buf, OPT_FLUSH);
1271 static int
1272 fish_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags, mode_t mode)
1274 (void) mode;
1276 fh->u.fish.append = 0;
1277 /* File will be written only, so no need to retrieve it */
1278 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR)))
1280 fh->u.fish.append = flags & O_APPEND;
1281 if (!fh->ino->localname)
1283 int tmp_handle = vfs_mkstemps (&fh->ino->localname, me->name,
1284 fh->ino->ent->name);
1285 if (tmp_handle == -1)
1286 return -1;
1287 close (tmp_handle);
1289 return 0;
1291 if (!fh->ino->localname)
1292 if (vfs_s_retrieve_file (me, fh->ino) == -1)
1293 return -1;
1294 if (!fh->ino->localname)
1295 vfs_die ("retrieve_file failed to fill in localname");
1296 return 0;
1299 static void
1300 fish_fill_names (struct vfs_class *me, fill_names_f func)
1302 struct vfs_s_super *super = MEDATA->supers;
1303 char *name;
1305 char gbuf[10];
1307 while (super)
1309 const char *flags = "";
1310 switch (SUP.flags)
1312 case FISH_FLAG_RSH:
1313 flags = ":r";
1314 break;
1315 case FISH_FLAG_COMPRESSED:
1316 flags = ":C";
1317 break;
1318 default:
1319 if (SUP.flags > FISH_FLAG_RSH)
1321 break;
1322 g_snprintf (gbuf, sizeof (gbuf), ":%d", SUP.flags);
1323 flags = gbuf;
1325 break;
1328 name = g_strconcat ("/#sh:", SUP.user, "@", SUP.host, flags, "/", SUP.cwdir, (char *) NULL);
1329 (*func) (name);
1330 g_free (name);
1331 super = super->next;
1335 static void *
1336 fish_open (struct vfs_class *me, const char *file, int flags, mode_t mode)
1339 sorry, i've places hack here
1340 cause fish don't able to open files with O_EXCL flag
1342 flags &= ~O_EXCL;
1343 return vfs_s_open (me, file, flags, mode);
1346 void
1347 init_fish (void)
1349 static struct vfs_s_subclass fish_subclass;
1351 tcp_init ();
1353 fish_subclass.flags = VFS_S_REMOTE;
1354 fish_subclass.archive_same = fish_archive_same;
1355 fish_subclass.open_archive = fish_open_archive;
1356 fish_subclass.free_archive = fish_free_archive;
1357 fish_subclass.fh_open = fish_fh_open;
1358 fish_subclass.dir_load = fish_dir_load;
1359 fish_subclass.file_store = fish_file_store;
1360 fish_subclass.linear_start = fish_linear_start;
1361 fish_subclass.linear_read = fish_linear_read;
1362 fish_subclass.linear_close = fish_linear_close;
1364 vfs_s_init_class (&vfs_fish_ops, &fish_subclass);
1365 vfs_fish_ops.name = "fish";
1366 vfs_fish_ops.prefix = "sh:";
1367 vfs_fish_ops.fill_names = fish_fill_names;
1368 vfs_fish_ops.chmod = fish_chmod;
1369 vfs_fish_ops.chown = fish_chown;
1370 vfs_fish_ops.open = fish_open;
1371 vfs_fish_ops.symlink = fish_symlink;
1372 vfs_fish_ops.link = fish_link;
1373 vfs_fish_ops.unlink = fish_unlink;
1374 vfs_fish_ops.rename = fish_rename;
1375 vfs_fish_ops.mkdir = fish_mkdir;
1376 vfs_fish_ops.rmdir = fish_rmdir;
1377 vfs_fish_ops.ctl = fish_ctl;
1378 vfs_register_class (&vfs_fish_ops);