Manual pages: mcmcdiffmceditmcview

root/src/vfs/shell/shell.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. shell_set_blksize
  2. shell_default_stat
  3. shell_load_script_from_file
  4. shell_decode_reply
  5. shell_get_reply
  6. shell_command
  7. G_GNUC_PRINTF
  8. G_GNUC_PRINTF
  9. G_GNUC_PRINTF
  10. shell_new_archive
  11. shell_free_archive
  12. shell_pipeopen
  13. shell_set_env
  14. shell_info
  15. shell_open_archive_pipeopen
  16. shell_open_archive_talk
  17. shell_open_archive_int
  18. shell_open_archive
  19. shell_archive_same
  20. shell_parse_ls
  21. shell_dir_load
  22. shell_file_store
  23. shell_linear_start
  24. shell_linear_abort
  25. shell_linear_read
  26. shell_linear_close
  27. shell_ctl
  28. shell_rename
  29. shell_link
  30. shell_symlink
  31. shell_stat
  32. shell_lstat
  33. shell_fstat
  34. shell_chmod
  35. shell_chown
  36. shell_utime
  37. shell_unlink
  38. shell_exists
  39. shell_mkdir
  40. shell_rmdir
  41. shell_fh_new
  42. shell_fh_open
  43. shell_fill_names
  44. shell_open
  45. vfs_init_shell

   1 /*
   2    Virtual File System: SHELL implementation for transferring files over
   3    shell connections.
   4 
   5    Copyright (C) 1998-2025
   6    Free Software Foundation, Inc.
   7 
   8    Written by:
   9    Pavel Machek, 1998
  10    Michal Svec, 2000
  11    Andrew Borodin <aborodin@vmail.ru>, 2010-2022
  12    Slava Zanko <slavazanko@gmail.com>, 2010, 2013
  13    Ilia Maslakov <il.smind@gmail.com>, 2010
  14 
  15    Derived from ftpfs.c.
  16 
  17    This file is part of the Midnight Commander.
  18 
  19    The Midnight Commander is free software: you can redistribute it
  20    and/or modify it under the terms of the GNU General Public License as
  21    published by the Free Software Foundation, either version 3 of the License,
  22    or (at your option) any later version.
  23 
  24    The Midnight Commander is distributed in the hope that it will be useful,
  25    but WITHOUT ANY WARRANTY; without even the implied warranty of
  26    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27    GNU General Public License for more details.
  28 
  29    You should have received a copy of the GNU General Public License
  30    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  31  */
  32 
  33 /**
  34  * \file
  35  * \brief Source: Virtual File System: SHELL implementation for transferring files over
  36  * shell connections
  37  * \author Pavel Machek
  38  * \author Michal Svec
  39  * \date 1998, 2000
  40  *
  41  * Derived from ftpfs.c
  42  * Read README.shell for protocol specification.
  43  *
  44  * Syntax of path is: \verbatim sh://user@host[:Cr]/path \endverbatim
  45  *      where C means you want compressed connection,
  46  *      and r means you want to use rsh
  47  *
  48  * Namespace: shell_vfs_ops exported.
  49  */
  50 
  51 /* Define this if your ssh can take -I option */
  52 
  53 #include <config.h>
  54 #include <errno.h>
  55 #include <pwd.h>
  56 #include <grp.h>
  57 #include <stdlib.h>
  58 #include <string.h>
  59 #include <inttypes.h>  // uintmax_t
  60 
  61 #include "lib/global.h"
  62 #include "lib/tty/tty.h"  // enable/disable interrupt key
  63 #include "lib/strutil.h"
  64 #include "lib/unixcompat.h"
  65 #include "lib/fileloc.h"
  66 #include "lib/util.h"  // my_exit()
  67 #include "lib/mcconfig.h"
  68 
  69 #include "src/execute.h"  // pre_exec, post_exec
  70 
  71 #include "lib/vfs/vfs.h"
  72 #include "lib/vfs/utilvfs.h"
  73 #include "lib/vfs/netutil.h"
  74 #include "lib/vfs/xdirentry.h"
  75 #include "lib/vfs/gc.h"  // vfs_stamp_create
  76 
  77 #include "shell.h"
  78 #include "shelldef.h"
  79 
  80 /*** global variables ****************************************************************************/
  81 
  82 int shell_directory_timeout = 900;
  83 
  84 /*** file scope macro definitions ****************************************************************/
  85 
  86 #define DO_RESOLVE_SYMLINK    1
  87 #define DO_OPEN               2
  88 #define DO_FREE_RESOURCE      4
  89 
  90 #define SHELL_FLAG_COMPRESSED 1
  91 #define SHELL_FLAG_RSH        2
  92 
  93 #define OPT_FLUSH             1
  94 #define OPT_IGNORE_ERROR      2
  95 
  96 /*
  97  * Reply codes.
  98  */
  99 #define PRELIM    1  // positive preliminary
 100 #define COMPLETE  2  // positive completion
 101 #define CONTINUE  3  // positive intermediate
 102 #define TRANSIENT 4  // transient negative completion
 103 #define ERROR     5  // permanent negative completion
 104 
 105 /* command wait_flag: */
 106 #define NONE        0x00
 107 #define WAIT_REPLY  0x01
 108 #define WANT_STRING 0x02
 109 
 110 /* environment flags */
 111 #define SHELL_HAVE_HEAD        1
 112 #define SHELL_HAVE_SED         2
 113 #define SHELL_HAVE_AWK         4
 114 #define SHELL_HAVE_PERL        8
 115 #define SHELL_HAVE_LSQ         16
 116 #define SHELL_HAVE_DATE_MDYT   32
 117 #define SHELL_HAVE_TAIL        64
 118 
 119 #define SHELL_SUPER(super)     ((shell_super_t *) (super))
 120 #define SHELL_FILE_HANDLER(fh) ((shell_file_handler_t *) fh)
 121 
 122 /*** file scope type declarations ****************************************************************/
 123 
 124 typedef struct
 125 {
 126     struct vfs_s_super base;  // base class
 127 
 128     int sockr;
 129     int sockw;
 130     char *scr_ls;
 131     char *scr_chmod;
 132     char *scr_utime;
 133     char *scr_exists;
 134     char *scr_mkdir;
 135     char *scr_unlink;
 136     char *scr_chown;
 137     char *scr_rmdir;
 138     char *scr_ln;
 139     char *scr_mv;
 140     char *scr_hardlink;
 141     char *scr_get;
 142     char *scr_send;
 143     char *scr_append;
 144     char *scr_info;
 145     int host_flags;
 146     GString *scr_env;
 147 } shell_super_t;
 148 
 149 typedef struct
 150 {
 151     vfs_file_handler_t base;  // base class
 152 
 153     off_t got;
 154     off_t total;
 155     gboolean append;
 156 } shell_file_handler_t;
 157 
 158 /*** forward declarations (file scope functions) *************************************************/
 159 
 160 /*** file scope variables ************************************************************************/
 161 
 162 static char reply_str[80];
 163 
 164 static struct vfs_s_subclass shell_subclass;
 165 static struct vfs_class *vfs_shell_ops = VFS_CLASS (&shell_subclass);
 166 
 167 /* --------------------------------------------------------------------------------------------- */
 168 /*** file scope functions ************************************************************************/
 169 /* --------------------------------------------------------------------------------------------- */
 170 
 171 static void
 172 shell_set_blksize (struct stat *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 173 {
 174 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
 175     // redefine block size
 176     s->st_blksize = 64 * 1024;  // FIXME
 177 #endif
 178 }
 179 
 180 /* --------------------------------------------------------------------------------------------- */
 181 
 182 static struct stat *
 183 shell_default_stat (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 184 {
 185     struct stat *s;
 186 
 187     s = vfs_s_default_stat (me, S_IFDIR | 0755);
 188     shell_set_blksize (s);
 189     vfs_adjust_stat (s);
 190 
 191     return s;
 192 }
 193 
 194 /* --------------------------------------------------------------------------------------------- */
 195 
 196 static char *
 197 shell_load_script_from_file (const char *hostname, const char *script_name, const char *def_content)
     /* [previous][next][first][last][top][bottom][index][help]  */
 198 {
 199     char *scr_filename = NULL;
 200     char *scr_content;
 201     gsize scr_len = 0;
 202 
 203     // 1st: scan user directory
 204     scr_filename = g_build_path (PATH_SEP_STR, mc_config_get_data_path (), VFS_SHELL_PREFIX,
 205                                  hostname, script_name, (char *) NULL);
 206     // silent about user dir
 207     g_file_get_contents (scr_filename, &scr_content, &scr_len, NULL);
 208     g_free (scr_filename);
 209     // 2nd: scan system dir
 210     if (scr_content == NULL)
 211     {
 212         scr_filename =
 213             g_build_path (PATH_SEP_STR, LIBEXECDIR, VFS_SHELL_PREFIX, script_name, (char *) NULL);
 214         g_file_get_contents (scr_filename, &scr_content, &scr_len, NULL);
 215         g_free (scr_filename);
 216     }
 217 
 218     if (scr_content != NULL)
 219         return scr_content;
 220 
 221     return g_strdup (def_content);
 222 }
 223 
 224 /* --------------------------------------------------------------------------------------------- */
 225 
 226 static int
 227 shell_decode_reply (char *s, gboolean was_garbage)
     /* [previous][next][first][last][top][bottom][index][help]  */
 228 {
 229     int code;
 230 
 231     if (sscanf (s, "%d", &code) == 0)
 232     {
 233         code = 500;
 234         return 5;
 235     }
 236     if (code < 100)
 237         return was_garbage ? ERROR : (code == 0 ? COMPLETE : PRELIM);
 238     return code / 100;
 239 }
 240 
 241 /* --------------------------------------------------------------------------------------------- */
 242 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
 243 
 244 static int
 245 shell_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 246 {
 247     char answer[BUF_1K];
 248     gboolean was_garbage = FALSE;
 249 
 250     while (TRUE)
 251     {
 252         if (!vfs_s_get_line (me, sock, answer, sizeof (answer), '\n'))
 253         {
 254             if (string_buf != NULL)
 255                 *string_buf = '\0';
 256             return 4;
 257         }
 258 
 259         if (strncmp (answer, "### ", 4) == 0)
 260             return shell_decode_reply (answer + 4, was_garbage ? 1 : 0);
 261 
 262         was_garbage = TRUE;
 263         if (string_buf != NULL)
 264             g_strlcpy (string_buf, answer, string_len);
 265     }
 266 }
 267 
 268 /* --------------------------------------------------------------------------------------------- */
 269 
 270 static int
 271 shell_command (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *cmd,
     /* [previous][next][first][last][top][bottom][index][help]  */
 272                size_t cmd_len)
 273 {
 274     ssize_t status;
 275     FILE *logfile = me->logfile;
 276 
 277     if (cmd_len == (size_t) (-1))
 278         cmd_len = strlen (cmd);
 279 
 280     if (logfile != NULL)
 281     {
 282         size_t ret;
 283 
 284         ret = fwrite (cmd, cmd_len, 1, logfile);
 285         ret = fflush (logfile);
 286         (void) ret;
 287     }
 288 
 289     tty_enable_interrupt_key ();
 290     status = write (SHELL_SUPER (super)->sockw, cmd, cmd_len);
 291     tty_disable_interrupt_key ();
 292 
 293     if (status < 0)
 294         return TRANSIENT;
 295 
 296     if (wait_reply)
 297         return shell_get_reply (me, SHELL_SUPER (super)->sockr,
 298                                 (wait_reply & WANT_STRING) != 0 ? reply_str : NULL,
 299                                 sizeof (reply_str) - 1);
 300     return COMPLETE;
 301 }
 302 
 303 /* --------------------------------------------------------------------------------------------- */
 304 
 305 static int G_GNUC_PRINTF (5, 0)
     /* [previous][next][first][last][top][bottom][index][help]  */
 306 shell_command_va (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *scr,
 307                   const char *vars, va_list ap)
 308 {
 309     int r;
 310     GString *command;
 311 
 312     command = mc_g_string_dup (SHELL_SUPER (super)->scr_env);
 313     g_string_append_vprintf (command, vars, ap);
 314     g_string_append (command, scr);
 315     r = shell_command (me, super, wait_reply, command->str, command->len);
 316     g_string_free (command, TRUE);
 317 
 318     return r;
 319 }
 320 
 321 /* --------------------------------------------------------------------------------------------- */
 322 
 323 static int G_GNUC_PRINTF (5, 6)
     /* [previous][next][first][last][top][bottom][index][help]  */
 324 shell_command_v (struct vfs_class *me, struct vfs_s_super *super, int wait_reply, const char *scr,
 325                  const char *vars, ...)
 326 {
 327     int r;
 328     va_list ap;
 329 
 330     va_start (ap, vars);
 331     r = shell_command_va (me, super, wait_reply, scr, vars, ap);
 332     va_end (ap);
 333 
 334     return r;
 335 }
 336 
 337 /* --------------------------------------------------------------------------------------------- */
 338 
 339 static int G_GNUC_PRINTF (5, 6)
     /* [previous][next][first][last][top][bottom][index][help]  */
 340 shell_send_command (struct vfs_class *me, struct vfs_s_super *super, int flags, const char *scr,
 341                     const char *vars, ...)
 342 {
 343     int r;
 344     va_list ap;
 345 
 346     va_start (ap, vars);
 347     r = shell_command_va (me, super, WAIT_REPLY, scr, vars, ap);
 348     va_end (ap);
 349     vfs_stamp_create (vfs_shell_ops, super);
 350 
 351     if (r != COMPLETE)
 352         ERRNOR (E_REMOTE, -1);
 353     if ((flags & OPT_FLUSH) != 0)
 354         vfs_s_invalidate (me, super);
 355 
 356     return 0;
 357 }
 358 
 359 /* --------------------------------------------------------------------------------------------- */
 360 
 361 static struct vfs_s_super *
 362 shell_new_archive (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 363 {
 364     shell_super_t *arch;
 365 
 366     arch = g_new0 (shell_super_t, 1);
 367     arch->base.me = me;
 368 
 369     return VFS_SUPER (arch);
 370 }
 371 
 372 /* --------------------------------------------------------------------------------------------- */
 373 
 374 static void
 375 shell_free_archive (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 376 {
 377     shell_super_t *shell_super = SHELL_SUPER (super);
 378 
 379     if ((shell_super->sockw != -1) || (shell_super->sockr != -1))
 380         vfs_print_message (_ ("shell: Disconnecting from %s"), super->name ? super->name : "???");
 381 
 382     if (shell_super->sockw != -1)
 383     {
 384         shell_command (me, super, NONE, "exit\n", -1);
 385         close (shell_super->sockw);
 386         shell_super->sockw = -1;
 387     }
 388 
 389     if (shell_super->sockr != -1)
 390     {
 391         close (shell_super->sockr);
 392         shell_super->sockr = -1;
 393     }
 394 
 395     g_free (shell_super->scr_ls);
 396     g_free (shell_super->scr_exists);
 397     g_free (shell_super->scr_mkdir);
 398     g_free (shell_super->scr_unlink);
 399     g_free (shell_super->scr_chown);
 400     g_free (shell_super->scr_chmod);
 401     g_free (shell_super->scr_utime);
 402     g_free (shell_super->scr_rmdir);
 403     g_free (shell_super->scr_ln);
 404     g_free (shell_super->scr_mv);
 405     g_free (shell_super->scr_hardlink);
 406     g_free (shell_super->scr_get);
 407     g_free (shell_super->scr_send);
 408     g_free (shell_super->scr_append);
 409     g_free (shell_super->scr_info);
 410     if (shell_super->scr_env != NULL)
 411         g_string_free (shell_super->scr_env, TRUE);
 412 }
 413 
 414 /* --------------------------------------------------------------------------------------------- */
 415 
 416 static void
 417 shell_pipeopen (struct vfs_s_super *super, const char *path, const char *argv[])
     /* [previous][next][first][last][top][bottom][index][help]  */
 418 {
 419     int fileset1[2], fileset2[2];
 420     int res;
 421 
 422     if ((pipe (fileset1) < 0) || (pipe (fileset2) < 0))
 423         vfs_die ("Cannot pipe(): %m.");
 424 
 425     res = my_fork ();
 426 
 427     if (res != 0)
 428     {
 429         if (res < 0)
 430             vfs_die ("Cannot fork(): %m.");
 431         // We are the parent
 432         close (fileset1[0]);
 433         SHELL_SUPER (super)->sockw = fileset1[1];
 434         close (fileset2[1]);
 435         SHELL_SUPER (super)->sockr = fileset2[0];
 436     }
 437     else
 438     {
 439         res = dup2 (fileset1[0], STDIN_FILENO);
 440         close (fileset1[0]);
 441         close (fileset1[1]);
 442         res = dup2 (fileset2[1], STDOUT_FILENO);
 443         close (STDERR_FILENO);
 444         // stderr to /dev/null
 445         res = open ("/dev/null", O_WRONLY);
 446         close (fileset2[0]);
 447         close (fileset2[1]);
 448         my_execvp (path, (char **) argv);
 449         my_exit (3);
 450     }
 451 }
 452 
 453 /* --------------------------------------------------------------------------------------------- */
 454 
 455 static GString *
 456 shell_set_env (int flags)
     /* [previous][next][first][last][top][bottom][index][help]  */
 457 {
 458     GString *ret;
 459 
 460     ret = g_string_sized_new (256);
 461 
 462     if ((flags & SHELL_HAVE_HEAD) != 0)
 463         g_string_append (ret, "SHELL_HAVE_HEAD=1 export SHELL_HAVE_HEAD; ");
 464 
 465     if ((flags & SHELL_HAVE_SED) != 0)
 466         g_string_append (ret, "SHELL_HAVE_SED=1 export SHELL_HAVE_SED; ");
 467 
 468     if ((flags & SHELL_HAVE_AWK) != 0)
 469         g_string_append (ret, "SHELL_HAVE_AWK=1 export SHELL_HAVE_AWK; ");
 470 
 471     if ((flags & SHELL_HAVE_PERL) != 0)
 472         g_string_append (ret, "SHELL_HAVE_PERL=1 export SHELL_HAVE_PERL; ");
 473 
 474     if ((flags & SHELL_HAVE_LSQ) != 0)
 475         g_string_append (ret, "SHELL_HAVE_LSQ=1 export SHELL_HAVE_LSQ; ");
 476 
 477     if ((flags & SHELL_HAVE_DATE_MDYT) != 0)
 478         g_string_append (ret, "SHELL_HAVE_DATE_MDYT=1 export SHELL_HAVE_DATE_MDYT; ");
 479 
 480     if ((flags & SHELL_HAVE_TAIL) != 0)
 481         g_string_append (ret, "SHELL_HAVE_TAIL=1 export SHELL_HAVE_TAIL; ");
 482 
 483     return ret;
 484 }
 485 
 486 /* --------------------------------------------------------------------------------------------- */
 487 
 488 static gboolean
 489 shell_info (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 490 {
 491     shell_super_t *shell_super = SHELL_SUPER (super);
 492 
 493     if (shell_command (me, super, NONE, shell_super->scr_info, -1) == COMPLETE)
 494     {
 495         while (TRUE)
 496         {
 497             int res;
 498             char buffer[BUF_8K] = "";
 499 
 500             res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), shell_super->sockr);
 501             if ((res == 0) || (res == EINTR))
 502                 ERRNOR (ECONNRESET, FALSE);
 503             if (strncmp (buffer, "### ", 4) == 0)
 504                 break;
 505             shell_super->host_flags = atol (buffer);
 506         }
 507         return TRUE;
 508     }
 509     ERRNOR (E_PROTO, FALSE);
 510 }
 511 
 512 /* --------------------------------------------------------------------------------------------- */
 513 
 514 static void
 515 shell_open_archive_pipeopen (struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 516 {
 517     char gbuf[10];
 518     const char *argv[10];  // All of 10 is used now
 519     const char *xsh = (super->path_element->port == SHELL_FLAG_RSH ? "rsh" : "ssh");
 520     int i = 0;
 521 
 522     argv[i++] = xsh;
 523     if (super->path_element->port == SHELL_FLAG_COMPRESSED)
 524         argv[i++] = "-C";
 525 
 526     if (super->path_element->port > SHELL_FLAG_RSH)
 527     {
 528         argv[i++] = "-p";
 529         g_snprintf (gbuf, sizeof (gbuf), "%d", super->path_element->port);
 530         argv[i++] = gbuf;
 531     }
 532 
 533     /*
 534      * Add the user name to the ssh command line only if it was explicitly
 535      * set in vfs URL. rsh/ssh will get current user by default
 536      * plus we can set convenient overrides in  ~/.ssh/config (explicit -l
 537      * option breaks it for some)
 538      */
 539 
 540     if (super->path_element->user != NULL)
 541     {
 542         argv[i++] = "-l";
 543         argv[i++] = super->path_element->user;
 544     }
 545     else
 546     {
 547         // The rest of the code assumes it to be a valid username
 548         super->path_element->user = vfs_get_local_username ();
 549     }
 550 
 551     argv[i++] = super->path_element->host;
 552     argv[i++] = "echo SHELL:; /bin/sh";
 553     argv[i++] = NULL;
 554 
 555     shell_pipeopen (super, xsh, argv);
 556 }
 557 
 558 /* --------------------------------------------------------------------------------------------- */
 559 
 560 static gboolean
 561 shell_open_archive_talk (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 562 {
 563     shell_super_t *shell_super = SHELL_SUPER (super);
 564     char answer[BUF_2K];
 565 
 566     printf ("\n%s\n", _ ("shell: Waiting for initial line..."));
 567 
 568     if (vfs_s_get_line (me, shell_super->sockr, answer, sizeof (answer), ':') == 0)
 569         return FALSE;
 570 
 571     if (strstr (answer, "assword") != NULL)
 572     {
 573         /* Currently, this does not work. ssh reads passwords from
 574            /dev/tty, not from stdin :-(. */
 575 
 576         printf ("\n%s\n", _ ("Sorry, we cannot do password authenticated connections for now."));
 577 
 578         return FALSE;
 579 #if 0
 580         if (super->path_element->password == NULL)
 581         {
 582             char *p, *op;
 583 
 584             p = g_strdup_printf (_("shell: Password is required for %s"),
 585                                  super->path_element->user);
 586             op = vfs_get_password (p);
 587             g_free (p);
 588             if (op == NULL)
 589                 return FALSE;
 590             super->path_element->password = op;
 591         }
 592 
 593         printf ("\n%s\n", _("shell: Sending password..."));
 594 
 595         {
 596             size_t str_len;
 597 
 598             str_len = strlen (super->path_element->password);
 599             if ((write (shell_super.sockw, super->path_element->password, str_len) !=
 600                  (ssize_t) str_len) || (write (shell_super->sockw, "\n", 1) != 1))
 601                 return FALSE;
 602         }
 603 #endif
 604     }
 605     return TRUE;
 606 }
 607 
 608 /* --------------------------------------------------------------------------------------------- */
 609 
 610 static int
 611 shell_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
     /* [previous][next][first][last][top][bottom][index][help]  */
 612 {
 613     gboolean ftalk;
 614 
 615     // hide panels
 616     pre_exec ();
 617 
 618     // open pipe
 619     shell_open_archive_pipeopen (super);
 620 
 621     // Start talk with ssh-server (password prompt, etc )
 622     ftalk = shell_open_archive_talk (me, super);
 623 
 624     // show panels
 625     post_exec ();
 626 
 627     if (!ftalk)
 628         ERRNOR (E_PROTO, -1);
 629 
 630     vfs_print_message ("%s", _ ("shell: Sending initial line..."));
 631 
 632     // Set up remote locale to C, otherwise dates cannot be recognized
 633     if (shell_command (me, super, WAIT_REPLY,
 634                        "LANG=C LC_ALL=C LC_TIME=C; export LANG LC_ALL LC_TIME;\n"
 635                        "echo '### 200'\n",
 636                        -1)
 637         != COMPLETE)
 638         ERRNOR (E_PROTO, -1);
 639 
 640     vfs_print_message ("%s", _ ("shell: Getting host info..."));
 641     if (shell_info (me, super))
 642         SHELL_SUPER (super)->scr_env = shell_set_env (SHELL_SUPER (super)->host_flags);
 643 
 644 #if 0
 645     super->name =
 646         g_strconcat ("sh://", super->path_element->user, "@", super->path_element->host,
 647                      PATH_SEP_STR, (char *) NULL);
 648 #else
 649     super->name = g_strdup (PATH_SEP_STR);
 650 #endif
 651 
 652     super->root = vfs_s_new_inode (me, super, shell_default_stat (me));
 653 
 654     return 0;
 655 }
 656 
 657 /* --------------------------------------------------------------------------------------------- */
 658 
 659 static int
 660 shell_open_archive (struct vfs_s_super *super, const vfs_path_t *vpath,
     /* [previous][next][first][last][top][bottom][index][help]  */
 661                     const vfs_path_element_t *vpath_element)
 662 {
 663     shell_super_t *shell_super = SHELL_SUPER (super);
 664 
 665     (void) vpath;
 666 
 667     super->path_element = vfs_path_element_clone (vpath_element);
 668 
 669     if (strncmp (vpath_element->vfs_prefix, "rsh", 3) == 0)
 670         super->path_element->port = SHELL_FLAG_RSH;
 671 
 672     shell_super->scr_ls = shell_load_script_from_file (super->path_element->host, VFS_SHELL_LS_FILE,
 673                                                        VFS_SHELL_LS_DEF_CONTENT);
 674     shell_super->scr_exists = shell_load_script_from_file (
 675         super->path_element->host, VFS_SHELL_EXISTS_FILE, VFS_SHELL_EXISTS_DEF_CONTENT);
 676     shell_super->scr_mkdir = shell_load_script_from_file (
 677         super->path_element->host, VFS_SHELL_MKDIR_FILE, VFS_SHELL_MKDIR_DEF_CONTENT);
 678     shell_super->scr_unlink = shell_load_script_from_file (
 679         super->path_element->host, VFS_SHELL_UNLINK_FILE, VFS_SHELL_UNLINK_DEF_CONTENT);
 680     shell_super->scr_chown = shell_load_script_from_file (
 681         super->path_element->host, VFS_SHELL_CHOWN_FILE, VFS_SHELL_CHOWN_DEF_CONTENT);
 682     shell_super->scr_chmod = shell_load_script_from_file (
 683         super->path_element->host, VFS_SHELL_CHMOD_FILE, VFS_SHELL_CHMOD_DEF_CONTENT);
 684     shell_super->scr_utime = shell_load_script_from_file (
 685         super->path_element->host, VFS_SHELL_UTIME_FILE, VFS_SHELL_UTIME_DEF_CONTENT);
 686     shell_super->scr_rmdir = shell_load_script_from_file (
 687         super->path_element->host, VFS_SHELL_RMDIR_FILE, VFS_SHELL_RMDIR_DEF_CONTENT);
 688     shell_super->scr_ln = shell_load_script_from_file (super->path_element->host, VFS_SHELL_LN_FILE,
 689                                                        VFS_SHELL_LN_DEF_CONTENT);
 690     shell_super->scr_mv = shell_load_script_from_file (super->path_element->host, VFS_SHELL_MV_FILE,
 691                                                        VFS_SHELL_MV_DEF_CONTENT);
 692     shell_super->scr_hardlink = shell_load_script_from_file (
 693         super->path_element->host, VFS_SHELL_HARDLINK_FILE, VFS_SHELL_HARDLINK_DEF_CONTENT);
 694     shell_super->scr_get = shell_load_script_from_file (
 695         super->path_element->host, VFS_SHELL_GET_FILE, VFS_SHELL_GET_DEF_CONTENT);
 696     shell_super->scr_send = shell_load_script_from_file (
 697         super->path_element->host, VFS_SHELL_SEND_FILE, VFS_SHELL_SEND_DEF_CONTENT);
 698     shell_super->scr_append = shell_load_script_from_file (
 699         super->path_element->host, VFS_SHELL_APPEND_FILE, VFS_SHELL_APPEND_DEF_CONTENT);
 700     shell_super->scr_info = shell_load_script_from_file (
 701         super->path_element->host, VFS_SHELL_INFO_FILE, VFS_SHELL_INFO_DEF_CONTENT);
 702 
 703     return shell_open_archive_int (vpath_element->class, super);
 704 }
 705 
 706 /* --------------------------------------------------------------------------------------------- */
 707 
 708 static int
 709 shell_archive_same (const vfs_path_element_t *vpath_element, struct vfs_s_super *super,
     /* [previous][next][first][last][top][bottom][index][help]  */
 710                     const vfs_path_t *vpath, void *cookie)
 711 {
 712     vfs_path_element_t *path_element;
 713     int result;
 714 
 715     (void) vpath;
 716     (void) cookie;
 717 
 718     path_element = vfs_path_element_clone (vpath_element);
 719 
 720     if (path_element->user == NULL)
 721         path_element->user = vfs_get_local_username ();
 722 
 723     result = ((strcmp (path_element->host, super->path_element->host) == 0)
 724               && (strcmp (path_element->user, super->path_element->user) == 0)
 725               && (path_element->port == super->path_element->port))
 726         ? 1
 727         : 0;
 728 
 729     vfs_path_element_free (path_element);
 730 
 731     return result;
 732 }
 733 
 734 /* --------------------------------------------------------------------------------------------- */
 735 
 736 static void
 737 shell_parse_ls (char *buffer, struct vfs_s_entry *ent)
     /* [previous][next][first][last][top][bottom][index][help]  */
 738 {
 739 #define ST ent->ino->st
 740 
 741     buffer++;
 742 
 743     switch (buffer[-1])
 744     {
 745     case ':':
 746     {
 747         char *filename;
 748         char *filename_bound;
 749         char *temp;
 750 
 751         filename = buffer;
 752 
 753         if (strcmp (filename, "\".\"") == 0 || strcmp (filename, "\"..\"") == 0)
 754             break;  // We'll do "." and ".." ourselves
 755 
 756         filename_bound = filename + strlen (filename);
 757 
 758         if (S_ISLNK (ST.st_mode))
 759         {
 760             char *linkname;
 761             char *linkname_bound;
 762             /* we expect: "escaped-name" -> "escaped-name"
 763                //     -> cannot occur in filenames,
 764                //     because it will be escaped to -\> */
 765 
 766             linkname_bound = filename_bound;
 767 
 768             if (*filename == '"')
 769                 ++filename;
 770 
 771             linkname = strstr (filename, "\" -> \"");
 772             if (linkname == NULL)
 773             {
 774                 // broken client, or smth goes wrong
 775                 linkname = filename_bound;
 776                 if (filename_bound > filename && *(filename_bound - 1) == '"')
 777                     --filename_bound;  // skip trailing "
 778             }
 779             else
 780             {
 781                 filename_bound = linkname;
 782                 linkname += 6;  // strlen ("\" -> \"")
 783                 if (*(linkname_bound - 1) == '"')
 784                     --linkname_bound;  // skip trailing "
 785             }
 786 
 787             ent->name = g_strndup (filename, filename_bound - filename);
 788             temp = ent->name;
 789             ent->name = str_shell_unescape (ent->name);
 790             g_free (temp);
 791 
 792             ent->ino->linkname = g_strndup (linkname, linkname_bound - linkname);
 793             temp = ent->ino->linkname;
 794             ent->ino->linkname = str_shell_unescape (ent->ino->linkname);
 795             g_free (temp);
 796         }
 797         else
 798         {
 799             // we expect: "escaped-name"
 800             if (filename_bound - filename > 2)
 801             {
 802                 /*
 803                    there is at least 2 "
 804                    and we skip them
 805                  */
 806                 if (*filename == '"')
 807                     ++filename;
 808                 if (*(filename_bound - 1) == '"')
 809                     --filename_bound;
 810             }
 811 
 812             ent->name = g_strndup (filename, filename_bound - filename);
 813             temp = ent->name;
 814             ent->name = str_shell_unescape (ent->name);
 815             g_free (temp);
 816         }
 817         break;
 818     }
 819 
 820     case 'S':
 821         ST.st_size = (off_t) g_ascii_strtoll (buffer, NULL, 10);
 822         break;
 823 
 824     case 'P':
 825     {
 826         size_t skipped;
 827 
 828         vfs_parse_filemode (buffer, &skipped, &ST.st_mode);
 829         break;
 830     }
 831 
 832     case 'R':
 833     {
 834         /*
 835            raw filemode:
 836            we expect: Roctal-filemode octal-filetype uid.gid
 837          */
 838         size_t skipped;
 839 
 840         vfs_parse_raw_filemode (buffer, &skipped, &ST.st_mode);
 841         break;
 842     }
 843 
 844     case 'd':
 845         vfs_split_text (buffer);
 846         vfs_zero_stat_times (&ST);
 847         if (vfs_parse_filedate (0, &ST.st_ctime) == 0)
 848             break;
 849         ST.st_atime = ST.st_mtime = ST.st_ctime;
 850         break;
 851 
 852     case 'D':
 853     {
 854         struct tm tim;
 855 
 856         memset (&tim, 0, sizeof (tim));
 857         if (sscanf (buffer, "%d %d %d %d %d %d", &tim.tm_year, &tim.tm_mon, &tim.tm_mday,
 858                     &tim.tm_hour, &tim.tm_min, &tim.tm_sec)
 859             != 6)
 860             break;
 861         vfs_zero_stat_times (&ST);
 862         ST.st_atime = ST.st_mtime = ST.st_ctime = mktime (&tim);
 863     }
 864     break;
 865 
 866     case 'E':
 867     {
 868         int maj, min;
 869 
 870         if (sscanf (buffer, "%d,%d", &maj, &min) != 2)
 871             break;
 872 #ifdef HAVE_STRUCT_STAT_ST_RDEV
 873         ST.st_rdev = makedev (maj, min);
 874 #endif
 875     }
 876     break;
 877 
 878     default:
 879         break;
 880     }
 881 
 882 #undef ST
 883 }
 884 
 885 /* --------------------------------------------------------------------------------------------- */
 886 
 887 static int
 888 shell_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, const char *remote_path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 889 {
 890     struct vfs_s_super *super = dir->super;
 891     char buffer[BUF_8K] = "\0";
 892     struct vfs_s_entry *ent = NULL;
 893     char *quoted_path;
 894     int reply_code;
 895 
 896     /*
 897      * Simple SHELL debug interface :]
 898      */
 899 #if 0
 900     if (me->logfile == NULL)
 901         me->logfile = fopen ("/tmp/mc-SHELL.sh", "w");
 902 #endif
 903 
 904     vfs_print_message (_ ("shell: Reading directory %s..."), remote_path);
 905 
 906     dir->timestamp = g_get_monotonic_time () + shell_directory_timeout * G_USEC_PER_SEC;
 907 
 908     quoted_path = str_shell_escape (remote_path);
 909     (void) shell_command_v (me, super, NONE, SHELL_SUPER (super)->scr_ls, "SHELL_FILENAME=%s;\n",
 910                             quoted_path);
 911     g_free (quoted_path);
 912 
 913     ent = vfs_s_generate_entry (me, NULL, dir, 0);
 914 
 915     while (TRUE)
 916     {
 917         int res;
 918 
 919         res =
 920             vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), SHELL_SUPER (super)->sockr);
 921 
 922         if ((res == 0) || (res == EINTR))
 923         {
 924             vfs_s_free_entry (me, ent);
 925             me->verrno = ECONNRESET;
 926             goto error;
 927         }
 928         if (me->logfile != NULL)
 929         {
 930             fputs (buffer, me->logfile);
 931             fputs ("\n", me->logfile);
 932             fflush (me->logfile);
 933         }
 934         if (strncmp (buffer, "### ", 4) == 0)
 935             break;
 936 
 937         if (buffer[0] != '\0')
 938             shell_parse_ls (buffer, ent);
 939         else if (ent->name != NULL)
 940         {
 941             vfs_s_insert_entry (me, dir, ent);
 942             ent = vfs_s_generate_entry (me, NULL, dir, 0);
 943         }
 944     }
 945 
 946     vfs_s_free_entry (me, ent);
 947     reply_code = shell_decode_reply (buffer + 4, 0);
 948     if (reply_code == COMPLETE)
 949     {
 950         vfs_print_message (_ ("%s: done."), me->name);
 951         return 0;
 952     }
 953 
 954     me->verrno = reply_code == ERROR ? EACCES : E_REMOTE;
 955 
 956 error:
 957     vfs_print_message (_ ("%s: failure"), me->name);
 958     return -1;
 959 }
 960 
 961 /* --------------------------------------------------------------------------------------------- */
 962 
 963 static int
 964 shell_file_store (struct vfs_class *me, vfs_file_handler_t *fh, char *name, char *localname)
     /* [previous][next][first][last][top][bottom][index][help]  */
 965 {
 966     shell_file_handler_t *shell = SHELL_FILE_HANDLER (fh);
 967     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
 968     shell_super_t *shell_super = SHELL_SUPER (super);
 969     int code;
 970     off_t total = 0;
 971     char buffer[BUF_8K];
 972     struct stat s;
 973     int h;
 974     char *quoted_name;
 975 
 976     h = open (localname, O_RDONLY);
 977     if (h == -1)
 978         ERRNOR (EIO, -1);
 979     if (fstat (h, &s) < 0)
 980     {
 981         close (h);
 982         ERRNOR (EIO, -1);
 983     }
 984 
 985     /* First, try this as stor:
 986      *
 987      *     ( head -c number ) | ( cat > file; cat >/dev/null )
 988      *
 989      *  If 'head' is not present on the remote system, 'dd' will be used.
 990      * Unfortunately, we cannot trust most non-GNU 'head' implementations
 991      * even if '-c' options is supported. Therefore, we separate GNU head
 992      * (and other modern heads?) using '-q' and '-' . This causes another
 993      * implementations to fail (because of "incorrect options").
 994      *
 995      *  Fallback is:
 996      *
 997      *     rest=<number>
 998      *     while [ $rest -gt 0 ]
 999      *     do
1000      *        cnt=`expr \( $rest + 255 \) / 256`
1001      *        n=`dd bs=256 count=$cnt | tee -a <target_file> | wc -c`
1002      *        rest=`expr $rest - $n`
1003      *     done
1004      *
1005      *  'dd' was not designed for full filling of input buffers,
1006      *  and does not report exact number of bytes (not blocks).
1007      *  Therefore a more complex shell script is needed.
1008      *
1009      *   On some systems non-GNU head writes "Usage:" error report to stdout
1010      *  instead of stderr. It makes impossible the use of "head || dd"
1011      *  algorithm for file appending case, therefore just "dd" is used for it.
1012      */
1013 
1014     quoted_name = str_shell_escape (name);
1015     vfs_print_message (_ ("shell: store %s: sending command..."), quoted_name);
1016 
1017     // FIXME: File size is limited to ULONG_MAX
1018     code = shell_command_v (
1019         me, super, WAIT_REPLY, shell->append ? shell_super->scr_append : shell_super->scr_send,
1020         "SHELL_FILENAME=%s SHELL_FILESIZE=%" PRIuMAX ";\n", quoted_name, (uintmax_t) s.st_size);
1021     g_free (quoted_name);
1022 
1023     if (code != PRELIM)
1024     {
1025         close (h);
1026         ERRNOR (E_REMOTE, -1);
1027     }
1028 
1029     while (TRUE)
1030     {
1031         ssize_t n, t;
1032 
1033         while ((n = read (h, buffer, sizeof (buffer))) < 0)
1034         {
1035             if ((errno == EINTR) && tty_got_interrupt ())
1036                 continue;
1037             vfs_print_message ("%s", _ ("shell: Local read failed, sending zeros"));
1038             close (h);
1039             h = open ("/dev/zero", O_RDONLY);
1040         }
1041 
1042         if (n == 0)
1043             break;
1044 
1045         t = write (shell_super->sockw, buffer, n);
1046         if (t != n)
1047         {
1048             if (t == -1)
1049                 me->verrno = errno;
1050             else
1051                 me->verrno = EIO;
1052             goto error_return;
1053         }
1054         tty_disable_interrupt_key ();
1055         total += n;
1056         vfs_print_message ("%s: %" PRIuMAX "/%" PRIuMAX, _ ("shell: storing file"),
1057                            (uintmax_t) total, (uintmax_t) s.st_size);
1058     }
1059     close (h);
1060 
1061     if (shell_get_reply (me, shell_super->sockr, NULL, 0) != COMPLETE)
1062         ERRNOR (E_REMOTE, -1);
1063     return 0;
1064 
1065 error_return:
1066     close (h);
1067     shell_get_reply (me, shell_super->sockr, NULL, 0);
1068     return -1;
1069 }
1070 
1071 /* --------------------------------------------------------------------------------------------- */
1072 
1073 static int
1074 shell_linear_start (struct vfs_class *me, vfs_file_handler_t *fh, off_t offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
1075 {
1076     shell_file_handler_t *shell = SHELL_FILE_HANDLER (fh);
1077     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
1078     char *name;
1079     char *quoted_name;
1080 
1081     name = vfs_s_fullpath (me, fh->ino);
1082     if (name == NULL)
1083         return 0;
1084     quoted_name = str_shell_escape (name);
1085     g_free (name);
1086     shell->append = FALSE;
1087 
1088     /*
1089      * Check whether the remote file is readable by using 'dd' to copy
1090      * a single byte from the remote file to /dev/null. If 'dd' completes
1091      * with exit status of 0 use 'cat' to send the file contents to the
1092      * standard output (i.e. over the network).
1093      */
1094 
1095     offset = shell_command_v (me, super, WANT_STRING, SHELL_SUPER (super)->scr_get,
1096                               "SHELL_FILENAME=%s SHELL_START_OFFSET=%" PRIuMAX ";\n", quoted_name,
1097                               (uintmax_t) offset);
1098     g_free (quoted_name);
1099 
1100     if (offset != PRELIM)
1101         ERRNOR (E_REMOTE, 0);
1102     fh->linear = LS_LINEAR_OPEN;
1103     shell->got = 0;
1104     errno = 0;
1105 #if SIZEOF_OFF_T == SIZEOF_LONG
1106     shell->total = (off_t) strtol (reply_str, NULL, 10);
1107 #else
1108     shell->total = (off_t) g_ascii_strtoll (reply_str, NULL, 10);
1109 #endif
1110     if (errno != 0)
1111         ERRNOR (E_REMOTE, 0);
1112     return 1;
1113 }
1114 
1115 /* --------------------------------------------------------------------------------------------- */
1116 
1117 static void
1118 shell_linear_abort (struct vfs_class *me, vfs_file_handler_t *fh)
     /* [previous][next][first][last][top][bottom][index][help]  */
1119 {
1120     shell_file_handler_t *shell = SHELL_FILE_HANDLER (fh);
1121     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
1122     char buffer[BUF_8K];
1123     ssize_t n;
1124 
1125     vfs_print_message ("%s", _ ("Aborting transfer..."));
1126 
1127     do
1128     {
1129         n = MIN ((off_t) sizeof (buffer), (shell->total - shell->got));
1130         if (n != 0)
1131         {
1132             n = read (SHELL_SUPER (super)->sockr, buffer, n);
1133             if (n < 0)
1134                 return;
1135             shell->got += n;
1136         }
1137     }
1138     while (n != 0);
1139 
1140     if (shell_get_reply (me, SHELL_SUPER (super)->sockr, NULL, 0) != COMPLETE)
1141         vfs_print_message ("%s", _ ("Error reported after abort."));
1142     else
1143         vfs_print_message ("%s", _ ("Aborted transfer would be successful."));
1144 }
1145 
1146 /* --------------------------------------------------------------------------------------------- */
1147 
1148 static ssize_t
1149 shell_linear_read (struct vfs_class *me, vfs_file_handler_t *fh, void *buf, size_t len)
     /* [previous][next][first][last][top][bottom][index][help]  */
1150 {
1151     shell_file_handler_t *shell = SHELL_FILE_HANDLER (fh);
1152     struct vfs_s_super *super = VFS_FILE_HANDLER_SUPER (fh);
1153     ssize_t n = 0;
1154 
1155     len = MIN ((size_t) (shell->total - shell->got), len);
1156     tty_disable_interrupt_key ();
1157     while (len != 0 && ((n = read (SHELL_SUPER (super)->sockr, buf, len)) < 0))
1158     {
1159         if ((errno == EINTR) && !tty_got_interrupt ())
1160             continue;
1161         break;
1162     }
1163     tty_enable_interrupt_key ();
1164 
1165     if (n > 0)
1166         shell->got += n;
1167     else if (n < 0)
1168         shell_linear_abort (me, fh);
1169     else if (shell_get_reply (me, SHELL_SUPER (super)->sockr, NULL, 0) != COMPLETE)
1170         ERRNOR (E_REMOTE, -1);
1171     ERRNOR (errno, n);
1172 }
1173 
1174 /* --------------------------------------------------------------------------------------------- */
1175 
1176 static void
1177 shell_linear_close (struct vfs_class *me, vfs_file_handler_t *fh)
     /* [previous][next][first][last][top][bottom][index][help]  */
1178 {
1179     shell_file_handler_t *shell = SHELL_FILE_HANDLER (fh);
1180 
1181     if (shell->total != shell->got)
1182         shell_linear_abort (me, fh);
1183 }
1184 
1185 /* --------------------------------------------------------------------------------------------- */
1186 
1187 static int
1188 shell_ctl (void *fh, int ctlop, void *arg)
     /* [previous][next][first][last][top][bottom][index][help]  */
1189 {
1190     (void) arg;
1191     (void) fh;
1192     (void) ctlop;
1193 
1194     return 0;
1195 
1196 #if 0
1197     switch (ctlop)
1198     {
1199     case VFS_CTL_IS_NOTREADY:
1200         {
1201             vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
1202             int v;
1203 
1204             if (file->linear == LS_NOT_LINEAR)
1205                 vfs_die ("You may not do this");
1206             if (file->linear == LS_LINEAR_CLOSED || file->linear == LS_LINEAR_PREOPEN)
1207                 return 0;
1208 
1209             v = vfs_s_select_on_two (VFS_FILE_HANDLER_SUPER (fh)->u.shell.sockr, 0);
1210 
1211             return (((v < 0) && (errno == EINTR)) || v == 0) ? 1 : 0;
1212         }
1213     default:
1214         return 0;
1215     }
1216 #endif
1217 }
1218 
1219 /* --------------------------------------------------------------------------------------------- */
1220 
1221 static int
1222 shell_rename (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1223 {
1224     const char *crpath1, *crpath2;
1225     char *rpath1, *rpath2;
1226     struct vfs_s_super *super, *super2;
1227     struct vfs_class *me;
1228     int ret;
1229 
1230     crpath1 = vfs_s_get_path (vpath1, &super, 0);
1231     if (crpath1 == NULL)
1232         return -1;
1233 
1234     crpath2 = vfs_s_get_path (vpath2, &super2, 0);
1235     if (crpath2 == NULL)
1236         return -1;
1237 
1238     rpath1 = str_shell_escape (crpath1);
1239     rpath2 = str_shell_escape (crpath2);
1240 
1241     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath1));
1242 
1243     ret = shell_send_command (me, super2, OPT_FLUSH, SHELL_SUPER (super)->scr_mv,
1244                               "SHELL_FILEFROM=%s SHELL_FILETO=%s;\n", rpath1, rpath2);
1245 
1246     g_free (rpath1);
1247     g_free (rpath2);
1248 
1249     return ret;
1250 }
1251 
1252 /* --------------------------------------------------------------------------------------------- */
1253 
1254 static int
1255 shell_link (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1256 {
1257     const char *crpath1, *crpath2;
1258     char *rpath1, *rpath2;
1259     struct vfs_s_super *super, *super2;
1260     struct vfs_class *me;
1261     int ret;
1262 
1263     crpath1 = vfs_s_get_path (vpath1, &super, 0);
1264     if (crpath1 == NULL)
1265         return -1;
1266 
1267     crpath2 = vfs_s_get_path (vpath2, &super2, 0);
1268     if (crpath2 == NULL)
1269         return -1;
1270 
1271     rpath1 = str_shell_escape (crpath1);
1272     rpath2 = str_shell_escape (crpath2);
1273 
1274     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath1));
1275 
1276     ret = shell_send_command (me, super2, OPT_FLUSH, SHELL_SUPER (super)->scr_hardlink,
1277                               "SHELL_FILEFROM=%s SHELL_FILETO=%s;\n", rpath1, rpath2);
1278 
1279     g_free (rpath1);
1280     g_free (rpath2);
1281 
1282     return ret;
1283 }
1284 
1285 /* --------------------------------------------------------------------------------------------- */
1286 
1287 static int
1288 shell_symlink (const vfs_path_t *vpath1, const vfs_path_t *vpath2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1289 {
1290     char *qsetto;
1291     const char *crpath;
1292     char *rpath;
1293     struct vfs_s_super *super;
1294     struct vfs_class *me;
1295     int ret;
1296 
1297     crpath = vfs_s_get_path (vpath2, &super, 0);
1298     if (crpath == NULL)
1299         return -1;
1300 
1301     rpath = str_shell_escape (crpath);
1302     qsetto = str_shell_escape (vfs_path_get_last_path_str (vpath1));
1303 
1304     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath2));
1305 
1306     ret = shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_ln,
1307                               "SHELL_FILEFROM=%s SHELL_FILETO=%s;\n", qsetto, rpath);
1308 
1309     g_free (qsetto);
1310     g_free (rpath);
1311 
1312     return ret;
1313 }
1314 
1315 /* --------------------------------------------------------------------------------------------- */
1316 
1317 static int
1318 shell_stat (const vfs_path_t *vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
1319 {
1320     int ret;
1321 
1322     ret = vfs_s_stat (vpath, buf);
1323     shell_set_blksize (buf);
1324     return ret;
1325 }
1326 
1327 /* --------------------------------------------------------------------------------------------- */
1328 
1329 static int
1330 shell_lstat (const vfs_path_t *vpath, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
1331 {
1332     int ret;
1333 
1334     ret = vfs_s_lstat (vpath, buf);
1335     shell_set_blksize (buf);
1336     return ret;
1337 }
1338 
1339 /* --------------------------------------------------------------------------------------------- */
1340 
1341 static int
1342 shell_fstat (void *vfs_info, struct stat *buf)
     /* [previous][next][first][last][top][bottom][index][help]  */
1343 {
1344     int ret;
1345 
1346     ret = vfs_s_fstat (vfs_info, buf);
1347     shell_set_blksize (buf);
1348     return ret;
1349 }
1350 
1351 /* --------------------------------------------------------------------------------------------- */
1352 
1353 static int
1354 shell_chmod (const vfs_path_t *vpath, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1355 {
1356     const char *crpath;
1357     char *rpath;
1358     struct vfs_s_super *super;
1359     struct vfs_class *me;
1360     int ret;
1361 
1362     crpath = vfs_s_get_path (vpath, &super, 0);
1363     if (crpath == NULL)
1364         return -1;
1365 
1366     rpath = str_shell_escape (crpath);
1367 
1368     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1369 
1370     ret = shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_chmod,
1371                               "SHELL_FILENAME=%s SHELL_FILEMODE=%4.4o;\n", rpath,
1372                               (unsigned int) (mode & 07777));
1373 
1374     g_free (rpath);
1375 
1376     return ret;
1377     ;
1378 }
1379 
1380 /* --------------------------------------------------------------------------------------------- */
1381 
1382 static int
1383 shell_chown (const vfs_path_t *vpath, uid_t owner, gid_t group)
     /* [previous][next][first][last][top][bottom][index][help]  */
1384 {
1385     char *sowner, *sgroup;
1386     struct passwd *pw;
1387     struct group *gr;
1388     const char *crpath;
1389     char *rpath;
1390     struct vfs_s_super *super;
1391     struct vfs_class *me;
1392     int ret;
1393 
1394     pw = getpwuid (owner);
1395     if (pw == NULL)
1396         return 0;
1397 
1398     gr = getgrgid (group);
1399     if (gr == NULL)
1400         return 0;
1401 
1402     sowner = pw->pw_name;
1403     sgroup = gr->gr_name;
1404 
1405     crpath = vfs_s_get_path (vpath, &super, 0);
1406     if (crpath == NULL)
1407         return -1;
1408 
1409     rpath = str_shell_escape (crpath);
1410 
1411     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1412 
1413     // FIXME: what should we report if chgrp succeeds but chown fails?
1414     ret = shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_chown,
1415                               "SHELL_FILENAME=%s SHELL_FILEOWNER=%s SHELL_FILEGROUP=%s;\n", rpath,
1416                               sowner, sgroup);
1417 
1418     g_free (rpath);
1419 
1420     return ret;
1421 }
1422 
1423 /* --------------------------------------------------------------------------------------------- */
1424 
1425 static int
1426 shell_utime (const vfs_path_t *vpath, mc_timesbuf_t *times)
     /* [previous][next][first][last][top][bottom][index][help]  */
1427 {
1428     char utcatime[16], utcmtime[16];
1429     char utcatime_w_nsec[30], utcmtime_w_nsec[30];
1430     mc_timespec_t atime, mtime;
1431     struct tm *gmt;
1432     const char *crpath;
1433     char *rpath;
1434     struct vfs_s_super *super;
1435     struct vfs_class *me;
1436     int ret;
1437 
1438     crpath = vfs_s_get_path (vpath, &super, 0);
1439     if (crpath == NULL)
1440         return -1;
1441 
1442     rpath = str_shell_escape (crpath);
1443 
1444     vfs_get_timespecs_from_timesbuf (times, &atime, &mtime);
1445 
1446     gmt = gmtime (&atime.tv_sec);
1447     g_snprintf (utcatime, sizeof (utcatime), "%04d%02d%02d%02d%02d.%02d", gmt->tm_year + 1900,
1448                 gmt->tm_mon + 1, gmt->tm_mday, gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
1449     g_snprintf (utcatime_w_nsec, sizeof (utcatime_w_nsec), "%04d-%02d-%02d %02d:%02d:%02d.%09ld",
1450                 gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, gmt->tm_hour, gmt->tm_min,
1451                 gmt->tm_sec, atime.tv_nsec);
1452 
1453     gmt = gmtime (&mtime.tv_sec);
1454     g_snprintf (utcmtime, sizeof (utcmtime), "%04d%02d%02d%02d%02d.%02d", gmt->tm_year + 1900,
1455                 gmt->tm_mon + 1, gmt->tm_mday, gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
1456     g_snprintf (utcmtime_w_nsec, sizeof (utcmtime_w_nsec), "%04d-%02d-%02d %02d:%02d:%02d.%09ld",
1457                 gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, gmt->tm_hour, gmt->tm_min,
1458                 gmt->tm_sec, mtime.tv_nsec);
1459 
1460     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1461 
1462     ret = shell_send_command (
1463         me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_utime,
1464         "SHELL_FILENAME=%s SHELL_FILEATIME=%ju SHELL_FILEMTIME=%ju "
1465         "SHELL_TOUCHATIME=%s SHELL_TOUCHMTIME=%s SHELL_TOUCHATIME_W_NSEC=\"%s\" "
1466         "SHELL_TOUCHMTIME_W_NSEC=\"%s\";\n",
1467         rpath, (uintmax_t) atime.tv_sec, (uintmax_t) mtime.tv_sec, utcatime, utcmtime,
1468         utcatime_w_nsec, utcmtime_w_nsec);
1469 
1470     g_free (rpath);
1471 
1472     return ret;
1473 }
1474 
1475 /* --------------------------------------------------------------------------------------------- */
1476 
1477 static int
1478 shell_unlink (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1479 {
1480     const char *crpath;
1481     char *rpath;
1482     struct vfs_s_super *super;
1483     struct vfs_class *me;
1484     int ret;
1485 
1486     crpath = vfs_s_get_path (vpath, &super, 0);
1487     if (crpath == NULL)
1488         return -1;
1489 
1490     rpath = str_shell_escape (crpath);
1491 
1492     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1493 
1494     ret = shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_unlink,
1495                               "SHELL_FILENAME=%s;\n", rpath);
1496 
1497     g_free (rpath);
1498 
1499     return ret;
1500 }
1501 
1502 /* --------------------------------------------------------------------------------------------- */
1503 
1504 static int
1505 shell_exists (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1506 {
1507     const char *crpath;
1508     char *rpath;
1509     struct vfs_s_super *super;
1510     struct vfs_class *me;
1511     int ret;
1512 
1513     crpath = vfs_s_get_path (vpath, &super, 0);
1514     if (crpath == NULL)
1515         return -1;
1516 
1517     rpath = str_shell_escape (crpath);
1518 
1519     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1520 
1521     ret = shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_exists,
1522                               "SHELL_FILENAME=%s;\n", rpath);
1523 
1524     g_free (rpath);
1525 
1526     return (ret == 0 ? 1 : 0);
1527 }
1528 
1529 /* --------------------------------------------------------------------------------------------- */
1530 
1531 static int
1532 shell_mkdir (const vfs_path_t *vpath, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1533 {
1534     const char *crpath;
1535     char *rpath;
1536     struct vfs_s_super *super;
1537     struct vfs_class *me;
1538     int ret;
1539 
1540     (void) mode;
1541 
1542     crpath = vfs_s_get_path (vpath, &super, 0);
1543     if (crpath == NULL)
1544         return -1;
1545 
1546     rpath = str_shell_escape (crpath);
1547 
1548     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1549 
1550     ret = shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_mkdir,
1551                               "SHELL_FILENAME=%s;\n", rpath);
1552     g_free (rpath);
1553 
1554     if (ret != 0)
1555         return ret;
1556 
1557     if (shell_exists (vpath) == 0)
1558     {
1559         me->verrno = EACCES;
1560         return -1;
1561     }
1562     return 0;
1563 }
1564 
1565 /* --------------------------------------------------------------------------------------------- */
1566 
1567 static int
1568 shell_rmdir (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1569 {
1570     const char *crpath;
1571     char *rpath;
1572     struct vfs_s_super *super;
1573     struct vfs_class *me;
1574     int ret;
1575 
1576     crpath = vfs_s_get_path (vpath, &super, 0);
1577     if (crpath == NULL)
1578         return -1;
1579 
1580     rpath = str_shell_escape (crpath);
1581 
1582     me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
1583 
1584     ret = shell_send_command (me, super, OPT_FLUSH, SHELL_SUPER (super)->scr_rmdir,
1585                               "SHELL_FILENAME=%s;\n", rpath);
1586 
1587     g_free (rpath);
1588 
1589     return ret;
1590 }
1591 
1592 /* --------------------------------------------------------------------------------------------- */
1593 
1594 static vfs_file_handler_t *
1595 shell_fh_new (struct vfs_s_inode *ino, gboolean changed)
     /* [previous][next][first][last][top][bottom][index][help]  */
1596 {
1597     shell_file_handler_t *fh;
1598 
1599     fh = g_new0 (shell_file_handler_t, 1);
1600     vfs_s_init_fh (VFS_FILE_HANDLER (fh), ino, changed);
1601 
1602     return VFS_FILE_HANDLER (fh);
1603 }
1604 
1605 /* --------------------------------------------------------------------------------------------- */
1606 
1607 static int
1608 shell_fh_open (struct vfs_class *me, vfs_file_handler_t *fh, int flags, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1609 {
1610     shell_file_handler_t *shell = SHELL_FILE_HANDLER (fh);
1611 
1612     (void) mode;
1613 
1614     // File will be written only, so no need to retrieve it
1615     if (((flags & O_WRONLY) == O_WRONLY) && ((flags & (O_RDONLY | O_RDWR)) == 0))
1616     {
1617         // user pressed the button [ Append ] in the "Copy" dialog
1618         if ((flags & O_APPEND) != 0)
1619             shell->append = TRUE;
1620 
1621         if (fh->ino->localname == NULL)
1622         {
1623             vfs_path_t *vpath = NULL;
1624             int tmp_handle;
1625 
1626             tmp_handle = vfs_mkstemps (&vpath, me->name, fh->ino->ent->name);
1627             if (tmp_handle == -1)
1628                 return (-1);
1629 
1630             fh->ino->localname = vfs_path_free (vpath, FALSE);
1631             close (tmp_handle);
1632         }
1633         return 0;
1634     }
1635 
1636     if (fh->ino->localname == NULL && vfs_s_retrieve_file (me, fh->ino) == -1)
1637         return (-1);
1638 
1639     if (fh->ino->localname == NULL)
1640         vfs_die ("retrieve_file failed to fill in localname");
1641     return 0;
1642 }
1643 
1644 /* --------------------------------------------------------------------------------------------- */
1645 
1646 static void
1647 shell_fill_names (struct vfs_class *me, fill_names_f func)
     /* [previous][next][first][last][top][bottom][index][help]  */
1648 {
1649     GList *iter;
1650 
1651     for (iter = VFS_SUBCLASS (me)->supers; iter != NULL; iter = g_list_next (iter))
1652     {
1653         const struct vfs_s_super *super = (const struct vfs_s_super *) iter->data;
1654 
1655         char *name;
1656         char gbuf[10];
1657         const char *flags = "";
1658 
1659         switch (super->path_element->port)
1660         {
1661         case SHELL_FLAG_RSH:
1662             flags = ":r";
1663             break;
1664         case SHELL_FLAG_COMPRESSED:
1665             flags = ":C";
1666             break;
1667         default:
1668             if (super->path_element->port > SHELL_FLAG_RSH)
1669             {
1670                 g_snprintf (gbuf, sizeof (gbuf), ":%d", super->path_element->port);
1671                 flags = gbuf;
1672             }
1673             break;
1674         }
1675 
1676         name = g_strconcat (vfs_shell_ops->prefix, VFS_PATH_URL_DELIMITER,
1677                             super->path_element->user, "@", super->path_element->host, flags,
1678                             PATH_SEP_STR, super->path_element->path, (char *) NULL);
1679         func (name);
1680         g_free (name);
1681     }
1682 }
1683 
1684 /* --------------------------------------------------------------------------------------------- */
1685 
1686 static void *
1687 shell_open (const vfs_path_t *vpath, int flags, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
1688 {
1689     /*
1690        sorry, i've places hack here
1691        cause shell don't able to open files with O_EXCL flag
1692      */
1693     flags &= ~O_EXCL;
1694     return vfs_s_open (vpath, flags, mode);
1695 }
1696 
1697 /* --------------------------------------------------------------------------------------------- */
1698 /*** public functions ****************************************************************************/
1699 /* --------------------------------------------------------------------------------------------- */
1700 
1701 void
1702 vfs_init_shell (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1703 {
1704     tcp_init ();
1705 
1706     vfs_init_subclass (&shell_subclass, "shell", VFSF_REMOTE | VFSF_USETMP, "sh");
1707     vfs_shell_ops->fill_names = shell_fill_names;
1708     vfs_shell_ops->stat = shell_stat;
1709     vfs_shell_ops->lstat = shell_lstat;
1710     vfs_shell_ops->fstat = shell_fstat;
1711     vfs_shell_ops->chmod = shell_chmod;
1712     vfs_shell_ops->chown = shell_chown;
1713     vfs_shell_ops->utime = shell_utime;
1714     vfs_shell_ops->open = shell_open;
1715     vfs_shell_ops->symlink = shell_symlink;
1716     vfs_shell_ops->link = shell_link;
1717     vfs_shell_ops->unlink = shell_unlink;
1718     vfs_shell_ops->rename = shell_rename;
1719     vfs_shell_ops->mkdir = shell_mkdir;
1720     vfs_shell_ops->rmdir = shell_rmdir;
1721     vfs_shell_ops->ctl = shell_ctl;
1722     shell_subclass.archive_same = shell_archive_same;
1723     shell_subclass.new_archive = shell_new_archive;
1724     shell_subclass.open_archive = shell_open_archive;
1725     shell_subclass.free_archive = shell_free_archive;
1726     shell_subclass.fh_new = shell_fh_new;
1727     shell_subclass.fh_open = shell_fh_open;
1728     shell_subclass.dir_load = shell_dir_load;
1729     shell_subclass.file_store = shell_file_store;
1730     shell_subclass.linear_start = shell_linear_start;
1731     shell_subclass.linear_read = shell_linear_read;
1732     shell_subclass.linear_close = shell_linear_close;
1733     vfs_register_class (vfs_shell_ops);
1734 }
1735 
1736 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */