translated tartially 'til file operations...
[midnight-commander.git] / vfs / mcfs.c
blob05b5b6fd96ecc64c04e519d75c76ed9df0652687
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 $Id$
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Library General Public License
13 as published by the Free Software Foundation; either version 2 of
14 the License, or (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU Library General Public License for more details.
21 You should have received a copy of the GNU Library General Public
22 License along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
25 /* Namespace: exports mcfs_vfs_ops, tcp_invalidate_socket */
27 #include <config.h>
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 <fcntl.h>
35 #include <pwd.h>
36 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
37 #include <netdb.h> /* struct hostent */
38 #include <sys/socket.h> /* AF_INET */
39 #include <netinet/in.h> /* struct in_addr */
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
42 #endif
44 #include "utilvfs.h"
46 #include "vfs.h"
47 #include "mcfs.h"
48 #include "tcputil.h"
49 #include "../src/dialog.h"
51 #define MCFS_MAX_CONNECTIONS 32
53 static struct _mcfs_connection {
54 char *host;
55 char *user;
56 char *home;
57 int sock;
58 int port;
59 int version;
60 } mcfs_connections [MCFS_MAX_CONNECTIONS];
62 #ifdef WITH_MCFS
64 #define mcserver_port 9876
66 typedef struct _mcfs_connection mcfs_connection;
68 typedef struct { int handle; mcfs_connection *conn; } mcfs_handle;
70 static int my_errno;
72 static char *mcfs_gethome (mcfs_connection *mc);
74 /* Extract the hostname and username from the path */
75 /* path is in the form: hostname:user/remote-dir */
76 static char *mcfs_get_host_and_username (char *path, char **host, char **user,
77 int *port, char **pass)
79 return vfs_split_url (path, host, user, port, pass, 0, 0);
82 static void mcfs_fill_names (vfs *me, void (*func)(char *))
84 int i;
85 char *name;
87 for (i = 0; i < MCFS_MAX_CONNECTIONS; i++){
88 if (mcfs_connections [i].host == 0)
89 continue;
90 name = g_strconcat ("/#mc:", mcfs_connections [i].user,
91 "@", mcfs_connections [i].host, NULL);
92 (*func) (name);
93 g_free (name);
97 /* This routine checks the server RPC version and logs the user in */
98 static int mcfs_login_server (int my_socket, char *user, int port,
99 int port_autodetected, char *netrcpass,
100 int *version)
102 int result;
103 char *pass;
105 /* Send the version number */
106 rpc_send (my_socket, RPC_INT, *version, RPC_END);
107 if (0 == rpc_get (my_socket, RPC_INT, &result, RPC_END))
108 return 0;
110 if (result != MC_VERSION_OK){
111 message_1s (1, _(" MCFS "), _(" The server does not support this version "));
112 close (my_socket);
113 return 0;
116 /* FIXME: figure out why last_current_dir used to be passed here */
117 rpc_send (my_socket, RPC_INT, MC_LOGIN, RPC_STRING, "/",
118 RPC_STRING, user, RPC_END);
120 if (0 == rpc_get (my_socket, RPC_INT, &result, RPC_END))
121 return 0;
123 if (result == MC_NEED_PASSWORD){
124 if (port > 1024 && port_autodetected){
125 int v;
126 v = query_dialog (_(" Warning "),
127 _(" The remote server is not running on a system port \n"
128 " you need a password to log in, but the information may \n"
129 " not be safe on the remote side. Continue? \n"), 3, 2,
130 _(" Yes "), _(" No "));
132 if (v == 1){
133 close (my_socket);
134 return 0;
137 if (netrcpass != NULL)
138 pass = g_strdup (netrcpass);
139 else
140 pass = vfs_get_password (_(" MCFS Password required "));
141 if (!pass){
142 rpc_send (my_socket, RPC_INT, MC_QUIT, RPC_END);
143 close (my_socket);
144 return 0;
146 rpc_send (my_socket, RPC_INT, MC_PASS, RPC_STRING, pass, RPC_END);
148 wipe_password (pass);
150 if (0 == rpc_get (my_socket, RPC_INT, &result, RPC_END))
151 return 0;
153 if (result != MC_LOGINOK){
154 message_1s (1, _(" MCFS "), _(" Invalid password "));
155 rpc_send (my_socket, RPC_INT, MC_QUIT, RPC_END);
156 close (my_socket);
157 return 0;
160 return my_socket;
163 /* This used to be in utilvfs.c, but as it deals with portmapper, it
164 is probably usefull for mcfs */
166 open_tcp_link (char *host, int *port, int *version, char *caller)
168 struct sockaddr_in server_address;
169 unsigned long inaddr;
170 struct hostent *hp;
171 int my_socket;
173 if (!*host)
174 return 0;
176 memset ((char *) &server_address, 0, sizeof (server_address));
177 server_address.sin_family = AF_INET;
179 /* Try to use the dotted decimal number */
180 if ((inaddr = inet_addr (host)) != -1)
181 memcpy ((char *) &server_address.sin_addr, (char *) &inaddr,
182 sizeof (inaddr));
183 else {
184 if ((hp = gethostbyname (host)) == NULL){
185 message_2s (1, caller, _(" Cannot locate hostname: %s "), host);
186 return 0;
188 memcpy ((char *) &server_address.sin_addr, (char *) hp->h_addr,
189 hp->h_length);
192 /* Try to contact a remote portmapper to obtain the listening port */
193 if (*port == 0){
194 *port = get_remote_port (&server_address, version);
195 if (*port < 1)
196 return 0;
197 } else
198 *version = 1;
200 server_address.sin_port = htons (*port);
202 if ((my_socket = socket (AF_INET, SOCK_STREAM, 0)) < 0){
203 message_2s (1, caller, _(" Cannot create socket: %s "),
204 unix_error_string(errno));
205 return 0;
207 if (connect (my_socket, (struct sockaddr *) &server_address,
208 sizeof (server_address)) < 0){
209 message_2s (1, caller, _(" Cannot connect to server: %s "),
210 unix_error_string (errno));
211 close (my_socket);
212 return 0;
214 return my_socket;
217 static int mcfs_open_tcp_link (char *host, char *user,
218 int *port, char *netrcpass, int *version)
220 int my_socket;
221 int old_port = *port;
223 my_socket = open_tcp_link (host, port, version, " MCfs ");
224 if (my_socket <= 0)
225 return 0;
227 /* We got the connection to the server, verify if the server
228 implements our version of the RPC mechanism and then login
229 the user.
231 return mcfs_login_server (my_socket, user, *port, old_port == 0,
232 netrcpass, version);
235 static int mcfs_get_free_bucket_init = 1;
236 static mcfs_connection *mcfs_get_free_bucket (void)
238 int i;
240 if (mcfs_get_free_bucket_init) {
241 mcfs_get_free_bucket_init = 0;
242 for (i = 0; i < MCFS_MAX_CONNECTIONS; i++)
243 mcfs_connections [i].host = 0;
245 for (i = 0; i < MCFS_MAX_CONNECTIONS; i++){
246 if (!mcfs_connections [i].host)
247 return &mcfs_connections [i];
249 /* This can't happend, since we have checked for max connections before */
250 vfs_die("Internal error: mcfs_get_free_bucket");
251 return 0; /* shut up, stupid gcc */
254 /* This routine keeps track of open connections */
255 /* Returns a connected socket to host */
256 static mcfs_connection *mcfs_open_link (char *host, char *user, int *port, char *netrcpass)
258 static int mcfs_open_connections = 0;
259 int i, sock, version;
260 mcfs_connection *bucket;
262 /* Is the link actually open? */
263 if (mcfs_get_free_bucket_init) {
264 mcfs_get_free_bucket_init = 0;
265 for (i = 0; i < MCFS_MAX_CONNECTIONS; i++)
266 mcfs_connections [i].host = 0;
267 } else for (i = 0; i < MCFS_MAX_CONNECTIONS; i++){
268 if (!mcfs_connections [i].host)
269 continue;
270 if ((strcmp (host, mcfs_connections [i].host) == 0) &&
271 (strcmp (user, mcfs_connections [i].user) == 0))
272 return &mcfs_connections [i];
274 if (mcfs_open_connections == MCFS_MAX_CONNECTIONS){
275 message_1s (1, MSG_ERROR, _(" Too many open connections "));
276 return 0;
279 if (!(sock = mcfs_open_tcp_link (host, user, port, netrcpass, &version)))
280 return 0;
282 bucket = mcfs_get_free_bucket ();
283 mcfs_open_connections++;
284 bucket->host = g_strdup (host);
285 bucket->user = g_strdup (user);
286 bucket->home = 0;
287 bucket->port = *port;
288 bucket->sock = sock;
289 bucket->version = version;
291 return bucket;
294 static int is_error (int result, int errno_num)
296 if (!(result == -1))
297 return my_errno = 0;
298 else
299 my_errno = errno_num;
300 return 1;
303 static int the_error (int result, int errno_num)
305 if (result == -1)
306 my_errno = errno_num;
307 else
308 my_errno = 0;
309 return result;
312 static char *mcfs_get_path (mcfs_connection **mc, char *path)
314 char *user, *host, *remote_path;
315 char *pass;
316 int port;
318 /* An absolute path name, try to determine connection socket */
319 if (strncmp (path, "/#mc:", 5))
320 return NULL;
321 path += 5;
323 /* Port = 0 means that open_tcp_link will try to contact the
324 * remote portmapper to get the port number
326 port = 0;
327 if ((remote_path = mcfs_get_host_and_username(path, &host, &user, &port, &pass)))
328 if (!(*mc = mcfs_open_link (host, user, &port, pass))){
329 g_free (remote_path);
330 remote_path = NULL;
332 g_free (host);
333 g_free (user);
334 if (pass)
335 wipe_password (pass);
337 if (!remote_path)
338 return NULL;
340 /* NOTE: tildes are deprecated. See ftpfs.c */
342 int f = !strcmp( remote_path, "/~" );
343 if (f || !strncmp( remote_path, "/~/", 3 )) {
344 char *s;
345 s = concat_dir_and_file( mcfs_gethome (*mc), remote_path +3-f );
346 g_free (remote_path);
347 remote_path = s;
350 return remote_path;
353 /* Simple function for routines returning only an integer from the server */
354 static int mcfs_handle_simple_error (int sock, int return_status)
356 int status, error;
358 if (0 == rpc_get (sock, RPC_INT, &status, RPC_INT, &error, RPC_END))
359 return the_error (-1, EIO);
361 if (is_error (status, error))
362 return -1;
363 if (return_status)
364 return status;
365 return 0;
368 /* Nice wrappers */
369 static int mcfs_rpc_two_paths (int command, char *s1, char *s2)
371 mcfs_connection *mc;
372 char *r1, *r2;
374 if ((r1 = mcfs_get_path (&mc, s1)) == 0)
375 return -1;
377 if ((r2 = mcfs_get_path (&mc, s2)) == 0){
378 g_free (r1);
379 return -1;
382 rpc_send (mc->sock,
383 RPC_INT, command,
384 RPC_STRING, r1,
385 RPC_STRING, r2,
386 RPC_END);
387 g_free (r1);
388 g_free (r2);
389 return mcfs_handle_simple_error (mc->sock, 0);
392 static int mcfs_rpc_path (int command, char *path)
394 mcfs_connection *mc;
395 char *remote_file;
397 if ((remote_file = mcfs_get_path (&mc, path)) == 0)
398 return -1;
400 rpc_send (mc->sock,
401 RPC_INT, command,
402 RPC_STRING, remote_file,
403 RPC_END);
405 g_free (remote_file);
406 return mcfs_handle_simple_error (mc->sock, 0);
409 static int mcfs_rpc_path_int (int command, char *path, int data)
411 mcfs_connection *mc;
412 char *remote_file;
414 if ((remote_file = mcfs_get_path (&mc, path)) == 0)
415 return -1;
417 rpc_send (mc->sock,
418 RPC_INT, command,
419 RPC_STRING, remote_file,
420 RPC_INT, data, RPC_END);
422 g_free (remote_file);
423 return mcfs_handle_simple_error (mc->sock, 0);
426 static int mcfs_rpc_path_int_int (int command, char *path, int n1, int n2)
428 mcfs_connection *mc;
429 char *remote_file;
431 if ((remote_file = mcfs_get_path (&mc, path)) == 0)
432 return -1;
434 rpc_send (mc->sock,
435 RPC_INT, command,
436 RPC_STRING, remote_file,
437 RPC_INT, n1,
438 RPC_INT, n2,
439 RPC_END);
441 g_free (remote_file);
442 return mcfs_handle_simple_error (mc->sock, 0);
445 static char *mcfs_gethome (mcfs_connection *mc)
447 char *buffer;
449 if (mc->home)
450 return g_strdup (mc->home);
451 else {
452 rpc_send (mc->sock, RPC_INT, MC_GETHOME, RPC_END);
453 if (0 == rpc_get (mc->sock, RPC_STRING, &buffer, RPC_END))
454 return g_strdup (PATH_SEP_STR);
455 mc->home = buffer;
456 return g_strdup (buffer);
460 /* The callbacks */
461 static void *mcfs_open (vfs *me, char *file, int flags, int mode)
463 char *remote_file;
464 mcfs_connection *mc;
465 int result, error_num;
466 mcfs_handle *remote_handle;
468 if (!(remote_file = mcfs_get_path (&mc, file)))
469 return 0;
471 rpc_send (mc->sock, RPC_INT, MC_OPEN, RPC_STRING, remote_file,
472 RPC_INT, flags, RPC_INT, mode, RPC_END);
473 g_free (remote_file);
475 if (0 == rpc_get (mc->sock, RPC_INT, &result, RPC_INT, &error_num, RPC_END))
476 return 0;
478 if (is_error (result, error_num))
479 return 0;
481 remote_handle = g_new (mcfs_handle, 2);
482 remote_handle->handle = result;
483 remote_handle->conn = mc;
485 return remote_handle;
488 static int mcfs_read (void *data, char *buffer, int count)
490 mcfs_handle *info = (mcfs_handle *) data;
491 int result, error;
492 int handle;
493 mcfs_connection *mc;
495 mc = info->conn;
496 handle = info->handle;
498 rpc_send (mc->sock, RPC_INT, MC_READ, RPC_INT, handle,
499 RPC_INT, count, RPC_END);
501 if (0 == rpc_get (mc->sock, RPC_INT, &result, RPC_INT, &error, RPC_END))
502 return the_error (-1, EIO);
504 if (is_error (result, error))
505 return 0;
507 if (0 == rpc_get (mc->sock, RPC_BLOCK, result, buffer, RPC_END))
508 return the_error (-1, EIO);
510 return result;
513 static int mcfs_write (void *data, char *buf, int nbyte)
515 mcfs_handle *info = (mcfs_handle *) data;
516 mcfs_connection *mc;
517 int handle;
519 mc = info->conn;
520 handle = info->handle;
522 rpc_send (mc->sock,
523 RPC_INT, MC_WRITE,
524 RPC_INT, handle,
525 RPC_INT, nbyte,
526 RPC_BLOCK, nbyte, buf,
527 RPC_END);
529 return mcfs_handle_simple_error (mc->sock, 1);
532 static int mcfs_close (void *data)
534 mcfs_handle *info = (mcfs_handle *) data;
535 mcfs_connection *mc;
536 int handle, result, error;
538 if (!data)
539 return -1;
541 handle = info->handle;
542 mc = info->conn;
544 rpc_send (mc->sock, RPC_INT, MC_CLOSE, RPC_INT, handle, RPC_END);
546 if (0 == rpc_get (mc->sock, RPC_INT, &result, RPC_INT, &error, RPC_END))
547 return the_error (-1, EIO);
549 is_error (result, error);
551 g_free (data);
552 return result;
555 static int mcfs_errno (vfs *me)
557 return my_errno;
560 typedef struct dir_entry {
561 char *text;
562 struct dir_entry *next;
563 struct stat my_stat;
564 int merrno;
565 } dir_entry;
567 typedef struct {
568 mcfs_connection *conn;
569 int handle;
570 dir_entry *entries;
571 dir_entry *current;
572 } opendir_info;
574 static void *mcfs_opendir (vfs *me, char *dirname)
576 opendir_info *mcfs_info;
577 mcfs_connection *mc;
578 int handle, error_num;
579 char *remote_dir;
580 int result;
582 if (!(remote_dir = mcfs_get_path (&mc, dirname)))
583 return 0;
585 rpc_send (mc->sock, RPC_INT, MC_OPENDIR, RPC_STRING, remote_dir, RPC_END);
586 g_free (remote_dir);
588 if (0 == rpc_get (mc->sock, RPC_INT, &result, RPC_INT, &error_num, RPC_END))
589 return 0;
591 if (is_error (result, error_num))
592 return 0;
594 handle = result;
596 mcfs_info = g_new (opendir_info, 1);
597 mcfs_info->conn = mc;
598 mcfs_info->handle = handle;
599 mcfs_info->entries = 0;
600 mcfs_info->current = 0;
602 return mcfs_info;
605 static int get_stat_info (mcfs_connection *mc, struct stat *buf);
607 static int mcfs_loaddir (opendir_info *mcfs_info)
609 int status, error;
610 mcfs_connection *mc = mcfs_info->conn;
611 int link = mc->sock;
612 int first = 1;
614 rpc_send (link, RPC_INT, MC_READDIR, RPC_INT, mcfs_info->handle, RPC_END);
616 for (;;){
617 int entry_len;
618 dir_entry *new_entry;
620 if (!rpc_get (link, RPC_INT, &entry_len, RPC_END))
621 return 0;
623 if (entry_len == 0)
624 break;
626 new_entry = g_new (dir_entry, 1);
627 new_entry->text = g_new0 (char, entry_len + 1);
629 new_entry->next = 0;
630 if (first){
631 mcfs_info->entries = new_entry;
632 mcfs_info->current = new_entry;
633 first = 0;
634 } else {
635 mcfs_info->current->next = new_entry;
636 mcfs_info->current = new_entry;
639 if (!rpc_get (link, RPC_BLOCK, entry_len, new_entry->text, RPC_END))
640 return 0;
642 /* Then we get the status from the lstat */
643 if (!rpc_get (link, RPC_INT, &status, RPC_INT, &error, RPC_END))
644 return 0;
646 if (is_error (status, error))
647 new_entry->merrno = error;
648 else {
649 new_entry->merrno = 0;
650 if (!get_stat_info (mc, &(new_entry->my_stat)))
651 return 0;
654 mcfs_info->current = mcfs_info->entries;
656 return 1;
659 static void mcfs_free_dir (dir_entry *de)
661 if (!de)
662 return;
663 mcfs_free_dir (de->next);
664 g_free (de->text);
665 g_free (de);
668 static union vfs_dirent mcfs_readdir_data;
670 /* The readdir routine loads the complete directory */
671 /* It's too slow to ask the server each time */
672 /* It now also sends the complete lstat information for each file */
673 static struct stat *cached_lstat_info;
675 static void *mcfs_readdir(void *info)
677 opendir_info *mcfs_info;
678 char *dirent_dest;
680 mcfs_info = (opendir_info *) info;
682 if (!mcfs_info->entries)
683 if (!mcfs_loaddir(mcfs_info))
684 return NULL;
686 if (mcfs_info->current == 0) {
687 cached_lstat_info = 0;
688 mcfs_free_dir(mcfs_info->entries);
689 mcfs_info->entries = 0;
690 return NULL;
692 dirent_dest = mcfs_readdir_data.dent.d_name;
693 strncpy(dirent_dest, mcfs_info->current->text, MC_MAXPATHLEN);
694 dirent_dest[MC_MAXPATHLEN] = 0;
695 cached_lstat_info = &mcfs_info->current->my_stat;
696 mcfs_info->current = mcfs_info->current->next;
698 compute_namelen(&mcfs_readdir_data.dent);
700 return &mcfs_readdir_data;
703 static int mcfs_closedir (void *info)
705 opendir_info *mcfs_info = (opendir_info *) info;
706 dir_entry *p, *q;
708 rpc_send (mcfs_info->conn->sock, RPC_INT, MC_CLOSEDIR,
709 RPC_INT, mcfs_info->handle, RPC_END);
711 for (p = mcfs_info->entries; p;){
712 q = p;
713 p = p->next;
714 g_free (q->text);
715 g_free (q);
717 g_free (info);
718 return 0;
721 static time_t mcfs_get_time (mcfs_connection *mc)
723 int sock = mc->sock;
725 if (mc->version == 1) {
726 struct tm tt;
728 rpc_get (sock,
729 RPC_INT, &tt.tm_sec,
730 RPC_INT, &tt.tm_min,
731 RPC_INT, &tt.tm_hour,
732 RPC_INT, &tt.tm_mday,
733 RPC_INT, &tt.tm_year,
734 RPC_INT, &tt.tm_mon,
735 RPC_END);
736 tt.tm_year -= 1900;
737 tt.tm_isdst = 0;
739 return mktime (&tt);
740 } else {
741 char *buf;
742 long tm;
744 rpc_get (sock,
745 RPC_STRING, &buf,
746 RPC_END);
747 sscanf (buf, "%lx", &tm);
748 g_free (buf);
750 return (time_t) tm;
754 static int get_stat_info (mcfs_connection *mc, struct stat *buf)
756 long mylong;
757 int sock = mc->sock;
759 buf->st_dev = 0;
761 rpc_get (sock, RPC_INT, &mylong, RPC_END);
762 #ifdef HAVE_ST_RDEV
763 buf->st_rdev = mylong;
764 #endif
765 rpc_get (sock, RPC_INT, &mylong, RPC_END);
766 buf->st_ino = mylong;
767 rpc_get (sock, RPC_INT, &mylong, RPC_END);
768 buf->st_mode = mylong;
769 rpc_get (sock, RPC_INT, &mylong, RPC_END);
770 buf->st_nlink = mylong;
771 rpc_get (sock, RPC_INT, &mylong, RPC_END);
772 buf->st_uid = mylong;
773 rpc_get (sock, RPC_INT, &mylong, RPC_END);
774 buf->st_gid = mylong;
775 rpc_get (sock, RPC_INT, &mylong, RPC_END);
776 buf->st_size = mylong;
778 if (!rpc_get (sock, RPC_INT, &mylong, RPC_END))
779 return 0;
780 #ifdef HAVE_ST_BLOCKS
781 buf->st_blocks = mylong;
782 #endif
783 buf->st_atime = mcfs_get_time (mc);
784 buf->st_mtime = mcfs_get_time (mc);
785 buf->st_ctime = mcfs_get_time (mc);
786 return 1;
789 static int mcfs_stat_cmd (int cmd, char *path, struct stat *buf)
791 char *remote_file;
792 mcfs_connection *mc;
793 int status, error;
795 if ((remote_file = mcfs_get_path (&mc, path)) == 0)
796 return -1;
798 rpc_send (mc->sock, RPC_INT, cmd, RPC_STRING, remote_file, RPC_END);
799 g_free (remote_file);
800 if (!rpc_get (mc->sock, RPC_INT, &status, RPC_INT, &error, RPC_END))
801 return the_error (-1, errno);
803 if (is_error (status, error))
804 return -1;
806 if (get_stat_info (mc, buf))
807 return 0;
808 else
809 return the_error (-1, EIO);
812 static int mcfs_stat (vfs *me, char *path, struct stat *buf)
814 return mcfs_stat_cmd (MC_STAT, path, buf);
817 static int mcfs_lstat (vfs *me, char *path, struct stat *buf)
819 int path_len = strlen (path);
820 int entry_len = strlen (mcfs_readdir_data.dent.d_name);
822 /* Hack ... */
823 if (strcmp (path + path_len - entry_len,
824 mcfs_readdir_data.dent.d_name) == 0 &&
825 cached_lstat_info){
826 *buf = *cached_lstat_info;
827 return 0;
829 return mcfs_stat_cmd (MC_LSTAT, path, buf);
832 static int mcfs_fstat (void *data, struct stat *buf)
834 mcfs_handle *info = (mcfs_handle *) data;
835 int result, error;
836 int handle, sock;
838 sock = info->conn->sock;
839 handle = info->handle;
841 rpc_send (sock, RPC_INT, MC_FSTAT, RPC_INT, handle, RPC_END);
842 if (!rpc_get (sock, RPC_INT, &result, RPC_INT, &error, RPC_END))
843 return the_error (-1, EIO);
845 if (is_error (result, error))
846 return -1;
848 if (get_stat_info (info->conn, buf))
849 return 0;
850 else
851 return the_error (-1, EIO);
854 static int mcfs_chmod (vfs *me, char *path, int mode)
856 return mcfs_rpc_path_int (MC_CHMOD, path, mode);
859 static int mcfs_chown (vfs *me, char *path, int owner, int group)
861 return mcfs_rpc_path_int_int (MC_CHOWN, path, owner, group);
864 static int mcfs_utime (vfs *me, char *path, struct utimbuf *times)
866 mcfs_connection *mc;
867 int status;
868 char *file;
870 if (!(file = mcfs_get_path (&mc, path)))
871 return -1;
873 status = 0;
874 if (mc->version >= 2) {
875 char abuf[BUF_SMALL];
876 char mbuf[BUF_SMALL];
877 long atime, mtime;
879 atime = (long) times->actime;
880 mtime = (long) times->modtime;
882 g_snprintf (abuf, sizeof(abuf), "%lx", atime);
883 g_snprintf (mbuf, sizeof(mbuf), "%lx", mtime);
885 rpc_send (mc->sock, RPC_INT, MC_UTIME,
886 RPC_STRING, file,
887 RPC_STRING, abuf,
888 RPC_STRING, mbuf,
889 RPC_END);
890 status = mcfs_handle_simple_error (mc->sock, 0);
893 g_free (file);
894 return (status);
897 static int mcfs_readlink (vfs *me, char *path, char *buf, int size)
899 char *remote_file, *stat_str;
900 int status, error;
901 mcfs_connection *mc;
903 if (!(remote_file = mcfs_get_path (&mc, path)))
904 return -1;
906 rpc_send (mc->sock, RPC_INT, MC_READLINK, RPC_STRING, remote_file, RPC_END);
907 g_free (remote_file);
908 if (!rpc_get (mc->sock, RPC_INT, &status, RPC_INT, &error, RPC_END))
909 return the_error (-1, EIO);
911 if (is_error (status, errno))
912 return -1;
914 if (!rpc_get (mc->sock, RPC_STRING, &stat_str, RPC_END))
915 return the_error (-1, EIO);
917 strncpy (buf, stat_str, size);
918 g_free (stat_str);
919 return strlen (buf);
922 static int mcfs_unlink (vfs *me, char *path)
924 return mcfs_rpc_path (MC_UNLINK, path);
927 static int mcfs_symlink (vfs *me, char *n1, char *n2)
929 return mcfs_rpc_two_paths (MC_SYMLINK, n1, n2);
932 static int mcfs_rename (vfs *me, char *a, char *b)
934 return mcfs_rpc_two_paths (MC_RENAME, a, b);
937 static int mcfs_chdir (vfs *me, char *path)
939 char *remote_dir;
940 mcfs_connection *mc;
941 int status, error;
943 if (!(remote_dir = mcfs_get_path (&mc, path)))
944 return -1;
946 rpc_send (mc->sock, RPC_INT, MC_CHDIR, RPC_STRING, remote_dir, RPC_END);
947 g_free (remote_dir);
948 if (!rpc_get (mc->sock, RPC_INT, &status, RPC_INT, &error, RPC_END))
949 return the_error (-1, EIO);
951 if (is_error (status, error))
952 return -1;
953 return 0;
956 static int mcfs_lseek (void *data, off_t offset, int whence)
958 mcfs_handle *info = (mcfs_handle *) data;
959 int handle, sock;
961 sock = info->conn->sock;
962 handle = info->handle;
964 rpc_send (sock,
965 RPC_INT, MC_LSEEK,
966 RPC_INT, handle,
967 RPC_INT, offset,
968 RPC_INT, whence,
969 RPC_END);
970 return mcfs_handle_simple_error (sock, 1);
973 static int mcfs_mknod (vfs *me, char *path, int mode, int dev)
975 return mcfs_rpc_path_int_int (MC_MKNOD, path, mode, dev);
978 static int mcfs_mkdir (vfs *me, char *path, mode_t mode)
980 return mcfs_rpc_path_int (MC_MKDIR, path, mode);
983 static int mcfs_rmdir (vfs *me, char *path)
985 return mcfs_rpc_path (MC_RMDIR, path);
988 static int mcfs_link (vfs *me, char *p1, char *p2)
990 return mcfs_rpc_two_paths (MC_LINK, p1, p2);
993 /* We do not free anything right now: we free resources when we run
994 * out of them
996 static vfsid mcfs_getid (vfs *me, char *p, struct vfs_stamping **parent)
998 *parent = NULL;
1000 return (vfsid) -1;
1003 static int mcfs_nothingisopen (vfsid id)
1005 return 0;
1008 static void mcfs_free (vfsid id)
1010 /* FIXME: Should not be empty */
1013 /* Gives up on a socket and reopnes the connection, the child own the socket
1014 * now
1016 static void
1017 my_forget (char *path)
1019 char *host, *user, *pass, *p;
1020 int port, i, vers;
1022 if (strncmp (path, "/#mc:", 5))
1023 return;
1025 path += 5;
1026 if (path[0] == '/' && path[1] == '/')
1027 path += 2;
1029 if ((p = mcfs_get_host_and_username (path, &host, &user, &port, &pass)) == 0) {
1030 g_free (host);
1031 g_free (user);
1032 if (pass)
1033 wipe_password (pass);
1034 return;
1036 for (i = 0; i < MCFS_MAX_CONNECTIONS; i++){
1037 if ((strcmp (host, mcfs_connections [i].host) == 0) &&
1038 (strcmp (user, mcfs_connections [i].user) == 0) &&
1039 (port == mcfs_connections [i].port)){
1041 /* close socket: the child owns it now */
1042 close (mcfs_connections [i].sock);
1044 /* reopen the connection */
1045 mcfs_connections [i].sock = mcfs_open_tcp_link (host, user, &port, pass, &vers);
1048 g_free (p);
1049 g_free (host);
1050 g_free (user);
1051 if (pass)
1052 wipe_password (pass);
1055 static int
1056 mcfs_setctl (vfs *me, char *path, int ctlop, char *arg)
1058 switch (ctlop) {
1059 case MCCTL_FORGET_ABOUT:
1060 my_forget(path);
1061 return 0;
1063 return 0;
1066 vfs vfs_mcfs_ops = {
1067 NULL, /* This is place of next pointer */
1068 "mcfs",
1069 F_NET, /* flags */
1070 "mc:", /* prefix */
1071 NULL, /* data */
1072 0, /* errno */
1073 NULL,
1074 NULL,
1075 mcfs_fill_names,
1076 NULL,
1078 mcfs_open,
1079 mcfs_close,
1080 mcfs_read,
1081 mcfs_write,
1083 mcfs_opendir,
1084 mcfs_readdir,
1085 mcfs_closedir,
1086 NULL,
1087 NULL,
1089 mcfs_stat,
1090 mcfs_lstat,
1091 mcfs_fstat,
1093 mcfs_chmod,
1094 mcfs_chown,
1095 mcfs_utime,
1097 mcfs_readlink,
1098 mcfs_symlink,
1099 mcfs_link,
1100 mcfs_unlink,
1102 mcfs_rename,
1103 mcfs_chdir,
1104 mcfs_errno,
1105 mcfs_lseek,
1106 mcfs_mknod,
1108 mcfs_getid,
1109 mcfs_nothingisopen,
1110 mcfs_free,
1112 NULL,
1113 NULL,
1115 mcfs_mkdir,
1116 mcfs_rmdir,
1117 NULL,
1118 mcfs_setctl
1120 MMAPNULL
1123 #endif /* WITH_MCFS */
1126 /* FIXME: This part should go to another c module, perhaps tcp.c */
1128 static void mcfs_free_bucket (int bucket)
1130 g_free (mcfs_connections [bucket].host);
1131 g_free (mcfs_connections [bucket].user);
1132 g_free (mcfs_connections [bucket].home);
1134 /* Set all the fields to zero */
1135 mcfs_connections [bucket].host =
1136 mcfs_connections [bucket].user =
1137 mcfs_connections [bucket].home = 0;
1138 mcfs_connections [bucket].sock =
1139 mcfs_connections [bucket].version = 0;
1142 static int mcfs_invalidate_socket (int sock)
1144 int i, j = -1;
1145 extern int mc_chdir (char *);
1147 for (i = 0; i < MCFS_MAX_CONNECTIONS; i++)
1148 if (mcfs_connections [i].sock == sock) {
1149 mcfs_free_bucket (i);
1150 j = 0;
1153 if (j == -1)
1154 return -1; /* It was not our sock */
1155 /* Break from any possible loop */
1156 mc_chdir ("/");
1157 return 0;
1160 void tcp_invalidate_socket (int sock)
1162 mcfs_invalidate_socket (sock);
1164 /* FIXME end: 'cause it is used not only by mcfs */