1 /* Virtual File System: Midnight Commander file system.
3 Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
4 2004, 2005, 2007 Free Software Foundation, Inc.
6 Written by Miguel de Icaza
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public License
12 as published by the Free Software Foundation; either version 2 of
13 the License, or (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public
21 License along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
24 /* Namespace: exports mcfs_vfs_ops, tcp_invalidate_socket */
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>
46 #include <rpc/pmap_prot.h>
47 #ifdef HAVE_RPC_PMAP_CLNT_H
48 #include <rpc/pmap_clnt.h>
52 #include "../src/global.h"
53 #include "../src/tty.h" /* enable/disable interrupt key */
54 #include "../src/wtools.h" /* message() */
55 #include "../src/main.h" /* print_vfs_message */
64 # define INADDR_NONE (0xffffffffU)
67 #define MCFS_MAX_CONNECTIONS 32
69 static struct _mcfs_connection
{
76 } mcfs_connections
[MCFS_MAX_CONNECTIONS
];
79 #define mcserver_port 9876
81 typedef struct _mcfs_connection mcfs_connection
;
85 mcfs_connection
*conn
;
88 static char *mcfs_gethome (mcfs_connection
* mc
);
90 static struct vfs_class vfs_mcfs_ops
;
92 /* Extract the hostname and username from the path */
93 /* path is in the form: hostname:user/remote-dir */
95 mcfs_get_host_and_username (const char *path
, char **host
, char **user
,
96 int *port
, char **pass
)
98 return vfs_split_url (path
, host
, user
, port
, pass
, 0, 0);
102 mcfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
109 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++) {
110 if (mcfs_connections
[i
].host
== 0)
112 name
= g_strconcat ("/#mc:", mcfs_connections
[i
].user
,
113 "@", mcfs_connections
[i
].host
, (char *) NULL
);
119 /* This routine checks the server RPC version and logs the user in */
121 mcfs_login_server (int my_socket
, char *user
, int port
,
122 int port_autodetected
, char *netrcpass
, int *version
)
127 /* Send the version number */
128 rpc_send (my_socket
, RPC_INT
, *version
, RPC_END
);
129 if (0 == rpc_get (my_socket
, RPC_INT
, &result
, RPC_END
))
132 if (result
!= MC_VERSION_OK
) {
133 message (1, _(" MCFS "),
134 _(" The server does not support this version "));
139 /* FIXME: figure out why last_current_dir used to be passed here */
140 rpc_send (my_socket
, RPC_INT
, MC_LOGIN
, RPC_STRING
, "/",
141 RPC_STRING
, user
, RPC_END
);
143 if (0 == rpc_get (my_socket
, RPC_INT
, &result
, RPC_END
))
146 if (result
== MC_NEED_PASSWORD
) {
147 if (port
> 1024 && port_autodetected
) {
149 v
= query_dialog (_("Warning"),
151 (" The remote server is not running on a system port \n"
152 " you need a password to log in, but the information may \n"
153 " not be safe on the remote side. Continue? \n"),
154 3, 2, _("&Yes"), _("&No"));
161 if (netrcpass
!= NULL
)
162 pass
= g_strdup (netrcpass
);
164 pass
= vfs_get_password (_(" MCFS Password required "));
166 rpc_send (my_socket
, RPC_INT
, MC_QUIT
, RPC_END
);
170 rpc_send (my_socket
, RPC_INT
, MC_PASS
, RPC_STRING
, pass
, RPC_END
);
172 wipe_password (pass
);
174 if (0 == rpc_get (my_socket
, RPC_INT
, &result
, RPC_END
))
177 if (result
!= MC_LOGINOK
) {
178 message (1, _(" MCFS "), _(" Invalid password "));
179 rpc_send (my_socket
, RPC_INT
, MC_QUIT
, RPC_END
);
188 mcfs_get_remote_port (struct sockaddr_in
*sin
, int *version
)
190 #ifdef HAVE_PMAP_GETMAPS
195 port
= mcserver_port
;
196 for (pl
= pmap_getmaps (sin
); pl
; pl
= pl
->pml_next
)
197 if (pl
->pml_map
.pm_prog
== RPC_PROGNUM
198 && pl
->pml_map
.pm_prot
== IPPROTO_TCP
199 && pl
->pml_map
.pm_vers
>= (unsigned long) *version
) {
200 *version
= (int) pl
->pml_map
.pm_vers
;
201 port
= pl
->pml_map
.pm_port
;
205 #ifdef HAVE_PMAP_GETPORT
207 for (*version
= RPC_PROGVER
; *version
>= 1; (*version
)--)
208 if (port
= pmap_getport (sin
, RPC_PROGNUM
, *version
, IPPROTO_TCP
))
210 #endif /* HAVE_PMAP_GETPORT */
212 return mcserver_port
;
213 #endif /* HAVE_PMAP_GETMAPS */
216 /* This used to be in utilvfs.c, but as it deals with portmapper, it
217 is probably useful for mcfs */
219 mcfs_create_tcp_link (const char *host
, int *port
, int *version
, const char *caller
)
221 struct sockaddr_in server_address
;
222 unsigned long inaddr
;
229 memset ((char *) &server_address
, 0, sizeof (server_address
));
230 server_address
.sin_family
= AF_INET
;
232 /* Try to use the dotted decimal number */
233 if ((inaddr
= inet_addr (host
)) != INADDR_NONE
)
234 memcpy ((char *) &server_address
.sin_addr
, (char *) &inaddr
,
237 if ((hp
= gethostbyname (host
)) == NULL
) {
238 message (1, caller
, _(" Cannot locate hostname: %s "),
242 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
,
246 /* Try to contact a remote portmapper to obtain the listening port */
248 *port
= mcfs_get_remote_port (&server_address
, version
);
254 server_address
.sin_port
= htons (*port
);
256 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
257 message (1, caller
, _(" Cannot create socket: %s "),
258 unix_error_string (errno
));
261 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
262 sizeof (server_address
)) < 0) {
263 message (1, caller
, _(" Cannot connect to server: %s "),
264 unix_error_string (errno
));
272 mcfs_open_tcp_link (char *host
, char *user
,
273 int *port
, char *netrcpass
, int *version
)
276 int old_port
= *port
;
278 my_socket
= mcfs_create_tcp_link (host
, port
, version
, " MCfs ");
282 /* We got the connection to the server, verify if the server
283 implements our version of the RPC mechanism and then login
286 return mcfs_login_server (my_socket
, user
, *port
, old_port
== 0,
290 static int mcfs_get_free_bucket_init
= 1;
291 static mcfs_connection
*
292 mcfs_get_free_bucket (void)
296 if (mcfs_get_free_bucket_init
) {
297 mcfs_get_free_bucket_init
= 0;
298 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++)
299 mcfs_connections
[i
].host
= 0;
301 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++) {
302 if (!mcfs_connections
[i
].host
)
303 return &mcfs_connections
[i
];
305 /* This can't happend, since we have checked for max connections before */
306 vfs_die ("Internal error: mcfs_get_free_bucket");
307 return 0; /* shut up, stupid gcc */
310 /* This routine keeps track of open connections */
311 /* Returns a connected socket to host */
312 static mcfs_connection
*
313 mcfs_open_link (char *host
, char *user
, int *port
, char *netrcpass
)
315 static int mcfs_open_connections
= 0;
316 int i
, sock
, version
;
317 mcfs_connection
*bucket
;
319 /* Is the link actually open? */
320 if (mcfs_get_free_bucket_init
) {
321 mcfs_get_free_bucket_init
= 0;
322 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++)
323 mcfs_connections
[i
].host
= 0;
325 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++) {
326 if (!mcfs_connections
[i
].host
)
328 if ((strcmp (host
, mcfs_connections
[i
].host
) == 0) &&
329 (strcmp (user
, mcfs_connections
[i
].user
) == 0))
330 return &mcfs_connections
[i
];
332 if (mcfs_open_connections
== MCFS_MAX_CONNECTIONS
) {
333 message (1, MSG_ERROR
, _(" Too many open connections "));
339 mcfs_open_tcp_link (host
, user
, port
, netrcpass
, &version
)))
342 bucket
= mcfs_get_free_bucket ();
343 mcfs_open_connections
++;
344 bucket
->host
= g_strdup (host
);
345 bucket
->user
= g_strdup (user
);
347 bucket
->port
= *port
;
349 bucket
->version
= version
;
355 mcfs_is_error (int result
, int errno_num
)
360 my_errno
= errno_num
;
365 mcfs_set_error (int result
, int errno_num
)
368 my_errno
= errno_num
;
375 mcfs_get_path (mcfs_connection
**mc
, const char *path
)
377 char *user
, *host
, *remote_path
;
381 /* An absolute path name, try to determine connection socket */
382 if (strncmp (path
, "/#mc:", 5))
386 /* Port = 0 means that mcfs_create_tcp_link will try to contact the
387 * remote portmapper to get the port number
391 mcfs_get_host_and_username (path
, &host
, &user
, &port
, &pass
)))
392 if (!(*mc
= mcfs_open_link (host
, user
, &port
, pass
))) {
393 g_free (remote_path
);
399 wipe_password (pass
);
404 /* NOTE: tildes are deprecated. See ftpfs.c */
406 int f
= !strcmp (remote_path
, "/~");
407 if (f
|| !strncmp (remote_path
, "/~/", 3)) {
409 s
= mhl_str_dir_plus_file (mcfs_gethome (*mc
),
410 remote_path
+ 3 - f
);
411 g_free (remote_path
);
418 /* Simple function for routines returning only an integer from the server */
420 mcfs_handle_simple_error (int sock
, int return_status
)
424 if (0 == rpc_get (sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
425 return mcfs_set_error (-1, EIO
);
427 if (mcfs_is_error (status
, error
))
436 mcfs_rpc_two_paths (int command
, const char *s1
, const char *s2
)
441 if ((r1
= mcfs_get_path (&mc
, s1
)) == 0)
444 if ((r2
= mcfs_get_path (&mc
, s2
)) == 0) {
450 RPC_INT
, command
, RPC_STRING
, r1
, RPC_STRING
, r2
, RPC_END
);
453 return mcfs_handle_simple_error (mc
->sock
, 0);
457 mcfs_rpc_path (int command
, const char *path
)
462 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
466 RPC_INT
, command
, RPC_STRING
, remote_file
, RPC_END
);
468 g_free (remote_file
);
469 return mcfs_handle_simple_error (mc
->sock
, 0);
473 mcfs_rpc_path_int (int command
, const char *path
, int data
)
478 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
483 RPC_STRING
, remote_file
, RPC_INT
, data
, RPC_END
);
485 g_free (remote_file
);
486 return mcfs_handle_simple_error (mc
->sock
, 0);
490 mcfs_rpc_path_int_int (int command
, const char *path
, int n1
, int n2
)
495 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
500 RPC_STRING
, remote_file
, RPC_INT
, n1
, RPC_INT
, n2
, RPC_END
);
502 g_free (remote_file
);
503 return mcfs_handle_simple_error (mc
->sock
, 0);
507 mcfs_gethome (mcfs_connection
*mc
)
512 return g_strdup (mc
->home
);
514 rpc_send (mc
->sock
, RPC_INT
, MC_GETHOME
, RPC_END
);
515 if (0 == rpc_get (mc
->sock
, RPC_STRING
, &buffer
, RPC_END
))
516 return g_strdup (PATH_SEP_STR
);
518 return g_strdup (buffer
);
524 mcfs_open (struct vfs_class
*me
, const char *file
, int flags
, int mode
)
528 int result
, error_num
;
529 mcfs_handle
*remote_handle
;
533 if (!(remote_file
= mcfs_get_path (&mc
, file
)))
536 rpc_send (mc
->sock
, RPC_INT
, MC_OPEN
, RPC_STRING
, remote_file
, RPC_INT
,
537 flags
, RPC_INT
, mode
, RPC_END
);
538 g_free (remote_file
);
541 rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error_num
, RPC_END
))
544 if (mcfs_is_error (result
, error_num
))
547 remote_handle
= g_new (mcfs_handle
, 2);
548 remote_handle
->handle
= result
;
549 remote_handle
->conn
= mc
;
551 return remote_handle
;
555 mcfs_read (void *data
, char *buffer
, int count
)
557 mcfs_handle
*info
= (mcfs_handle
*) data
;
563 handle
= info
->handle
;
565 rpc_send (mc
->sock
, RPC_INT
, MC_READ
, RPC_INT
, handle
,
566 RPC_INT
, count
, RPC_END
);
569 rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error
, RPC_END
))
570 return mcfs_set_error (-1, EIO
);
572 if (mcfs_is_error (result
, error
))
575 if (0 == rpc_get (mc
->sock
, RPC_BLOCK
, result
, buffer
, RPC_END
))
576 return mcfs_set_error (-1, EIO
);
582 mcfs_write (void *data
, const char *buf
, int nbyte
)
584 mcfs_handle
*info
= (mcfs_handle
*) data
;
589 handle
= info
->handle
;
594 RPC_INT
, nbyte
, RPC_BLOCK
, nbyte
, buf
, RPC_END
);
596 return mcfs_handle_simple_error (mc
->sock
, 1);
600 mcfs_close (void *data
)
602 mcfs_handle
*info
= (mcfs_handle
*) data
;
604 int handle
, result
, error
;
609 handle
= info
->handle
;
612 rpc_send (mc
->sock
, RPC_INT
, MC_CLOSE
, RPC_INT
, handle
, RPC_END
);
615 rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error
, RPC_END
))
616 return mcfs_set_error (-1, EIO
);
618 mcfs_is_error (result
, error
);
625 mcfs_errno (struct vfs_class
*me
)
632 typedef struct dir_entry
{
634 struct dir_entry
*next
;
640 mcfs_connection
*conn
;
647 mcfs_opendir (struct vfs_class
*me
, const char *dirname
)
649 opendir_info
*mcfs_info
;
651 int handle
, error_num
;
657 if (!(remote_dir
= mcfs_get_path (&mc
, dirname
)))
660 rpc_send (mc
->sock
, RPC_INT
, MC_OPENDIR
, RPC_STRING
, remote_dir
,
665 rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error_num
, RPC_END
))
668 if (mcfs_is_error (result
, error_num
))
673 mcfs_info
= g_new (opendir_info
, 1);
674 mcfs_info
->conn
= mc
;
675 mcfs_info
->handle
= handle
;
676 mcfs_info
->entries
= 0;
677 mcfs_info
->current
= 0;
682 static int mcfs_get_stat_info (mcfs_connection
* mc
, struct stat
*buf
);
685 mcfs_loaddir (opendir_info
*mcfs_info
)
688 mcfs_connection
*mc
= mcfs_info
->conn
;
692 rpc_send (link
, RPC_INT
, MC_READDIR
, RPC_INT
, mcfs_info
->handle
,
697 dir_entry
*new_entry
;
699 if (!rpc_get (link
, RPC_INT
, &entry_len
, RPC_END
))
705 new_entry
= g_new (dir_entry
, 1);
706 new_entry
->text
= g_new0 (char, entry_len
+ 1);
710 mcfs_info
->entries
= new_entry
;
711 mcfs_info
->current
= new_entry
;
714 mcfs_info
->current
->next
= new_entry
;
715 mcfs_info
->current
= new_entry
;
719 (link
, RPC_BLOCK
, entry_len
, new_entry
->text
, RPC_END
))
722 /* Then we get the status from the lstat */
723 if (!rpc_get (link
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
726 if (mcfs_is_error (status
, error
))
727 new_entry
->merrno
= error
;
729 new_entry
->merrno
= 0;
730 if (!mcfs_get_stat_info (mc
, &(new_entry
->my_stat
)))
734 mcfs_info
->current
= mcfs_info
->entries
;
740 mcfs_free_dir (dir_entry
*de
)
744 mcfs_free_dir (de
->next
);
749 static union vfs_dirent mcfs_readdir_data
;
751 /* The readdir routine loads the complete directory */
752 /* It's too slow to ask the server each time */
753 /* It now also sends the complete lstat information for each file */
754 static struct stat
*cached_lstat_info
;
757 mcfs_readdir (void *info
)
759 opendir_info
*mcfs_info
;
762 mcfs_info
= (opendir_info
*) info
;
764 if (!mcfs_info
->entries
)
765 if (!mcfs_loaddir (mcfs_info
))
768 if (mcfs_info
->current
== 0) {
769 cached_lstat_info
= 0;
770 mcfs_free_dir (mcfs_info
->entries
);
771 mcfs_info
->entries
= 0;
774 dirent_dest
= mcfs_readdir_data
.dent
.d_name
;
775 g_strlcpy (dirent_dest
, mcfs_info
->current
->text
, MC_MAXPATHLEN
);
776 cached_lstat_info
= &mcfs_info
->current
->my_stat
;
777 mcfs_info
->current
= mcfs_info
->current
->next
;
779 compute_namelen (&mcfs_readdir_data
.dent
);
781 return &mcfs_readdir_data
;
785 mcfs_closedir (void *info
)
787 opendir_info
*mcfs_info
= (opendir_info
*) info
;
790 rpc_send (mcfs_info
->conn
->sock
, RPC_INT
, MC_CLOSEDIR
,
791 RPC_INT
, mcfs_info
->handle
, RPC_END
);
793 for (p
= mcfs_info
->entries
; p
;) {
804 mcfs_get_time (mcfs_connection
*mc
)
808 if (mc
->version
== 1) {
814 RPC_INT
, &tt
.tm_hour
,
815 RPC_INT
, &tt
.tm_mday
,
816 RPC_INT
, &tt
.tm_year
, RPC_INT
, &tt
.tm_mon
, RPC_END
);
825 rpc_get (sock
, RPC_STRING
, &buf
, RPC_END
);
826 sscanf (buf
, "%lx", &tm
);
834 mcfs_get_stat_info (mcfs_connection
*mc
, struct stat
*buf
)
841 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
842 #ifdef HAVE_STRUCT_STAT_ST_RDEV
843 buf
->st_rdev
= mylong
;
845 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
846 buf
->st_ino
= mylong
;
847 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
848 buf
->st_mode
= mylong
;
849 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
850 buf
->st_nlink
= mylong
;
851 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
852 buf
->st_uid
= mylong
;
853 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
854 buf
->st_gid
= mylong
;
855 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
856 buf
->st_size
= mylong
;
858 if (!rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
))
860 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
861 buf
->st_blocks
= mylong
;
863 buf
->st_atime
= mcfs_get_time (mc
);
864 buf
->st_mtime
= mcfs_get_time (mc
);
865 buf
->st_ctime
= mcfs_get_time (mc
);
870 mcfs_stat_cmd (int cmd
, const char *path
, struct stat
*buf
)
876 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
879 rpc_send (mc
->sock
, RPC_INT
, cmd
, RPC_STRING
, remote_file
, RPC_END
);
880 g_free (remote_file
);
881 if (!rpc_get (mc
->sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
882 return mcfs_set_error (-1, errno
);
884 if (mcfs_is_error (status
, error
))
887 if (mcfs_get_stat_info (mc
, buf
))
890 return mcfs_set_error (-1, EIO
);
894 mcfs_stat (struct vfs_class
*me
, const char *path
, struct stat
*buf
)
898 return mcfs_stat_cmd (MC_STAT
, path
, buf
);
902 mcfs_lstat (struct vfs_class
*me
, const char *path
, struct stat
*buf
)
904 int path_len
= strlen (path
);
905 int entry_len
= strlen (mcfs_readdir_data
.dent
.d_name
);
910 if (strcmp (path
+ path_len
- entry_len
,
911 mcfs_readdir_data
.dent
.d_name
) == 0 && cached_lstat_info
) {
912 *buf
= *cached_lstat_info
;
915 return mcfs_stat_cmd (MC_LSTAT
, path
, buf
);
919 mcfs_fstat (void *data
, struct stat
*buf
)
921 mcfs_handle
*info
= (mcfs_handle
*) data
;
925 sock
= info
->conn
->sock
;
926 handle
= info
->handle
;
928 rpc_send (sock
, RPC_INT
, MC_FSTAT
, RPC_INT
, handle
, RPC_END
);
929 if (!rpc_get (sock
, RPC_INT
, &result
, RPC_INT
, &error
, RPC_END
))
930 return mcfs_set_error (-1, EIO
);
932 if (mcfs_is_error (result
, error
))
935 if (mcfs_get_stat_info (info
->conn
, buf
))
938 return mcfs_set_error (-1, EIO
);
942 mcfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
946 return mcfs_rpc_path_int (MC_CHMOD
, path
, mode
);
950 mcfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
954 return mcfs_rpc_path_int_int (MC_CHOWN
, path
, owner
, group
);
958 mcfs_utime (struct vfs_class
*me
, const char *path
, struct utimbuf
*times
)
966 if (!(file
= mcfs_get_path (&mc
, path
)))
970 if (mc
->version
>= 2) {
971 char abuf
[BUF_SMALL
];
972 char mbuf
[BUF_SMALL
];
975 atime
= (long) times
->actime
;
976 mtime
= (long) times
->modtime
;
978 g_snprintf (abuf
, sizeof (abuf
), "%lx", atime
);
979 g_snprintf (mbuf
, sizeof (mbuf
), "%lx", mtime
);
981 rpc_send (mc
->sock
, RPC_INT
, MC_UTIME
,
983 RPC_STRING
, abuf
, RPC_STRING
, mbuf
, RPC_END
);
984 status
= mcfs_handle_simple_error (mc
->sock
, 0);
992 mcfs_readlink (struct vfs_class
*me
, const char *path
, char *buf
, size_t size
)
994 char *remote_file
, *stat_str
;
1001 if (!(remote_file
= mcfs_get_path (&mc
, path
)))
1004 rpc_send (mc
->sock
, RPC_INT
, MC_READLINK
, RPC_STRING
, remote_file
,
1006 g_free (remote_file
);
1007 if (!rpc_get (mc
->sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
1008 return mcfs_set_error (-1, EIO
);
1010 if (mcfs_is_error (status
, errno
))
1013 if (!rpc_get (mc
->sock
, RPC_STRING
, &stat_str
, RPC_END
))
1014 return mcfs_set_error (-1, EIO
);
1016 len
= strlen (stat_str
);
1019 /* readlink() does not append a NUL character to buf */
1020 memcpy (buf
, stat_str
, size
);
1026 mcfs_unlink (struct vfs_class
*me
, const char *path
)
1030 return mcfs_rpc_path (MC_UNLINK
, path
);
1034 mcfs_symlink (struct vfs_class
*me
, const char *n1
, const char *n2
)
1038 return mcfs_rpc_two_paths (MC_SYMLINK
, n1
, n2
);
1042 mcfs_rename (struct vfs_class
*me
, const char *a
, const char *b
)
1046 return mcfs_rpc_two_paths (MC_RENAME
, a
, b
);
1050 mcfs_chdir (struct vfs_class
*me
, const char *path
)
1053 mcfs_connection
*mc
;
1058 if (!(remote_dir
= mcfs_get_path (&mc
, path
)))
1061 rpc_send (mc
->sock
, RPC_INT
, MC_CHDIR
, RPC_STRING
, remote_dir
,
1063 g_free (remote_dir
);
1064 if (!rpc_get (mc
->sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
1065 return mcfs_set_error (-1, EIO
);
1067 if (mcfs_is_error (status
, error
))
1073 mcfs_lseek (void *data
, off_t offset
, int whence
)
1075 mcfs_handle
*info
= (mcfs_handle
*) data
;
1078 sock
= info
->conn
->sock
;
1079 handle
= info
->handle
;
1081 /* FIXME: off_t may be too long to fit */
1082 rpc_send (sock
, RPC_INT
, MC_LSEEK
, RPC_INT
, handle
, RPC_INT
,
1083 (int) offset
, RPC_INT
, whence
, RPC_END
);
1085 return mcfs_handle_simple_error (sock
, 1);
1089 mcfs_mknod (struct vfs_class
*me
, const char *path
, int mode
, int dev
)
1093 return mcfs_rpc_path_int_int (MC_MKNOD
, path
, mode
, dev
);
1097 mcfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1101 return mcfs_rpc_path_int (MC_MKDIR
, path
, mode
);
1105 mcfs_rmdir (struct vfs_class
*me
, const char *path
)
1109 return mcfs_rpc_path (MC_RMDIR
, path
);
1113 mcfs_link (struct vfs_class
*me
, const char *p1
, const char *p2
)
1117 return mcfs_rpc_two_paths (MC_LINK
, p1
, p2
);
1120 /* Gives up on a socket and reopens the connection, the child own the socket
1124 mcfs_forget (const char *path
)
1126 char *host
, *user
, *pass
, *p
;
1129 if (strncmp (path
, "/#mc:", 5))
1133 if (path
[0] == '/' && path
[1] == '/')
1137 mcfs_get_host_and_username (path
, &host
, &user
, &port
,
1142 wipe_password (pass
);
1145 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++) {
1146 if ((strcmp (host
, mcfs_connections
[i
].host
) == 0) &&
1147 (strcmp (user
, mcfs_connections
[i
].user
) == 0) &&
1148 (port
== mcfs_connections
[i
].port
)) {
1150 /* close socket: the child owns it now */
1151 close (mcfs_connections
[i
].sock
);
1153 /* reopen the connection */
1154 mcfs_connections
[i
].sock
=
1155 mcfs_open_tcp_link (host
, user
, &port
, pass
, &vers
);
1162 wipe_password (pass
);
1166 mcfs_setctl (struct vfs_class
*me
, const char *path
, int ctlop
, void *arg
)
1172 case VFS_SETCTL_FORGET
:
1182 vfs_mcfs_ops
.name
= "mcfs";
1183 vfs_mcfs_ops
.prefix
= "mc:";
1184 vfs_mcfs_ops
.fill_names
= mcfs_fill_names
;
1185 vfs_mcfs_ops
.open
= mcfs_open
;
1186 vfs_mcfs_ops
.close
= mcfs_close
;
1187 vfs_mcfs_ops
.read
= mcfs_read
;
1188 vfs_mcfs_ops
.write
= mcfs_write
;
1189 vfs_mcfs_ops
.opendir
= mcfs_opendir
;
1190 vfs_mcfs_ops
.readdir
= mcfs_readdir
;
1191 vfs_mcfs_ops
.closedir
= mcfs_closedir
;
1192 vfs_mcfs_ops
.stat
= mcfs_stat
;
1193 vfs_mcfs_ops
.lstat
= mcfs_lstat
;
1194 vfs_mcfs_ops
.fstat
= mcfs_fstat
;
1195 vfs_mcfs_ops
.chmod
= mcfs_chmod
;
1196 vfs_mcfs_ops
.chown
= mcfs_chown
;
1197 vfs_mcfs_ops
.utime
= mcfs_utime
;
1198 vfs_mcfs_ops
.readlink
= mcfs_readlink
;
1199 vfs_mcfs_ops
.symlink
= mcfs_symlink
;
1200 vfs_mcfs_ops
.link
= mcfs_link
;
1201 vfs_mcfs_ops
.unlink
= mcfs_unlink
;
1202 vfs_mcfs_ops
.rename
= mcfs_rename
;
1203 vfs_mcfs_ops
.chdir
= mcfs_chdir
;
1204 vfs_mcfs_ops
.ferrno
= mcfs_errno
;
1205 vfs_mcfs_ops
.lseek
= mcfs_lseek
;
1206 vfs_mcfs_ops
.mknod
= mcfs_mknod
;
1207 vfs_mcfs_ops
.mkdir
= mcfs_mkdir
;
1208 vfs_mcfs_ops
.rmdir
= mcfs_rmdir
;
1209 vfs_mcfs_ops
.setctl
= mcfs_setctl
;
1210 vfs_register_class (&vfs_mcfs_ops
);
1214 mcfs_free_bucket (int bucket
)
1216 g_free (mcfs_connections
[bucket
].host
);
1217 g_free (mcfs_connections
[bucket
].user
);
1218 g_free (mcfs_connections
[bucket
].home
);
1220 /* Set all the fields to zero */
1221 mcfs_connections
[bucket
].host
=
1222 mcfs_connections
[bucket
].user
= mcfs_connections
[bucket
].home
= 0;
1223 mcfs_connections
[bucket
].sock
= mcfs_connections
[bucket
].version
= 0;
1227 mcfs_invalidate_socket (int sock
)
1231 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++)
1232 if (mcfs_connections
[i
].sock
== sock
) {
1233 mcfs_free_bucket (i
);
1238 return -1; /* It was not our sock */
1239 /* Break from any possible loop */
1245 tcp_invalidate_socket (int sock
)
1247 mcfs_invalidate_socket (sock
);
1250 #endif /* WITH_MCFS */