fish.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113
  1. /* Virtual File System: FISH implementation for transfering files over
  2. shell connections.
  3. Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
  4. 2007 Free Software Foundation, Inc.
  5. Written by: 1998 Pavel Machek
  6. Spaces fix: 2000 Michal Svec
  7. Derived from ftpfs.c.
  8. This program is free software; you can redistribute it and/or
  9. modify it under the terms of the GNU Library General Public License
  10. as published by the Free Software Foundation; either version 2 of
  11. the License, or (at your option) any later version.
  12. This program is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. GNU Library General Public License for more details.
  16. You should have received a copy of the GNU Library General Public
  17. License along with this program; if not, write to the Free Software
  18. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
  19. /*
  20. * Read README.fish for protocol specification.
  21. *
  22. * Syntax of path is: /#sh:user@host[:Cr]/path
  23. * where C means you want compressed connection,
  24. * and r means you want to use rsh
  25. *
  26. * Namespace: fish_vfs_ops exported.
  27. */
  28. /* Define this if your ssh can take -I option */
  29. #include <config.h>
  30. #include <errno.h>
  31. #include "../src/global.h"
  32. #include "../src/tty.h" /* enable/disable interrupt key */
  33. #include "../src/wtools.h" /* message() */
  34. #include "../src/main.h" /* print_vfs_message */
  35. #include "utilvfs.h"
  36. #include "xdirentry.h"
  37. #include "vfs.h"
  38. #include "vfs-impl.h"
  39. #include "gc.h" /* vfs_stamp_create */
  40. #include "tcputil.h"
  41. #include "../src/unixcompat.h"
  42. #include "fish.h"
  43. int fish_directory_timeout = 900;
  44. #define DO_RESOLVE_SYMLINK 1
  45. #define DO_OPEN 2
  46. #define DO_FREE_RESOURCE 4
  47. #define FISH_FLAG_COMPRESSED 1
  48. #define FISH_FLAG_RSH 2
  49. #define OPT_FLUSH 1
  50. #define OPT_IGNORE_ERROR 2
  51. /*
  52. * Reply codes.
  53. */
  54. #define PRELIM 1 /* positive preliminary */
  55. #define COMPLETE 2 /* positive completion */
  56. #define CONTINUE 3 /* positive intermediate */
  57. #define TRANSIENT 4 /* transient negative completion */
  58. #define ERROR 5 /* permanent negative completion */
  59. /* command wait_flag: */
  60. #define NONE 0x00
  61. #define WAIT_REPLY 0x01
  62. #define WANT_STRING 0x02
  63. static char reply_str [80];
  64. static struct vfs_class vfs_fish_ops;
  65. static int
  66. fish_command (struct vfs_class *me, struct vfs_s_super *super,
  67. int wait_reply, const char *fmt, ...)
  68. __attribute__ ((format (__printf__, 4, 5)));
  69. static int fish_decode_reply (char *s, int was_garbage)
  70. {
  71. int code;
  72. if (!sscanf(s, "%d", &code)) {
  73. code = 500;
  74. return 5;
  75. }
  76. if (code<100) return was_garbage ? ERROR : (!code ? COMPLETE : PRELIM);
  77. return code / 100;
  78. }
  79. /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
  80. static int fish_get_reply (struct vfs_class *me, int sock, char *string_buf, int string_len)
  81. {
  82. char answer[1024];
  83. int was_garbage = 0;
  84. for (;;) {
  85. if (!vfs_s_get_line(me, sock, answer, sizeof(answer), '\n')) {
  86. if (string_buf)
  87. *string_buf = 0;
  88. return 4;
  89. }
  90. if (strncmp(answer, "### ", 4)) {
  91. was_garbage = 1;
  92. if (string_buf)
  93. g_strlcpy(string_buf, answer, string_len);
  94. } else return fish_decode_reply(answer+4, was_garbage);
  95. }
  96. }
  97. #define SUP super->u.fish
  98. static int
  99. fish_command (struct vfs_class *me, struct vfs_s_super *super,
  100. int wait_reply, const char *fmt, ...)
  101. {
  102. va_list ap;
  103. char *str;
  104. int status;
  105. FILE *logfile = MEDATA->logfile;
  106. va_start (ap, fmt);
  107. str = g_strdup_vprintf (fmt, ap);
  108. va_end (ap);
  109. if (logfile) {
  110. fwrite (str, strlen (str), 1, logfile);
  111. fflush (logfile);
  112. }
  113. enable_interrupt_key ();
  114. status = write (SUP.sockw, str, strlen (str));
  115. g_free (str);
  116. disable_interrupt_key ();
  117. if (status < 0)
  118. return TRANSIENT;
  119. if (wait_reply)
  120. return fish_get_reply (me, SUP.sockr,
  121. (wait_reply & WANT_STRING) ? reply_str :
  122. NULL, sizeof (reply_str) - 1);
  123. return COMPLETE;
  124. }
  125. static void
  126. fish_free_archive (struct vfs_class *me, struct vfs_s_super *super)
  127. {
  128. if ((SUP.sockw != -1) || (SUP.sockr != -1)) {
  129. print_vfs_message (_("fish: Disconnecting from %s"),
  130. super->name ? super->name : "???");
  131. fish_command (me, super, NONE, "#BYE\nexit\n");
  132. close (SUP.sockw);
  133. close (SUP.sockr);
  134. SUP.sockw = SUP.sockr = -1;
  135. }
  136. g_free (SUP.host);
  137. g_free (SUP.user);
  138. g_free (SUP.cwdir);
  139. g_free (SUP.password);
  140. }
  141. static void
  142. fish_pipeopen(struct vfs_s_super *super, const char *path, const char *argv[])
  143. {
  144. int fileset1[2], fileset2[2];
  145. int res;
  146. if ((pipe(fileset1)<0) || (pipe(fileset2)<0))
  147. vfs_die("Cannot pipe(): %m.");
  148. if ((res = fork())) {
  149. if (res<0) vfs_die("Cannot fork(): %m.");
  150. /* We are the parent */
  151. close(fileset1[0]);
  152. SUP.sockw = fileset1[1];
  153. close(fileset2[1]);
  154. SUP.sockr = fileset2[0];
  155. } else {
  156. close(0);
  157. dup(fileset1[0]);
  158. close(fileset1[0]); close(fileset1[1]);
  159. close(1); close(2);
  160. dup(fileset2[1]);
  161. /* stderr to /dev/null */
  162. open ("/dev/null", O_WRONLY);
  163. close(fileset2[0]); close(fileset2[1]);
  164. execvp(path, const_cast(char **, argv));
  165. _exit(3);
  166. }
  167. }
  168. /* The returned directory should always contain a trailing slash */
  169. static char *fish_getcwd(struct vfs_class *me, struct vfs_s_super *super)
  170. {
  171. if (fish_command (me, super, WANT_STRING, "#PWD\npwd; echo '### 200'\n") == COMPLETE)
  172. return g_strconcat (reply_str, "/", (char *) NULL);
  173. ERRNOR (EIO, NULL);
  174. }
  175. static int
  176. fish_open_archive_int (struct vfs_class *me, struct vfs_s_super *super)
  177. {
  178. {
  179. const char *argv[10];
  180. const char *xsh = (SUP.flags == FISH_FLAG_RSH ? "rsh" : "ssh");
  181. int i = 0;
  182. argv[i++] = xsh;
  183. if (SUP.flags == FISH_FLAG_COMPRESSED)
  184. argv[i++] = "-C";
  185. argv[i++] = "-l";
  186. argv[i++] = SUP.user;
  187. argv[i++] = SUP.host;
  188. argv[i++] = "echo FISH:; /bin/sh";
  189. argv[i++] = NULL;
  190. fish_pipeopen (super, xsh, argv);
  191. }
  192. {
  193. char answer[2048];
  194. print_vfs_message (_("fish: Waiting for initial line..."));
  195. if (!vfs_s_get_line (me, SUP.sockr, answer, sizeof (answer), ':'))
  196. ERRNOR (E_PROTO, -1);
  197. print_vfs_message ("%s", answer);
  198. if (strstr (answer, "assword")) {
  199. /* Currently, this does not work. ssh reads passwords from
  200. /dev/tty, not from stdin :-(. */
  201. message (1, MSG_ERROR,
  202. _
  203. ("Sorry, we cannot do password authenticated connections for now."));
  204. ERRNOR (EPERM, -1);
  205. if (!SUP.password) {
  206. char *p, *op;
  207. p = g_strconcat (_(" fish: Password required for "),
  208. SUP.user, " ", (char *) NULL);
  209. op = vfs_get_password (p);
  210. g_free (p);
  211. if (op == NULL)
  212. ERRNOR (EPERM, -1);
  213. SUP.password = op;
  214. }
  215. print_vfs_message (_("fish: Sending password..."));
  216. write (SUP.sockw, SUP.password, strlen (SUP.password));
  217. write (SUP.sockw, "\n", 1);
  218. }
  219. }
  220. print_vfs_message (_("fish: Sending initial line..."));
  221. /*
  222. * Run `start_fish_server'. If it doesn't exist - no problem,
  223. * we'll talk directly to the shell.
  224. */
  225. if (fish_command
  226. (me, super, WAIT_REPLY,
  227. "#FISH\necho; start_fish_server 2>&1; echo '### 200'\n") !=
  228. COMPLETE)
  229. ERRNOR (E_PROTO, -1);
  230. print_vfs_message (_("fish: Handshaking version..."));
  231. if (fish_command
  232. (me, super, WAIT_REPLY,
  233. "#VER 0.0.0\necho '### 000'\n") != COMPLETE)
  234. ERRNOR (E_PROTO, -1);
  235. /* Set up remote locale to C, otherwise dates cannot be recognized */
  236. if (fish_command
  237. (me, super, WAIT_REPLY,
  238. "LANG=C; LC_ALL=C; LC_TIME=C\n"
  239. "export LANG; export LC_ALL; export LC_TIME\n" "echo '### 200'\n")
  240. != COMPLETE)
  241. ERRNOR (E_PROTO, -1);
  242. print_vfs_message (_("fish: Setting up current directory..."));
  243. SUP.cwdir = fish_getcwd (me, super);
  244. print_vfs_message (_("fish: Connected, home %s."), SUP.cwdir);
  245. #if 0
  246. super->name =
  247. g_strconcat ("/#sh:", SUP.user, "@", SUP.host, "/", (char *) NULL);
  248. #endif
  249. super->name = g_strdup (PATH_SEP_STR);
  250. super->root =
  251. vfs_s_new_inode (me, super,
  252. vfs_s_default_stat (me, S_IFDIR | 0755));
  253. return 0;
  254. }
  255. static int
  256. fish_open_archive (struct vfs_class *me, struct vfs_s_super *super,
  257. const char *archive_name, char *op)
  258. {
  259. char *host, *user, *password, *p;
  260. int flags;
  261. (void) archive_name;
  262. p = vfs_split_url (strchr (op, ':') + 1, &host, &user, &flags,
  263. &password, 0, URL_NOSLASH);
  264. g_free (p);
  265. SUP.host = host;
  266. SUP.user = user;
  267. SUP.flags = flags;
  268. if (!strncmp (op, "rsh:", 4))
  269. SUP.flags |= FISH_FLAG_RSH;
  270. SUP.cwdir = NULL;
  271. if (password)
  272. SUP.password = password;
  273. return fish_open_archive_int (me, super);
  274. }
  275. static int
  276. fish_archive_same (struct vfs_class *me, struct vfs_s_super *super,
  277. const char *archive_name, char *op, void *cookie)
  278. {
  279. char *host, *user;
  280. int flags;
  281. (void) me;
  282. (void) archive_name;
  283. (void) cookie;
  284. op = vfs_split_url (strchr (op, ':') + 1, &host, &user, &flags, 0, 0,
  285. URL_NOSLASH);
  286. g_free (op);
  287. flags = ((strcmp (host, SUP.host) == 0)
  288. && (strcmp (user, SUP.user) == 0) && (flags == SUP.flags));
  289. g_free (host);
  290. g_free (user);
  291. return flags;
  292. }
  293. static int
  294. fish_dir_load(struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path)
  295. {
  296. struct vfs_s_super *super = dir->super;
  297. char buffer[8192];
  298. struct vfs_s_entry *ent = NULL;
  299. FILE *logfile;
  300. char *quoted_path;
  301. int reply_code;
  302. #if 0
  303. /*
  304. * Simple FISH debug interface :]
  305. */
  306. if (!(MEDATA->logfile))
  307. {
  308. MEDATA->logfile = fopen("/tmp/mc-FISH.sh", "w");
  309. }
  310. #endif // 0
  311. logfile = MEDATA->logfile;
  312. print_vfs_message(_("fish: Reading directory %s..."), remote_path);
  313. gettimeofday(&dir->timestamp, NULL);
  314. dir->timestamp.tv_sec += fish_directory_timeout;
  315. quoted_path = shell_escape (remote_path);
  316. fish_command (me, super, NONE,
  317. "#LIST /%s\n"
  318. "if `perl -v > /dev/null 2>&1` ; then\n"
  319. "perl -e '\n"
  320. "use strict;\n"
  321. "use POSIX;\n"
  322. "use Fcntl;\n"
  323. "use POSIX \":fcntl_h\"; #S_ISLNK was here until 5.6\n"
  324. "import Fcntl \":mode\" unless defined &S_ISLNK; #and is now here\n"
  325. "my $dirname = $ARGV[0];\n"
  326. "if (opendir ( DIR, $dirname )) {\n"
  327. "while( (my $filename = readdir(DIR))){\n"
  328. "my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = lstat(\"$dirname/$filename\");\n"
  329. "my $mloctime= scalar localtime $mtime;\n"
  330. "my $shell_escape_regex= s/([;<>\\*\\|`&\\$!#\\(\\)\\[\\]\\{\\}:'\\''\"\\ \\\\])/\\\\$1/g;\n"
  331. "my $e_filename = $filename;\n"
  332. "$e_filename =~ $shell_escape_regex;\n"
  333. "if (S_ISLNK($mode) ) {\n"
  334. "my $linkname = readlink (\"$dirname/$filename\");\n"
  335. "$linkname =~ $shell_escape_regex;\n"
  336. "\n"
  337. "printf(\"R%%o %%o $uid.$gid\\n"
  338. "S$size\\n"
  339. "d$mloctime\\n"
  340. ":\\\"$e_filename\\\" -> \\\"$linkname\\\"\\n"
  341. "\\n\", S_IMODE($mode), S_IFMT($mode));\n"
  342. "} else {\n"
  343. "printf(\"R%%o %%o $uid.$gid\\n"
  344. "S$size\\n"
  345. "d$mloctime\\n"
  346. ":\\\"$e_filename\\\"\\n"
  347. "\\n\", S_IMODE($mode), S_IFMT($mode));\n"
  348. "}}\n"
  349. "printf(\"### 200\\n\");\n"
  350. "closedir(DIR);\n"
  351. "} else {\n"
  352. "printf(\"### 500\\n\");\n"
  353. "}\n"
  354. "exit 0\n"
  355. "' /%s ||\n" /* ARGV[0] - path to browse */
  356. " echo '### 500'\n" /* do not hang if perl is to eval it */
  357. "elif `ls -1 /%s >/dev/null 2>&1` ; then\n"
  358. "if `ls -Q /%s >/dev/null 2>&1`; then\n"
  359. "LSOPT=\"-Qlan\";\n"
  360. "ADD=0;\n"
  361. "else\n"
  362. "LSOPT=\"-lan\";\n"
  363. "ADD=1;\n"
  364. "fi\n"
  365. "ls $LSOPT \"/%s\" 2>/dev/null | grep '^[^cbt]' | (\n"
  366. "while read p l u g s m d y n; do\n"
  367. "if [ $ADD = 0 ]; then\n"
  368. "echo \"P$p $u.$g\nS$s\nd$m $d $y\n:$n\n\"\n"
  369. "elif `sed --version >/dev/null 2>&1` ; then\n"
  370. "file=`echo $n | sed -e 's#^\\(.*\\) -> \\(.*\\)#\\1\" -> \"\\2#'`\n"
  371. "echo \"P$p $u $g\nS$s\nd$m $d $y\n:\"$file\"\n\"\n"
  372. "else\n"
  373. "echo \"P$p $u $g\nS$s\nd$m $d $y\n:\"$n\"\n\"\n"
  374. "fi\n"
  375. "done )\n"
  376. "ls $LSOPT /%s 2>/dev/null | grep '^[cb]' | (\n"
  377. "while read p l u g a i m d y n; do\n"
  378. "if [ $ADD = 0 ]; then\n"
  379. "echo \"P$p $u.$g\nE$a$i\nd$m $d $y\n:$n\n\"\n"
  380. "elif `sed --version >/dev/null 2>&1` ; then\n"
  381. "file=`echo $n | sed -e 's#^\\(.*\\) -> \\(.*\\)#\\1\" -> \"\\2#'`\n"
  382. "echo \"P$p $u $g\nS$s\nd$m $d $y\n:\"$file\"\n\"\n"
  383. "else\n"
  384. "echo \"P$p $u $g\nS$s\nd$m $d $y\n:\"$n\"\n\"\n"
  385. "fi\n"
  386. "done)\n"
  387. "echo '### 200'\n"
  388. "else\n"
  389. "echo '### 500'\n"
  390. "fi\n",
  391. quoted_path, quoted_path, quoted_path, quoted_path, quoted_path, quoted_path);
  392. g_free (quoted_path);
  393. ent = vfs_s_generate_entry(me, NULL, dir, 0);
  394. while (1) {
  395. int res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), SUP.sockr);
  396. if ((!res) || (res == EINTR)) {
  397. vfs_s_free_entry(me, ent);
  398. me->verrno = ECONNRESET;
  399. goto error;
  400. }
  401. if (logfile) {
  402. fputs (buffer, logfile);
  403. fputs ("\n", logfile);
  404. fflush (logfile);
  405. }
  406. if (!strncmp(buffer, "### ", 4))
  407. break;
  408. if ((!buffer[0])) {
  409. if (ent->name) {
  410. vfs_s_insert_entry(me, dir, ent);
  411. ent = vfs_s_generate_entry(me, NULL, dir, 0);
  412. }
  413. continue;
  414. }
  415. #define ST ent->ino->st
  416. switch(buffer[0]) {
  417. case ':': {
  418. char *data_start = buffer+1;
  419. char *filename = data_start;
  420. char *linkname = data_start;
  421. char *filename_bound = filename + strlen(filename);
  422. char *linkname_bound = filename_bound;
  423. if (!strcmp(data_start, "\".\"") || !strcmp(data_start, "\"..\""))
  424. break; /* We'll do "." and ".." ourselves */
  425. if (S_ISLNK(ST.st_mode)) {
  426. // we expect: "escaped-name" -> "escaped-name"
  427. // -> cannot occur in filenames,
  428. // because it will be escaped to -\>
  429. if (*filename == '"')
  430. ++filename;
  431. linkname = strstr(filename, "\" -> \"");
  432. if (!linkname)
  433. {
  434. /* broken client, or smth goes wrong */
  435. linkname = filename_bound;
  436. if (filename_bound > filename
  437. && *(filename_bound - 1) == '"')
  438. --filename_bound; // skip trailing "
  439. }
  440. else
  441. {
  442. filename_bound = linkname;
  443. linkname += 6; // strlen ("\" -> \"")
  444. if (*(linkname_bound - 1) == '"')
  445. --linkname_bound; // skip trailing "
  446. }
  447. ent->name = str_dup_range(filename, filename_bound);
  448. shell_unescape(ent->name);
  449. ent->ino->linkname = str_dup_range(linkname, linkname_bound);
  450. shell_unescape(ent->ino->linkname);
  451. } else {
  452. // we expect: "escaped-name"
  453. if (filename_bound - filename > 2)
  454. {
  455. // there is at least 2 "
  456. // and we skip them
  457. if (*filename == '"')
  458. ++filename;
  459. if (*(filename_bound - 1) == '"')
  460. --filename_bound;
  461. }
  462. ent->name = str_dup_range(filename, filename_bound);
  463. shell_unescape(ent->name);
  464. }
  465. break;
  466. }
  467. case 'S':
  468. #ifdef HAVE_ATOLL
  469. ST.st_size = (off_t) atoll (buffer+1);
  470. #else
  471. ST.st_size = (off_t) atof (buffer+1);
  472. #endif
  473. break;
  474. case 'P': {
  475. size_t skipped;
  476. vfs_parse_filemode (buffer + 1, &skipped, &ST.st_mode);
  477. break;
  478. }
  479. case 'R': {
  480. // raw filemode:
  481. // we expect: Roctal-filemode octal-filetype uid.gid
  482. size_t skipped;
  483. vfs_parse_raw_filemode (buffer + 1, &skipped, &ST.st_mode);
  484. break;
  485. }
  486. case 'd': {
  487. vfs_split_text(buffer+1);
  488. if (!vfs_parse_filedate(0, &ST.st_ctime))
  489. break;
  490. ST.st_atime = ST.st_mtime = ST.st_ctime;
  491. }
  492. break;
  493. case 'D': {
  494. struct tm tim;
  495. if (sscanf(buffer+1, "%d %d %d %d %d %d", &tim.tm_year, &tim.tm_mon,
  496. &tim.tm_mday, &tim.tm_hour, &tim.tm_min, &tim.tm_sec) != 6)
  497. break;
  498. ST.st_atime = ST.st_mtime = ST.st_ctime = mktime(&tim);
  499. }
  500. break;
  501. case 'E': {
  502. int maj, min;
  503. if (sscanf(buffer+1, "%d,%d", &maj, &min) != 2)
  504. break;
  505. #ifdef HAVE_STRUCT_STAT_ST_RDEV
  506. ST.st_rdev = makedev (maj, min);
  507. #endif
  508. }
  509. }
  510. }
  511. vfs_s_free_entry (me, ent);
  512. reply_code = fish_decode_reply(buffer + 4, 0);
  513. if (reply_code == COMPLETE) {
  514. g_free (SUP.cwdir);
  515. SUP.cwdir = g_strdup (remote_path);
  516. print_vfs_message (_("%s: done."), me->name);
  517. return 0;
  518. } else if (reply_code == ERROR) {
  519. me->verrno = EACCES;
  520. } else {
  521. me->verrno = E_REMOTE;
  522. }
  523. error:
  524. print_vfs_message (_("%s: failure"), me->name);
  525. return -1;
  526. }
  527. static int
  528. fish_file_store(struct vfs_class *me, struct vfs_s_fh *fh, char *name, char *localname)
  529. {
  530. struct vfs_s_super *super = FH_SUPER;
  531. int n, total;
  532. char buffer[8192];
  533. struct stat s;
  534. int was_error = 0;
  535. int h;
  536. char *quoted_name;
  537. h = open (localname, O_RDONLY);
  538. if (h == -1)
  539. ERRNOR (EIO, -1);
  540. if (fstat(h, &s)<0) {
  541. close (h);
  542. ERRNOR (EIO, -1);
  543. }
  544. /* First, try this as stor:
  545. *
  546. * ( head -c number ) | ( cat > file; cat >/dev/null )
  547. *
  548. * If `head' is not present on the remote system, `dd' will be used.
  549. * Unfortunately, we cannot trust most non-GNU `head' implementations
  550. * even if `-c' options is supported. Therefore, we separate GNU head
  551. * (and other modern heads?) using `-q' and `-' . This causes another
  552. * implementations to fail (because of "incorrect options").
  553. *
  554. * Fallback is:
  555. *
  556. * rest=<number>
  557. * while [ $rest -gt 0 ]
  558. * do
  559. * cnt=`expr \( $rest + 255 \) / 256`
  560. * n=`dd bs=256 count=$cnt | tee -a <target_file> | wc -c`
  561. * rest=`expr $rest - $n`
  562. * done
  563. *
  564. * `dd' was not designed for full filling of input buffers,
  565. * and does not report exact number of bytes (not blocks).
  566. * Therefore a more complex shell script is needed.
  567. *
  568. * On some systems non-GNU head writes "Usage:" error report to stdout
  569. * instead of stderr. It makes impossible the use of "head || dd"
  570. * algorithm for file appending case, therefore just "dd" is used for it.
  571. */
  572. quoted_name = shell_escape(name);
  573. print_vfs_message(_("fish: store %s: sending command..."), quoted_name );
  574. /* FIXME: File size is limited to ULONG_MAX */
  575. if (!fh->u.fish.append)
  576. n = fish_command (me, super, WAIT_REPLY,
  577. "#STOR %lu /%s\n"
  578. "echo '### 001'\n"
  579. "file=/%s\n"
  580. "res=`exec 3>&1\n"
  581. "(\n"
  582. "head -c %lu -q - || echo DD >&3\n"
  583. ") 2>/dev/null | (\n"
  584. "cat > $file\n"
  585. "cat > /dev/null\n"
  586. ")`; [ \"$res\" = DD ] && {\n"
  587. "> \"$file\"\n"
  588. "rest=%lu\n"
  589. "while [ $rest -gt 0 ]\n"
  590. "do\n"
  591. " cnt=`expr \\( $rest + 255 \\) / 256`\n"
  592. " n=`dd bs=256 count=$cnt | tee -a \"$file\" | wc -c`\n"
  593. " rest=`expr $rest - $n`\n"
  594. "done\n"
  595. "}; echo '### 200'\n",
  596. (unsigned long) s.st_size, quoted_name,
  597. quoted_name, (unsigned long) s.st_size,
  598. (unsigned long) s.st_size);
  599. else
  600. n = fish_command (me, super, WAIT_REPLY,
  601. "#STOR %lu /%s\n"
  602. "echo '### 001'\n"
  603. "{\n"
  604. "file=/%s\n"
  605. "rest=%lu\n"
  606. "while [ $rest -gt 0 ]\n"
  607. "do\n"
  608. " cnt=`expr \\( $rest + 255 \\) / 256`\n"
  609. " n=`dd bs=256 count=$cnt | tee -a $file | wc -c`\n"
  610. " rest=`expr $rest - $n`\n"
  611. "done\n"
  612. "}; echo '### 200'\n",
  613. (unsigned long) s.st_size, quoted_name,
  614. quoted_name, (unsigned long) s.st_size);
  615. if (n != PRELIM) {
  616. close (h);
  617. ERRNOR(E_REMOTE, -1);
  618. }
  619. total = 0;
  620. while (1) {
  621. int t;
  622. while ((n = read(h, buffer, sizeof(buffer))) < 0) {
  623. if ((errno == EINTR) && got_interrupt())
  624. continue;
  625. print_vfs_message(_("fish: Local read failed, sending zeros") );
  626. close(h);
  627. h = open( "/dev/zero", O_RDONLY );
  628. }
  629. if (n == 0)
  630. break;
  631. if ((t = write (SUP.sockw, buffer, n)) != n) {
  632. if (t == -1) {
  633. me->verrno = errno;
  634. } else {
  635. me->verrno = EIO;
  636. }
  637. goto error_return;
  638. }
  639. disable_interrupt_key();
  640. total += n;
  641. print_vfs_message(_("fish: storing %s %d (%lu)"),
  642. was_error ? _("zeros") : _("file"), total,
  643. (unsigned long) s.st_size);
  644. }
  645. close(h);
  646. g_free(quoted_name);
  647. if ((fish_get_reply (me, SUP.sockr, NULL, 0) != COMPLETE) || was_error)
  648. ERRNOR (E_REMOTE, -1);
  649. return 0;
  650. error_return:
  651. close(h);
  652. fish_get_reply(me, SUP.sockr, NULL, 0);
  653. g_free(quoted_name);
  654. return -1;
  655. }
  656. static int
  657. fish_linear_start (struct vfs_class *me, struct vfs_s_fh *fh, off_t offset)
  658. {
  659. char *name;
  660. char *quoted_name;
  661. if (offset)
  662. ERRNOR (E_NOTSUPP, 0);
  663. name = vfs_s_fullpath (me, fh->ino);
  664. if (!name)
  665. return 0;
  666. quoted_name = shell_escape(name);
  667. fh->u.fish.append = 0;
  668. /*
  669. * Check whether the remote file is readable by using `dd' to copy
  670. * a single byte from the remote file to /dev/null. If `dd' completes
  671. * with exit status of 0 use `cat' to send the file contents to the
  672. * standard output (i.e. over the network).
  673. */
  674. offset = fish_command (me, FH_SUPER, WANT_STRING,
  675. "#RETR /%s\n"
  676. "if dd if=/%s of=/dev/null bs=1 count=1 2>/dev/null ;\n"
  677. "then\n"
  678. "ls -ln /%s 2>/dev/null | (\n"
  679. "read p l u g s r\n"
  680. "echo $s\n"
  681. ")\n"
  682. "echo '### 100'\n"
  683. "cat /%s\n"
  684. "echo '### 200'\n"
  685. "else\n"
  686. "echo '### 500'\n"
  687. "fi\n",
  688. quoted_name, quoted_name, quoted_name, quoted_name );
  689. g_free (quoted_name);
  690. if (offset != PRELIM) ERRNOR (E_REMOTE, 0);
  691. fh->linear = LS_LINEAR_OPEN;
  692. fh->u.fish.got = 0;
  693. errno = 0;
  694. #if SIZEOF_OFF_T == SIZEOF_LONG
  695. fh->u.fish.total = strtol (reply_str, NULL, 10);
  696. #else
  697. fh->u.fish.total = strtoll (reply_str, NULL, 10);
  698. #endif
  699. if (errno != 0)
  700. ERRNOR (E_REMOTE, 0);
  701. return 1;
  702. }
  703. static void
  704. fish_linear_abort (struct vfs_class *me, struct vfs_s_fh *fh)
  705. {
  706. struct vfs_s_super *super = FH_SUPER;
  707. char buffer[8192];
  708. int n;
  709. print_vfs_message( _("Aborting transfer...") );
  710. do {
  711. n = MIN(8192, fh->u.fish.total - fh->u.fish.got);
  712. if (n) {
  713. if ((n = read(SUP.sockr, buffer, n)) < 0)
  714. return;
  715. fh->u.fish.got += n;
  716. }
  717. } while (n);
  718. if (fish_get_reply (me, SUP.sockr, NULL, 0) != COMPLETE)
  719. print_vfs_message( _("Error reported after abort.") );
  720. else
  721. print_vfs_message( _("Aborted transfer would be successful.") );
  722. }
  723. static int
  724. fish_linear_read (struct vfs_class *me, struct vfs_s_fh *fh, void *buf, int len)
  725. {
  726. struct vfs_s_super *super = FH_SUPER;
  727. int n = 0;
  728. len = MIN( fh->u.fish.total - fh->u.fish.got, len );
  729. disable_interrupt_key();
  730. while (len && ((n = read (SUP.sockr, buf, len))<0)) {
  731. if ((errno == EINTR) && !got_interrupt())
  732. continue;
  733. break;
  734. }
  735. enable_interrupt_key();
  736. if (n>0) fh->u.fish.got += n;
  737. if (n<0) fish_linear_abort(me, fh);
  738. if ((!n) && ((fish_get_reply (me, SUP.sockr, NULL, 0) != COMPLETE)))
  739. ERRNOR (E_REMOTE, -1);
  740. ERRNOR (errno, n);
  741. }
  742. static void
  743. fish_linear_close (struct vfs_class *me, struct vfs_s_fh *fh)
  744. {
  745. if (fh->u.fish.total != fh->u.fish.got)
  746. fish_linear_abort(me, fh);
  747. }
  748. static int
  749. fish_ctl (void *fh, int ctlop, void *arg)
  750. {
  751. (void) arg;
  752. return 0;
  753. #if 0
  754. switch (ctlop) {
  755. case VFS_CTL_IS_NOTREADY:
  756. {
  757. int v;
  758. if (!FH->linear)
  759. vfs_die ("You may not do this");
  760. if (FH->linear == LS_LINEAR_CLOSED || FH->linear == LS_LINEAR_PREOPEN)
  761. return 0;
  762. v = vfs_s_select_on_two (FH_SUPER->u.fish.sockr, 0);
  763. if (((v < 0) && (errno == EINTR)) || v == 0)
  764. return 1;
  765. return 0;
  766. }
  767. default:
  768. return 0;
  769. }
  770. #endif
  771. }
  772. static int
  773. fish_send_command(struct vfs_class *me, struct vfs_s_super *super, const char *cmd, int flags)
  774. {
  775. int r;
  776. r = fish_command (me, super, WAIT_REPLY, "%s", cmd);
  777. vfs_stamp_create (&vfs_fish_ops, super);
  778. if (r != COMPLETE) ERRNOR (E_REMOTE, -1);
  779. if (flags & OPT_FLUSH)
  780. vfs_s_invalidate(me, super);
  781. return 0;
  782. }
  783. #define PREFIX \
  784. char buf[BUF_LARGE]; \
  785. const char *crpath; \
  786. char *rpath, *mpath = g_strdup (path); \
  787. struct vfs_s_super *super; \
  788. if (!(crpath = vfs_s_get_path_mangle (me, mpath, &super, 0))) { \
  789. g_free (mpath); \
  790. return -1; \
  791. } \
  792. rpath = shell_escape(crpath); \
  793. g_free (mpath);
  794. #define POSTFIX(flags) \
  795. g_free (rpath); \
  796. return fish_send_command(me, super, buf, flags);
  797. static int
  798. fish_chmod (struct vfs_class *me, const char *path, int mode)
  799. {
  800. PREFIX
  801. g_snprintf(buf, sizeof(buf), "#CHMOD %4.4o /%s\n"
  802. "chmod %4.4o /%s 2>/dev/null\n"
  803. "echo '### 000'\n",
  804. mode & 07777, rpath,
  805. mode & 07777, rpath);
  806. POSTFIX(OPT_FLUSH);
  807. }
  808. #define FISH_OP(name, string) \
  809. static int fish_##name (struct vfs_class *me, const char *path1, const char *path2) \
  810. { \
  811. char buf[BUF_LARGE]; \
  812. const char *crpath1, *crpath2; \
  813. char *rpath1, *rpath2, *mpath1, *mpath2; \
  814. struct vfs_s_super *super1, *super2; \
  815. if (!(crpath1 = vfs_s_get_path_mangle (me, mpath1 = g_strdup(path1), &super1, 0))) { \
  816. g_free (mpath1); \
  817. return -1; \
  818. } \
  819. if (!(crpath2 = vfs_s_get_path_mangle (me, mpath2 = g_strdup(path2), &super2, 0))) { \
  820. g_free (mpath1); \
  821. g_free (mpath2); \
  822. return -1; \
  823. } \
  824. rpath1 = shell_escape (crpath1); \
  825. g_free (mpath1); \
  826. rpath2 = shell_escape (crpath2); \
  827. g_free (mpath2); \
  828. g_snprintf(buf, sizeof(buf), string "\n", rpath1, rpath2, rpath1, rpath2); \
  829. g_free (rpath1); \
  830. g_free (rpath2); \
  831. return fish_send_command(me, super2, buf, OPT_FLUSH); \
  832. }
  833. FISH_OP(rename, "#RENAME /%s /%s\n"
  834. "mv /%s /%s 2>/dev/null\n"
  835. "echo '### 000'" )
  836. FISH_OP(link, "#LINK /%s /%s\n"
  837. "ln /%s /%s 2>/dev/null\n"
  838. "echo '### 000'" )
  839. static int fish_symlink (struct vfs_class *me, const char *setto, const char *path)
  840. {
  841. char *qsetto;
  842. PREFIX
  843. qsetto = shell_escape (setto);
  844. g_snprintf(buf, sizeof(buf),
  845. "#SYMLINK %s /%s\n"
  846. "ln -s %s /%s 2>/dev/null\n"
  847. "echo '### 000'\n",
  848. qsetto, rpath, qsetto, rpath);
  849. g_free (qsetto);
  850. POSTFIX(OPT_FLUSH);
  851. }
  852. static int
  853. fish_chown (struct vfs_class *me, const char *path, int owner, int group)
  854. {
  855. char *sowner, *sgroup;
  856. struct passwd *pw;
  857. struct group *gr;
  858. if ((pw = getpwuid (owner)) == NULL)
  859. return 0;
  860. if ((gr = getgrgid (group)) == NULL)
  861. return 0;
  862. sowner = pw->pw_name;
  863. sgroup = gr->gr_name;
  864. {
  865. PREFIX
  866. g_snprintf (buf, sizeof(buf),
  867. "#CHOWN %s /%s\n"
  868. "chown %s /%s 2>/dev/null\n"
  869. "echo '### 000'\n",
  870. sowner, rpath,
  871. sowner, rpath);
  872. fish_send_command (me, super, buf, OPT_FLUSH);
  873. /* FIXME: what should we report if chgrp succeeds but chown fails? */
  874. g_snprintf (buf, sizeof(buf),
  875. "#CHGRP /%s /%s\n"
  876. "chgrp %s /%s 2>/dev/null\n"
  877. "echo '### 000'\n",
  878. sgroup, rpath,
  879. sgroup, rpath);
  880. /* fish_send_command(me, super, buf, OPT_FLUSH); */
  881. POSTFIX (OPT_FLUSH)
  882. }
  883. }
  884. static int fish_unlink (struct vfs_class *me, const char *path)
  885. {
  886. PREFIX
  887. g_snprintf(buf, sizeof(buf),
  888. "#DELE /%s\n"
  889. "rm -f /%s 2>/dev/null\n"
  890. "echo '### 000'\n",
  891. rpath, rpath);
  892. POSTFIX(OPT_FLUSH);
  893. }
  894. static int fish_mkdir (struct vfs_class *me, const char *path, mode_t mode)
  895. {
  896. PREFIX
  897. (void) mode;
  898. g_snprintf(buf, sizeof(buf),
  899. "#MKD /%s\n"
  900. "mkdir /%s 2>/dev/null\n"
  901. "echo '### 000'\n",
  902. rpath, rpath);
  903. POSTFIX(OPT_FLUSH);
  904. }
  905. static int fish_rmdir (struct vfs_class *me, const char *path)
  906. {
  907. PREFIX
  908. g_snprintf(buf, sizeof(buf),
  909. "#RMD /%s\n"
  910. "rmdir /%s 2>/dev/null\n"
  911. "echo '### 000'\n",
  912. rpath, rpath);
  913. POSTFIX(OPT_FLUSH);
  914. }
  915. static int
  916. fish_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags,
  917. int mode)
  918. {
  919. (void) mode;
  920. fh->u.fish.append = 0;
  921. /* File will be written only, so no need to retrieve it */
  922. if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY | O_RDWR))) {
  923. fh->u.fish.append = flags & O_APPEND;
  924. if (!fh->ino->localname) {
  925. int tmp_handle =
  926. vfs_mkstemps (&fh->ino->localname, me->name,
  927. fh->ino->ent->name);
  928. if (tmp_handle == -1)
  929. return -1;
  930. close (tmp_handle);
  931. }
  932. return 0;
  933. }
  934. if (!fh->ino->localname)
  935. if (vfs_s_retrieve_file (me, fh->ino) == -1)
  936. return -1;
  937. if (!fh->ino->localname)
  938. vfs_die ("retrieve_file failed to fill in localname");
  939. return 0;
  940. }
  941. static void
  942. fish_fill_names (struct vfs_class *me, fill_names_f func)
  943. {
  944. struct vfs_s_super *super = MEDATA->supers;
  945. const char *flags;
  946. char *name;
  947. while (super){
  948. switch (SUP.flags & (FISH_FLAG_RSH | FISH_FLAG_COMPRESSED)) {
  949. case FISH_FLAG_RSH:
  950. flags = ":r";
  951. break;
  952. case FISH_FLAG_COMPRESSED:
  953. flags = ":C";
  954. break;
  955. case FISH_FLAG_RSH | FISH_FLAG_COMPRESSED:
  956. flags = "";
  957. break;
  958. default:
  959. flags = "";
  960. break;
  961. }
  962. name = g_strconcat ("/#sh:", SUP.user, "@", SUP.host, flags,
  963. "/", SUP.cwdir, (char *) NULL);
  964. (*func)(name);
  965. g_free (name);
  966. super = super->next;
  967. }
  968. }
  969. void
  970. init_fish (void)
  971. {
  972. static struct vfs_s_subclass fish_subclass;
  973. fish_subclass.flags = VFS_S_REMOTE;
  974. fish_subclass.archive_same = fish_archive_same;
  975. fish_subclass.open_archive = fish_open_archive;
  976. fish_subclass.free_archive = fish_free_archive;
  977. fish_subclass.fh_open = fish_fh_open;
  978. fish_subclass.dir_load = fish_dir_load;
  979. fish_subclass.file_store = fish_file_store;
  980. fish_subclass.linear_start = fish_linear_start;
  981. fish_subclass.linear_read = fish_linear_read;
  982. fish_subclass.linear_close = fish_linear_close;
  983. vfs_s_init_class (&vfs_fish_ops, &fish_subclass);
  984. vfs_fish_ops.name = "fish";
  985. vfs_fish_ops.prefix = "sh:";
  986. vfs_fish_ops.fill_names = fish_fill_names;
  987. vfs_fish_ops.chmod = fish_chmod;
  988. vfs_fish_ops.chown = fish_chown;
  989. vfs_fish_ops.symlink = fish_symlink;
  990. vfs_fish_ops.link = fish_link;
  991. vfs_fish_ops.unlink = fish_unlink;
  992. vfs_fish_ops.rename = fish_rename;
  993. vfs_fish_ops.mkdir = fish_mkdir;
  994. vfs_fish_ops.rmdir = fish_rmdir;
  995. vfs_fish_ops.ctl = fish_ctl;
  996. vfs_register_class (&vfs_fish_ops);
  997. }