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/wtools.h" /* message() */
62 #include "../src/main.h" /* print_vfs_message */
71 # define INADDR_NONE (0xffffffffU)
74 #define MCFS_MAX_CONNECTIONS 32
76 static struct _mcfs_connection
{
83 } mcfs_connections
[MCFS_MAX_CONNECTIONS
];
86 #define mcserver_port 9876
88 typedef struct _mcfs_connection mcfs_connection
;
92 mcfs_connection
*conn
;
95 static char *mcfs_gethome (mcfs_connection
* mc
);
97 static struct vfs_class vfs_mcfs_ops
;
99 /* Extract the hostname and username from the path */
100 /* path is in the form: hostname:user/remote-dir */
102 mcfs_get_host_and_username (const char *path
, char **host
, char **user
,
103 int *port
, char **pass
)
105 return vfs_split_url (path
, host
, user
, port
, pass
, 0, 0);
109 mcfs_fill_names (struct vfs_class
*me
, fill_names_f func
)
116 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++) {
117 if (mcfs_connections
[i
].host
== 0)
119 name
= g_strconcat ("/#mc:", mcfs_connections
[i
].user
,
120 "@", mcfs_connections
[i
].host
, (char *) NULL
);
126 /* This routine checks the server RPC version and logs the user in */
128 mcfs_login_server (int my_socket
, char *user
, int port
,
129 int port_autodetected
, char *netrcpass
, int *version
)
134 /* Send the version number */
135 rpc_send (my_socket
, RPC_INT
, *version
, RPC_END
);
136 if (0 == rpc_get (my_socket
, RPC_INT
, &result
, RPC_END
))
139 if (result
!= MC_VERSION_OK
) {
140 message (D_ERROR
, _(" MCFS "),
141 _(" The server does not support this version "));
146 /* FIXME: figure out why last_current_dir used to be passed here */
147 rpc_send (my_socket
, RPC_INT
, MC_LOGIN
, RPC_STRING
, "/",
148 RPC_STRING
, user
, RPC_END
);
150 if (0 == rpc_get (my_socket
, RPC_INT
, &result
, RPC_END
))
153 if (result
== MC_NEED_PASSWORD
) {
154 if (port
> 1024 && port_autodetected
) {
156 v
= query_dialog (_("Warning"),
158 (" The remote server is not running on a system port \n"
159 " you need a password to log in, but the information may \n"
160 " not be safe on the remote side. Continue? \n"),
161 D_ERROR
, 2, _("&Yes"), _("&No"));
168 if (netrcpass
!= NULL
)
169 pass
= g_strdup (netrcpass
);
171 pass
= vfs_get_password (_(" MCFS Password required "));
173 rpc_send (my_socket
, RPC_INT
, MC_QUIT
, RPC_END
);
177 rpc_send (my_socket
, RPC_INT
, MC_PASS
, RPC_STRING
, pass
, RPC_END
);
179 wipe_password (pass
);
181 if (0 == rpc_get (my_socket
, RPC_INT
, &result
, RPC_END
))
184 if (result
!= MC_LOGINOK
) {
185 message (D_ERROR
, _(" MCFS "), _(" Invalid password "));
186 rpc_send (my_socket
, RPC_INT
, MC_QUIT
, RPC_END
);
195 mcfs_get_remote_port (struct sockaddr_in
*sin
, int *version
)
197 #ifdef HAVE_PMAP_GETMAPS
202 port
= mcserver_port
;
203 for (pl
= pmap_getmaps (sin
); pl
; pl
= pl
->pml_next
)
204 if (pl
->pml_map
.pm_prog
== RPC_PROGNUM
205 && pl
->pml_map
.pm_prot
== IPPROTO_TCP
206 && pl
->pml_map
.pm_vers
>= (unsigned long) *version
) {
207 *version
= (int) pl
->pml_map
.pm_vers
;
208 port
= pl
->pml_map
.pm_port
;
212 #ifdef HAVE_PMAP_GETPORT
214 for (*version
= RPC_PROGVER
; *version
>= 1; (*version
)--)
215 if (port
= pmap_getport (sin
, RPC_PROGNUM
, *version
, IPPROTO_TCP
))
217 #endif /* HAVE_PMAP_GETPORT */
219 return mcserver_port
;
220 #endif /* HAVE_PMAP_GETMAPS */
223 /* This used to be in utilvfs.c, but as it deals with portmapper, it
224 is probably useful for mcfs */
226 mcfs_create_tcp_link (const char *host
, int *port
, int *version
, const char *caller
)
228 struct sockaddr_in server_address
;
229 unsigned long inaddr
;
236 memset ((char *) &server_address
, 0, sizeof (server_address
));
237 server_address
.sin_family
= AF_INET
;
239 /* Try to use the dotted decimal number */
240 if ((inaddr
= inet_addr (host
)) != INADDR_NONE
)
241 memcpy ((char *) &server_address
.sin_addr
, (char *) &inaddr
,
244 if ((hp
= gethostbyname (host
)) == NULL
) {
245 message (D_ERROR
, caller
, _(" Cannot locate hostname: %s "),
249 memcpy ((char *) &server_address
.sin_addr
, (char *) hp
->h_addr
,
253 /* Try to contact a remote portmapper to obtain the listening port */
255 *port
= mcfs_get_remote_port (&server_address
, version
);
261 server_address
.sin_port
= htons (*port
);
263 if ((my_socket
= socket (AF_INET
, SOCK_STREAM
, 0)) < 0) {
264 message (D_ERROR
, caller
, _(" Cannot create socket: %s "),
265 unix_error_string (errno
));
268 if (connect (my_socket
, (struct sockaddr
*) &server_address
,
269 sizeof (server_address
)) < 0) {
270 message (D_ERROR
, caller
, _(" Cannot connect to server: %s "),
271 unix_error_string (errno
));
279 mcfs_open_tcp_link (char *host
, char *user
,
280 int *port
, char *netrcpass
, int *version
)
283 int old_port
= *port
;
285 my_socket
= mcfs_create_tcp_link (host
, port
, version
, " MCfs ");
289 /* We got the connection to the server, verify if the server
290 implements our version of the RPC mechanism and then login
293 return mcfs_login_server (my_socket
, user
, *port
, old_port
== 0,
297 static int mcfs_get_free_bucket_init
= 1;
298 static mcfs_connection
*
299 mcfs_get_free_bucket (void)
303 if (mcfs_get_free_bucket_init
) {
304 mcfs_get_free_bucket_init
= 0;
305 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++)
306 mcfs_connections
[i
].host
= 0;
308 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++) {
309 if (!mcfs_connections
[i
].host
)
310 return &mcfs_connections
[i
];
312 /* This can't happend, since we have checked for max connections before */
313 vfs_die ("Internal error: mcfs_get_free_bucket");
314 return 0; /* shut up, stupid gcc */
317 /* This routine keeps track of open connections */
318 /* Returns a connected socket to host */
319 static mcfs_connection
*
320 mcfs_open_link (char *host
, char *user
, int *port
, char *netrcpass
)
322 static int mcfs_open_connections
= 0;
323 int i
, sock
, version
;
324 mcfs_connection
*bucket
;
326 /* Is the link actually open? */
327 if (mcfs_get_free_bucket_init
) {
328 mcfs_get_free_bucket_init
= 0;
329 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++)
330 mcfs_connections
[i
].host
= 0;
332 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++) {
333 if (!mcfs_connections
[i
].host
)
335 if ((strcmp (host
, mcfs_connections
[i
].host
) == 0) &&
336 (strcmp (user
, mcfs_connections
[i
].user
) == 0))
337 return &mcfs_connections
[i
];
339 if (mcfs_open_connections
== MCFS_MAX_CONNECTIONS
) {
340 message (D_ERROR
, MSG_ERROR
, _(" Too many open connections "));
346 mcfs_open_tcp_link (host
, user
, port
, netrcpass
, &version
)))
349 bucket
= mcfs_get_free_bucket ();
350 mcfs_open_connections
++;
351 bucket
->host
= g_strdup (host
);
352 bucket
->user
= g_strdup (user
);
354 bucket
->port
= *port
;
356 bucket
->version
= version
;
362 mcfs_is_error (int result
, int errno_num
)
367 my_errno
= errno_num
;
372 mcfs_set_error (int result
, int errno_num
)
375 my_errno
= errno_num
;
382 mcfs_get_path (mcfs_connection
**mc
, const char *path
)
384 char *user
, *host
, *remote_path
;
388 /* An absolute path name, try to determine connection socket */
389 if (strncmp (path
, "/#mc:", 5))
393 /* Port = 0 means that mcfs_create_tcp_link will try to contact the
394 * remote portmapper to get the port number
398 mcfs_get_host_and_username (path
, &host
, &user
, &port
, &pass
)))
399 if (!(*mc
= mcfs_open_link (host
, user
, &port
, pass
))) {
400 g_free (remote_path
);
406 wipe_password (pass
);
411 /* NOTE: tildes are deprecated. See ftpfs.c */
413 int f
= !strcmp (remote_path
, "/~");
414 if (f
|| !strncmp (remote_path
, "/~/", 3)) {
416 s
= concat_dir_and_file (mcfs_gethome (*mc
),
417 remote_path
+ 3 - f
);
418 g_free (remote_path
);
425 /* Simple function for routines returning only an integer from the server */
427 mcfs_handle_simple_error (int sock
, int return_status
)
431 if (0 == rpc_get (sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
432 return mcfs_set_error (-1, EIO
);
434 if (mcfs_is_error (status
, error
))
443 mcfs_rpc_two_paths (int command
, const char *s1
, const char *s2
)
448 if ((r1
= mcfs_get_path (&mc
, s1
)) == 0)
451 if ((r2
= mcfs_get_path (&mc
, s2
)) == 0) {
457 RPC_INT
, command
, RPC_STRING
, r1
, RPC_STRING
, r2
, RPC_END
);
460 return mcfs_handle_simple_error (mc
->sock
, 0);
464 mcfs_rpc_path (int command
, const char *path
)
469 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
473 RPC_INT
, command
, RPC_STRING
, remote_file
, RPC_END
);
475 g_free (remote_file
);
476 return mcfs_handle_simple_error (mc
->sock
, 0);
480 mcfs_rpc_path_int (int command
, const char *path
, int data
)
485 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
490 RPC_STRING
, remote_file
, RPC_INT
, data
, RPC_END
);
492 g_free (remote_file
);
493 return mcfs_handle_simple_error (mc
->sock
, 0);
497 mcfs_rpc_path_int_int (int command
, const char *path
, int n1
, int n2
)
502 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
507 RPC_STRING
, remote_file
, RPC_INT
, n1
, RPC_INT
, n2
, RPC_END
);
509 g_free (remote_file
);
510 return mcfs_handle_simple_error (mc
->sock
, 0);
514 mcfs_gethome (mcfs_connection
*mc
)
519 return g_strdup (mc
->home
);
521 rpc_send (mc
->sock
, RPC_INT
, MC_GETHOME
, RPC_END
);
522 if (0 == rpc_get (mc
->sock
, RPC_STRING
, &buffer
, RPC_END
))
523 return g_strdup (PATH_SEP_STR
);
525 return g_strdup (buffer
);
531 mcfs_open (struct vfs_class
*me
, const char *file
, int flags
, int mode
)
535 int result
, error_num
;
536 mcfs_handle
*remote_handle
;
540 if (!(remote_file
= mcfs_get_path (&mc
, file
)))
543 rpc_send (mc
->sock
, RPC_INT
, MC_OPEN
, RPC_STRING
, remote_file
, RPC_INT
,
544 flags
, RPC_INT
, mode
, RPC_END
);
545 g_free (remote_file
);
548 rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error_num
, RPC_END
))
551 if (mcfs_is_error (result
, error_num
))
554 remote_handle
= g_new (mcfs_handle
, 2);
555 remote_handle
->handle
= result
;
556 remote_handle
->conn
= mc
;
558 return remote_handle
;
562 mcfs_read (void *data
, char *buffer
, int count
)
564 mcfs_handle
*info
= (mcfs_handle
*) data
;
570 handle
= info
->handle
;
572 rpc_send (mc
->sock
, RPC_INT
, MC_READ
, RPC_INT
, handle
,
573 RPC_INT
, count
, RPC_END
);
576 rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error
, RPC_END
))
577 return mcfs_set_error (-1, EIO
);
579 if (mcfs_is_error (result
, error
))
582 if (0 == rpc_get (mc
->sock
, RPC_BLOCK
, result
, buffer
, RPC_END
))
583 return mcfs_set_error (-1, EIO
);
589 mcfs_write (void *data
, const char *buf
, int nbyte
)
591 mcfs_handle
*info
= (mcfs_handle
*) data
;
596 handle
= info
->handle
;
601 RPC_INT
, nbyte
, RPC_BLOCK
, nbyte
, buf
, RPC_END
);
603 return mcfs_handle_simple_error (mc
->sock
, 1);
607 mcfs_close (void *data
)
609 mcfs_handle
*info
= (mcfs_handle
*) data
;
611 int handle
, result
, error
;
616 handle
= info
->handle
;
619 rpc_send (mc
->sock
, RPC_INT
, MC_CLOSE
, RPC_INT
, handle
, RPC_END
);
622 rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error
, RPC_END
))
623 return mcfs_set_error (-1, EIO
);
625 mcfs_is_error (result
, error
);
632 mcfs_errno (struct vfs_class
*me
)
639 typedef struct dir_entry
{
641 struct dir_entry
*next
;
647 mcfs_connection
*conn
;
654 mcfs_opendir (struct vfs_class
*me
, const char *dirname
)
656 opendir_info
*mcfs_info
;
658 int handle
, error_num
;
664 if (!(remote_dir
= mcfs_get_path (&mc
, dirname
)))
667 rpc_send (mc
->sock
, RPC_INT
, MC_OPENDIR
, RPC_STRING
, remote_dir
,
672 rpc_get (mc
->sock
, RPC_INT
, &result
, RPC_INT
, &error_num
, RPC_END
))
675 if (mcfs_is_error (result
, error_num
))
680 mcfs_info
= g_new (opendir_info
, 1);
681 mcfs_info
->conn
= mc
;
682 mcfs_info
->handle
= handle
;
683 mcfs_info
->entries
= 0;
684 mcfs_info
->current
= 0;
689 static int mcfs_get_stat_info (mcfs_connection
* mc
, struct stat
*buf
);
692 mcfs_loaddir (opendir_info
*mcfs_info
)
695 mcfs_connection
*mc
= mcfs_info
->conn
;
699 rpc_send (link
, RPC_INT
, MC_READDIR
, RPC_INT
, mcfs_info
->handle
,
704 dir_entry
*new_entry
;
706 if (!rpc_get (link
, RPC_INT
, &entry_len
, RPC_END
))
712 new_entry
= g_new (dir_entry
, 1);
713 new_entry
->text
= g_new0 (char, entry_len
+ 1);
717 mcfs_info
->entries
= new_entry
;
718 mcfs_info
->current
= new_entry
;
721 mcfs_info
->current
->next
= new_entry
;
722 mcfs_info
->current
= new_entry
;
726 (link
, RPC_BLOCK
, entry_len
, new_entry
->text
, RPC_END
))
729 /* Then we get the status from the lstat */
730 if (!rpc_get (link
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
733 if (mcfs_is_error (status
, error
))
734 new_entry
->merrno
= error
;
736 new_entry
->merrno
= 0;
737 if (!mcfs_get_stat_info (mc
, &(new_entry
->my_stat
)))
741 mcfs_info
->current
= mcfs_info
->entries
;
747 mcfs_free_dir (dir_entry
*de
)
751 mcfs_free_dir (de
->next
);
756 static union vfs_dirent mcfs_readdir_data
;
758 /* The readdir routine loads the complete directory */
759 /* It's too slow to ask the server each time */
760 /* It now also sends the complete lstat information for each file */
761 static struct stat
*cached_lstat_info
;
764 mcfs_readdir (void *info
)
766 opendir_info
*mcfs_info
;
769 mcfs_info
= (opendir_info
*) info
;
771 if (!mcfs_info
->entries
)
772 if (!mcfs_loaddir (mcfs_info
))
775 if (mcfs_info
->current
== 0) {
776 cached_lstat_info
= 0;
777 mcfs_free_dir (mcfs_info
->entries
);
778 mcfs_info
->entries
= 0;
781 dirent_dest
= mcfs_readdir_data
.dent
.d_name
;
782 g_strlcpy (dirent_dest
, mcfs_info
->current
->text
, MC_MAXPATHLEN
);
783 cached_lstat_info
= &mcfs_info
->current
->my_stat
;
784 mcfs_info
->current
= mcfs_info
->current
->next
;
786 compute_namelen (&mcfs_readdir_data
.dent
);
788 return &mcfs_readdir_data
;
792 mcfs_closedir (void *info
)
794 opendir_info
*mcfs_info
= (opendir_info
*) info
;
797 rpc_send (mcfs_info
->conn
->sock
, RPC_INT
, MC_CLOSEDIR
,
798 RPC_INT
, mcfs_info
->handle
, RPC_END
);
800 for (p
= mcfs_info
->entries
; p
;) {
811 mcfs_get_time (mcfs_connection
*mc
)
815 if (mc
->version
== 1) {
821 RPC_INT
, &tt
.tm_hour
,
822 RPC_INT
, &tt
.tm_mday
,
823 RPC_INT
, &tt
.tm_year
, RPC_INT
, &tt
.tm_mon
, RPC_END
);
832 rpc_get (sock
, RPC_STRING
, &buf
, RPC_END
);
833 sscanf (buf
, "%lx", &tm
);
841 mcfs_get_stat_info (mcfs_connection
*mc
, struct stat
*buf
)
848 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
849 #ifdef HAVE_STRUCT_STAT_ST_RDEV
850 buf
->st_rdev
= mylong
;
852 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
853 buf
->st_ino
= mylong
;
854 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
855 buf
->st_mode
= mylong
;
856 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
857 buf
->st_nlink
= mylong
;
858 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
859 buf
->st_uid
= mylong
;
860 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
861 buf
->st_gid
= mylong
;
862 rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
);
863 buf
->st_size
= mylong
;
865 if (!rpc_get (sock
, RPC_INT
, &mylong
, RPC_END
))
867 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
868 buf
->st_blocks
= mylong
;
870 buf
->st_atime
= mcfs_get_time (mc
);
871 buf
->st_mtime
= mcfs_get_time (mc
);
872 buf
->st_ctime
= mcfs_get_time (mc
);
877 mcfs_stat_cmd (int cmd
, const char *path
, struct stat
*buf
)
883 if ((remote_file
= mcfs_get_path (&mc
, path
)) == 0)
886 rpc_send (mc
->sock
, RPC_INT
, cmd
, RPC_STRING
, remote_file
, RPC_END
);
887 g_free (remote_file
);
888 if (!rpc_get (mc
->sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
889 return mcfs_set_error (-1, errno
);
891 if (mcfs_is_error (status
, error
))
894 if (mcfs_get_stat_info (mc
, buf
))
897 return mcfs_set_error (-1, EIO
);
901 mcfs_stat (struct vfs_class
*me
, const char *path
, struct stat
*buf
)
905 return mcfs_stat_cmd (MC_STAT
, path
, buf
);
909 mcfs_lstat (struct vfs_class
*me
, const char *path
, struct stat
*buf
)
911 int path_len
= strlen (path
);
912 int entry_len
= strlen (mcfs_readdir_data
.dent
.d_name
);
917 if (strcmp (path
+ path_len
- entry_len
,
918 mcfs_readdir_data
.dent
.d_name
) == 0 && cached_lstat_info
) {
919 *buf
= *cached_lstat_info
;
922 return mcfs_stat_cmd (MC_LSTAT
, path
, buf
);
926 mcfs_fstat (void *data
, struct stat
*buf
)
928 mcfs_handle
*info
= (mcfs_handle
*) data
;
932 sock
= info
->conn
->sock
;
933 handle
= info
->handle
;
935 rpc_send (sock
, RPC_INT
, MC_FSTAT
, RPC_INT
, handle
, RPC_END
);
936 if (!rpc_get (sock
, RPC_INT
, &result
, RPC_INT
, &error
, RPC_END
))
937 return mcfs_set_error (-1, EIO
);
939 if (mcfs_is_error (result
, error
))
942 if (mcfs_get_stat_info (info
->conn
, buf
))
945 return mcfs_set_error (-1, EIO
);
949 mcfs_chmod (struct vfs_class
*me
, const char *path
, int mode
)
953 return mcfs_rpc_path_int (MC_CHMOD
, path
, mode
);
957 mcfs_chown (struct vfs_class
*me
, const char *path
, int owner
, int group
)
961 return mcfs_rpc_path_int_int (MC_CHOWN
, path
, owner
, group
);
965 mcfs_utime (struct vfs_class
*me
, const char *path
, struct utimbuf
*times
)
973 if (!(file
= mcfs_get_path (&mc
, path
)))
977 if (mc
->version
>= 2) {
978 char abuf
[BUF_SMALL
];
979 char mbuf
[BUF_SMALL
];
982 atime
= (long) times
->actime
;
983 mtime
= (long) times
->modtime
;
985 g_snprintf (abuf
, sizeof (abuf
), "%lx", atime
);
986 g_snprintf (mbuf
, sizeof (mbuf
), "%lx", mtime
);
988 rpc_send (mc
->sock
, RPC_INT
, MC_UTIME
,
990 RPC_STRING
, abuf
, RPC_STRING
, mbuf
, RPC_END
);
991 status
= mcfs_handle_simple_error (mc
->sock
, 0);
999 mcfs_readlink (struct vfs_class
*me
, const char *path
, char *buf
, size_t size
)
1001 char *remote_file
, *stat_str
;
1003 mcfs_connection
*mc
;
1008 if (!(remote_file
= mcfs_get_path (&mc
, path
)))
1011 rpc_send (mc
->sock
, RPC_INT
, MC_READLINK
, RPC_STRING
, remote_file
,
1013 g_free (remote_file
);
1014 if (!rpc_get (mc
->sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
1015 return mcfs_set_error (-1, EIO
);
1017 if (mcfs_is_error (status
, errno
))
1020 if (!rpc_get (mc
->sock
, RPC_STRING
, &stat_str
, RPC_END
))
1021 return mcfs_set_error (-1, EIO
);
1023 len
= strlen (stat_str
);
1026 /* readlink() does not append a NUL character to buf */
1027 memcpy (buf
, stat_str
, size
);
1033 mcfs_unlink (struct vfs_class
*me
, const char *path
)
1037 return mcfs_rpc_path (MC_UNLINK
, path
);
1041 mcfs_symlink (struct vfs_class
*me
, const char *n1
, const char *n2
)
1045 return mcfs_rpc_two_paths (MC_SYMLINK
, n1
, n2
);
1049 mcfs_rename (struct vfs_class
*me
, const char *a
, const char *b
)
1053 return mcfs_rpc_two_paths (MC_RENAME
, a
, b
);
1057 mcfs_chdir (struct vfs_class
*me
, const char *path
)
1060 mcfs_connection
*mc
;
1065 if (!(remote_dir
= mcfs_get_path (&mc
, path
)))
1068 rpc_send (mc
->sock
, RPC_INT
, MC_CHDIR
, RPC_STRING
, remote_dir
,
1070 g_free (remote_dir
);
1071 if (!rpc_get (mc
->sock
, RPC_INT
, &status
, RPC_INT
, &error
, RPC_END
))
1072 return mcfs_set_error (-1, EIO
);
1074 if (mcfs_is_error (status
, error
))
1080 mcfs_lseek (void *data
, off_t offset
, int whence
)
1082 mcfs_handle
*info
= (mcfs_handle
*) data
;
1085 sock
= info
->conn
->sock
;
1086 handle
= info
->handle
;
1088 /* FIXME: off_t may be too long to fit */
1089 rpc_send (sock
, RPC_INT
, MC_LSEEK
, RPC_INT
, handle
, RPC_INT
,
1090 (int) offset
, RPC_INT
, whence
, RPC_END
);
1092 return mcfs_handle_simple_error (sock
, 1);
1096 mcfs_mknod (struct vfs_class
*me
, const char *path
, int mode
, int dev
)
1100 return mcfs_rpc_path_int_int (MC_MKNOD
, path
, mode
, dev
);
1104 mcfs_mkdir (struct vfs_class
*me
, const char *path
, mode_t mode
)
1108 return mcfs_rpc_path_int (MC_MKDIR
, path
, mode
);
1112 mcfs_rmdir (struct vfs_class
*me
, const char *path
)
1116 return mcfs_rpc_path (MC_RMDIR
, path
);
1120 mcfs_link (struct vfs_class
*me
, const char *p1
, const char *p2
)
1124 return mcfs_rpc_two_paths (MC_LINK
, p1
, p2
);
1127 /* Gives up on a socket and reopens the connection, the child own the socket
1131 mcfs_forget (const char *path
)
1133 char *host
, *user
, *pass
, *p
;
1136 if (strncmp (path
, "/#mc:", 5))
1140 if (path
[0] == '/' && path
[1] == '/')
1144 mcfs_get_host_and_username (path
, &host
, &user
, &port
,
1149 wipe_password (pass
);
1152 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++) {
1153 if ((strcmp (host
, mcfs_connections
[i
].host
) == 0) &&
1154 (strcmp (user
, mcfs_connections
[i
].user
) == 0) &&
1155 (port
== mcfs_connections
[i
].port
)) {
1157 /* close socket: the child owns it now */
1158 close (mcfs_connections
[i
].sock
);
1160 /* reopen the connection */
1161 mcfs_connections
[i
].sock
=
1162 mcfs_open_tcp_link (host
, user
, &port
, pass
, &vers
);
1169 wipe_password (pass
);
1173 mcfs_setctl (struct vfs_class
*me
, const char *path
, int ctlop
, void *arg
)
1179 case VFS_SETCTL_FORGET
:
1189 vfs_mcfs_ops
.name
= "mcfs";
1190 vfs_mcfs_ops
.prefix
= "mc:";
1191 vfs_mcfs_ops
.fill_names
= mcfs_fill_names
;
1192 vfs_mcfs_ops
.open
= mcfs_open
;
1193 vfs_mcfs_ops
.close
= mcfs_close
;
1194 vfs_mcfs_ops
.read
= mcfs_read
;
1195 vfs_mcfs_ops
.write
= mcfs_write
;
1196 vfs_mcfs_ops
.opendir
= mcfs_opendir
;
1197 vfs_mcfs_ops
.readdir
= mcfs_readdir
;
1198 vfs_mcfs_ops
.closedir
= mcfs_closedir
;
1199 vfs_mcfs_ops
.stat
= mcfs_stat
;
1200 vfs_mcfs_ops
.lstat
= mcfs_lstat
;
1201 vfs_mcfs_ops
.fstat
= mcfs_fstat
;
1202 vfs_mcfs_ops
.chmod
= mcfs_chmod
;
1203 vfs_mcfs_ops
.chown
= mcfs_chown
;
1204 vfs_mcfs_ops
.utime
= mcfs_utime
;
1205 vfs_mcfs_ops
.readlink
= mcfs_readlink
;
1206 vfs_mcfs_ops
.symlink
= mcfs_symlink
;
1207 vfs_mcfs_ops
.link
= mcfs_link
;
1208 vfs_mcfs_ops
.unlink
= mcfs_unlink
;
1209 vfs_mcfs_ops
.rename
= mcfs_rename
;
1210 vfs_mcfs_ops
.chdir
= mcfs_chdir
;
1211 vfs_mcfs_ops
.ferrno
= mcfs_errno
;
1212 vfs_mcfs_ops
.lseek
= mcfs_lseek
;
1213 vfs_mcfs_ops
.mknod
= mcfs_mknod
;
1214 vfs_mcfs_ops
.mkdir
= mcfs_mkdir
;
1215 vfs_mcfs_ops
.rmdir
= mcfs_rmdir
;
1216 vfs_mcfs_ops
.setctl
= mcfs_setctl
;
1217 vfs_register_class (&vfs_mcfs_ops
);
1221 mcfs_free_bucket (int bucket
)
1223 g_free (mcfs_connections
[bucket
].host
);
1224 g_free (mcfs_connections
[bucket
].user
);
1225 g_free (mcfs_connections
[bucket
].home
);
1227 /* Set all the fields to zero */
1228 mcfs_connections
[bucket
].host
=
1229 mcfs_connections
[bucket
].user
= mcfs_connections
[bucket
].home
= 0;
1230 mcfs_connections
[bucket
].sock
= mcfs_connections
[bucket
].version
= 0;
1234 mcfs_invalidate_socket (int sock
)
1238 for (i
= 0; i
< MCFS_MAX_CONNECTIONS
; i
++)
1239 if (mcfs_connections
[i
].sock
== sock
) {
1240 mcfs_free_bucket (i
);
1245 return -1; /* It was not our sock */
1246 /* Break from any possible loop */
1252 tcp_invalidate_socket (int sock
)
1254 mcfs_invalidate_socket (sock
);
1257 void mcfs__unused(void)
1260 CFLAGS="-ansi -pedantic -Wall -Wextra -Werror"
1263 #endif /* ENABLE_VFS_MCFS */