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

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