Corrected a misdeleted row that created a crash on f1-f1 key pressing...
[midnight-commander.git] / vfs / fish.c
blob77543f4da986022b36eb619205c22bdd7c86b31e
1 /* Virtual File System: FISH implementation for transfering files over
2 shell connections.
4 Copyright (C) 1998 The Free Software Foundation
6 Written by: 1998 Pavel Machek
7 Spaces fix: 2000 Michal Svec
9 $Id$
11 Derived from ftpfs.c.
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Library General Public License
15 as published by the Free Software Foundation; either version 2 of
16 the License, or (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU Library General Public License for more details.
23 You should have received a copy of the GNU Library General Public
24 License along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
28 * Read README.fish for protocol specification.
30 * Syntax of path is: /#sh:user@host[:Cr]/path
31 * where C means you want compressed connection,
32 * and r means you want to use rsh
34 * Namespace: fish_vfs_ops exported.
37 /* Define this if your ssh can take -I option */
39 #include <config.h>
41 #undef HAVE_HACKED_SSH
43 #include "utilvfs.h"
44 #include "../src/dialog.h" /* For MSG_ERROR */
46 #include "xdirentry.h"
47 #include "vfs.h"
48 #include "tcputil.h"
49 #include "container.h"
50 #include "fish.h"
53 * Reply codes.
55 #define PRELIM 1 /* positive preliminary */
56 #define COMPLETE 2 /* positive completion */
57 #define CONTINUE 3 /* positive intermediate */
58 #define TRANSIENT 4 /* transient negative completion */
59 #define ERROR 5 /* permanent negative completion */
61 /* If true, the directory cache is forced to reload */
62 static int force_expiration = 0;
64 /* FIXME: prev two variables should be killed */
66 /* command wait_flag: */
67 #define NONE 0x00
68 #define WAIT_REPLY 0x01
69 #define WANT_STRING 0x02
70 static char reply_str [80];
72 static int
73 command (vfs *me, vfs_s_super *super, int wait_reply, const char *fmt, ...)
74 __attribute__ ((format (printf, 4, 5)));
76 static int decode_reply (char *s, int was_garbage)
78 int code;
79 if (!sscanf(s, "%d", &code)) {
80 code = 500;
81 return 5;
83 if (code<100) return was_garbage ? ERROR : (!code ? COMPLETE : PRELIM);
84 return code / 100;
87 /* Returns a reply code, check /usr/include/arpa/ftp.h for possible values */
88 static int get_reply (vfs *me, int sock, char *string_buf, int string_len)
90 char answer[1024];
91 int was_garbage = 0;
93 for (;;) {
94 if (!vfs_s_get_line(me, sock, answer, sizeof(answer), '\n')) {
95 if (string_buf)
96 *string_buf = 0;
97 return 4;
99 if (strncmp(answer, "### ", 4)) {
100 was_garbage = 1;
101 if (string_buf) {
102 strncpy(string_buf, answer, string_len - 1);
103 *(string_buf + string_len - 1) = 0;
105 } else return decode_reply(answer+4, was_garbage);
109 #define SUP super->u.fish
111 static int
112 command (vfs *me, vfs_s_super *super, int wait_reply, const char *fmt, ...)
114 va_list ap;
115 char *str;
116 int status;
117 FILE *logfile = MEDATA->logfile;
119 va_start (ap, fmt);
121 str = g_strdup_vprintf (fmt, ap);
122 va_end (ap);
124 if (logfile){
125 fwrite (str, strlen (str), 1, logfile);
126 fflush (logfile);
129 enable_interrupt_key();
131 status = write(SUP.sockw, str, strlen(str));
132 g_free (str);
134 disable_interrupt_key();
135 if (status < 0)
136 return TRANSIENT;
138 if (wait_reply)
139 return get_reply (me, SUP.sockr, (wait_reply & WANT_STRING) ? reply_str : NULL, sizeof (reply_str)-1);
140 return COMPLETE;
143 static void
144 free_archive (vfs *me, vfs_s_super *super)
146 if ((SUP.sockw != -1) || (SUP.sockr != -1)){
147 print_vfs_message (_("fish: Disconnecting from %s"), super->name?super->name:"???");
148 command(me, super, NONE, "#BYE\nexit\n");
149 close(SUP.sockw);
150 close(SUP.sockr);
151 SUP.sockw = SUP.sockr = -1;
153 g_free (SUP.host);
154 g_free (SUP.user);
155 g_free (SUP.cwdir);
156 g_free (SUP.password);
159 static void
160 pipeopen(vfs_s_super *super, char *path, char *argv[])
162 int fileset1[2], fileset2[2];
163 int res;
165 if ((pipe(fileset1)<0) || (pipe(fileset2)<0))
166 vfs_die("Could not pipe(): %m.");
168 if ((res = fork())) {
169 if (res<0) vfs_die("Could not fork(): %m.");
170 /* We are the parent */
171 close(fileset1[0]);
172 SUP.sockw = fileset1[1];
173 close(fileset2[1]);
174 SUP.sockr = fileset2[0];
175 } else {
176 close(0);
177 dup(fileset1[0]);
178 close(fileset1[0]); close(fileset1[1]);
179 close(1); close(2);
180 dup(fileset2[1]);
181 /* stderr to /dev/null */
182 open ("/dev/null", O_WRONLY);
183 close(fileset2[0]); close(fileset2[1]);
184 execvp(path, argv);
185 _exit(3);
189 /* The returned directory should always contain a trailing slash */
190 static char *fish_getcwd(vfs *me, vfs_s_super *super)
192 if (command(me, super, WANT_STRING, "#PWD\npwd; echo '### 200'\n") == COMPLETE)
193 return g_strconcat (reply_str, "/", NULL);
194 ERRNOR (EIO, NULL);
196 static int
197 open_archive_int (vfs *me, vfs_s_super *super)
199 char *argv[100];
200 char *xsh = (SUP.flags == FISH_FLAG_RSH ? "rsh" : "ssh");
201 int i = 0;
203 argv[i++] = xsh;
204 #ifdef HAVE_HACKED_SSH
205 argv[i++] = "-I";
206 #endif
207 argv[i++] = "-l";
208 argv[i++] = SUP.user;
209 argv[i++] = SUP.host;
210 if (SUP.flags == FISH_FLAG_COMPRESSED)
211 argv[i++] = "-C";
212 argv[i++] = "echo FISH:; /bin/sh";
213 argv[i++] = NULL;
215 #if 0
216 /* Debugging hack */
217 if (!MEDATA->logfile)
218 MEDATA->logfile = fopen( "/home/pavel/talk.fish", "w+" ); /* FIXME */
219 #endif
221 pipeopen(super, xsh, argv );
224 char answer[2048];
225 print_vfs_message( _("fish: Waiting for initial line...") );
226 if (!vfs_s_get_line(me, SUP.sockr, answer, sizeof(answer), ':'))
227 ERRNOR (E_PROTO, -1);
228 print_vfs_message( answer );
229 if (strstr(answer, "assword")) {
231 /* Currently, this does not work. ssh reads passwords from
232 /dev/tty, not from stdin :-(. */
234 #ifndef HAVE_HACKED_SSH
235 message_1s (1, MSG_ERROR, _("Sorry, we can not do password authenticated connections for now."));
236 ERRNOR (EPERM, -1);
237 #endif
238 if (!SUP.password){
239 char *p, *op;
240 p = g_strconcat (_(" fish: Password required for "), SUP.user,
241 " ", NULL);
242 op = vfs_get_password (p);
243 g_free (p);
244 if (op == NULL)
245 ERRNOR (EPERM, -1);
246 SUP.password = g_strdup (op);
247 wipe_password(op);
249 print_vfs_message( _("fish: Sending password...") );
250 write(SUP.sockw, SUP.password, strlen(SUP.password));
251 write(SUP.sockw, "\n", 1);
255 print_vfs_message( _("fish: Sending initial line...") );
257 * Run `start_fish_server'. If it doesn't exist - no problem,
258 * we'll talk directly to the shell.
260 if (command (me, super, WAIT_REPLY,
261 "#FISH\necho; start_fish_server 2>&1;"
262 " echo '### 200'\n") != COMPLETE)
263 ERRNOR (E_PROTO, -1);
265 print_vfs_message( _("fish: Handshaking version...") );
266 if (command (me, super, WAIT_REPLY, "#VER 0.0.0\necho '### 000'\n") != COMPLETE)
267 ERRNOR (E_PROTO, -1);
269 /* Set up remote locale to C, otherwise dates cannot be recognized */
270 if (command (me, super, WAIT_REPLY, "LANG=C; LC_ALL=C; LC_TIME=C\n"
271 "export LANG; export LC_ALL; export LC_TIME\n"
272 "echo '### 200'\n") != COMPLETE)
273 ERRNOR (E_PROTO, -1);
275 print_vfs_message( _("fish: Setting up current directory...") );
276 SUP.cwdir = fish_getcwd (me, super);
277 print_vfs_message( _("fish: Connected, home %s."), SUP.cwdir );
278 #if 0
279 super->name = g_strconcat ( "/#sh:", SUP.user, "@", SUP.host, "/", NULL );
280 #endif
281 super->name = g_strdup(PATH_SEP_STR);
283 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat(me, S_IFDIR | 0755));
284 return 0;
287 static int
288 open_archive (vfs *me, vfs_s_super *super, char *archive_name, char *op)
290 char *host, *user, *password, *p;
291 int flags;
293 p = vfs_split_url (strchr(op, ':')+1, &host, &user, &flags, &password, 0, URL_NOSLASH);
295 if (p)
296 g_free (p);
298 SUP.host = host;
299 SUP.user = user;
300 SUP.flags = flags;
301 if (!strncmp( op, "rsh:", 4 ))
302 SUP.flags |= FISH_FLAG_RSH;
303 SUP.cwdir = NULL;
304 if (password)
305 SUP.password = password;
306 return open_archive_int (me, super);
309 static int
310 archive_same(vfs *me, vfs_s_super *super, char *archive_name, char *op, void *cookie)
312 char *host, *user;
313 int flags;
315 op = vfs_split_url (strchr(op, ':')+1, &host, &user, &flags, 0, 0, URL_NOSLASH);
317 if (op)
318 g_free (op);
320 flags = ((strcmp (host, SUP.host) == 0) &&
321 (strcmp (user, SUP.user) == 0) &&
322 (flags == SUP.flags));
323 g_free (host);
324 g_free (user);
326 return flags;
330 fish_which (vfs *me, char *path)
332 if (!strncmp (path, "/#sh:", 5))
333 return 1;
334 if (!strncmp (path, "/#ssh:", 6))
335 return 1;
336 if (!strncmp (path, "/#rsh:", 6))
337 return 1;
338 return 0;
341 static int
342 dir_uptodate(vfs *me, vfs_s_inode *ino)
344 struct timeval tim;
346 gettimeofday(&tim, NULL);
347 if (force_expiration) {
348 force_expiration = 0;
349 return 0;
351 if (tim.tv_sec < ino->u.fish.timestamp.tv_sec)
352 return 1;
353 return 0;
356 static int
357 dir_load(vfs *me, vfs_s_inode *dir, char *remote_path)
359 vfs_s_super *super = dir->super;
360 char buffer[8192];
361 vfs_s_entry *ent = NULL;
362 FILE *logfile;
364 logfile = MEDATA->logfile;
366 print_vfs_message(_("fish: Reading directory %s..."), remote_path);
368 gettimeofday(&dir->u.fish.timestamp, NULL);
369 dir->u.fish.timestamp.tv_sec += 10; /* was 360: 10 is good for
370 stressing direntry layer a bit */
372 command(me, super, NONE,
373 "#LIST /%s\n"
374 "ls -lLa \"/%s\" 2>/dev/null | grep '^[^cbt]' | (\n"
375 "while read p x u g s m d y n; do\n"
376 "echo \"P$p $u.$g\nS$s\nd$m $d $y\n:$n\n\"\n"
377 "done\n"
378 ")\n"
379 "ls -lLa \"/%s\" 2>/dev/null | grep '^[cb]' | (\n"
380 "while read p x u g a i m d y n; do\n"
381 "echo \"P$p $u.$g\nE$a$i\nd$m $d $y\n:$n\n\"\n"
382 "done\n"
383 ")\n"
384 "echo '### 200'\n",
385 remote_path, remote_path, remote_path);
387 #define SIMPLE_ENTRY vfs_s_generate_entry(me, NULL, dir, 0)
388 ent = SIMPLE_ENTRY;
389 while (1) {
390 int res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), SUP.sockr);
391 if ((!res) || (res == EINTR)) {
392 vfs_s_free_entry(me, ent);
393 me->verrno = ECONNRESET;
394 goto error;
396 if (logfile) {
397 fputs (buffer, logfile);
398 fputs ("\n", logfile);
399 fflush (logfile);
401 if (!strncmp(buffer, "### ", 4))
402 break;
403 if ((!buffer[0])) {
404 if (ent->name) {
405 vfs_s_insert_entry(me, dir, ent);
406 ent = SIMPLE_ENTRY;
408 continue;
411 #define ST ent->ino->st
413 switch(buffer[0]) {
414 case ':': {
415 /* char *c; */
416 if (!strcmp(buffer+1, ".") || !strcmp(buffer+1, ".."))
417 break; /* We'll do . and .. ourself */
418 ent->name = g_strdup(buffer+1);
419 /* if ((c=strchr(ent->name, ' ')))
420 *c = 0; / * this is ugly, but we can not handle " " in name */
421 break;
423 case 'S': ST.st_size = atoi(buffer+1); break;
424 case 'P': {
425 int i;
426 if ((i = vfs_parse_filetype(buffer[1])) ==-1)
427 break;
428 ST.st_mode = i;
429 if ((i = vfs_parse_filemode(buffer+2)) ==-1)
430 break;
431 ST.st_mode |= i;
432 if (S_ISLNK(ST.st_mode))
433 ST.st_mode = 0;
435 break;
436 case 'd': {
437 vfs_split_text(buffer+1);
438 if (!vfs_parse_filedate(0, &ST.st_ctime))
439 break;
440 ST.st_atime = ST.st_mtime = ST.st_ctime;
442 break;
443 case 'D': {
444 struct tm tim;
445 if (sscanf(buffer+1, "%d %d %d %d %d %d", &tim.tm_year, &tim.tm_mon,
446 &tim.tm_mday, &tim.tm_hour, &tim.tm_min, &tim.tm_sec) != 6)
447 break;
448 ST.st_atime = ST.st_mtime = ST.st_ctime = mktime(&tim);
450 break;
451 case 'E': {
452 int maj, min;
453 if (sscanf(buffer+1, "%d,%d", &maj, &min) != 2)
454 break;
455 #ifdef HAVE_ST_RDEV
456 ST.st_rdev = (maj << 8) | min;
457 #endif
459 case 'L': ent->ino->linkname = g_strdup(buffer+1);
460 break;
464 vfs_s_free_entry (me, ent);
465 me->verrno = E_REMOTE;
466 if (decode_reply(buffer+4, 0) == COMPLETE) {
467 g_free (SUP.cwdir);
468 SUP.cwdir = g_strdup (remote_path);
469 print_vfs_message (_("%s: done."), me->name);
470 return 0;
473 error:
474 print_vfs_message (_("%s: failure"), me->name);
475 return 1;
478 static int
479 file_store(vfs *me, vfs_s_fh *fh, char *name, char *localname)
481 vfs_s_super *super = FH_SUPER;
482 int n, total;
483 char buffer[8192];
484 struct stat s;
485 int was_error = 0;
486 int h;
488 h = open(localname, O_RDONLY);
490 if (fstat(h, &s)<0)
491 ERRNOR (EIO, -1);
493 /* Use this as stor: ( dd block ; dd smallblock ) | ( cat > file; cat > /dev/null ) */
495 print_vfs_message(_("fish: store %s: sending command..."), name );
497 * FIXME: Limit size to unsigned long for now.
498 * Files longer than 256 * ULONG_MAX are not supported.
500 if (!fh->u.fish.append)
501 n = command (me, super, WAIT_REPLY,
502 "#STOR %lu /%s\n"
503 "> \"/%s\"\n"
504 "echo '### 001'\n"
505 "(\n"
506 "dd ibs=256 obs=4096 count=%lu\n"
507 "dd bs=%lu count=1\n"
508 ") 2>/dev/null | (\n"
509 "cat > \"/%s\"\n"
510 "cat > /dev/null\n"
511 "); echo '### 200'\n",
512 (unsigned long) s.st_size, name, name,
513 (unsigned long) (s.st_size >> 8),
514 ((unsigned long) s.st_size) & (256 - 1), name);
515 else
516 n = command (me, super, WAIT_REPLY,
517 "#STOR %lu /%s\n"
518 "echo '### 001'\n"
519 "(\n"
520 "dd ibs=256 obs=4096 count=%lu\n"
521 "dd bs=%lu count=1\n"
522 ") 2>/dev/null | (\n"
523 "cat >> \"/%s\"\n"
524 "cat > /dev/null\n"
525 "); echo '### 200'\n",
526 (unsigned long) s.st_size, name,
527 (unsigned long) (s.st_size >> 8),
528 ((unsigned long) s.st_size) & (256 - 1), name);
530 if (n != PRELIM)
531 ERRNOR(E_REMOTE, -1);
533 total = 0;
535 while (1) {
536 while ((n = read(h, buffer, sizeof(buffer))) < 0) {
537 if ((errno == EINTR) && got_interrupt)
538 continue;
539 print_vfs_message(_("fish: Local read failed, sending zeros") );
540 close(h);
541 h = open( "/dev/zero", O_RDONLY );
543 if (n == 0)
544 break;
545 while (write(SUP.sockw, buffer, n) < 0) {
546 me->verrno = errno;
547 goto error_return;
549 disable_interrupt_key();
550 total += n;
551 print_vfs_message(_("fish: storing %s %d (%lu)"),
552 was_error ? _("zeros") : _("file"), total,
553 (unsigned long) s.st_size);
555 if ((get_reply (me, SUP.sockr, NULL, 0) != COMPLETE) || was_error)
556 ERRNOR (E_REMOTE, -1);
557 close(h);
558 return 0;
559 error_return:
560 close(h);
561 get_reply(me, SUP.sockr, NULL, 0);
562 return -1;
565 static int linear_start(vfs *me, vfs_s_fh *fh, int offset)
567 char *name;
568 if (offset)
569 ERRNOR (E_NOTSUPP, 0);
570 /* fe->local_stat.st_mtime = 0; FIXME: what is this good for? */
571 name = vfs_s_fullpath (me, fh->ino);
572 if (!name)
573 return 0;
574 fh->u.fish.append = 0;
575 offset = command(me, FH_SUPER, WANT_STRING,
576 "#RETR /%s\n"
577 "ls -l \"/%s\" 2>/dev/null | (\n"
578 "read var1 var2 var3 var4 var5 var6\n"
579 "echo \"$var5\"\n"
580 ")\n"
581 "echo '### 100'\n"
582 "cat \"/%s\"\n"
583 "echo '### 200'\n",
584 name, name, name );
585 g_free (name);
586 if (offset != PRELIM) ERRNOR (E_REMOTE, 0);
587 fh->linear = LS_LINEAR_OPEN;
588 fh->u.fish.got = 0;
589 if (sscanf( reply_str, "%d", &fh->u.fish.total )!=1)
590 ERRNOR (E_REMOTE, 0);
591 return 1;
594 static void
595 linear_abort (vfs *me, vfs_s_fh *fh)
597 vfs_s_super *super = FH_SUPER;
598 char buffer[8192];
599 int n;
601 print_vfs_message( _("Aborting transfer...") );
602 do {
603 n = MIN(8192, fh->u.fish.total - fh->u.fish.got);
604 if (n)
605 if ((n = read(SUP.sockr, buffer, n)) < 0)
606 return;
607 } while (n);
609 if (get_reply (me, SUP.sockr, NULL, 0) != COMPLETE)
610 print_vfs_message( _("Error reported after abort.") );
611 else
612 print_vfs_message( _("Aborted transfer would be successful.") );
615 static int
616 linear_read (vfs *me, vfs_s_fh *fh, void *buf, int len)
618 vfs_s_super *super = FH_SUPER;
619 int n = 0;
620 len = MIN( fh->u.fish.total - fh->u.fish.got, len );
621 disable_interrupt_key();
622 while (len && ((n = read (SUP.sockr, buf, len))<0)) {
623 if ((errno == EINTR) && !got_interrupt())
624 continue;
625 break;
627 enable_interrupt_key();
629 if (n>0) fh->u.fish.got += n;
630 if (n<0) linear_abort(me, fh);
631 if ((!n) && ((get_reply (me, SUP.sockr, NULL, 0) != COMPLETE)))
632 ERRNOR (E_REMOTE, -1);
633 ERRNOR (errno, n);
636 static void
637 linear_close (vfs *me, vfs_s_fh *fh)
639 if (fh->u.fish.total != fh->u.fish.got)
640 linear_abort(me, fh);
641 else if (stat (fh->ino->localname, &fh->ino->u.fish.local_stat) < 0)
642 fh->ino->u.fish.local_stat.st_mtime = 0;
645 static int
646 fish_ctl (void *fh, int ctlop, int arg)
648 return 0;
649 switch (ctlop) {
650 case MCCTL_IS_NOTREADY:
652 int v;
654 if (!FH->linear)
655 vfs_die ("You may not do this");
656 if (FH->linear == LS_LINEAR_CLOSED)
657 return 0;
659 v = vfs_s_select_on_two (FH_SUPER->u.fish.sockr, 0);
660 if (((v < 0) && (errno == EINTR)) || v == 0)
661 return 1;
662 return 0;
664 default:
665 return 0;
669 static int
670 send_fish_command(vfs *me, vfs_s_super *super, char *cmd, int flags)
672 int r;
674 r = command (me, super, WAIT_REPLY, cmd);
675 vfs_add_noncurrent_stamps (&vfs_fish_ops, (vfsid) super, NULL);
676 if (r != COMPLETE) ERRNOR (E_REMOTE, -1);
677 if (flags & OPT_FLUSH)
678 vfs_s_invalidate(me, super);
679 return 0;
682 #define PREFIX \
683 char buf[BUF_LARGE]; \
684 char *rpath; \
685 vfs_s_super *super; \
686 if (!(rpath = vfs_s_get_path_mangle(me, path, &super, 0))) \
687 return -1;
689 #define POSTFIX(flags) \
690 return send_fish_command(me, super, buf, flags);
692 static int
693 fish_chmod (vfs *me, char *path, int mode)
695 PREFIX
696 g_snprintf(buf, sizeof(buf), "#CHMOD %4.4o /%s\n"
697 "chmod %4.4o \"/%s\" 2>/dev/null\n"
698 "echo '### 000'\n",
699 mode & 07777, rpath,
700 mode & 07777, rpath);
701 POSTFIX(OPT_FLUSH);
704 #define FISH_OP(name, chk, string) \
705 static int fish_##name (vfs *me, char *path1, char *path2) \
707 char buf[BUF_LARGE]; \
708 char *rpath1 = NULL, *rpath2 = NULL; \
709 vfs_s_super *super1, *super2; \
710 if (!(rpath1 = vfs_s_get_path_mangle(me, path1, &super1, 0))) \
711 return -1; \
712 if (!(rpath2 = vfs_s_get_path_mangle(me, path2, &super2, 0))) \
713 return -1; \
714 g_snprintf(buf, 1023, string "\n", rpath1, rpath2, rpath1, rpath2 ); \
715 return send_fish_command(me, super2, buf, OPT_FLUSH); \
718 #define XTEST if (bucket1 != bucket2) { ERRNOR (EXDEV, -1); }
719 FISH_OP(rename, XTEST, "#RENAME /%s /%s\n"
720 "mv \"/%s\" \"/%s\" 2>/dev/null\n"
721 "echo '### 000'" )
722 FISH_OP(link, XTEST, "#LINK /%s /%s\n"
723 "ln \"/%s\" \"/%s\" 2>/dev/null\n"
724 "echo '### 000'" )
726 static int fish_symlink (vfs *me, char *setto, char *path)
728 PREFIX
729 g_snprintf(buf, sizeof(buf),
730 "#SYMLINK %s /%s\n"
731 "ln -s \"%s\" \"/%s\" 2>/dev/null\n"
732 "echo '### 000'\n",
733 setto, rpath, setto, rpath);
734 POSTFIX(OPT_FLUSH);
737 static int
738 fish_chown (vfs *me, char *path, int owner, int group)
740 char *sowner, *sgroup;
741 struct passwd *pw;
742 struct group *gr;
743 PREFIX
745 if ((pw = getpwuid (owner)) == NULL)
746 return 0;
748 if ((gr = getgrgid (group)) == NULL)
749 return 0;
751 sowner = pw->pw_name;
752 sgroup = gr->gr_name;
753 g_snprintf(buf, sizeof(buf),
754 "#CHOWN /%s /%s\n"
755 "chown %s \"/%s\" 2>/dev/null\n"
756 "echo '### 000'\n",
757 sowner, rpath,
758 sowner, rpath);
759 send_fish_command(me, super, buf, OPT_FLUSH);
760 /* FIXME: what should we report if chgrp succeeds but chown fails? */
761 g_snprintf(buf, sizeof(buf),
762 "#CHGRP /%s /%s\n"
763 "chgrp %s \"/%s\" 2>/dev/null\n"
764 "echo '### 000'\n",
765 sgroup, rpath,
766 sgroup, rpath);
767 /* send_fish_command(me, super, buf, OPT_FLUSH); */
768 POSTFIX(OPT_FLUSH)
771 static int fish_unlink (vfs *me, char *path)
773 PREFIX
774 g_snprintf(buf, sizeof(buf),
775 "#DELE /%s\n"
776 "rm -f \"/%s\" 2>/dev/null\n"
777 "echo '### 000'\n",
778 rpath, rpath);
779 POSTFIX(OPT_FLUSH);
782 static int fish_mkdir (vfs *me, char *path, mode_t mode)
784 PREFIX
785 g_snprintf(buf, sizeof(buf),
786 "#MKD /%s\n"
787 "mkdir \"/%s\" 2>/dev/null\n"
788 "echo '### 000'\n",
789 rpath, rpath);
790 POSTFIX(OPT_FLUSH);
793 static int fish_rmdir (vfs *me, char *path)
795 PREFIX
796 g_snprintf(buf, sizeof(buf),
797 "#RMD /%s\n"
798 "rmdir \"/%s\" 2>/dev/null\n"
799 "echo '### 000'\n",
800 rpath, rpath);
801 POSTFIX(OPT_FLUSH);
804 static int fish_fh_open (vfs *me, vfs_s_fh *fh, int flags, int mode)
806 fh->u.fish.append = 0;
807 /* File will be written only, so no need to retrieve it */
808 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY|O_RDWR))){
809 fh->u.fish.append = flags & O_APPEND;
810 if (!fh->ino->localname){
811 int tmp_handle = mc_mkstemps (&fh->ino->localname, me->name, NULL);
812 if (tmp_handle == -1)
813 return -1;
814 close (tmp_handle);
816 return 0;
818 if (!fh->ino->localname)
819 if (vfs_s_retrieve_file (me, fh->ino)==-1)
820 return -1;
821 if (!fh->ino->localname)
822 vfs_die( "retrieve_file failed to fill in localname" );
823 return 0;
826 static struct vfs_s_data fish_data = {
827 NULL,
830 NULL,
832 NULL, /* init_inode */
833 NULL, /* free_inode */
834 NULL, /* init_entry */
836 NULL, /* archive_check */
837 archive_same,
838 open_archive,
839 free_archive,
841 fish_fh_open, /* fh_open */
842 NULL, /* fh_close */
844 vfs_s_find_entry_linear,
845 dir_load,
846 dir_uptodate,
847 file_store,
849 linear_start,
850 linear_read,
851 linear_close
854 static void
855 fish_fill_names (vfs *me, void (*func)(char *))
857 struct vfs_s_super * super = fish_data.supers;
858 char *flags;
859 char *name;
861 while (super){
862 switch (SUP.flags & (FISH_FLAG_RSH | FISH_FLAG_COMPRESSED)) {
863 case FISH_FLAG_RSH:
864 flags = ":r";
865 break;
866 case FISH_FLAG_COMPRESSED:
867 flags = ":C";
868 break;
869 case FISH_FLAG_RSH | FISH_FLAG_COMPRESSED:
870 flags = "";
871 break;
872 default:
873 flags = "";
874 break;
877 name = g_strconcat ("/#sh:", SUP.user, "@", SUP.host, flags,
878 "/", SUP.cwdir, NULL);
879 (*func)(name);
880 g_free (name);
881 super = super->next;
885 vfs vfs_fish_ops = {
886 NULL, /* This is place of next pointer */
887 "fish",
888 F_EXEC, /* flags */
889 "sh:", /* prefix */
890 &fish_data, /* data */
891 0, /* errno */
892 NULL,
893 NULL,
894 fish_fill_names,
895 NULL,
897 vfs_s_open,
898 vfs_s_close,
899 vfs_s_read,
900 vfs_s_write,
902 vfs_s_opendir,
903 vfs_s_readdir,
904 vfs_s_closedir,
905 vfs_s_telldir,
906 vfs_s_seekdir,
908 vfs_s_stat,
909 vfs_s_lstat,
910 vfs_s_fstat,
912 fish_chmod,
913 fish_chown,
914 NULL, /* utime */
916 vfs_s_readlink,
917 fish_symlink, /* symlink */
918 fish_link, /* link */
919 fish_unlink,
921 fish_rename, /* rename */
922 vfs_s_chdir,
923 vfs_s_ferrno,
924 vfs_s_lseek,
925 NULL, /* mknod */
927 vfs_s_getid,
928 vfs_s_nothingisopen,
929 vfs_s_free,
931 NULL, /* vfs_s_getlocalcopy, */
932 NULL, /* vfs_s_ungetlocalcopy, */
934 fish_mkdir,
935 fish_rmdir,
936 fish_ctl,
937 vfs_s_setctl
939 MMAPNULL