* syntax/Syntax: Associate *.itcl with tcl.syntax.
[midnight-commander.git] / vfs / fish.c
blob3539ac5dc80dcc8d2747c1a53e28bab7d5308cd7
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 ifree (SUP.host);
154 ifree (SUP.home);
155 ifree (SUP.user);
156 ifree (SUP.cwdir);
157 ifree (SUP.password);
160 static void
161 pipeopen(vfs_s_super *super, char *path, char *argv[])
163 int fileset1[2], fileset2[2];
164 int res;
166 if ((pipe(fileset1)<0) || (pipe(fileset2)<0))
167 vfs_die("Could not pipe(): %m.");
169 if ((res = fork())) {
170 if (res<0) vfs_die("Could not fork(): %m.");
171 /* We are the parent */
172 close(fileset1[0]);
173 SUP.sockw = fileset1[1];
174 close(fileset2[1]);
175 SUP.sockr = fileset2[0];
176 } else {
177 close(0);
178 dup(fileset1[0]);
179 close(fileset1[0]); close(fileset1[1]);
180 close(1); close(2);
181 dup(fileset2[1]);
182 /* stderr to /dev/null */
183 open ("/dev/null", O_WRONLY);
184 close(fileset2[0]); close(fileset2[1]);
185 execvp(path, argv);
186 vfs_die("Exec failed.");
190 /* The returned directory should always contain a trailing slash */
191 static char *fish_getcwd(vfs *me, vfs_s_super *super)
193 if (command(me, super, WANT_STRING, "#PWD\npwd; echo '### 200'\n") == COMPLETE)
194 return g_strconcat (reply_str, "/", NULL);
195 ERRNOR (EIO, NULL);
197 static int
198 open_archive_int (vfs *me, vfs_s_super *super)
200 char *argv[100];
201 char *xsh = (SUP.flags == FISH_FLAG_RSH ? "rsh" : "ssh");
202 int i = 0;
204 argv[i++] = xsh;
205 #ifdef HAVE_HACKED_SSH
206 argv[i++] = "-I";
207 #endif
208 argv[i++] = "-l";
209 argv[i++] = SUP.user;
210 argv[i++] = SUP.host;
211 if (SUP.flags == FISH_FLAG_COMPRESSED)
212 argv[i++] = "-C";
213 argv[i++] = "echo FISH:; /bin/sh";
214 argv[i++] = NULL;
216 #if 0
217 /* Debugging hack */
218 if (!MEDATA->logfile)
219 MEDATA->logfile = fopen( "/home/pavel/talk.fish", "w+" ); /* FIXME */
220 #endif
222 pipeopen(super, xsh, argv );
225 char answer[2048];
226 print_vfs_message( _("fish: Waiting for initial line...") );
227 if (!vfs_s_get_line(me, SUP.sockr, answer, sizeof(answer), ':'))
228 ERRNOR (E_PROTO, -1);
229 print_vfs_message( answer );
230 if (strstr(answer, "assword")) {
232 /* Currently, this does not work. ssh reads passwords from
233 /dev/tty, not from stdin :-(. */
235 #ifndef HAVE_HACKED_SSH
236 message_1s (1, MSG_ERROR, _("Sorry, we can not do password authenticated connections for now."));
237 ERRNOR (EPERM, -1);
238 #endif
239 if (!SUP.password){
240 char *p, *op;
241 p = g_strconcat (_(" fish: Password required for "), SUP.user,
242 " ", NULL);
243 op = vfs_get_password (p);
244 g_free (p);
245 if (op == NULL)
246 ERRNOR (EPERM, -1);
247 SUP.password = g_strdup (op);
248 wipe_password(op);
250 print_vfs_message( _("fish: Sending password...") );
251 write(SUP.sockw, SUP.password, strlen(SUP.password));
252 write(SUP.sockw, "\n", 1);
256 print_vfs_message( _("fish: Sending initial line...") );
258 * Run `start_fish_server'. If it doesn't exist - no problem,
259 * we'll talk directly to the shell.
261 if (command (me, super, WAIT_REPLY,
262 "#FISH\necho; start_fish_server 2>&1;"
263 " echo '### 200'\n") != COMPLETE)
264 ERRNOR (E_PROTO, -1);
266 print_vfs_message( _("fish: Handshaking version...") );
267 if (command (me, super, WAIT_REPLY, "#VER 0.0.0\necho '### 000'\n") != COMPLETE)
268 ERRNOR (E_PROTO, -1);
270 /* Set up remote locale to C, otherwise dates cannot be recognized */
271 if (command (me, super, WAIT_REPLY, "LANG=C; LC_ALL=C; LC_TIME=C\n"
272 "export LANG; export LC_ALL; export LC_TIME\n"
273 "echo '### 200'\n") != COMPLETE)
274 ERRNOR (E_PROTO, -1);
276 print_vfs_message( _("fish: Setting up current directory...") );
277 SUP.home = fish_getcwd (me, super);
278 print_vfs_message( _("fish: Connected, home %s."), SUP.home );
279 #if 0
280 super->name = g_strconcat ( "/#sh:", SUP.user, "@", SUP.host, "/", NULL );
281 #endif
282 super->name = g_strdup(PATH_SEP_STR);
284 super->root = vfs_s_new_inode (me, super, vfs_s_default_stat(me, S_IFDIR | 0755));
285 return 0;
288 static int
289 open_archive (vfs *me, vfs_s_super *super, char *archive_name, char *op)
291 char *host, *user, *password, *p;
292 int flags;
294 p = vfs_split_url (strchr(op, ':')+1, &host, &user, &flags, &password, 0, URL_NOSLASH);
296 if (p)
297 g_free (p);
299 SUP.host = host;
300 SUP.user = user;
301 SUP.flags = flags;
302 if (!strncmp( op, "rsh:", 4 ))
303 SUP.flags |= FISH_FLAG_RSH;
304 SUP.home = NULL;
305 if (password)
306 SUP.password = password;
307 return open_archive_int (me, super);
310 static int
311 archive_same(vfs *me, vfs_s_super *super, char *archive_name, char *op, void *cookie)
313 char *host, *user;
314 int flags;
316 op = vfs_split_url (strchr(op, ':')+1, &host, &user, &flags, 0, 0, URL_NOSLASH);
318 if (op)
319 g_free (op);
321 flags = ((strcmp (host, SUP.host) == 0) &&
322 (strcmp (user, SUP.user) == 0) &&
323 (flags == SUP.flags));
324 g_free (host);
325 g_free (user);
327 return flags;
331 fish_which (vfs *me, char *path)
333 if (!strncmp (path, "/#sh:", 5))
334 return 1;
335 if (!strncmp (path, "/#ssh:", 6))
336 return 1;
337 if (!strncmp (path, "/#rsh:", 6))
338 return 1;
339 return 0;
342 static int
343 dir_uptodate(vfs *me, vfs_s_inode *ino)
345 struct timeval tim;
347 gettimeofday(&tim, NULL);
348 if (force_expiration) {
349 force_expiration = 0;
350 return 0;
352 if (tim.tv_sec < ino->u.fish.timestamp.tv_sec)
353 return 1;
354 return 0;
357 static int
358 dir_load(vfs *me, vfs_s_inode *dir, char *remote_path)
360 vfs_s_super *super = dir->super;
361 char buffer[8192];
362 vfs_s_entry *ent = NULL;
363 FILE *logfile;
365 logfile = MEDATA->logfile;
367 print_vfs_message(_("fish: Reading directory %s..."), remote_path);
369 gettimeofday(&dir->u.fish.timestamp, NULL);
370 dir->u.fish.timestamp.tv_sec += 10; /* was 360: 10 is good for
371 stressing direntry layer a bit */
373 command(me, super, NONE,
374 "#LIST /%s\n"
375 "ls -lLa \"/%s\" 2>/dev/null | grep '^[^cbt]' | (\n"
376 "while read p x u g s m d y n; do\n"
377 "echo \"P$p $u.$g\nS$s\nd$m $d $y\n:$n\n\"\n"
378 "done\n"
379 ")\n"
380 "ls -lLa \"/%s\" 2>/dev/null | grep '^[cb]' | (\n"
381 "while read p x u g a i m d y n; do\n"
382 "echo \"P$p $u.$g\nE$a$i\nd$m $d $y\n:$n\n\"\n"
383 "done\n"
384 ")\n"
385 "echo '### 200'\n",
386 remote_path, remote_path, remote_path);
388 #define SIMPLE_ENTRY vfs_s_generate_entry(me, NULL, dir, 0)
389 ent = SIMPLE_ENTRY;
390 while (1) {
391 int res = vfs_s_get_line_interruptible (me, buffer, sizeof (buffer), SUP.sockr);
392 if ((!res) || (res == EINTR)) {
393 vfs_s_free_entry(me, ent);
394 me->verrno = ECONNRESET;
395 goto error;
397 if (logfile) {
398 fputs (buffer, logfile);
399 fputs ("\n", logfile);
400 fflush (logfile);
402 if (!strncmp(buffer, "### ", 4))
403 break;
404 if ((!buffer[0])) {
405 if (ent->name) {
406 vfs_s_insert_entry(me, dir, ent);
407 ent = SIMPLE_ENTRY;
409 continue;
412 #define ST ent->ino->st
414 switch(buffer[0]) {
415 case ':': {
416 /* char *c; */
417 if (!strcmp(buffer+1, ".") || !strcmp(buffer+1, ".."))
418 break; /* We'll do . and .. ourself */
419 ent->name = g_strdup(buffer+1);
420 /* if ((c=strchr(ent->name, ' ')))
421 *c = 0; / * this is ugly, but we can not handle " " in name */
422 break;
424 case 'S': ST.st_size = atoi(buffer+1); break;
425 case 'P': {
426 int i;
427 if ((i = vfs_parse_filetype(buffer[1])) ==-1)
428 break;
429 ST.st_mode = i;
430 if ((i = vfs_parse_filemode(buffer+2)) ==-1)
431 break;
432 ST.st_mode |= i;
433 if (S_ISLNK(ST.st_mode))
434 ST.st_mode = 0;
436 break;
437 case 'd': {
438 vfs_split_text(buffer+1);
439 if (!vfs_parse_filedate(0, &ST.st_ctime))
440 break;
441 ST.st_atime = ST.st_mtime = ST.st_ctime;
443 break;
444 case 'D': {
445 struct tm tim;
446 if (sscanf(buffer+1, "%d %d %d %d %d %d", &tim.tm_year, &tim.tm_mon,
447 &tim.tm_mday, &tim.tm_hour, &tim.tm_min, &tim.tm_sec) != 6)
448 break;
449 ST.st_atime = ST.st_mtime = ST.st_ctime = mktime(&tim);
451 break;
452 case 'E': {
453 int maj, min;
454 if (sscanf(buffer+1, "%d,%d", &maj, &min) != 2)
455 break;
456 #ifdef HAVE_ST_RDEV
457 ST.st_rdev = (maj << 8) | min;
458 #endif
460 case 'L': ent->ino->linkname = g_strdup(buffer+1);
461 break;
465 vfs_s_free_entry (me, ent);
466 me->verrno = E_REMOTE;
467 if (decode_reply(buffer+4, 0) == COMPLETE)
468 return 0;
470 error:
471 print_vfs_message(_("fish: failed"));
472 return 1;
475 static int
476 file_store(vfs *me, vfs_s_fh *fh, char *name, char *localname)
478 vfs_s_super *super = FH_SUPER;
479 int n, total;
480 char buffer[8192];
481 struct stat s;
482 int was_error = 0;
483 int h;
485 h = open(localname, O_RDONLY);
487 if (fstat(h, &s)<0)
488 ERRNOR (EIO, -1);
490 /* Use this as stor: ( dd block ; dd smallblock ) | ( cat > file; cat > /dev/null ) */
492 print_vfs_message(_("fish: store %s: sending command..."), name );
494 * FIXME: Limit size to unsigned long for now.
495 * Files longer than 256 * ULONG_MAX are not supported.
497 if (command (me, super, WAIT_REPLY,
498 "#STOR %lu /%s\n"
499 "> \"/%s\"\n"
500 "echo '### 001'\n"
501 "(\n"
502 "dd ibs=256 obs=4096 count=%lu\n"
503 "dd bs=%lu count=1\n"
504 ") 2>/dev/null | (\n"
505 "cat > \"/%s\"\n"
506 "cat > /dev/null\n"
507 "); echo '### 200'\n",
508 (unsigned long) s.st_size, name, name,
509 (unsigned long) (s.st_size >> 8),
510 ((unsigned long) s.st_size) & (256 - 1), name)
511 != PRELIM)
512 ERRNOR(E_REMOTE, -1);
514 total = 0;
516 while (1) {
517 while ((n = read(h, buffer, sizeof(buffer))) < 0) {
518 if ((errno == EINTR) && got_interrupt)
519 continue;
520 print_vfs_message(_("fish: Local read failed, sending zeros") );
521 close(h);
522 h = open( "/dev/zero", O_RDONLY );
524 if (n == 0)
525 break;
526 while (write(SUP.sockw, buffer, n) < 0) {
527 me->verrno = errno;
528 goto error_return;
530 disable_interrupt_key();
531 total += n;
532 print_vfs_message(_("fish: storing %s %d (%lu)"),
533 was_error ? _("zeros") : _("file"), total,
534 (unsigned long) s.st_size);
536 if ((get_reply (me, SUP.sockr, NULL, 0) != COMPLETE) || was_error)
537 ERRNOR (E_REMOTE, -1);
538 close(h);
539 return 0;
540 error_return:
541 close(h);
542 get_reply(me, SUP.sockr, NULL, 0);
543 return -1;
546 static int linear_start(vfs *me, vfs_s_fh *fh, int offset)
548 char *name;
549 if (offset)
550 ERRNOR (E_NOTSUPP, 0);
551 /* fe->local_stat.st_mtime = 0; FIXME: what is this good for? */
552 name = vfs_s_fullpath (me, fh->ino);
553 if (!name)
554 return 0;
555 offset = command(me, FH_SUPER, WANT_STRING,
556 "#RETR /%s\n"
557 "ls -l \"/%s\" 2>/dev/null | (\n"
558 "read var1 var2 var3 var4 var5 var6\n"
559 "echo \"$var5\"\n"
560 ")\n"
561 "echo '### 100'\n"
562 "cat \"/%s\"\n"
563 "echo '### 200'\n",
564 name, name, name );
565 g_free (name);
566 if (offset != PRELIM) ERRNOR (E_REMOTE, 0);
567 fh->linear = LS_LINEAR_OPEN;
568 fh->u.fish.got = 0;
569 if (sscanf( reply_str, "%d", &fh->u.fish.total )!=1)
570 ERRNOR (E_REMOTE, 0);
571 return 1;
574 static void
575 linear_abort (vfs *me, vfs_s_fh *fh)
577 vfs_s_super *super = FH_SUPER;
578 char buffer[8192];
579 int n;
581 print_vfs_message( _("Aborting transfer...") );
582 do {
583 n = MIN(8192, fh->u.fish.total - fh->u.fish.got);
584 if (n)
585 if ((n = read(SUP.sockr, buffer, n)) < 0)
586 return;
587 } while (n);
589 if (get_reply (me, SUP.sockr, NULL, 0) != COMPLETE)
590 print_vfs_message( _("Error reported after abort.") );
591 else
592 print_vfs_message( _("Aborted transfer would be successful.") );
595 static int
596 linear_read (vfs *me, vfs_s_fh *fh, void *buf, int len)
598 vfs_s_super *super = FH_SUPER;
599 int n = 0;
600 len = MIN( fh->u.fish.total - fh->u.fish.got, len );
601 disable_interrupt_key();
602 while (len && ((n = read (SUP.sockr, buf, len))<0)) {
603 if ((errno == EINTR) && !got_interrupt())
604 continue;
605 break;
607 enable_interrupt_key();
609 if (n>0) fh->u.fish.got += n;
610 if (n<0) linear_abort(me, fh);
611 if ((!n) && ((get_reply (me, SUP.sockr, NULL, 0) != COMPLETE)))
612 ERRNOR (E_REMOTE, -1);
613 ERRNOR (errno, n);
616 static void
617 linear_close (vfs *me, vfs_s_fh *fh)
619 if (fh->u.fish.total != fh->u.fish.got)
620 linear_abort(me, fh);
621 else if (stat (fh->ino->localname, &fh->ino->u.fish.local_stat) < 0)
622 fh->ino->u.fish.local_stat.st_mtime = 0;
625 static int
626 fish_ctl (void *fh, int ctlop, int arg)
628 return 0;
629 switch (ctlop) {
630 case MCCTL_IS_NOTREADY:
632 int v;
634 if (!FH->linear)
635 vfs_die ("You may not do this");
636 if (FH->linear == LS_LINEAR_CLOSED)
637 return 0;
639 v = vfs_s_select_on_two (FH_SUPER->u.fish.sockr, 0);
640 if (((v < 0) && (errno == EINTR)) || v == 0)
641 return 1;
642 return 0;
644 default:
645 return 0;
649 static int
650 send_fish_command(vfs *me, vfs_s_super *super, char *cmd, int flags)
652 int r;
654 r = command (me, super, WAIT_REPLY, cmd);
655 vfs_add_noncurrent_stamps (&vfs_fish_ops, (vfsid) super, NULL);
656 if (r != COMPLETE) ERRNOR (E_REMOTE, -1);
657 if (flags & OPT_FLUSH)
658 vfs_s_invalidate(me, super);
659 return 0;
662 #define PREFIX \
663 char buf[BUF_LARGE]; \
664 char *rpath; \
665 vfs_s_super *super; \
666 if (!(rpath = vfs_s_get_path_mangle(me, path, &super, 0))) \
667 return -1;
669 #define POSTFIX(flags) \
670 return send_fish_command(me, super, buf, flags);
672 static int
673 fish_chmod (vfs *me, char *path, int mode)
675 PREFIX
676 g_snprintf(buf, sizeof(buf), "#CHMOD %4.4o /%s\n"
677 "chmod %4.4o \"/%s\" 2>/dev/null\n"
678 "echo '### 000'\n",
679 mode & 07777, rpath,
680 mode & 07777, rpath);
681 POSTFIX(OPT_FLUSH);
684 #define FISH_OP(name, chk, string) \
685 static int fish_##name (vfs *me, char *path1, char *path2) \
687 char buf[BUF_LARGE]; \
688 char *rpath1 = NULL, *rpath2 = NULL; \
689 vfs_s_super *super1, *super2; \
690 if (!(rpath1 = vfs_s_get_path_mangle(me, path1, &super1, 0))) \
691 return -1; \
692 if (!(rpath2 = vfs_s_get_path_mangle(me, path2, &super2, 0))) \
693 return -1; \
694 g_snprintf(buf, 1023, string "\n", rpath1, rpath2, rpath1, rpath2 ); \
695 return send_fish_command(me, super2, buf, OPT_FLUSH); \
698 #define XTEST if (bucket1 != bucket2) { ERRNOR (EXDEV, -1); }
699 FISH_OP(rename, XTEST, "#RENAME /%s /%s\n"
700 "mv \"/%s\" \"/%s\" 2>/dev/null\n"
701 "echo '### 000'" )
702 FISH_OP(link, XTEST, "#LINK /%s /%s\n"
703 "ln \"/%s\" \"/%s\" 2>/dev/null\n"
704 "echo '### 000'" )
706 static int fish_symlink (vfs *me, char *setto, char *path)
708 PREFIX
709 g_snprintf(buf, sizeof(buf),
710 "#SYMLINK %s /%s\n"
711 "ln -s \"%s\" \"/%s\" 2>/dev/null\n"
712 "echo '### 000'\n",
713 setto, rpath, setto, rpath);
714 POSTFIX(OPT_FLUSH);
717 static int
718 fish_chown (vfs *me, char *path, int owner, int group)
720 char *sowner, *sgroup;
721 struct passwd *pw;
722 struct group *gr;
723 PREFIX
725 if ((pw = getpwuid (owner)) == NULL)
726 return 0;
728 if ((gr = getgrgid (group)) == NULL)
729 return 0;
731 sowner = pw->pw_name;
732 sgroup = gr->gr_name;
733 g_snprintf(buf, sizeof(buf),
734 "#CHOWN /%s /%s\n"
735 "chown %s \"/%s\" 2>/dev/null\n"
736 "echo '### 000'\n",
737 sowner, rpath,
738 sowner, rpath);
739 send_fish_command(me, super, buf, OPT_FLUSH);
740 /* FIXME: what should we report if chgrp succeeds but chown fails? */
741 g_snprintf(buf, sizeof(buf),
742 "#CHGRP /%s /%s\n"
743 "chgrp %s \"/%s\" 2>/dev/null\n"
744 "echo '### 000'\n",
745 sgroup, rpath,
746 sgroup, rpath);
747 /* send_fish_command(me, super, buf, OPT_FLUSH); */
748 POSTFIX(OPT_FLUSH)
751 static int fish_unlink (vfs *me, char *path)
753 PREFIX
754 g_snprintf(buf, sizeof(buf),
755 "#DELE /%s\n"
756 "rm -f \"/%s\" 2>/dev/null\n"
757 "echo '### 000'\n",
758 rpath, rpath);
759 POSTFIX(OPT_FLUSH);
762 static int fish_mkdir (vfs *me, char *path, mode_t mode)
764 PREFIX
765 g_snprintf(buf, sizeof(buf),
766 "#MKD /%s\n"
767 "mkdir \"/%s\" 2>/dev/null\n"
768 "echo '### 000'\n",
769 rpath, rpath);
770 POSTFIX(OPT_FLUSH);
773 static int fish_rmdir (vfs *me, char *path)
775 PREFIX
776 g_snprintf(buf, sizeof(buf),
777 "#RMD /%s\n"
778 "rmdir \"/%s\" 2>/dev/null\n"
779 "echo '### 000'\n",
780 rpath, rpath);
781 POSTFIX(OPT_FLUSH);
784 static int fish_fh_open (vfs *me, vfs_s_fh *fh, int flags, int mode)
786 /* File will be written only, so no need to retrieve it */
787 if (((flags & O_WRONLY) == O_WRONLY) && !(flags & (O_RDONLY|O_RDWR))){
788 if (!fh->ino->localname){
789 int tmp_handle = mc_mkstemps (&fh->ino->localname, me->name, NULL);
790 if (tmp_handle == -1)
791 return -1;
792 close (tmp_handle);
794 return 0;
796 if (!fh->ino->localname)
797 if (vfs_s_retrieve_file (me, fh->ino)==-1)
798 return -1;
799 if (!fh->ino->localname)
800 vfs_die( "retrieve_file failed to fill in localname" );
801 return 0;
804 static struct vfs_s_data fish_data = {
805 NULL,
808 NULL,
810 NULL, /* init_inode */
811 NULL, /* free_inode */
812 NULL, /* init_entry */
814 NULL, /* archive_check */
815 archive_same,
816 open_archive,
817 free_archive,
819 fish_fh_open, /* fh_open */
820 NULL, /* fh_close */
822 vfs_s_find_entry_linear,
823 dir_load,
824 dir_uptodate,
825 file_store,
827 linear_start,
828 linear_read,
829 linear_close
832 vfs vfs_fish_ops = {
833 NULL, /* This is place of next pointer */
834 "fish",
835 F_EXEC, /* flags */
836 "sh:", /* prefix */
837 &fish_data, /* data */
838 0, /* errno */
839 NULL,
840 NULL,
841 vfs_s_fill_names,
842 NULL,
844 vfs_s_open,
845 vfs_s_close,
846 vfs_s_read,
847 vfs_s_write,
849 vfs_s_opendir,
850 vfs_s_readdir,
851 vfs_s_closedir,
852 vfs_s_telldir,
853 vfs_s_seekdir,
855 vfs_s_stat,
856 vfs_s_lstat,
857 vfs_s_fstat,
859 fish_chmod,
860 fish_chown,
861 NULL, /* utime */
863 vfs_s_readlink,
864 fish_symlink, /* symlink */
865 fish_link, /* link */
866 fish_unlink,
868 fish_rename, /* rename */
869 vfs_s_chdir,
870 vfs_s_ferrno,
871 vfs_s_lseek,
872 NULL, /* mknod */
874 vfs_s_getid,
875 vfs_s_nothingisopen,
876 vfs_s_free,
878 NULL, /* vfs_s_getlocalcopy, */
879 NULL, /* vfs_s_ungetlocalcopy, */
881 fish_mkdir,
882 fish_rmdir,
883 fish_ctl,
884 vfs_s_setctl
886 MMAPNULL