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. */
26 * \brief Source: Midnight Commander file system
27 * \author Miguel de Icaza
28 * \author Andrej Borsenkow
29 * \author Norbert Warmuth
31 * Namespace: exports mcfs_vfs_ops, tcp_invalidate_socket
36 #ifdef ENABLE_VFS_MCFS
44 #include <sys/types.h> /* POSIX-required by sys/socket.h and netdb.h */
45 #include <netdb.h> /* struct hostent */
46 #include <sys/socket.h> /* AF_INET */
47 #include <netinet/in.h> /* struct in_addr */
48 #ifdef HAVE_ARPA_INET_H
49 #include <arpa/inet.h>
54 #include <rpc/pmap_prot.h>
55 #ifdef HAVE_RPC_PMAP_CLNT_H
56 #include <rpc/pmap_clnt.h>
60 #include "../src/global.h"
61 #include "../src/tty.h" /* enable/disable interrupt key */
62 #include "../src/wtools.h" /* message() */
63 #include "../src/main.h" /* print_vfs_message */
72 # define INADDR_NONE (0xffffffffU)
75 #define MCFS_MAX_CONNECTIONS 32
77 static struct _mcfs_connection
{
84 } mcfs_connections
[MCFS_MAX_CONNECTIONS
];
87 #define mcserver_port 9876
89 typedef struct _mcfs_connection mcfs_connection
;
93 mcfs_connection
*conn
;
96 static char *mcfs_gethome (mcfs_connection
* mc
);
98 static struct vfs_class vfs_mcfs_ops
;
100 /* Extract the hostname and username from the path */
101 /* path is in the form: hostname:user/remote-dir */
103 mcfs_get_host_and_username (const char *path
, char **host
, char **user
,
104 int *port
, char **pass
)
106 return vfs_split_url (path
, host
, user
, port
, pass
, 0, 0);
110 mcfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
117 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++) {
118 if (mcfs_connections
[i
].host
== 0)
120 name
= g_strconcat ("/#mc:", mcfs_connections
[i
].user
,
121 "@", mcfs_connections
[i
].host
, (char *) NULL
);
127 /* This routine checks the server RPC version and logs the user in */
129 mcfs_login_server (int my_socket
, char *user
, int port
,
130 int port_autodetected
, char *netrcpass
, int *version
)
135 /* Send the version number */
136 rpc_send (my_socket
, RPC_INT
, *version
, RPC_END
);
137 if (0 == rpc_get (my_socket
, RPC_INT
, &result
, RPC_END
))
140 if (result
!= MC_VERSION_OK
) {
141 message (D_ERROR
, _(" MCFS "),
142 _(" The server does not support this version "));
147 /* FIXME: figure out why last_current_dir used to be passed here */
148 rpc_send (my_socket
, RPC_INT
, MC_LOGIN
, RPC_STRING
, "/",
149 RPC_STRING
, user
, RPC_END
);
151 if (0 == rpc_get (my_socket
, RPC_INT
, &result
, RPC_END
))
154 if (result
== MC_NEED_PASSWORD
) {
155 if (port
> 1024 && port_autodetected
) {
157 v
= query_dialog (_("Warning"),
159 (" The remote server is not running on a system port \n"
160 " you need a password to log in, but the information may \n"
161 " not be safe on the remote side. Continue? \n"),
162 D_ERROR
, 2, _("&Yes"), _("&No"));
169 if (netrcpass
!= NULL
)
170 pass
= g_strdup (netrcpass
);
172 pass
= vfs_get_password (_(" MCFS Password required "));
174 rpc_send (my_socket
, RPC_INT
, MC_QUIT
, RPC_END
);
178 rpc_send (my_socket
, RPC_INT
, MC_PASS
, RPC_STRING
, pass
, RPC_END
);
180 wipe_password (pass
);
182 if (0 == rpc_get (my_socket
, RPC_INT
, &result
, RPC_END
))
185 if (result
!= MC_LOGINOK
) {
186 message (D_ERROR
, _(" MCFS "), _(" Invalid password "));
187 rpc_send (my_socket
, RPC_INT
, MC_QUIT
, RPC_END
);
196 mcfs_get_remote_port (struct sockaddr_in
*sin
, int *version
)
198 #ifdef HAVE_PMAP_GETMAPS
203 port
= mcserver_port
;
204 for (pl
= pmap_getmaps (sin
); pl
; pl
= pl
->pml_next
)
205 if (pl
->pml_map
.pm_prog
== RPC_PROGNUM
206 && pl
->pml_map
.pm_prot
== IPPROTO_TCP
207 && pl
->pml_map
.pm_vers
>= (unsigned long) *version
) {
208 *version
= (int) pl
->pml_map
.pm_vers
;
209 port
= pl
->pml_map
.pm_port
;
213 #ifdef HAVE_PMAP_GETPORT
215 for (*version
= RPC_PROGVER
; *version
>= 1; (*version
)--)
216 if (port
= pmap_getport (sin
, RPC_PROGNUM
, *version
, IPPROTO_TCP
))
218 #endif /* HAVE_PMAP_GETPORT */
220 return mcserver_port
;
221 #endif /* HAVE_PMAP_GETMAPS */
224 /* This used to be in utilvfs.c, but as it deals with portmapper, it
225 is probably useful for mcfs */
227 mcfs_create_tcp_link (const char *host
, int *port
, int *version
, const char *caller
)
229 struct sockaddr_in server_address
;
230 unsigned long inaddr
;
237 memset ((char *) &server_address
, 0, sizeof (server_address
));
238 server_address
.sin_family
= AF_INET
;
240 /* Try to use the dotted decimal number */
241 if ((inaddr
= inet_addr (host
)) != INADDR_NONE
)
242 memcpy ((char *) &server_address
.sin_addr
, (char *) &inaddr
,
245 if ((hp
= gethostbyname (host
)) == NULL
) {
246 message (D_ERROR
, caller
, _(" Cannot locate hostname: %s "),
250 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
,
254 /* Try to contact a remote portmapper to obtain the listening port */
256 *port
= mcfs_get_remote_port (&server_address
, version
);
262 server_address
.sin_port
= htons (*port
);
264 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
265 message (D_ERROR
, caller
, _(" Cannot create socket: %s "),
266 unix_error_string (errno
));
269 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
270 sizeof (server_address
)) < 0) {
271 message (D_ERROR
, caller
, _(" Cannot connect to server: %s "),
272 unix_error_string (errno
));
280 mcfs_open_tcp_link (char *host
, char *user
,
281 int *port
, char *netrcpass
, int *version
)
284 int old_port
= *port
;
286 my_socket
= mcfs_create_tcp_link (host
, port
, version
, " MCfs ");
290 /* We got the connection to the server, verify if the server
291 implements our version of the RPC mechanism and then login
294 return mcfs_login_server (my_socket
, user
, *port
, old_port
== 0,
298 static int mcfs_get_free_bucket_init
= 1;
299 static mcfs_connection
*
300 mcfs_get_free_bucket (void)
304 if (mcfs_get_free_bucket_init
) {
305 mcfs_get_free_bucket_init
= 0;
306 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++)
307 mcfs_connections
[i
].host
= 0;
309 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++) {
310 if (!mcfs_connections
[i
].host
)
311 return &mcfs_connections
[i
];
313 /* This can't happend, since we have checked for max connections before */
314 vfs_die ("Internal error: mcfs_get_free_bucket");
315 return 0; /* shut up, stupid gcc */
318 /* This routine keeps track of open connections */
319 /* Returns a connected socket to host */
320 static mcfs_connection
*
321 mcfs_open_link (char *host
, char *user
, int *port
, char *netrcpass
)
323 static int mcfs_open_connections
= 0;
324 int i
, sock
, version
;
325 mcfs_connection
*bucket
;
327 /* Is the link actually open? */
328 if (mcfs_get_free_bucket_init
) {
329 mcfs_get_free_bucket_init
= 0;
330 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++)
331 mcfs_connections
[i
].host
= 0;
333 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++) {
334 if (!mcfs_connections
[i
].host
)
336 if ((strcmp (host
, mcfs_connections
[i
].host
) == 0) &&
337 (strcmp (user
, mcfs_connections
[i
].user
) == 0))
338 return &mcfs_connections
[i
];
340 if (mcfs_open_connections
== MCFS_MAX_CONNECTIONS
) {
341 message (D_ERROR
, MSG_ERROR
, _(" Too many open connections "));
347 mcfs_open_tcp_link (host
, user
, port
, netrcpass
, &version
)))
350 bucket
= mcfs_get_free_bucket ();
351 mcfs_open_connections
++;
352 bucket
->host
= g_strdup (host
);
353 bucket
->user
= g_strdup (user
);
355 bucket
->port
= *port
;
357 bucket
->version
= version
;
363 mcfs_is_error (int result
, int errno_num
)
368 my_errno
= errno_num
;
373 mcfs_set_error (int result
, int errno_num
)
376 my_errno
= errno_num
;
383 mcfs_get_path (mcfs_connection
**mc
, const char *path
)
385 char *user
, *host
, *remote_path
;
389 /* An absolute path name, try to determine connection socket */
390 if (strncmp (path
, "/#mc:", 5))
394 /* Port = 0 means that mcfs_create_tcp_link will try to contact the
395 * remote portmapper to get the port number
399 mcfs_get_host_and_username (path
, &host
, &user
, &port
, &pass
)))
400 if (!(*mc
= mcfs_open_link (host
, user
, &port
, pass
))) {
401 g_free (remote_path
);
407 wipe_password (pass
);
412 /* NOTE: tildes are deprecated. See ftpfs.c */
414 int f
= !strcmp (remote_path
, "/~");
415 if (f
|| !strncmp (remote_path
, "/~/", 3)) {
417 s
= concat_dir_and_file (mcfs_gethome (*mc
),
418 remote_path
+ 3 - f
);
419 g_free (remote_path
);
426 /* Simple function for routines returning only an integer from the server */
428 mcfs_handle_simple_error (int sock
, int return_status
)
432 if (0 == rpc_get (sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
433 return mcfs_set_error (-1, EIO
);
435 if (mcfs_is_error (status
, error
))
444 mcfs_rpc_two_paths (int command
, const char *s1
, const char *s2
)
449 if ((r1
= mcfs_get_path (&mc
, s1
)) == 0)
452 if ((r2
= mcfs_get_path (&mc
, s2
)) == 0) {
458 RPC_INT
, command
, RPC_STRING
, r1
, RPC_STRING
, r2
, RPC_END
);
461 return mcfs_handle_simple_error (mc
->sock
, 0);
465 mcfs_rpc_path (int command
, const char *path
)
470 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
474 RPC_INT
, command
, RPC_STRING
, remote_file
, RPC_END
);
476 g_free (remote_file
);
477 return mcfs_handle_simple_error (mc
->sock
, 0);
481 mcfs_rpc_path_int (int command
, const char *path
, int data
)
486 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
491 RPC_STRING
, remote_file
, RPC_INT
, data
, RPC_END
);
493 g_free (remote_file
);
494 return mcfs_handle_simple_error (mc
->sock
, 0);
498 mcfs_rpc_path_int_int (int command
, const char *path
, int n1
, int n2
)
503 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
508 RPC_STRING
, remote_file
, RPC_INT
, n1
, RPC_INT
, n2
, RPC_END
);
510 g_free (remote_file
);
511 return mcfs_handle_simple_error (mc
->sock
, 0);
515 mcfs_gethome (mcfs_connection
*mc
)
520 return g_strdup (mc
->home
);
522 rpc_send (mc
->sock
, RPC_INT
, MC_GETHOME
, RPC_END
);
523 if (0 == rpc_get (mc
->sock
, RPC_STRING
, &buffer
, RPC_END
))
524 return g_strdup (PATH_SEP_STR
);
526 return g_strdup (buffer
);
532 mcfs_open (struct vfs_class
*me
, const char *file
, int flags
, int mode
)
536 int result
, error_num
;
537 mcfs_handle
*remote_handle
;
541 if (!(remote_file
= mcfs_get_path (&mc
, file
)))
544 rpc_send (mc
->sock
, RPC_INT
, MC_OPEN
, RPC_STRING
, remote_file
, RPC_INT
,
545 flags
, RPC_INT
, mode
, RPC_END
);
546 g_free (remote_file
);
549 rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error_num
, RPC_END
))
552 if (mcfs_is_error (result
, error_num
))
555 remote_handle
= g_new (mcfs_handle
, 2);
556 remote_handle
->handle
= result
;
557 remote_handle
->conn
= mc
;
559 return remote_handle
;
563 mcfs_read (void *data
, char *buffer
, int count
)
565 mcfs_handle
*info
= (mcfs_handle
*) data
;
571 handle
= info
->handle
;
573 rpc_send (mc
->sock
, RPC_INT
, MC_READ
, RPC_INT
, handle
,
574 RPC_INT
, count
, RPC_END
);
577 rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error
, RPC_END
))
578 return mcfs_set_error (-1, EIO
);
580 if (mcfs_is_error (result
, error
))
583 if (0 == rpc_get (mc
->sock
, RPC_BLOCK
, result
, buffer
, RPC_END
))
584 return mcfs_set_error (-1, EIO
);
590 mcfs_write (void *data
, const char *buf
, int nbyte
)
592 mcfs_handle
*info
= (mcfs_handle
*) data
;
597 handle
= info
->handle
;
602 RPC_INT
, nbyte
, RPC_BLOCK
, nbyte
, buf
, RPC_END
);
604 return mcfs_handle_simple_error (mc
->sock
, 1);
608 mcfs_close (void *data
)
610 mcfs_handle
*info
= (mcfs_handle
*) data
;
612 int handle
, result
, error
;
617 handle
= info
->handle
;
620 rpc_send (mc
->sock
, RPC_INT
, MC_CLOSE
, RPC_INT
, handle
, RPC_END
);
623 rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error
, RPC_END
))
624 return mcfs_set_error (-1, EIO
);
626 mcfs_is_error (result
, error
);
633 mcfs_errno (struct vfs_class
*me
)
640 typedef struct dir_entry
{
642 struct dir_entry
*next
;
648 mcfs_connection
*conn
;
655 mcfs_opendir (struct vfs_class
*me
, const char *dirname
)
657 opendir_info
*mcfs_info
;
659 int handle
, error_num
;
665 if (!(remote_dir
= mcfs_get_path (&mc
, dirname
)))
668 rpc_send (mc
->sock
, RPC_INT
, MC_OPENDIR
, RPC_STRING
, remote_dir
,
673 rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error_num
, RPC_END
))
676 if (mcfs_is_error (result
, error_num
))
681 mcfs_info
= g_new (opendir_info
, 1);
682 mcfs_info
->conn
= mc
;
683 mcfs_info
->handle
= handle
;
684 mcfs_info
->entries
= 0;
685 mcfs_info
->current
= 0;
690 static int mcfs_get_stat_info (mcfs_connection
* mc
, struct stat
*buf
);
693 mcfs_loaddir (opendir_info
*mcfs_info
)
696 mcfs_connection
*mc
= mcfs_info
->conn
;
700 rpc_send (link
, RPC_INT
, MC_READDIR
, RPC_INT
, mcfs_info
->handle
,
705 dir_entry
*new_entry
;
707 if (!rpc_get (link
, RPC_INT
, &entry_len
, RPC_END
))
713 new_entry
= g_new (dir_entry
, 1);
714 new_entry
->text
= g_new0 (char, entry_len
+ 1);
718 mcfs_info
->entries
= new_entry
;
719 mcfs_info
->current
= new_entry
;
722 mcfs_info
->current
->next
= new_entry
;
723 mcfs_info
->current
= new_entry
;
727 (link
, RPC_BLOCK
, entry_len
, new_entry
->text
, RPC_END
))
730 /* Then we get the status from the lstat */
731 if (!rpc_get (link
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
734 if (mcfs_is_error (status
, error
))
735 new_entry
->merrno
= error
;
737 new_entry
->merrno
= 0;
738 if (!mcfs_get_stat_info (mc
, &(new_entry
->my_stat
)))
742 mcfs_info
->current
= mcfs_info
->entries
;
748 mcfs_free_dir (dir_entry
*de
)
752 mcfs_free_dir (de
->next
);
757 static union vfs_dirent mcfs_readdir_data
;
759 /* The readdir routine loads the complete directory */
760 /* It's too slow to ask the server each time */
761 /* It now also sends the complete lstat information for each file */
762 static struct stat
*cached_lstat_info
;
765 mcfs_readdir (void *info
)
767 opendir_info
*mcfs_info
;
770 mcfs_info
= (opendir_info
*) info
;
772 if (!mcfs_info
->entries
)
773 if (!mcfs_loaddir (mcfs_info
))
776 if (mcfs_info
->current
== 0) {
777 cached_lstat_info
= 0;
778 mcfs_free_dir (mcfs_info
->entries
);
779 mcfs_info
->entries
= 0;
782 dirent_dest
= mcfs_readdir_data
.dent
.d_name
;
783 g_strlcpy (dirent_dest
, mcfs_info
->current
->text
, MC_MAXPATHLEN
);
784 cached_lstat_info
= &mcfs_info
->current
->my_stat
;
785 mcfs_info
->current
= mcfs_info
->current
->next
;
787 compute_namelen (&mcfs_readdir_data
.dent
);
789 return &mcfs_readdir_data
;
793 mcfs_closedir (void *info
)
795 opendir_info
*mcfs_info
= (opendir_info
*) info
;
798 rpc_send (mcfs_info
->conn
->sock
, RPC_INT
, MC_CLOSEDIR
,
799 RPC_INT
, mcfs_info
->handle
, RPC_END
);
801 for (p
= mcfs_info
->entries
; p
;) {
812 mcfs_get_time (mcfs_connection
*mc
)
816 if (mc
->version
== 1) {
822 RPC_INT
, &tt
.tm_hour
,
823 RPC_INT
, &tt
.tm_mday
,
824 RPC_INT
, &tt
.tm_year
, RPC_INT
, &tt
.tm_mon
, RPC_END
);
833 rpc_get (sock
, RPC_STRING
, &buf
, RPC_END
);
834 sscanf (buf
, "%lx", &tm
);
842 mcfs_get_stat_info (mcfs_connection
*mc
, struct stat
*buf
)
849 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
850 #ifdef HAVE_STRUCT_STAT_ST_RDEV
851 buf
->st_rdev
= mylong
;
853 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
854 buf
->st_ino
= mylong
;
855 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
856 buf
->st_mode
= mylong
;
857 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
858 buf
->st_nlink
= mylong
;
859 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
860 buf
->st_uid
= mylong
;
861 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
862 buf
->st_gid
= mylong
;
863 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
864 buf
->st_size
= mylong
;
866 if (!rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
))
868 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
869 buf
->st_blocks
= mylong
;
871 buf
->st_atime
= mcfs_get_time (mc
);
872 buf
->st_mtime
= mcfs_get_time (mc
);
873 buf
->st_ctime
= mcfs_get_time (mc
);
878 mcfs_stat_cmd (int cmd
, const char *path
, struct stat
*buf
)
884 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
887 rpc_send (mc
->sock
, RPC_INT
, cmd
, RPC_STRING
, remote_file
, RPC_END
);
888 g_free (remote_file
);
889 if (!rpc_get (mc
->sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
890 return mcfs_set_error (-1, errno
);
892 if (mcfs_is_error (status
, error
))
895 if (mcfs_get_stat_info (mc
, buf
))
898 return mcfs_set_error (-1, EIO
);
902 mcfs_stat (struct vfs_class
*me
, const char *path
, struct stat
*buf
)
906 return mcfs_stat_cmd (MC_STAT
, path
, buf
);
910 mcfs_lstat (struct vfs_class
*me
, const char *path
, struct stat
*buf
)
912 int path_len
= strlen (path
);
913 int entry_len
= strlen (mcfs_readdir_data
.dent
.d_name
);
918 if (strcmp (path
+ path_len
- entry_len
,
919 mcfs_readdir_data
.dent
.d_name
) == 0 && cached_lstat_info
) {
920 *buf
= *cached_lstat_info
;
923 return mcfs_stat_cmd (MC_LSTAT
, path
, buf
);
927 mcfs_fstat (void *data
, struct stat
*buf
)
929 mcfs_handle
*info
= (mcfs_handle
*) data
;
933 sock
= info
->conn
->sock
;
934 handle
= info
->handle
;
936 rpc_send (sock
, RPC_INT
, MC_FSTAT
, RPC_INT
, handle
, RPC_END
);
937 if (!rpc_get (sock
, RPC_INT
, &result
, RPC_INT
, &error
, RPC_END
))
938 return mcfs_set_error (-1, EIO
);
940 if (mcfs_is_error (result
, error
))
943 if (mcfs_get_stat_info (info
->conn
, buf
))
946 return mcfs_set_error (-1, EIO
);
950 mcfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
954 return mcfs_rpc_path_int (MC_CHMOD
, path
, mode
);
958 mcfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
962 return mcfs_rpc_path_int_int (MC_CHOWN
, path
, owner
, group
);
966 mcfs_utime (struct vfs_class
*me
, const char *path
, struct utimbuf
*times
)
974 if (!(file
= mcfs_get_path (&mc
, path
)))
978 if (mc
->version
>= 2) {
979 char abuf
[BUF_SMALL
];
980 char mbuf
[BUF_SMALL
];
983 atime
= (long) times
->actime
;
984 mtime
= (long) times
->modtime
;
986 g_snprintf (abuf
, sizeof (abuf
), "%lx", atime
);
987 g_snprintf (mbuf
, sizeof (mbuf
), "%lx", mtime
);
989 rpc_send (mc
->sock
, RPC_INT
, MC_UTIME
,
991 RPC_STRING
, abuf
, RPC_STRING
, mbuf
, RPC_END
);
992 status
= mcfs_handle_simple_error (mc
->sock
, 0);
1000 mcfs_readlink (struct vfs_class
*me
, const char *path
, char *buf
, size_t size
)
1002 char *remote_file
, *stat_str
;
1004 mcfs_connection
*mc
;
1009 if (!(remote_file
= mcfs_get_path (&mc
, path
)))
1012 rpc_send (mc
->sock
, RPC_INT
, MC_READLINK
, RPC_STRING
, remote_file
,
1014 g_free (remote_file
);
1015 if (!rpc_get (mc
->sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
1016 return mcfs_set_error (-1, EIO
);
1018 if (mcfs_is_error (status
, errno
))
1021 if (!rpc_get (mc
->sock
, RPC_STRING
, &stat_str
, RPC_END
))
1022 return mcfs_set_error (-1, EIO
);
1024 len
= strlen (stat_str
);
1027 /* readlink() does not append a NUL character to buf */
1028 memcpy (buf
, stat_str
, size
);
1034 mcfs_unlink (struct vfs_class
*me
, const char *path
)
1038 return mcfs_rpc_path (MC_UNLINK
, path
);
1042 mcfs_symlink (struct vfs_class
*me
, const char *n1
, const char *n2
)
1046 return mcfs_rpc_two_paths (MC_SYMLINK
, n1
, n2
);
1050 mcfs_rename (struct vfs_class
*me
, const char *a
, const char *b
)
1054 return mcfs_rpc_two_paths (MC_RENAME
, a
, b
);
1058 mcfs_chdir (struct vfs_class
*me
, const char *path
)
1061 mcfs_connection
*mc
;
1066 if (!(remote_dir
= mcfs_get_path (&mc
, path
)))
1069 rpc_send (mc
->sock
, RPC_INT
, MC_CHDIR
, RPC_STRING
, remote_dir
,
1071 g_free (remote_dir
);
1072 if (!rpc_get (mc
->sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
1073 return mcfs_set_error (-1, EIO
);
1075 if (mcfs_is_error (status
, error
))
1081 mcfs_lseek (void *data
, off_t offset
, int whence
)
1083 mcfs_handle
*info
= (mcfs_handle
*) data
;
1086 sock
= info
->conn
->sock
;
1087 handle
= info
->handle
;
1089 /* FIXME: off_t may be too long to fit */
1090 rpc_send (sock
, RPC_INT
, MC_LSEEK
, RPC_INT
, handle
, RPC_INT
,
1091 (int) offset
, RPC_INT
, whence
, RPC_END
);
1093 return mcfs_handle_simple_error (sock
, 1);
1097 mcfs_mknod (struct vfs_class
*me
, const char *path
, int mode
, int dev
)
1101 return mcfs_rpc_path_int_int (MC_MKNOD
, path
, mode
, dev
);
1105 mcfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1109 return mcfs_rpc_path_int (MC_MKDIR
, path
, mode
);
1113 mcfs_rmdir (struct vfs_class
*me
, const char *path
)
1117 return mcfs_rpc_path (MC_RMDIR
, path
);
1121 mcfs_link (struct vfs_class
*me
, const char *p1
, const char *p2
)
1125 return mcfs_rpc_two_paths (MC_LINK
, p1
, p2
);
1128 /* Gives up on a socket and reopens the connection, the child own the socket
1132 mcfs_forget (const char *path
)
1134 char *host
, *user
, *pass
, *p
;
1137 if (strncmp (path
, "/#mc:", 5))
1141 if (path
[0] == '/' && path
[1] == '/')
1145 mcfs_get_host_and_username (path
, &host
, &user
, &port
,
1150 wipe_password (pass
);
1153 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++) {
1154 if ((strcmp (host
, mcfs_connections
[i
].host
) == 0) &&
1155 (strcmp (user
, mcfs_connections
[i
].user
) == 0) &&
1156 (port
== mcfs_connections
[i
].port
)) {
1158 /* close socket: the child owns it now */
1159 close (mcfs_connections
[i
].sock
);
1161 /* reopen the connection */
1162 mcfs_connections
[i
].sock
=
1163 mcfs_open_tcp_link (host
, user
, &port
, pass
, &vers
);
1170 wipe_password (pass
);
1174 mcfs_setctl (struct vfs_class
*me
, const char *path
, int ctlop
, void *arg
)
1180 case VFS_SETCTL_FORGET
:
1190 vfs_mcfs_ops
.name
= "mcfs";
1191 vfs_mcfs_ops
.prefix
= "mc:";
1192 vfs_mcfs_ops
.fill_names
= mcfs_fill_names
;
1193 vfs_mcfs_ops
.open
= mcfs_open
;
1194 vfs_mcfs_ops
.close
= mcfs_close
;
1195 vfs_mcfs_ops
.read
= mcfs_read
;
1196 vfs_mcfs_ops
.write
= mcfs_write
;
1197 vfs_mcfs_ops
.opendir
= mcfs_opendir
;
1198 vfs_mcfs_ops
.readdir
= mcfs_readdir
;
1199 vfs_mcfs_ops
.closedir
= mcfs_closedir
;
1200 vfs_mcfs_ops
.stat
= mcfs_stat
;
1201 vfs_mcfs_ops
.lstat
= mcfs_lstat
;
1202 vfs_mcfs_ops
.fstat
= mcfs_fstat
;
1203 vfs_mcfs_ops
.chmod
= mcfs_chmod
;
1204 vfs_mcfs_ops
.chown
= mcfs_chown
;
1205 vfs_mcfs_ops
.utime
= mcfs_utime
;
1206 vfs_mcfs_ops
.readlink
= mcfs_readlink
;
1207 vfs_mcfs_ops
.symlink
= mcfs_symlink
;
1208 vfs_mcfs_ops
.link
= mcfs_link
;
1209 vfs_mcfs_ops
.unlink
= mcfs_unlink
;
1210 vfs_mcfs_ops
.rename
= mcfs_rename
;
1211 vfs_mcfs_ops
.chdir
= mcfs_chdir
;
1212 vfs_mcfs_ops
.ferrno
= mcfs_errno
;
1213 vfs_mcfs_ops
.lseek
= mcfs_lseek
;
1214 vfs_mcfs_ops
.mknod
= mcfs_mknod
;
1215 vfs_mcfs_ops
.mkdir
= mcfs_mkdir
;
1216 vfs_mcfs_ops
.rmdir
= mcfs_rmdir
;
1217 vfs_mcfs_ops
.setctl
= mcfs_setctl
;
1218 vfs_register_class (&vfs_mcfs_ops
);
1222 mcfs_free_bucket (int bucket
)
1224 g_free (mcfs_connections
[bucket
].host
);
1225 g_free (mcfs_connections
[bucket
].user
);
1226 g_free (mcfs_connections
[bucket
].home
);
1228 /* Set all the fields to zero */
1229 mcfs_connections
[bucket
].host
=
1230 mcfs_connections
[bucket
].user
= mcfs_connections
[bucket
].home
= 0;
1231 mcfs_connections
[bucket
].sock
= mcfs_connections
[bucket
].version
= 0;
1235 mcfs_invalidate_socket (int sock
)
1239 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++)
1240 if (mcfs_connections
[i
].sock
== sock
) {
1241 mcfs_free_bucket (i
);
1246 return -1; /* It was not our sock */
1247 /* Break from any possible loop */
1253 tcp_invalidate_socket (int sock
)
1255 mcfs_invalidate_socket (sock
);
1258 void mcfs__unused(void)
1261 CFLAGS="-ansi -pedantic -Wall -Wextra -Werror"
1264 #endif /* ENABLE_VFS_MCFS */