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

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