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_get_atime
  37. shell_get_mtime
  38. shell_utime
  39. shell_unlink
  40. shell_exists
  41. shell_mkdir
  42. shell_rmdir
  43. shell_fh_new
  44. shell_fh_open
  45. shell_fill_names
  46. shell_open
  47. vfs_init_shell

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

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