fish.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850
  1. /* Virtual File System: FISH implementation for transfering files over
  2. shell connections.
  3. Copyright (C) 1998 The Free Software Foundation
  4. Written by: 1998 Pavel Machek
  5. Spaces fix: 2000 Michal Svec
  6. $Id$
  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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. #undef HAVE_HACKED_SSH
  31. #include "utilvfs.h"
  32. #include "../src/dialog.h" /* For MSG_ERROR */
  33. #include "xdirentry.h"
  34. #include "vfs.h"
  35. #include "tcputil.h"
  36. #include "container.h"
  37. #include "fish.h"
  38. /*
  39. * Reply codes.
  40. */
  41. #define PRELIM 1 /* positive preliminary */
  42. #define COMPLETE 2 /* positive completion */
  43. #define CONTINUE 3 /* positive intermediate */
  44. #define TRANSIENT 4 /* transient negative completion */
  45. #define ERROR 5 /* permanent negative completion */
  46. /* If true, the directory cache is forced to reload */
  47. static int force_expiration = 0;
  48. /* FIXME: prev two variables should be killed */
  49. /* command wait_flag: */
  50. #define NONE 0x00
  51. #define WAIT_REPLY 0x01
  52. #define WANT_STRING 0x02
  53. static char reply_str [80];
  54. static int decode_reply (char *s, int was_garbage)
  55. {
  56. int code;
  57. if (!sscanf(s, "%d", &code)) {
  58. code = 500;
  59. return 5;
  60. }
  61. if (code<100) return was_garbage ? ERROR : (!code ? COMPLETE : PRELIM);
  62. return code / 100;
  63. }
  64. /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
  65. static int get_reply (vfs *me, int sock, char *string_buf, int string_len)
  66. {
  67. char answer[1024];
  68. int was_garbage = 0;
  69. for (;;) {
  70. if (!vfs_s_get_line(me, sock, answer, sizeof(answer), '\n')) {
  71. if (string_buf)
  72. *string_buf = 0;
  73. return 4;
  74. }
  75. if (strncmp(answer, "### ", 4)) {
  76. was_garbage = 1;
  77. if (string_buf) {
  78. strncpy(string_buf, answer, string_len - 1);
  79. *(string_buf + string_len - 1) = 0;
  80. }
  81. } else return decode_reply(answer+4, was_garbage);
  82. }
  83. }
  84. #define SUP super->u.fish
  85. static int command (vfs *me, vfs_s_super *super, int wait_reply, char *fmt, ...)
  86. {
  87. va_list ap;
  88. char *str;
  89. int status;
  90. FILE *logfile = MEDATA->logfile;
  91. va_start (ap, fmt);
  92. str = g_strdup_vprintf (fmt, ap);
  93. va_end (ap);
  94. if (logfile){
  95. fwrite (str, strlen (str), 1, logfile);
  96. fflush (logfile);
  97. }
  98. enable_interrupt_key();
  99. status = write(SUP.sockw, str, strlen(str));
  100. g_free (str);
  101. disable_interrupt_key();
  102. if (status < 0)
  103. return TRANSIENT;
  104. if (wait_reply)
  105. return get_reply (me, SUP.sockr, (wait_reply & WANT_STRING) ? reply_str : NULL, sizeof (reply_str)-1);
  106. return COMPLETE;
  107. }
  108. static void
  109. free_archive (vfs *me, vfs_s_super *super)
  110. {
  111. if ((SUP.sockw != -1) || (SUP.sockr != -1)){
  112. print_vfs_message (_("fish: Disconnecting from %s"), super->name?super->name:"???");
  113. command(me, super, NONE, "#BYE\nexit\n");
  114. close(SUP.sockw);
  115. close(SUP.sockr);
  116. SUP.sockw = SUP.sockr = -1;
  117. }
  118. ifree (SUP.host);
  119. ifree (SUP.home);
  120. ifree (SUP.user);
  121. ifree (SUP.cwdir);
  122. ifree (SUP.password);
  123. }
  124. static void
  125. pipeopen(vfs_s_super *super, char *path, char *argv[])
  126. {
  127. int fileset1[2], fileset2[2];
  128. int res;
  129. if ((pipe(fileset1)<0) || (pipe(fileset2)<0))
  130. vfs_die("Could not pipe(): %m.");
  131. if ((res = fork())) {
  132. if (res<0) vfs_die("Could not fork(): %m.");
  133. /* We are the parent */
  134. close(fileset1[0]);
  135. SUP.sockw = fileset1[1];
  136. close(fileset2[1]);
  137. SUP.sockr = fileset2[0];
  138. } else {
  139. close(0);
  140. dup(fileset1[0]);
  141. close(fileset1[0]); close(fileset1[1]);
  142. close(1); close(2);
  143. dup(fileset2[1]);
  144. /* stderr to /dev/null */
  145. open ("/dev/null", O_WRONLY);
  146. close(fileset2[0]); close(fileset2[1]);
  147. execvp(path, argv);
  148. vfs_die("Exec failed.");
  149. }
  150. }
  151. /* The returned directory should always contain a trailing slash */
  152. static char *fish_getcwd(vfs *me, vfs_s_super *super)
  153. {
  154. if (command(me, super, WANT_STRING, "#PWD\npwd; echo '### 200'\n") == COMPLETE)
  155. return g_strconcat (reply_str, "/", NULL);
  156. ERRNOR (EIO, NULL);
  157. }
  158. static int
  159. open_archive_int (vfs *me, vfs_s_super *super)
  160. {
  161. char *argv[100];
  162. char *xsh = (SUP.flags == FISH_FLAG_RSH ? "rsh" : "ssh");
  163. int i = 0;
  164. argv[i++] = xsh;
  165. #ifdef HAVE_HACKED_SSH
  166. argv[i++] = "-I";
  167. #endif
  168. argv[i++] = "-l";
  169. argv[i++] = SUP.user;
  170. argv[i++] = SUP.host;
  171. if (SUP.flags == FISH_FLAG_COMPRESSED)
  172. argv[i++] = "-C";
  173. argv[i++] = "echo FISH:; /bin/sh";
  174. argv[i++] = NULL;
  175. #if 0
  176. /* Debugging hack */
  177. if (!MEDATA->logfile)
  178. MEDATA->logfile = fopen( "/home/pavel/talk.fish", "w+" ); /* FIXME */
  179. #endif
  180. pipeopen(super, xsh, argv );
  181. {
  182. char answer[2048];
  183. print_vfs_message( _("fish: Waiting for initial line...") );
  184. if (!vfs_s_get_line(me, SUP.sockr, answer, sizeof(answer), ':'))
  185. ERRNOR (E_PROTO, -1);
  186. print_vfs_message( answer );
  187. if (strstr(answer, "assword")) {
  188. /* Currently, this does not work. ssh reads passwords from
  189. /dev/tty, not from stdin :-(. */
  190. #ifndef HAVE_HACKED_SSH
  191. message_1s (1, MSG_ERROR, _("Sorry, we can not do password authenticated connections for now."));
  192. ERRNOR (EPERM, -1);
  193. #endif
  194. if (!SUP.password){
  195. char *p, *op;
  196. p = g_strconcat (_(" fish: Password required for "), SUP.user,
  197. " ", NULL);
  198. op = vfs_get_password (p);
  199. g_free (p);
  200. if (op == NULL)
  201. ERRNOR (EPERM, -1);
  202. SUP.password = g_strdup (op);
  203. wipe_password(op);
  204. }
  205. print_vfs_message( _("fish: Sending password...") );
  206. write(SUP.sockw, SUP.password, strlen(SUP.password));
  207. write(SUP.sockw, "\n", 1);
  208. }
  209. }
  210. print_vfs_message( _("fish: Sending initial line...") );
  211. /*
  212. * Run `start_fish_server'. If it doesn't exist - no problem,
  213. * we'll talk directly to the shell.
  214. */
  215. if (command (me, super, WAIT_REPLY,
  216. "#FISH\necho; start_fish_server 2>&1;"
  217. " echo '### 200'\n") != COMPLETE)
  218. ERRNOR (E_PROTO, -1);
  219. print_vfs_message( _("fish: Handshaking version...") );
  220. if (command (me, super, WAIT_REPLY, "#VER 0.0.0\necho '### 000'\n") != COMPLETE)
  221. ERRNOR (E_PROTO, -1);
  222. print_vfs_message( _("fish: Setting up current directory...") );
  223. SUP.home = fish_getcwd (me, super);
  224. print_vfs_message( _("fish: Connected, home %s."), SUP.home );
  225. #if 0
  226. super->name = g_strconcat ( "/#sh:", SUP.user, "@", SUP.host, "/", NULL );
  227. #endif
  228. super->name = g_strdup(PATH_SEP_STR);
  229. super->root = vfs_s_new_inode (me, super, vfs_s_default_stat(me, S_IFDIR | 0755));
  230. return 0;
  231. }
  232. static int
  233. open_archive (vfs *me, vfs_s_super *super, char *archive_name, char *op)
  234. {
  235. char *host, *user, *password, *p;
  236. int flags;
  237. p = vfs_split_url (strchr(op, ':')+1, &host, &user, &flags, &password, 0, URL_NOSLASH);
  238. if (p)
  239. g_free (p);
  240. SUP.host = host;
  241. SUP.user = user;
  242. SUP.flags = flags;
  243. if (!strncmp( op, "rsh:", 4 ))
  244. SUP.flags |= FISH_FLAG_RSH;
  245. SUP.home = NULL;
  246. if (password)
  247. SUP.password = password;
  248. return open_archive_int (me, super);
  249. }
  250. static int
  251. archive_same(vfs *me, vfs_s_super *super, char *archive_name, char *op, void *cookie)
  252. {
  253. char *host, *user;
  254. int flags;
  255. op = vfs_split_url (strchr(op, ':')+1, &host, &user, &flags, 0, 0, URL_NOSLASH);
  256. if (op)
  257. g_free (op);
  258. flags = ((strcmp (host, SUP.host) == 0) &&
  259. (strcmp (user, SUP.user) == 0) &&
  260. (flags == SUP.flags));
  261. g_free (host);
  262. g_free (user);
  263. return flags;
  264. }
  265. int
  266. fish_which (vfs *me, char *path)
  267. {
  268. if (!strncmp (path, "/#sh:", 5))
  269. return 1;
  270. if (!strncmp (path, "/#ssh:", 6))
  271. return 1;
  272. if (!strncmp (path, "/#rsh:", 6))
  273. return 1;
  274. return 0;
  275. }
  276. static int
  277. dir_uptodate(vfs *me, vfs_s_inode *ino)
  278. {
  279. struct timeval tim;
  280. gettimeofday(&tim, NULL);
  281. if (force_expiration) {
  282. force_expiration = 0;
  283. return 0;
  284. }
  285. if (tim.tv_sec < ino->u.fish.timestamp.tv_sec)
  286. return 1;
  287. return 0;
  288. }
  289. static int
  290. dir_load(vfs *me, vfs_s_inode *dir, char *remote_path)
  291. {
  292. vfs_s_super *super = dir->super;
  293. char buffer[8192];
  294. vfs_s_entry *ent = NULL;
  295. FILE *logfile;
  296. logfile = MEDATA->logfile;
  297. print_vfs_message(_("fish: Reading directory %s..."), remote_path);
  298. gettimeofday(&dir->u.fish.timestamp, NULL);
  299. dir->u.fish.timestamp.tv_sec += 10; /* was 360: 10 is good for
  300. stressing direntry layer a bit */
  301. command(me, super, NONE,
  302. "#LIST /%s\n"
  303. "ls -lLa \"/%s\" 2>/dev/null | grep '^[^cbt]' | (\n"
  304. "while read p x u g s m d y n; do\n"
  305. "echo \"P$p $u.$g\nS$s\nd$m $d $y\n:$n\n\"\n"
  306. "done\n"
  307. ")\n"
  308. "ls -lLa \"/%s\" 2>/dev/null | grep '^[cb]' | (\n"
  309. "while read p x u g a i m d y n; do\n"
  310. "echo \"P$p $u.$g\nE$a$i\nd$m $d $y\n:$n\n\"\n"
  311. "done\n"
  312. ")\n"
  313. "echo '### 200'\n",
  314. remote_path, remote_path, remote_path);
  315. #define SIMPLE_ENTRY vfs_s_generate_entry(me, NULL, dir, 0)
  316. ent = SIMPLE_ENTRY;
  317. while (1) {
  318. int res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), SUP.sockr);
  319. if ((!res) || (res == EINTR)) {
  320. vfs_s_free_entry(me, ent);
  321. me->verrno = ECONNRESET;
  322. goto error;
  323. }
  324. if (logfile) {
  325. fputs (buffer, logfile);
  326. fputs ("\n", logfile);
  327. fflush (logfile);
  328. }
  329. if (!strncmp(buffer, "### ", 4))
  330. break;
  331. if ((!buffer[0])) {
  332. if (ent->name) {
  333. vfs_s_insert_entry(me, dir, ent);
  334. ent = SIMPLE_ENTRY;
  335. }
  336. continue;
  337. }
  338. #define ST ent->ino->st
  339. switch(buffer[0]) {
  340. case ':': {
  341. /* char *c; */
  342. if (!strcmp(buffer+1, ".") || !strcmp(buffer+1, ".."))
  343. break; /* We'll do . and .. ourself */
  344. ent->name = g_strdup(buffer+1);
  345. /* if ((c=strchr(ent->name, ' ')))
  346. *c = 0; / * this is ugly, but we can not handle " " in name */
  347. break;
  348. }
  349. case 'S': ST.st_size = atoi(buffer+1); break;
  350. case 'P': {
  351. int i;
  352. if ((i = vfs_parse_filetype(buffer[1])) ==-1)
  353. break;
  354. ST.st_mode = i;
  355. if ((i = vfs_parse_filemode(buffer+2)) ==-1)
  356. break;
  357. ST.st_mode |= i;
  358. if (S_ISLNK(ST.st_mode))
  359. ST.st_mode = 0;
  360. }
  361. break;
  362. case 'd': {
  363. vfs_split_text(buffer+1);
  364. if (!vfs_parse_filedate(0, &ST.st_ctime))
  365. break;
  366. ST.st_atime = ST.st_mtime = ST.st_ctime;
  367. }
  368. break;
  369. case 'D': {
  370. struct tm tim;
  371. if (sscanf(buffer+1, "%d %d %d %d %d %d", &tim.tm_year, &tim.tm_mon,
  372. &tim.tm_mday, &tim.tm_hour, &tim.tm_min, &tim.tm_sec) != 6)
  373. break;
  374. ST.st_atime = ST.st_mtime = ST.st_ctime = mktime(&tim);
  375. }
  376. break;
  377. case 'E': {
  378. int maj, min;
  379. if (sscanf(buffer+1, "%d,%d", &maj, &min) != 2)
  380. break;
  381. #ifdef HAVE_ST_RDEV
  382. ST.st_rdev = (maj << 8) | min;
  383. #endif
  384. }
  385. case 'L': ent->ino->linkname = g_strdup(buffer+1);
  386. break;
  387. }
  388. }
  389. vfs_s_free_entry (me, ent);
  390. me->verrno = E_REMOTE;
  391. if (decode_reply(buffer+4, 0) == COMPLETE)
  392. return 0;
  393. error:
  394. print_vfs_message(_("fish: failed"));
  395. return 1;
  396. }
  397. static int
  398. file_store(vfs *me, vfs_s_super *super, char *name, char *localname)
  399. {
  400. int n, total;
  401. char buffer[8192];
  402. struct stat s;
  403. int was_error = 0;
  404. int h;
  405. h = open(localname, O_RDONLY);
  406. if (fstat(h, &s)<0)
  407. ERRNOR (EIO, -1);
  408. /* Use this as stor: ( dd block ; dd smallblock ) | ( cat > file; cat > /dev/null ) */
  409. print_vfs_message(_("fish: store %s: sending command..."), name );
  410. if (command (me, super, WAIT_REPLY,
  411. "#STOR %d /%s\n"
  412. "> \"/%s\"\n"
  413. "echo '### 001'\n"
  414. "(\n"
  415. "dd bs=4096 count=%d\n"
  416. "dd bs=%d count=1\n"
  417. ") 2>/dev/null | (\n"
  418. "cat > \"/%s\"\n"
  419. "cat > /dev/null\n"
  420. "); echo '### 200'\n",
  421. /* ")\n" Why can't it be like this?
  422. "echo '### 200'\n", */
  423. s.st_size, name, name,
  424. s.st_size / 4096, s.st_size % 4096, name)
  425. != PRELIM)
  426. ERRNOR(E_REMOTE, -1);
  427. total = 0;
  428. while (1) {
  429. while ((n = read(h, buffer, sizeof(buffer))) < 0) {
  430. if ((errno == EINTR) && got_interrupt)
  431. continue;
  432. print_vfs_message(_("fish: Local read failed, sending zeros") );
  433. close(h);
  434. h = open( "/dev/zero", O_RDONLY );
  435. }
  436. if (n == 0)
  437. break;
  438. while (write(SUP.sockw, buffer, n) < 0) {
  439. me->verrno = errno;
  440. goto error_return;
  441. }
  442. disable_interrupt_key();
  443. total += n;
  444. print_vfs_message(_("fish: storing %s %d (%d)"),
  445. was_error ? _("zeros") : _("file"), total, s.st_size);
  446. }
  447. if ((get_reply (me, SUP.sockr, NULL, 0) != COMPLETE) || was_error)
  448. ERRNOR (E_REMOTE, -1);
  449. close(h);
  450. return 0;
  451. error_return:
  452. close(h);
  453. get_reply(me, SUP.sockr, NULL, 0);
  454. return -1;
  455. }
  456. static int linear_start(vfs *me, vfs_s_fh *fh, int offset)
  457. {
  458. char *name;
  459. if (offset)
  460. ERRNOR (E_NOTSUPP, 0);
  461. /* fe->local_stat.st_mtime = 0; FIXME: what is this good for? */
  462. name = vfs_s_fullpath (me, fh->ino);
  463. if (!name)
  464. return 0;
  465. offset = command(me, FH_SUPER, WANT_STRING,
  466. "#RETR /%s\n"
  467. "ls -l \"/%s\" 2>/dev/null | (\n"
  468. "read var1 var2 var3 var4 var5 var6\n"
  469. "echo \"$var5\"\n"
  470. ")\n"
  471. "echo '### 100'\n"
  472. "cat \"/%s\"\n"
  473. "echo '### 200'\n",
  474. name, name, name );
  475. g_free (name);
  476. if (offset != PRELIM) ERRNOR (E_REMOTE, 0);
  477. fh->linear = LS_LINEAR_OPEN;
  478. fh->u.fish.got = 0;
  479. if (sscanf( reply_str, "%d", &fh->u.fish.total )!=1)
  480. ERRNOR (E_REMOTE, 0);
  481. return 1;
  482. }
  483. static void
  484. linear_abort (vfs *me, vfs_s_fh *fh)
  485. {
  486. vfs_s_super *super = FH_SUPER;
  487. char buffer[8192];
  488. int n;
  489. print_vfs_message( _("Aborting transfer...") );
  490. do {
  491. n = MIN(8192, fh->u.fish.total - fh->u.fish.got);
  492. if (n)
  493. if ((n = read(SUP.sockr, buffer, n)) < 0)
  494. return;
  495. } while (n);
  496. if (get_reply (me, SUP.sockr, NULL, 0) != COMPLETE)
  497. print_vfs_message( _("Error reported after abort.") );
  498. else
  499. print_vfs_message( _("Aborted transfer would be successful.") );
  500. }
  501. static int
  502. linear_read (vfs *me, vfs_s_fh *fh, void *buf, int len)
  503. {
  504. vfs_s_super *super = FH_SUPER;
  505. int n = 0;
  506. len = MIN( fh->u.fish.total - fh->u.fish.got, len );
  507. disable_interrupt_key();
  508. while (len && ((n = read (SUP.sockr, buf, len))<0)) {
  509. if ((errno == EINTR) && !got_interrupt())
  510. continue;
  511. break;
  512. }
  513. enable_interrupt_key();
  514. if (n>0) fh->u.fish.got += n;
  515. if (n<0) linear_abort(me, fh);
  516. if ((!n) && ((get_reply (me, SUP.sockr, NULL, 0) != COMPLETE)))
  517. ERRNOR (E_REMOTE, -1);
  518. ERRNOR (errno, n);
  519. }
  520. static void
  521. linear_close (vfs *me, vfs_s_fh *fh)
  522. {
  523. if (fh->u.fish.total != fh->u.fish.got)
  524. linear_abort(me, fh);
  525. }
  526. static int
  527. fish_ctl (void *fh, int ctlop, int arg)
  528. {
  529. return 0;
  530. switch (ctlop) {
  531. case MCCTL_IS_NOTREADY:
  532. {
  533. int v;
  534. if (!FH->linear)
  535. vfs_die ("You may not do this");
  536. if (FH->linear == LS_LINEAR_CLOSED)
  537. return 0;
  538. v = vfs_s_select_on_two (FH_SUPER->u.fish.sockr, 0);
  539. if (((v < 0) && (errno == EINTR)) || v == 0)
  540. return 1;
  541. return 0;
  542. }
  543. default:
  544. return 0;
  545. }
  546. }
  547. static int
  548. send_fish_command(vfs *me, vfs_s_super *super, char *cmd, int flags)
  549. {
  550. int r;
  551. r = command (me, super, WAIT_REPLY, cmd);
  552. vfs_add_noncurrent_stamps (&vfs_fish_ops, (vfsid) super, NULL);
  553. if (r != COMPLETE) ERRNOR (E_REMOTE, -1);
  554. if (flags & OPT_FLUSH)
  555. vfs_s_invalidate(me, super);
  556. return 0;
  557. }
  558. #define PREFIX \
  559. char buf[BUF_LARGE]; \
  560. char *rpath; \
  561. vfs_s_super *super; \
  562. if (!(rpath = vfs_s_get_path_mangle(me, path, &super, 0))) \
  563. return -1;
  564. #define POSTFIX(flags) \
  565. return send_fish_command(me, super, buf, flags);
  566. static int
  567. fish_chmod (vfs *me, char *path, int mode)
  568. {
  569. PREFIX
  570. g_snprintf(buf, sizeof(buf), "#CHMOD %4.4o /%s\n"
  571. "chmod %4.4o \"/%s\" 2>/dev/null\n"
  572. "echo '### 000'\n",
  573. mode & 07777, rpath,
  574. mode & 07777, rpath);
  575. POSTFIX(OPT_FLUSH);
  576. }
  577. #define FISH_OP(name, chk, string) \
  578. static int fish_##name (vfs *me, char *path1, char *path2) \
  579. { \
  580. char buf[BUF_LARGE]; \
  581. char *rpath1 = NULL, *rpath2 = NULL; \
  582. vfs_s_super *super1, *super2; \
  583. if (!(rpath1 = vfs_s_get_path_mangle(me, path1, &super1, 0))) \
  584. return -1; \
  585. if (!(rpath2 = vfs_s_get_path_mangle(me, path2, &super2, 0))) \
  586. return -1; \
  587. g_snprintf(buf, 1023, string "\n", rpath1, rpath2, rpath1, rpath2 ); \
  588. return send_fish_command(me, super2, buf, OPT_FLUSH); \
  589. }
  590. #define XTEST if (bucket1 != bucket2) { ERRNOR (EXDEV, -1); }
  591. FISH_OP(rename, XTEST, "#RENAME /%s /%s\n"
  592. "mv \"/%s\" \"/%s\" 2>/dev/null\n"
  593. "echo '### 000'" );
  594. FISH_OP(link, XTEST, "#LINK /%s /%s\n"
  595. "ln \"/%s\" \"/%s\" 2>/dev/null\n"
  596. "echo '### 000'" );
  597. static int fish_symlink (vfs *me, char *setto, char *path)
  598. {
  599. PREFIX
  600. g_snprintf(buf, sizeof(buf),
  601. "#SYMLINK %s /%s\n"
  602. "ln -s \"%s\" \"/%s\" 2>/dev/null\n"
  603. "echo '### 000'\n",
  604. setto, rpath, setto, rpath);
  605. POSTFIX(OPT_FLUSH);
  606. }
  607. static int
  608. fish_chown (vfs *me, char *path, int owner, int group)
  609. {
  610. char *sowner, *sgroup;
  611. PREFIX
  612. sowner = getpwuid( owner )->pw_name;
  613. sgroup = getgrgid( group )->gr_name;
  614. g_snprintf(buf, sizeof(buf),
  615. "#CHOWN /%s /%s\n"
  616. "chown %s \"/%s\" 2>/dev/null\n"
  617. "echo '### 000'\n",
  618. sowner, rpath,
  619. sowner, rpath);
  620. send_fish_command(me, super, buf, OPT_FLUSH);
  621. /* FIXME: what should we report if chgrp succeeds but chown fails? */
  622. g_snprintf(buf, sizeof(buf),
  623. "#CHGRP /%s /%s\n"
  624. "chgrp %s \"/%s\" 2>/dev/null\n"
  625. "echo '### 000'\n",
  626. sgroup, rpath,
  627. sgroup, rpath);
  628. /* send_fish_command(me, super, buf, OPT_FLUSH); */
  629. POSTFIX(OPT_FLUSH)
  630. }
  631. static int fish_unlink (vfs *me, char *path)
  632. {
  633. PREFIX
  634. g_snprintf(buf, sizeof(buf),
  635. "#DELE /%s\n"
  636. "rm -f \"/%s\" 2>/dev/null\n"
  637. "echo '### 000'\n",
  638. rpath, rpath);
  639. POSTFIX(OPT_FLUSH);
  640. }
  641. static int fish_mkdir (vfs *me, char *path, mode_t mode)
  642. {
  643. PREFIX
  644. g_snprintf(buf, sizeof(buf),
  645. "#MKD /%s\n"
  646. "mkdir \"/%s\" 2>/dev/null\n"
  647. "echo '### 000'\n",
  648. rpath, rpath);
  649. POSTFIX(OPT_FLUSH);
  650. }
  651. static int fish_rmdir (vfs *me, char *path)
  652. {
  653. PREFIX
  654. g_snprintf(buf, sizeof(buf),
  655. "#RMD /%s\n"
  656. "rmdir \"/%s\" 2>/dev/null\n"
  657. "echo '### 000'\n",
  658. rpath, rpath);
  659. POSTFIX(OPT_FLUSH);
  660. }
  661. static int fish_fh_open (vfs *me, vfs_s_fh *fh, int flags, int mode)
  662. {
  663. if (!fh->ino->localname)
  664. if (vfs_s_retrieve_file (me, fh->ino)==-1)
  665. return -1;
  666. if (!fh->ino->localname)
  667. vfs_die( "retrieve_file failed to fill in localname" );
  668. return 0;
  669. }
  670. static struct vfs_s_data fish_data = {
  671. NULL,
  672. 0,
  673. 0,
  674. NULL,
  675. NULL, /* init_inode */
  676. NULL, /* free_inode */
  677. NULL, /* init_entry */
  678. NULL, /* archive_check */
  679. archive_same,
  680. open_archive,
  681. free_archive,
  682. fish_fh_open, /* fh_open */
  683. NULL, /* fh_close */
  684. vfs_s_find_entry_linear,
  685. dir_load,
  686. dir_uptodate,
  687. file_store,
  688. linear_start,
  689. linear_read,
  690. linear_close
  691. };
  692. vfs vfs_fish_ops = {
  693. NULL, /* This is place of next pointer */
  694. "fish",
  695. F_EXEC, /* flags */
  696. "sh:", /* prefix */
  697. &fish_data, /* data */
  698. 0, /* errno */
  699. NULL,
  700. NULL,
  701. vfs_s_fill_names,
  702. NULL,
  703. vfs_s_open,
  704. vfs_s_close,
  705. vfs_s_read,
  706. vfs_s_write,
  707. vfs_s_opendir,
  708. vfs_s_readdir,
  709. vfs_s_closedir,
  710. vfs_s_telldir,
  711. vfs_s_seekdir,
  712. vfs_s_stat,
  713. vfs_s_lstat,
  714. vfs_s_fstat,
  715. fish_chmod,
  716. fish_chown,
  717. NULL, /* utime */
  718. vfs_s_readlink,
  719. fish_symlink, /* symlink */
  720. fish_link, /* link */
  721. fish_unlink,
  722. fish_rename, /* rename */
  723. vfs_s_chdir,
  724. vfs_s_ferrno,
  725. vfs_s_lseek,
  726. NULL, /* mknod */
  727. vfs_s_getid,
  728. vfs_s_nothingisopen,
  729. vfs_s_free,
  730. NULL, /* vfs_s_getlocalcopy, */
  731. NULL, /* vfs_s_ungetlocalcopy, */
  732. fish_mkdir,
  733. fish_rmdir,
  734. fish_ctl,
  735. vfs_s_setctl
  736. MMAPNULL
  737. };