mc.sh & mc.csh creation fixed...
[midnight-commander.git] / vfs / mcfs.c
blobe1f8cf358b8706c403fdd13cfd87dd8f69abb7cd
1 /* Virtual File System: Midnight Commander file system.
3 Copyright (C) 1995, 1996, 1997 The Free Software Foundation
5 Written by Miguel de Icaza
6 Andrej Borsenkow
7 Norbert Warmuth
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public License
11 as published by the Free Software Foundation; either version 2 of
12 the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Library General Public License for more details.
19 You should have received a copy of the GNU Library General Public
20 License along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23 /* Namespace: exports mcfs_vfs_ops, tcp_invalidate_socket */
25 #include <config.h>
27 #ifdef WITH_MCFS
28 #include <stdio.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <pwd.h>
35 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
36 #include <netdb.h> /* struct hostent */
37 #include <sys/socket.h> /* AF_INET */
38 #include <netinet/in.h> /* struct in_addr */
39 #ifdef HAVE_ARPA_INET_H
40 #include <arpa/inet.h>
41 #endif
43 #ifdef HAVE_PMAP_SET
44 #include <rpc/rpc.h>
45 #include <rpc/pmap_prot.h>
46 #ifdef HAVE_RPC_PMAP_CLNT_H
47 #include <rpc/pmap_clnt.h>
48 #endif
49 #endif
51 #include "utilvfs.h"
53 #include "vfs.h"
54 #include "mcfs.h"
55 #include "mcfsutil.h"
56 #include "tcputil.h"
57 #include "../src/dialog.h"
59 #define MCFS_MAX_CONNECTIONS 32
61 static struct _mcfs_connection {
62 char *host;
63 char *user;
64 char *home;
65 int sock;
66 int port;
67 int version;
68 } mcfs_connections[MCFS_MAX_CONNECTIONS];
71 #define mcserver_port 9876
73 typedef struct _mcfs_connection mcfs_connection;
75 typedef struct {
76 int handle;
77 mcfs_connection *conn;
78 } mcfs_handle;
80 static int my_errno;
82 static char *mcfs_gethome (mcfs_connection * mc);
84 /* Extract the hostname and username from the path */
85 /* path is in the form: hostname:user/remote-dir */
86 static char *
87 mcfs_get_host_and_username (char *path, char **host, char **user,
88 int *port, char **pass)
90 return vfs_split_url (path, host, user, port, pass, 0, 0);
93 static void
94 mcfs_fill_names (vfs *me, void (*func) (char *))
96 int i;
97 char *name;
99 for (i = 0; i < MCFS_MAX_CONNECTIONS; i++) {
100 if (mcfs_connections[i].host == 0)
101 continue;
102 name = g_strconcat ("/#mc:", mcfs_connections[i].user,
103 "@", mcfs_connections[i].host, NULL);
104 (*func) (name);
105 g_free (name);
109 /* This routine checks the server RPC version and logs the user in */
110 static int
111 mcfs_login_server (int my_socket, char *user, int port,
112 int port_autodetected, char *netrcpass, int *version)
114 int result;
115 char *pass;
117 /* Send the version number */
118 rpc_send (my_socket, RPC_INT, *version, RPC_END);
119 if (0 == rpc_get (my_socket, RPC_INT, &result, RPC_END))
120 return 0;
122 if (result != MC_VERSION_OK) {
123 message_1s (1, _(" MCFS "),
124 _(" The server does not support this version "));
125 close (my_socket);
126 return 0;
129 /* FIXME: figure out why last_current_dir used to be passed here */
130 rpc_send (my_socket, RPC_INT, MC_LOGIN, RPC_STRING, "/",
131 RPC_STRING, user, RPC_END);
133 if (0 == rpc_get (my_socket, RPC_INT, &result, RPC_END))
134 return 0;
136 if (result == MC_NEED_PASSWORD) {
137 if (port > 1024 && port_autodetected) {
138 int v;
139 v = query_dialog (_("Warning"),
141 (" The remote server is not running on a system port \n"
142 " you need a password to log in, but the information may \n"
143 " not be safe on the remote side. Continue? \n"),
144 3, 2, _("&Yes"), _("&No"));
146 if (v == 1) {
147 close (my_socket);
148 return 0;
151 if (netrcpass != NULL)
152 pass = g_strdup (netrcpass);
153 else
154 pass = vfs_get_password (_(" MCFS Password required "));
155 if (!pass) {
156 rpc_send (my_socket, RPC_INT, MC_QUIT, RPC_END);
157 close (my_socket);
158 return 0;
160 rpc_send (my_socket, RPC_INT, MC_PASS, RPC_STRING, pass, RPC_END);
162 wipe_password (pass);
164 if (0 == rpc_get (my_socket, RPC_INT, &result, RPC_END))
165 return 0;
167 if (result != MC_LOGINOK) {
168 message_1s (1, _(" MCFS "), _(" Invalid password "));
169 rpc_send (my_socket, RPC_INT, MC_QUIT, RPC_END);
170 close (my_socket);
171 return 0;
174 return my_socket;
177 static int
178 get_remote_port (struct sockaddr_in *sin, int *version)
180 #ifdef HAVE_PMAP_GETMAPS
181 int port;
182 struct pmaplist *pl;
184 *version = 1;
185 port = mcserver_port;
186 for (pl = pmap_getmaps (sin); pl; pl = pl->pml_next)
187 if (pl->pml_map.pm_prog == RPC_PROGNUM
188 && pl->pml_map.pm_prot == IPPROTO_TCP
189 && pl->pml_map.pm_vers >= *version) {
190 *version = pl->pml_map.pm_vers;
191 port = pl->pml_map.pm_port;
193 return port;
194 #else
195 #ifdef HAVE_PMAP_GETPORT
196 int port;
197 for (*version = RPC_PROGVER; *version >= 1; (*version)--)
198 if (port = pmap_getport (sin, RPC_PROGNUM, *version, IPPROTO_TCP))
199 return port;
200 #endif /* HAVE_PMAP_GETPORT */
201 #endif /* HAVE_PMAP_GETMAPS */
202 *version = 1;
203 return mcserver_port;
206 /* This used to be in utilvfs.c, but as it deals with portmapper, it
207 is probably usefull for mcfs */
208 static int
209 open_tcp_link (char *host, int *port, int *version, char *caller)
211 struct sockaddr_in server_address;
212 unsigned long inaddr;
213 struct hostent *hp;
214 int my_socket;
216 if (!*host)
217 return 0;
219 memset ((char *) &server_address, 0, sizeof (server_address));
220 server_address.sin_family = AF_INET;
222 /* Try to use the dotted decimal number */
223 if ((inaddr = inet_addr (host)) != -1)
224 memcpy ((char *) &server_address.sin_addr, (char *) &inaddr,
225 sizeof (inaddr));
226 else {
227 if ((hp = gethostbyname (host)) == NULL) {
228 message_2s (1, caller, _(" Cannot locate hostname: %s "),
229 host);
230 return 0;
232 memcpy ((char *) &server_address.sin_addr, (char *) hp->h_addr,
233 hp->h_length);
236 /* Try to contact a remote portmapper to obtain the listening port */
237 if (*port == 0) {
238 *port = get_remote_port (&server_address, version);
239 if (*port < 1)
240 return 0;
241 } else
242 *version = 1;
244 server_address.sin_port = htons (*port);
246 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
247 message_2s (1, caller, _(" Cannot create socket: %s "),
248 unix_error_string (errno));
249 return 0;
251 if (connect (my_socket, (struct sockaddr *) &server_address,
252 sizeof (server_address)) < 0) {
253 message_2s (1, caller, _(" Cannot connect to server: %s "),
254 unix_error_string (errno));
255 close (my_socket);
256 return 0;
258 return my_socket;
261 static int
262 mcfs_open_tcp_link (char *host, char *user,
263 int *port, char *netrcpass, int *version)
265 int my_socket;
266 int old_port = *port;
268 my_socket = open_tcp_link (host, port, version, " MCfs ");
269 if (my_socket <= 0)
270 return 0;
272 /* We got the connection to the server, verify if the server
273 implements our version of the RPC mechanism and then login
274 the user.
276 return mcfs_login_server (my_socket, user, *port, old_port == 0,
277 netrcpass, version);
280 static int mcfs_get_free_bucket_init = 1;
281 static mcfs_connection *
282 mcfs_get_free_bucket (void)
284 int i;
286 if (mcfs_get_free_bucket_init) {
287 mcfs_get_free_bucket_init = 0;
288 for (i = 0; i < MCFS_MAX_CONNECTIONS; i++)
289 mcfs_connections[i].host = 0;
291 for (i = 0; i < MCFS_MAX_CONNECTIONS; i++) {
292 if (!mcfs_connections[i].host)
293 return &mcfs_connections[i];
295 /* This can't happend, since we have checked for max connections before */
296 vfs_die ("Internal error: mcfs_get_free_bucket");
297 return 0; /* shut up, stupid gcc */
300 /* This routine keeps track of open connections */
301 /* Returns a connected socket to host */
302 static mcfs_connection *
303 mcfs_open_link (char *host, char *user, int *port, char *netrcpass)
305 static int mcfs_open_connections = 0;
306 int i, sock, version;
307 mcfs_connection *bucket;
309 /* Is the link actually open? */
310 if (mcfs_get_free_bucket_init) {
311 mcfs_get_free_bucket_init = 0;
312 for (i = 0; i < MCFS_MAX_CONNECTIONS; i++)
313 mcfs_connections[i].host = 0;
314 } else
315 for (i = 0; i < MCFS_MAX_CONNECTIONS; i++) {
316 if (!mcfs_connections[i].host)
317 continue;
318 if ((strcmp (host, mcfs_connections[i].host) == 0) &&
319 (strcmp (user, mcfs_connections[i].user) == 0))
320 return &mcfs_connections[i];
322 if (mcfs_open_connections == MCFS_MAX_CONNECTIONS) {
323 message_1s (1, MSG_ERROR, _(" Too many open connections "));
324 return 0;
327 if (!
328 (sock =
329 mcfs_open_tcp_link (host, user, port, netrcpass, &version)))
330 return 0;
332 bucket = mcfs_get_free_bucket ();
333 mcfs_open_connections++;
334 bucket->host = g_strdup (host);
335 bucket->user = g_strdup (user);
336 bucket->home = 0;
337 bucket->port = *port;
338 bucket->sock = sock;
339 bucket->version = version;
341 return bucket;
344 static int
345 is_error (int result, int errno_num)
347 if (!(result == -1))
348 return my_errno = 0;
349 else
350 my_errno = errno_num;
351 return 1;
354 static int
355 the_error (int result, int errno_num)
357 if (result == -1)
358 my_errno = errno_num;
359 else
360 my_errno = 0;
361 return result;
364 static char *
365 mcfs_get_path (mcfs_connection **mc, char *path)
367 char *user, *host, *remote_path;
368 char *pass;
369 int port;
371 /* An absolute path name, try to determine connection socket */
372 if (strncmp (path, "/#mc:", 5))
373 return NULL;
374 path += 5;
376 /* Port = 0 means that open_tcp_link will try to contact the
377 * remote portmapper to get the port number
379 port = 0;
380 if ((remote_path =
381 mcfs_get_host_and_username (path, &host, &user, &port, &pass)))
382 if (!(*mc = mcfs_open_link (host, user, &port, pass))) {
383 g_free (remote_path);
384 remote_path = NULL;
386 g_free (host);
387 g_free (user);
388 if (pass)
389 wipe_password (pass);
391 if (!remote_path)
392 return NULL;
394 /* NOTE: tildes are deprecated. See ftpfs.c */
396 int f = !strcmp (remote_path, "/~");
397 if (f || !strncmp (remote_path, "/~/", 3)) {
398 char *s;
399 s = concat_dir_and_file (mcfs_gethome (*mc),
400 remote_path + 3 - f);
401 g_free (remote_path);
402 remote_path = s;
405 return remote_path;
408 /* Simple function for routines returning only an integer from the server */
409 static int
410 mcfs_handle_simple_error (int sock, int return_status)
412 int status, error;
414 if (0 == rpc_get (sock, RPC_INT, &status, RPC_INT, &error, RPC_END))
415 return the_error (-1, EIO);
417 if (is_error (status, error))
418 return -1;
419 if (return_status)
420 return status;
421 return 0;
424 /* Nice wrappers */
425 static int
426 mcfs_rpc_two_paths (int command, char *s1, char *s2)
428 mcfs_connection *mc;
429 char *r1, *r2;
431 if ((r1 = mcfs_get_path (&mc, s1)) == 0)
432 return -1;
434 if ((r2 = mcfs_get_path (&mc, s2)) == 0) {
435 g_free (r1);
436 return -1;
439 rpc_send (mc->sock,
440 RPC_INT, command, RPC_STRING, r1, RPC_STRING, r2, RPC_END);
441 g_free (r1);
442 g_free (r2);
443 return mcfs_handle_simple_error (mc->sock, 0);
446 static int
447 mcfs_rpc_path (int command, char *path)
449 mcfs_connection *mc;
450 char *remote_file;
452 if ((remote_file = mcfs_get_path (&mc, path)) == 0)
453 return -1;
455 rpc_send (mc->sock,
456 RPC_INT, command, RPC_STRING, remote_file, RPC_END);
458 g_free (remote_file);
459 return mcfs_handle_simple_error (mc->sock, 0);
462 static int
463 mcfs_rpc_path_int (int command, char *path, int data)
465 mcfs_connection *mc;
466 char *remote_file;
468 if ((remote_file = mcfs_get_path (&mc, path)) == 0)
469 return -1;
471 rpc_send (mc->sock,
472 RPC_INT, command,
473 RPC_STRING, remote_file, RPC_INT, data, RPC_END);
475 g_free (remote_file);
476 return mcfs_handle_simple_error (mc->sock, 0);
479 static int
480 mcfs_rpc_path_int_int (int command, char *path, int n1, int n2)
482 mcfs_connection *mc;
483 char *remote_file;
485 if ((remote_file = mcfs_get_path (&mc, path)) == 0)
486 return -1;
488 rpc_send (mc->sock,
489 RPC_INT, command,
490 RPC_STRING, remote_file, RPC_INT, n1, RPC_INT, n2, RPC_END);
492 g_free (remote_file);
493 return mcfs_handle_simple_error (mc->sock, 0);
496 static char *
497 mcfs_gethome (mcfs_connection *mc)
499 char *buffer;
501 if (mc->home)
502 return g_strdup (mc->home);
503 else {
504 rpc_send (mc->sock, RPC_INT, MC_GETHOME, RPC_END);
505 if (0 == rpc_get (mc->sock, RPC_STRING, &buffer, RPC_END))
506 return g_strdup (PATH_SEP_STR);
507 mc->home = buffer;
508 return g_strdup (buffer);
512 /* The callbacks */
513 static void *
514 mcfs_open (vfs *me, char *file, int flags, int mode)
516 char *remote_file;
517 mcfs_connection *mc;
518 int result, error_num;
519 mcfs_handle *remote_handle;
521 if (!(remote_file = mcfs_get_path (&mc, file)))
522 return 0;
524 rpc_send (mc->sock, RPC_INT, MC_OPEN, RPC_STRING, remote_file,
525 RPC_INT, flags, RPC_INT, mode, RPC_END);
526 g_free (remote_file);
528 if (0 ==
529 rpc_get (mc->sock, RPC_INT, &result, RPC_INT, &error_num, RPC_END))
530 return 0;
532 if (is_error (result, error_num))
533 return 0;
535 remote_handle = g_new (mcfs_handle, 2);
536 remote_handle->handle = result;
537 remote_handle->conn = mc;
539 return remote_handle;
542 static int
543 mcfs_read (void *data, char *buffer, int count)
545 mcfs_handle *info = (mcfs_handle *) data;
546 int result, error;
547 int handle;
548 mcfs_connection *mc;
550 mc = info->conn;
551 handle = info->handle;
553 rpc_send (mc->sock, RPC_INT, MC_READ, RPC_INT, handle,
554 RPC_INT, count, RPC_END);
556 if (0 ==
557 rpc_get (mc->sock, RPC_INT, &result, RPC_INT, &error, RPC_END))
558 return the_error (-1, EIO);
560 if (is_error (result, error))
561 return 0;
563 if (0 == rpc_get (mc->sock, RPC_BLOCK, result, buffer, RPC_END))
564 return the_error (-1, EIO);
566 return result;
569 static int
570 mcfs_write (void *data, char *buf, int nbyte)
572 mcfs_handle *info = (mcfs_handle *) data;
573 mcfs_connection *mc;
574 int handle;
576 mc = info->conn;
577 handle = info->handle;
579 rpc_send (mc->sock,
580 RPC_INT, MC_WRITE,
581 RPC_INT, handle,
582 RPC_INT, nbyte, RPC_BLOCK, nbyte, buf, RPC_END);
584 return mcfs_handle_simple_error (mc->sock, 1);
587 static int
588 mcfs_close (void *data)
590 mcfs_handle *info = (mcfs_handle *) data;
591 mcfs_connection *mc;
592 int handle, result, error;
594 if (!data)
595 return -1;
597 handle = info->handle;
598 mc = info->conn;
600 rpc_send (mc->sock, RPC_INT, MC_CLOSE, RPC_INT, handle, RPC_END);
602 if (0 ==
603 rpc_get (mc->sock, RPC_INT, &result, RPC_INT, &error, RPC_END))
604 return the_error (-1, EIO);
606 is_error (result, error);
608 g_free (data);
609 return result;
612 static int
613 mcfs_errno (vfs *me)
615 return my_errno;
618 typedef struct dir_entry {
619 char *text;
620 struct dir_entry *next;
621 struct stat my_stat;
622 int merrno;
623 } dir_entry;
625 typedef struct {
626 mcfs_connection *conn;
627 int handle;
628 dir_entry *entries;
629 dir_entry *current;
630 } opendir_info;
632 static void *
633 mcfs_opendir (vfs *me, char *dirname)
635 opendir_info *mcfs_info;
636 mcfs_connection *mc;
637 int handle, error_num;
638 char *remote_dir;
639 int result;
641 if (!(remote_dir = mcfs_get_path (&mc, dirname)))
642 return 0;
644 rpc_send (mc->sock, RPC_INT, MC_OPENDIR, RPC_STRING, remote_dir,
645 RPC_END);
646 g_free (remote_dir);
648 if (0 ==
649 rpc_get (mc->sock, RPC_INT, &result, RPC_INT, &error_num, RPC_END))
650 return 0;
652 if (is_error (result, error_num))
653 return 0;
655 handle = result;
657 mcfs_info = g_new (opendir_info, 1);
658 mcfs_info->conn = mc;
659 mcfs_info->handle = handle;
660 mcfs_info->entries = 0;
661 mcfs_info->current = 0;
663 return mcfs_info;
666 static int get_stat_info (mcfs_connection * mc, struct stat *buf);
668 static int
669 mcfs_loaddir (opendir_info *mcfs_info)
671 int status, error;
672 mcfs_connection *mc = mcfs_info->conn;
673 int link = mc->sock;
674 int first = 1;
676 rpc_send (link, RPC_INT, MC_READDIR, RPC_INT, mcfs_info->handle,
677 RPC_END);
679 for (;;) {
680 int entry_len;
681 dir_entry *new_entry;
683 if (!rpc_get (link, RPC_INT, &entry_len, RPC_END))
684 return 0;
686 if (entry_len == 0)
687 break;
689 new_entry = g_new (dir_entry, 1);
690 new_entry->text = g_new0 (char, entry_len + 1);
692 new_entry->next = 0;
693 if (first) {
694 mcfs_info->entries = new_entry;
695 mcfs_info->current = new_entry;
696 first = 0;
697 } else {
698 mcfs_info->current->next = new_entry;
699 mcfs_info->current = new_entry;
702 if (!rpc_get
703 (link, RPC_BLOCK, entry_len, new_entry->text, RPC_END))
704 return 0;
706 /* Then we get the status from the lstat */
707 if (!rpc_get (link, RPC_INT, &status, RPC_INT, &error, RPC_END))
708 return 0;
710 if (is_error (status, error))
711 new_entry->merrno = error;
712 else {
713 new_entry->merrno = 0;
714 if (!get_stat_info (mc, &(new_entry->my_stat)))
715 return 0;
718 mcfs_info->current = mcfs_info->entries;
720 return 1;
723 static void
724 mcfs_free_dir (dir_entry *de)
726 if (!de)
727 return;
728 mcfs_free_dir (de->next);
729 g_free (de->text);
730 g_free (de);
733 static union vfs_dirent mcfs_readdir_data;
735 /* The readdir routine loads the complete directory */
736 /* It's too slow to ask the server each time */
737 /* It now also sends the complete lstat information for each file */
738 static struct stat *cached_lstat_info;
740 static void *
741 mcfs_readdir (void *info)
743 opendir_info *mcfs_info;
744 char *dirent_dest;
746 mcfs_info = (opendir_info *) info;
748 if (!mcfs_info->entries)
749 if (!mcfs_loaddir (mcfs_info))
750 return NULL;
752 if (mcfs_info->current == 0) {
753 cached_lstat_info = 0;
754 mcfs_free_dir (mcfs_info->entries);
755 mcfs_info->entries = 0;
756 return NULL;
758 dirent_dest = mcfs_readdir_data.dent.d_name;
759 strncpy (dirent_dest, mcfs_info->current->text, MC_MAXPATHLEN);
760 dirent_dest[MC_MAXPATHLEN] = 0;
761 cached_lstat_info = &mcfs_info->current->my_stat;
762 mcfs_info->current = mcfs_info->current->next;
764 compute_namelen (&mcfs_readdir_data.dent);
766 return &mcfs_readdir_data;
769 static int
770 mcfs_closedir (void *info)
772 opendir_info *mcfs_info = (opendir_info *) info;
773 dir_entry *p, *q;
775 rpc_send (mcfs_info->conn->sock, RPC_INT, MC_CLOSEDIR,
776 RPC_INT, mcfs_info->handle, RPC_END);
778 for (p = mcfs_info->entries; p;) {
779 q = p;
780 p = p->next;
781 g_free (q->text);
782 g_free (q);
784 g_free (info);
785 return 0;
788 static time_t
789 mcfs_get_time (mcfs_connection *mc)
791 int sock = mc->sock;
793 if (mc->version == 1) {
794 struct tm tt;
796 rpc_get (sock,
797 RPC_INT, &tt.tm_sec,
798 RPC_INT, &tt.tm_min,
799 RPC_INT, &tt.tm_hour,
800 RPC_INT, &tt.tm_mday,
801 RPC_INT, &tt.tm_year, RPC_INT, &tt.tm_mon, RPC_END);
802 tt.tm_year -= 1900;
803 tt.tm_isdst = 0;
805 return mktime (&tt);
806 } else {
807 char *buf;
808 long tm;
810 rpc_get (sock, RPC_STRING, &buf, RPC_END);
811 sscanf (buf, "%lx", &tm);
812 g_free (buf);
814 return (time_t) tm;
818 static int
819 get_stat_info (mcfs_connection *mc, struct stat *buf)
821 long mylong;
822 int sock = mc->sock;
824 buf->st_dev = 0;
826 rpc_get (sock, RPC_INT, &mylong, RPC_END);
827 #ifdef HAVE_ST_RDEV
828 buf->st_rdev = mylong;
829 #endif
830 rpc_get (sock, RPC_INT, &mylong, RPC_END);
831 buf->st_ino = mylong;
832 rpc_get (sock, RPC_INT, &mylong, RPC_END);
833 buf->st_mode = mylong;
834 rpc_get (sock, RPC_INT, &mylong, RPC_END);
835 buf->st_nlink = mylong;
836 rpc_get (sock, RPC_INT, &mylong, RPC_END);
837 buf->st_uid = mylong;
838 rpc_get (sock, RPC_INT, &mylong, RPC_END);
839 buf->st_gid = mylong;
840 rpc_get (sock, RPC_INT, &mylong, RPC_END);
841 buf->st_size = mylong;
843 if (!rpc_get (sock, RPC_INT, &mylong, RPC_END))
844 return 0;
845 #ifdef HAVE_ST_BLOCKS
846 buf->st_blocks = mylong;
847 #endif
848 buf->st_atime = mcfs_get_time (mc);
849 buf->st_mtime = mcfs_get_time (mc);
850 buf->st_ctime = mcfs_get_time (mc);
851 return 1;
854 static int
855 mcfs_stat_cmd (int cmd, char *path, struct stat *buf)
857 char *remote_file;
858 mcfs_connection *mc;
859 int status, error;
861 if ((remote_file = mcfs_get_path (&mc, path)) == 0)
862 return -1;
864 rpc_send (mc->sock, RPC_INT, cmd, RPC_STRING, remote_file, RPC_END);
865 g_free (remote_file);
866 if (!rpc_get (mc->sock, RPC_INT, &status, RPC_INT, &error, RPC_END))
867 return the_error (-1, errno);
869 if (is_error (status, error))
870 return -1;
872 if (get_stat_info (mc, buf))
873 return 0;
874 else
875 return the_error (-1, EIO);
878 static int
879 mcfs_stat (vfs *me, char *path, struct stat *buf)
881 return mcfs_stat_cmd (MC_STAT, path, buf);
884 static int
885 mcfs_lstat (vfs *me, char *path, struct stat *buf)
887 int path_len = strlen (path);
888 int entry_len = strlen (mcfs_readdir_data.dent.d_name);
890 /* Hack ... */
891 if (strcmp (path + path_len - entry_len,
892 mcfs_readdir_data.dent.d_name) == 0 && cached_lstat_info) {
893 *buf = *cached_lstat_info;
894 return 0;
896 return mcfs_stat_cmd (MC_LSTAT, path, buf);
899 static int
900 mcfs_fstat (void *data, struct stat *buf)
902 mcfs_handle *info = (mcfs_handle *) data;
903 int result, error;
904 int handle, sock;
906 sock = info->conn->sock;
907 handle = info->handle;
909 rpc_send (sock, RPC_INT, MC_FSTAT, RPC_INT, handle, RPC_END);
910 if (!rpc_get (sock, RPC_INT, &result, RPC_INT, &error, RPC_END))
911 return the_error (-1, EIO);
913 if (is_error (result, error))
914 return -1;
916 if (get_stat_info (info->conn, buf))
917 return 0;
918 else
919 return the_error (-1, EIO);
922 static int
923 mcfs_chmod (vfs *me, char *path, int mode)
925 return mcfs_rpc_path_int (MC_CHMOD, path, mode);
928 static int
929 mcfs_chown (vfs *me, char *path, int owner, int group)
931 return mcfs_rpc_path_int_int (MC_CHOWN, path, owner, group);
934 static int
935 mcfs_utime (vfs *me, char *path, struct utimbuf *times)
937 mcfs_connection *mc;
938 int status;
939 char *file;
941 if (!(file = mcfs_get_path (&mc, path)))
942 return -1;
944 status = 0;
945 if (mc->version >= 2) {
946 char abuf[BUF_SMALL];
947 char mbuf[BUF_SMALL];
948 long atime, mtime;
950 atime = (long) times->actime;
951 mtime = (long) times->modtime;
953 g_snprintf (abuf, sizeof (abuf), "%lx", atime);
954 g_snprintf (mbuf, sizeof (mbuf), "%lx", mtime);
956 rpc_send (mc->sock, RPC_INT, MC_UTIME,
957 RPC_STRING, file,
958 RPC_STRING, abuf, RPC_STRING, mbuf, RPC_END);
959 status = mcfs_handle_simple_error (mc->sock, 0);
962 g_free (file);
963 return (status);
966 static int
967 mcfs_readlink (vfs *me, char *path, char *buf, int size)
969 char *remote_file, *stat_str;
970 int status, error;
971 mcfs_connection *mc;
973 if (!(remote_file = mcfs_get_path (&mc, path)))
974 return -1;
976 rpc_send (mc->sock, RPC_INT, MC_READLINK, RPC_STRING, remote_file,
977 RPC_END);
978 g_free (remote_file);
979 if (!rpc_get (mc->sock, RPC_INT, &status, RPC_INT, &error, RPC_END))
980 return the_error (-1, EIO);
982 if (is_error (status, errno))
983 return -1;
985 if (!rpc_get (mc->sock, RPC_STRING, &stat_str, RPC_END))
986 return the_error (-1, EIO);
988 strncpy (buf, stat_str, size);
989 g_free (stat_str);
990 return strlen (buf);
993 static int
994 mcfs_unlink (vfs *me, char *path)
996 return mcfs_rpc_path (MC_UNLINK, path);
999 static int
1000 mcfs_symlink (vfs *me, char *n1, char *n2)
1002 return mcfs_rpc_two_paths (MC_SYMLINK, n1, n2);
1005 static int
1006 mcfs_rename (vfs *me, char *a, char *b)
1008 return mcfs_rpc_two_paths (MC_RENAME, a, b);
1011 static int
1012 mcfs_chdir (vfs *me, char *path)
1014 char *remote_dir;
1015 mcfs_connection *mc;
1016 int status, error;
1018 if (!(remote_dir = mcfs_get_path (&mc, path)))
1019 return -1;
1021 rpc_send (mc->sock, RPC_INT, MC_CHDIR, RPC_STRING, remote_dir,
1022 RPC_END);
1023 g_free (remote_dir);
1024 if (!rpc_get (mc->sock, RPC_INT, &status, RPC_INT, &error, RPC_END))
1025 return the_error (-1, EIO);
1027 if (is_error (status, error))
1028 return -1;
1029 return 0;
1032 static int
1033 mcfs_lseek (void *data, off_t offset, int whence)
1035 mcfs_handle *info = (mcfs_handle *) data;
1036 int handle, sock;
1038 sock = info->conn->sock;
1039 handle = info->handle;
1041 /* FIXME: off_t may be too long to fit */
1042 rpc_send (sock, RPC_INT, MC_LSEEK, RPC_INT, handle, RPC_INT,
1043 (int) offset, RPC_INT, whence, RPC_END);
1045 return mcfs_handle_simple_error (sock, 1);
1048 static int
1049 mcfs_mknod (vfs *me, char *path, int mode, int dev)
1051 return mcfs_rpc_path_int_int (MC_MKNOD, path, mode, dev);
1054 static int
1055 mcfs_mkdir (vfs *me, char *path, mode_t mode)
1057 return mcfs_rpc_path_int (MC_MKDIR, path, mode);
1060 static int
1061 mcfs_rmdir (vfs *me, char *path)
1063 return mcfs_rpc_path (MC_RMDIR, path);
1066 static int
1067 mcfs_link (vfs *me, char *p1, char *p2)
1069 return mcfs_rpc_two_paths (MC_LINK, p1, p2);
1072 /* We do not free anything right now: we free resources when we run
1073 * out of them
1075 static vfsid
1076 mcfs_getid (vfs *me, char *p, struct vfs_stamping **parent)
1078 *parent = NULL;
1080 return (vfsid) - 1;
1083 static int
1084 mcfs_nothingisopen (vfsid id)
1086 return 0;
1089 static void
1090 mcfs_free (vfsid id)
1092 /* FIXME: Should not be empty */
1095 /* Gives up on a socket and reopnes the connection, the child own the socket
1096 * now
1098 static void
1099 my_forget (char *path)
1101 char *host, *user, *pass, *p;
1102 int port, i, vers;
1104 if (strncmp (path, "/#mc:", 5))
1105 return;
1107 path += 5;
1108 if (path[0] == '/' && path[1] == '/')
1109 path += 2;
1111 if ((p =
1112 mcfs_get_host_and_username (path, &host, &user, &port,
1113 &pass)) == 0) {
1114 g_free (host);
1115 g_free (user);
1116 if (pass)
1117 wipe_password (pass);
1118 return;
1120 for (i = 0; i < MCFS_MAX_CONNECTIONS; i++) {
1121 if ((strcmp (host, mcfs_connections[i].host) == 0) &&
1122 (strcmp (user, mcfs_connections[i].user) == 0) &&
1123 (port == mcfs_connections[i].port)) {
1125 /* close socket: the child owns it now */
1126 close (mcfs_connections[i].sock);
1128 /* reopen the connection */
1129 mcfs_connections[i].sock =
1130 mcfs_open_tcp_link (host, user, &port, pass, &vers);
1133 g_free (p);
1134 g_free (host);
1135 g_free (user);
1136 if (pass)
1137 wipe_password (pass);
1140 static int
1141 mcfs_setctl (vfs *me, char *path, int ctlop, char *arg)
1143 switch (ctlop) {
1144 case MCCTL_FORGET_ABOUT:
1145 my_forget (path);
1146 return 0;
1148 return 0;
1151 vfs vfs_mcfs_ops = {
1152 NULL, /* This is place of next pointer */
1153 "mcfs",
1154 F_NET, /* flags */
1155 "mc:", /* prefix */
1156 NULL, /* data */
1157 0, /* errno */
1158 NULL,
1159 NULL,
1160 mcfs_fill_names,
1161 NULL,
1163 mcfs_open,
1164 mcfs_close,
1165 mcfs_read,
1166 mcfs_write,
1168 mcfs_opendir,
1169 mcfs_readdir,
1170 mcfs_closedir,
1171 NULL,
1172 NULL,
1174 mcfs_stat,
1175 mcfs_lstat,
1176 mcfs_fstat,
1178 mcfs_chmod,
1179 mcfs_chown,
1180 mcfs_utime,
1182 mcfs_readlink,
1183 mcfs_symlink,
1184 mcfs_link,
1185 mcfs_unlink,
1187 mcfs_rename,
1188 mcfs_chdir,
1189 mcfs_errno,
1190 mcfs_lseek,
1191 mcfs_mknod,
1193 mcfs_getid,
1194 mcfs_nothingisopen,
1195 mcfs_free,
1197 NULL,
1198 NULL,
1200 mcfs_mkdir,
1201 mcfs_rmdir,
1202 NULL,
1203 mcfs_setctl MMAPNULL
1207 static void
1208 mcfs_free_bucket (int bucket)
1210 g_free (mcfs_connections[bucket].host);
1211 g_free (mcfs_connections[bucket].user);
1212 g_free (mcfs_connections[bucket].home);
1214 /* Set all the fields to zero */
1215 mcfs_connections[bucket].host =
1216 mcfs_connections[bucket].user = mcfs_connections[bucket].home = 0;
1217 mcfs_connections[bucket].sock = mcfs_connections[bucket].version = 0;
1220 static int
1221 mcfs_invalidate_socket (int sock)
1223 int i, j = -1;
1224 extern int mc_chdir (char *);
1226 for (i = 0; i < MCFS_MAX_CONNECTIONS; i++)
1227 if (mcfs_connections[i].sock == sock) {
1228 mcfs_free_bucket (i);
1229 j = 0;
1232 if (j == -1)
1233 return -1; /* It was not our sock */
1234 /* Break from any possible loop */
1235 mc_chdir ("/");
1236 return 0;
1239 void
1240 tcp_invalidate_socket (int sock)
1242 mcfs_invalidate_socket (sock);
1245 #endif /* WITH_MCFS */