1 /* Virtual File System: Midnight Commander file system.
3 Copyright (C) 1995, 1996, 1997 The Free Software Foundation
5 Written by Miguel de Icaza
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., 675 Mass Ave, Cambridge, MA 02139, USA. */
23 /* Namespace: exports mcfs_vfs_ops, tcp_invalidate_socket */
34 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
35 #include <netdb.h> /* struct hostent */
36 #include <sys/socket.h> /* AF_INET */
37 #include <netinet/in.h> /* struct in_addr */
38 #include <arpa/inet.h>
40 #include <sys/timeb.h> /* alex: for struct timeb definition */
41 #endif /* SCO_FLAVOR */
53 #include "../src/dialog.h"
55 #define MCFS_MAX_CONNECTIONS 32
56 #define mcserver_port 9876
58 static mcfs_open_connections
= 0;
59 static struct _mcfs_connection
{
66 } mcfs_connections
[MCFS_MAX_CONNECTIONS
];
68 typedef struct _mcfs_connection mcfs_connection
;
70 typedef struct { int handle
; mcfs_connection
*conn
; } mcfs_handle
;
74 static char *mcfs_gethome (mcfs_connection
*mc
);
76 /* Extract the hostname and username from the path */
77 /* path is in the form: hostname:user/remote-dir */
78 static char *mcfs_get_host_and_username (char *path
, char **host
, char **user
,
79 int *port
, char **pass
)
81 return vfs_split_url (path
, host
, user
, port
, pass
, 0, 0);
84 static void mcfs_fill_names (vfs
*me
, void (*func
)(char *))
89 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++){
90 if (mcfs_connections
[i
].host
== 0)
92 name
= g_strconcat ("/#mc:", mcfs_connections
[i
].user
,
93 "@", mcfs_connections
[i
].host
, NULL
);
99 static void mcfs_free_bucket (int bucket
)
101 g_free (mcfs_connections
[bucket
].host
);
102 g_free (mcfs_connections
[bucket
].user
);
103 g_free (mcfs_connections
[bucket
].home
);
105 /* Set all the fields to zero */
106 mcfs_connections
[bucket
].host
=
107 mcfs_connections
[bucket
].user
=
108 mcfs_connections
[bucket
].home
= 0;
109 mcfs_connections
[bucket
].sock
=
110 mcfs_connections
[bucket
].version
= 0;
113 /* FIXME: This part should go to another c module, perhaps tcp.c */
114 static int mcfs_invalidate_socket (int);
116 void tcp_invalidate_socket (int sock
)
118 mcfs_invalidate_socket (sock
);
120 /* FIXME end: 'cause it is used not only by mcfs */
122 static int mcfs_invalidate_socket (int sock
)
125 extern int mc_chdir (char *);
127 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++)
128 if (mcfs_connections
[i
].sock
== sock
) {
129 mcfs_free_bucket (i
);
134 return -1; /* It was not our sock */
135 /* Break from any possible loop */
140 /* This routine checks the server RPC version and logs the user in */
141 static int mcfs_login_server (int my_socket
, char *user
, int port
,
142 int port_autodetected
, char *netrcpass
,
148 /* Send the version number */
149 rpc_send (my_socket
, RPC_INT
, *version
, RPC_END
);
150 if (0 == rpc_get (my_socket
, RPC_INT
, &result
, RPC_END
))
153 if (result
!= MC_VERSION_OK
){
154 message_1s (1, _(" MCFS "), _(" The server does not support this version "));
159 /* FIXME: figure out why last_current_dir used to be passed here */
160 rpc_send (my_socket
, RPC_INT
, MC_LOGIN
, RPC_STRING
, "/",
161 RPC_STRING
, user
, RPC_END
);
163 if (0 == rpc_get (my_socket
, RPC_INT
, &result
, RPC_END
))
166 if (result
== MC_NEED_PASSWORD
){
167 if (port
> 1024 && port_autodetected
){
169 #ifndef VFS_STANDALONE
170 v
= query_dialog (_(" Warning "),
171 _(" The remote server is not running on a system port \n"
172 " you need a password to log in, but the information may \n"
173 " not be safe on the remote side. Continue? \n"), 3, 2,
174 _(" Yes "), _(" No "));
176 message_1s( 1, " MCFS ", _(" The remote server is running on strange port. Giving up.\n"));
185 if (netrcpass
!= NULL
)
186 pass
= g_strdup (netrcpass
);
188 pass
= vfs_get_password (_(" MCFS Password required "));
190 rpc_send (my_socket
, RPC_INT
, MC_QUIT
, RPC_END
);
194 rpc_send (my_socket
, RPC_INT
, MC_PASS
, RPC_STRING
, pass
, RPC_END
);
196 wipe_password (pass
);
198 if (0 == rpc_get (my_socket
, RPC_INT
, &result
, RPC_END
))
201 if (result
!= MC_LOGINOK
){
202 message_1s (1, " MCFS ", _(" Invalid password "));
203 rpc_send (my_socket
, RPC_INT
, MC_QUIT
, RPC_END
);
211 /* This used to be in utilvfs.c, but as it deals with portmapper, it
212 is probably usefull for mcfs */
214 open_tcp_link (char *host
, int *port
, int *version
, char *caller
)
216 struct sockaddr_in server_address
;
217 unsigned long inaddr
;
224 bzero ((char *) &server_address
, sizeof (server_address
));
225 server_address
.sin_family
= AF_INET
;
227 /* Try to use the dotted decimal number */
228 if ((inaddr
= inet_addr (host
)) != -1)
229 bcopy ((char *) &inaddr
, (char *) &server_address
.sin_addr
,
232 if ((hp
= gethostbyname (host
)) == NULL
){
233 message_2s (1, caller
, " Can't locate hostname: %s ", host
);
236 bcopy ((char *) hp
->h_addr
, (char *) &server_address
.sin_addr
,
240 /* Try to contact a remote portmapper to obtain the listening port */
242 *port
= get_remote_port (&server_address
, version
);
248 server_address
.sin_port
= htons (*port
);
250 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0){
251 message_2s (1, caller
, " Can't create socket: %s ",
252 unix_error_string(errno
));
255 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
256 sizeof (server_address
)) < 0){
257 message_2s (1, caller
, " Can't connect to server: %s ",
258 unix_error_string (errno
));
265 static int mcfs_open_tcp_link (char *host
, char *user
,
266 int *port
, char *netrcpass
, int *version
)
269 int old_port
= *port
;
271 my_socket
= open_tcp_link (host
, port
, version
, " MCfs ");
275 /* We got the connection to the server, verify if the server
276 implements our version of the RPC mechanism and then login
279 return mcfs_login_server (my_socket
, user
, *port
, old_port
== 0,
283 static int mcfs_get_free_bucket_init
= 1;
284 static mcfs_connection
*mcfs_get_free_bucket ()
288 if (mcfs_get_free_bucket_init
) {
289 mcfs_get_free_bucket_init
= 0;
290 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++)
291 mcfs_connections
[i
].host
= 0;
293 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++){
294 if (!mcfs_connections
[i
].host
)
295 return &mcfs_connections
[i
];
297 /* This can't happend, since we have checked for max connections before */
298 vfs_die("Internal error: mcfs_get_free_bucket");
299 return 0; /* shut up, stupid gcc */
302 /* This routine keeps track of open connections */
303 /* Returns a connected socket to host */
304 static mcfs_connection
*mcfs_open_link (char *host
, char *user
, int *port
, char *netrcpass
)
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 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++){
315 if (!mcfs_connections
[i
].host
)
317 if ((strcmp (host
, mcfs_connections
[i
].host
) == 0) &&
318 (strcmp (user
, mcfs_connections
[i
].user
) == 0))
319 return &mcfs_connections
[i
];
321 if (mcfs_open_connections
== MCFS_MAX_CONNECTIONS
){
322 message_1s (1, MSG_ERROR
, _(" Too many open connections "));
326 if (!(sock
= mcfs_open_tcp_link (host
, user
, port
, netrcpass
, &version
)))
329 bucket
= mcfs_get_free_bucket ();
330 mcfs_open_connections
++;
331 bucket
->host
= g_strdup (host
);
332 bucket
->user
= g_strdup (user
);
334 bucket
->port
= *port
;
336 bucket
->version
= version
;
341 static int is_error (int result
, int errno_num
)
346 my_errno
= errno_num
;
350 static int the_error (int result
, int errno_num
)
353 my_errno
= errno_num
;
359 static char *mcfs_get_path (mcfs_connection
**mc
, char *path
)
361 char *user
, *host
, *remote_path
;
365 /* An absolute path name, try to determine connection socket */
366 if (strncmp (path
, "/#mc:", 5))
370 /* Port = 0 means that open_tcp_link will try to contact the
371 * remote portmapper to get the port number
374 if ((remote_path
= mcfs_get_host_and_username(path
, &host
, &user
, &port
, &pass
)))
375 if (!(*mc
= mcfs_open_link (host
, user
, &port
, pass
))){
376 g_free (remote_path
);
382 wipe_password (pass
);
387 /* NOTE: tildes are deprecated. See ftpfs.c */
389 int f
= !strcmp( remote_path
, "/~" );
390 if (f
|| !strncmp( remote_path
, "/~/", 3 )) {
392 s
= concat_dir_and_file( mcfs_gethome (*mc
), remote_path
+3-f
);
393 g_free (remote_path
);
400 /* Simple function for routines returning only an integer from the server */
401 static int mcfs_handle_simple_error (int sock
, int return_status
)
405 if (0 == rpc_get (sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
406 return the_error (-1, EIO
);
408 if (is_error (status
, error
))
416 static int mcfs_rpc_two_paths (int command
, char *s1
, char *s2
)
421 if ((r1
= mcfs_get_path (&mc
, s1
)) == 0)
424 if ((r2
= mcfs_get_path (&mc
, s2
)) == 0){
436 return mcfs_handle_simple_error (mc
->sock
, 0);
439 static int mcfs_rpc_path (int command
, char *path
)
444 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
449 RPC_STRING
, remote_file
,
452 g_free (remote_file
);
453 return mcfs_handle_simple_error (mc
->sock
, 0);
456 static int mcfs_rpc_path_int (int command
, char *path
, int data
)
461 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
466 RPC_STRING
, remote_file
,
467 RPC_INT
, data
, RPC_END
);
469 g_free (remote_file
);
470 return mcfs_handle_simple_error (mc
->sock
, 0);
473 static int mcfs_rpc_path_int_int (int command
, char *path
, int n1
, int n2
)
478 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
483 RPC_STRING
, remote_file
,
488 g_free (remote_file
);
489 return mcfs_handle_simple_error (mc
->sock
, 0);
492 static char *mcfs_gethome (mcfs_connection
*mc
)
497 return g_strdup (mc
->home
);
499 rpc_send (mc
->sock
, RPC_INT
, MC_GETHOME
, RPC_END
);
500 if (0 == rpc_get (mc
->sock
, RPC_STRING
, &buffer
, RPC_END
))
501 return g_strdup (PATH_SEP_STR
);
503 return g_strdup (buffer
);
508 static void *mcfs_open (vfs
*me
, char *file
, int flags
, int mode
)
512 int result
, error_num
;
513 mcfs_handle
*remote_handle
;
515 if (!(remote_file
= mcfs_get_path (&mc
, file
)))
518 rpc_send (mc
->sock
, RPC_INT
, MC_OPEN
, RPC_STRING
, remote_file
,
519 RPC_INT
, flags
, RPC_INT
, mode
, RPC_END
);
520 g_free (remote_file
);
522 if (0 == rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error_num
, RPC_END
))
525 if (is_error (result
, error_num
))
528 remote_handle
= g_new (mcfs_handle
, 2);
529 remote_handle
->handle
= result
;
530 remote_handle
->conn
= mc
;
532 return remote_handle
;
535 static int mcfs_read (void *data
, char *buffer
, int count
)
537 mcfs_handle
*info
= (mcfs_handle
*) data
;
543 handle
= info
->handle
;
545 rpc_send (mc
->sock
, RPC_INT
, MC_READ
, RPC_INT
, handle
,
546 RPC_INT
, count
, RPC_END
);
548 if (0 == rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error
, RPC_END
))
549 return the_error (-1, EIO
);
551 if (is_error (result
, error
))
554 if (0 == rpc_get (mc
->sock
, RPC_BLOCK
, result
, buffer
, RPC_END
))
555 return the_error (-1, EIO
);
560 static int mcfs_write (void *data
, char *buf
, int nbyte
)
562 mcfs_handle
*info
= (mcfs_handle
*) data
;
567 handle
= info
->handle
;
573 RPC_BLOCK
, nbyte
, buf
,
576 return mcfs_handle_simple_error (mc
->sock
, 1);
579 static int mcfs_close (void *data
)
581 mcfs_handle
*info
= (mcfs_handle
*) data
;
583 int handle
, result
, error
;
588 handle
= info
->handle
;
591 rpc_send (mc
->sock
, RPC_INT
, MC_CLOSE
, RPC_INT
, handle
, RPC_END
);
593 if (0 == rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error
, RPC_END
))
594 return the_error (-1, EIO
);
596 is_error (result
, error
);
602 static int mcfs_errno (vfs
*me
)
607 typedef struct dir_entry
{
609 struct dir_entry
*next
;
615 mcfs_connection
*conn
;
621 static void *mcfs_opendir (vfs
*me
, char *dirname
)
623 opendir_info
*mcfs_info
;
625 int handle
, error_num
;
629 if (!(remote_dir
= mcfs_get_path (&mc
, dirname
)))
632 rpc_send (mc
->sock
, RPC_INT
, MC_OPENDIR
, RPC_STRING
, remote_dir
, RPC_END
);
635 if (0 == rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error_num
, RPC_END
))
638 if (is_error (result
, error_num
))
643 mcfs_info
= g_new (opendir_info
, 1);
644 mcfs_info
->conn
= mc
;
645 mcfs_info
->handle
= handle
;
646 mcfs_info
->entries
= 0;
647 mcfs_info
->current
= 0;
652 static int get_stat_info (mcfs_connection
*mc
, struct stat
*buf
);
654 static int mcfs_loaddir (opendir_info
*mcfs_info
)
657 mcfs_connection
*mc
= mcfs_info
->conn
;
661 rpc_send (link
, RPC_INT
, MC_READDIR
, RPC_INT
, mcfs_info
->handle
, RPC_END
);
665 dir_entry
*new_entry
;
667 if (!rpc_get (link
, RPC_INT
, &entry_len
, RPC_END
))
673 new_entry
= g_new (dir_entry
, 1);
674 new_entry
->text
= g_new0 (char, entry_len
+ 1);
678 mcfs_info
->entries
= new_entry
;
679 mcfs_info
->current
= new_entry
;
682 mcfs_info
->current
->next
= new_entry
;
683 mcfs_info
->current
= new_entry
;
686 if (!rpc_get (link
, RPC_BLOCK
, entry_len
, new_entry
->text
, RPC_END
))
689 /* Then we get the status from the lstat */
690 if (!rpc_get (link
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
693 if (is_error (status
, error
))
694 new_entry
->merrno
= error
;
696 new_entry
->merrno
= 0;
697 if (!get_stat_info (mc
, &(new_entry
->my_stat
)))
701 mcfs_info
->current
= mcfs_info
->entries
;
706 static void mcfs_free_dir (dir_entry
*de
)
710 mcfs_free_dir (de
->next
);
716 * On some operating systems (Slowaris 2 for example)
717 * the d_name member is just a char long (Nice trick that break everything,
718 * so we need to set up some space for the filename.
722 #ifdef NEED_EXTRA_DIRENT_BUFFER
723 char extra_buffer
[MC_MAXPATHLEN
];
727 /* The readdir routine loads the complete directory */
728 /* It's too slow to ask the server each time */
729 /* It now also sends the complete lstat information for each file */
730 static struct stat
*cached_lstat_info
;
731 static void *mcfs_readdir (void *info
)
733 opendir_info
*mcfs_info
;
736 mcfs_info
= (opendir_info
*) info
;
738 if (!mcfs_info
->entries
)
739 if (!mcfs_loaddir (mcfs_info
))
742 if (mcfs_info
->current
== 0){
743 cached_lstat_info
= 0;
744 mcfs_free_dir (mcfs_info
->entries
);
745 mcfs_info
->entries
= 0;
748 dirent_dest
= &(mcfs_readdir_data
.dent
.d_name
[0]);
749 strcpy (dirent_dest
, mcfs_info
->current
->text
);
750 cached_lstat_info
= &mcfs_info
->current
->my_stat
;
751 mcfs_info
->current
= mcfs_info
->current
->next
;
753 #ifndef DIRENT_LENGTH_COMPUTED
754 mcfs_readdir_data
.dent
.d_namlen
= strlen (mcfs_readdir_data
.dent
.d_name
);
757 return &mcfs_readdir_data
;
760 static int mcfs_closedir (void *info
)
762 opendir_info
*mcfs_info
= (opendir_info
*) info
;
765 rpc_send (mcfs_info
->conn
->sock
, RPC_INT
, MC_CLOSEDIR
,
766 RPC_INT
, mcfs_info
->handle
, RPC_END
);
768 for (p
= mcfs_info
->entries
; p
;){
778 static time_t mcfs_get_time (mcfs_connection
*mc
)
782 if (mc
->version
== 1) {
788 RPC_INT
, &tt
.tm_hour
,
789 RPC_INT
, &tt
.tm_mday
,
790 RPC_INT
, &tt
.tm_year
,
804 sscanf (buf
, "%lx", &tm
);
811 static int get_stat_info (mcfs_connection
*mc
, struct stat
*buf
)
818 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
820 buf
->st_rdev
= mylong
;
822 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
823 buf
->st_ino
= mylong
;
824 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
825 buf
->st_mode
= mylong
;
826 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
827 buf
->st_nlink
= mylong
;
828 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
829 buf
->st_uid
= mylong
;
830 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
831 buf
->st_gid
= mylong
;
832 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
833 buf
->st_size
= mylong
;
835 if (!rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
))
837 #ifdef HAVE_ST_BLOCKS
838 buf
->st_blocks
= mylong
;
840 buf
->st_atime
= mcfs_get_time (mc
);
841 buf
->st_mtime
= mcfs_get_time (mc
);
842 buf
->st_ctime
= mcfs_get_time (mc
);
846 static int mcfs_stat_cmd (int cmd
, char *path
, struct stat
*buf
)
852 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
855 rpc_send (mc
->sock
, RPC_INT
, cmd
, RPC_STRING
, remote_file
, RPC_END
);
856 g_free (remote_file
);
857 if (!rpc_get (mc
->sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
858 return the_error (-1, errno
);
860 if (is_error (status
, error
))
863 if (get_stat_info (mc
, buf
))
866 return the_error (-1, EIO
);
869 static int mcfs_stat (vfs
*me
, char *path
, struct stat
*buf
)
871 return mcfs_stat_cmd (MC_STAT
, path
, buf
);
874 static int mcfs_lstat (vfs
*me
, char *path
, struct stat
*buf
)
876 int path_len
= strlen (path
);
877 int entry_len
= strlen (mcfs_readdir_data
.dent
.d_name
);
880 if (strcmp (path
+ path_len
- entry_len
,
881 mcfs_readdir_data
.dent
.d_name
) == 0 &&
883 *buf
= *cached_lstat_info
;
886 return mcfs_stat_cmd (MC_LSTAT
, path
, buf
);
889 static int mcfs_fstat (void *data
, struct stat
*buf
)
891 mcfs_handle
*info
= (mcfs_handle
*) data
;
895 sock
= info
->conn
->sock
;
896 handle
= info
->handle
;
898 rpc_send (sock
, RPC_INT
, MC_FSTAT
, RPC_INT
, handle
, RPC_END
);
899 if (!rpc_get (sock
, RPC_INT
, &result
, RPC_INT
, &error
, RPC_END
))
900 return the_error (-1, EIO
);
902 if (is_error (result
, error
))
905 if (get_stat_info (info
->conn
, buf
))
908 return the_error (-1, EIO
);
911 static int mcfs_chmod (vfs
*me
, char *path
, int mode
)
913 return mcfs_rpc_path_int (MC_CHMOD
, path
, mode
);
916 static int mcfs_chown (vfs
*me
, char *path
, int owner
, int group
)
918 return mcfs_rpc_path_int_int (MC_CHOWN
, path
, owner
, group
);
921 static int mcfs_utime (vfs
*me
, char *path
, struct utimbuf
*times
)
927 if (!(file
= mcfs_get_path (&mc
, path
)))
931 if (mc
->version
>= 2) {
932 char abuf
[BUF_SMALL
];
933 char mbuf
[BUF_SMALL
];
936 atime
= (long) times
->actime
;
937 mtime
= (long) times
->modtime
;
939 g_snprintf (abuf
, sizeof(abuf
), "%lx", atime
);
940 g_snprintf (mbuf
, sizeof(mbuf
), "%lx", mtime
);
942 rpc_send (mc
->sock
, RPC_INT
, MC_UTIME
,
947 status
= mcfs_handle_simple_error (mc
->sock
, 0);
954 static int mcfs_readlink (vfs
*me
, char *path
, char *buf
, int size
)
956 char *remote_file
, *stat_str
;
960 if (!(remote_file
= mcfs_get_path (&mc
, path
)))
963 rpc_send (mc
->sock
, RPC_INT
, MC_READLINK
, RPC_STRING
, remote_file
, RPC_END
);
964 g_free (remote_file
);
965 if (!rpc_get (mc
->sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
966 return the_error (-1, EIO
);
968 if (is_error (status
, errno
))
971 if (!rpc_get (mc
->sock
, RPC_STRING
, &stat_str
, RPC_END
))
972 return the_error (-1, EIO
);
974 strncpy (buf
, stat_str
, size
);
979 static int mcfs_unlink (vfs
*me
, char *path
)
981 return mcfs_rpc_path (MC_UNLINK
, path
);
984 static int mcfs_symlink (vfs
*me
, char *n1
, char *n2
)
986 return mcfs_rpc_two_paths (MC_SYMLINK
, n1
, n2
);
989 static int mcfs_rename (vfs
*me
, char *a
, char *b
)
991 return mcfs_rpc_two_paths (MC_RENAME
, a
, b
);
994 static int mcfs_chdir (vfs
*me
, char *path
)
1000 if (!(remote_dir
= mcfs_get_path (&mc
, path
)))
1003 rpc_send (mc
->sock
, RPC_INT
, MC_CHDIR
, RPC_STRING
, remote_dir
, RPC_END
);
1004 g_free (remote_dir
);
1005 if (!rpc_get (mc
->sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
1006 return the_error (-1, EIO
);
1008 if (is_error (status
, error
))
1013 static int mcfs_lseek (void *data
, off_t offset
, int whence
)
1015 mcfs_handle
*info
= (mcfs_handle
*) data
;
1018 sock
= info
->conn
->sock
;
1019 handle
= info
->handle
;
1027 return mcfs_handle_simple_error (sock
, 1);
1030 static int mcfs_mknod (vfs
*me
, char *path
, int mode
, int dev
)
1032 return mcfs_rpc_path_int_int (MC_MKNOD
, path
, mode
, dev
);
1035 static int mcfs_mkdir (vfs
*me
, char *path
, mode_t mode
)
1037 return mcfs_rpc_path_int (MC_MKDIR
, path
, mode
);
1040 static int mcfs_rmdir (vfs
*me
, char *path
)
1042 return mcfs_rpc_path (MC_RMDIR
, path
);
1045 static int mcfs_link (vfs
*me
, char *p1
, char *p2
)
1047 return mcfs_rpc_two_paths (MC_LINK
, p1
, p2
);
1050 /* We do not free anything right now: we free resources when we run
1053 static vfsid
mcfs_getid (vfs
*me
, char *p
, struct vfs_stamping
**parent
)
1060 static int mcfs_nothingisopen (vfsid id
)
1065 static void mcfs_free (vfsid id
)
1067 /* FIXME: Should not be empty */
1070 /* Gives up on a socket and reopnes the connection, the child own the socket
1074 my_forget (char *path
)
1076 char *host
, *user
, *pass
, *p
;
1079 if (strncmp (path
, "/#mc:", 5))
1083 if (path
[0] == '/' && path
[1] == '/')
1086 if ((p
= mcfs_get_host_and_username (path
, &host
, &user
, &port
, &pass
)) == 0) {
1090 wipe_password (pass
);
1093 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++){
1094 if ((strcmp (host
, mcfs_connections
[i
].host
) == 0) &&
1095 (strcmp (user
, mcfs_connections
[i
].user
) == 0) &&
1096 (port
== mcfs_connections
[i
].port
)){
1098 /* close socket: the child owns it now */
1099 close (mcfs_connections
[i
].sock
);
1101 /* reopen the connection */
1102 mcfs_connections
[i
].sock
= mcfs_open_tcp_link (host
, user
, &port
, pass
, &vers
);
1109 wipe_password (pass
);
1113 mcfs_setctl (vfs
*me
, char *path
, int ctlop
, char *arg
)
1116 case MCCTL_FORGET_ABOUT
:
1123 vfs vfs_mcfs_ops
= {
1124 NULL
, /* This is place of next pointer */
1125 "Midnight Commander's private remote filesystem",