2 * Shared code between the fish.c and the ftp.c file systems
6 * Actually, this code is not being used by fish.c any more :-).
8 * Namespace pollution: X_hint_reread, X_flushdir.
10 static int store_file (struct direntry
*fe
);
11 static int retrieve_file (struct direntry
*fe
);
12 static int remove_temp_file (char *file_name
);
13 static struct dir
*retrieve_dir (struct connection
*bucket
,
15 int resolve_symlinks
);
16 static void my_forget (char *path
);
18 static int linear_start (struct direntry
*fe
, int from
);
19 static int linear_read (struct direntry
*fe
, void *buf
, int len
);
20 static void linear_close (struct direntry
*fe
);
23 select_on_two (int fd1
, int fd2
)
26 struct timeval timeout
;
28 int maxfd
= (fd1
> fd2
? fd1
: fd2
) + 1;
35 v
= select (maxfd
, &set
, 0, 0, &timeout
);
38 if (FD_ISSET (fd1
, &set
))
40 if (FD_ISSET (fd2
, &set
))
46 get_line (int sock
, char *buf
, int buf_len
, char term
)
51 for (i
= 0; i
< buf_len
- 1; i
++, buf
++) {
52 if (read(sock
, buf
, sizeof(char)) <= 0)
55 fwrite (buf
, 1, 1, logfile
);
64 while ((status
= read(sock
, &c
, sizeof(c
))) > 0){
66 fwrite (&c
, 1, 1, logfile
);
76 direntry_destructor (void *data
)
78 struct direntry
*fe
= data
;
87 if (fe
->local_filename
) {
88 if (fe
->local_is_temp
) {
89 if (!fe
->local_stat
.st_mtime
)
90 unlink(fe
->local_filename
);
94 if (stat (fe
->local_filename
, &sb
) >=0 &&
95 fe
->local_stat
.st_mtime
== sb
.st_mtime
)
96 unlink (fe
->local_filename
); /* Delete only if it hasn't changed */
99 g_free(fe
->local_filename
);
100 fe
->local_filename
= NULL
;
102 if (fe
->remote_filename
)
103 g_free(fe
->remote_filename
);
110 dir_destructor(void *data
)
112 struct dir
*fd
= data
;
117 g_free(fd
->remote_path
);
118 linklist_destroy(fd
->file_list
, direntry_destructor
);
123 get_line_interruptible (char *buffer
, int size
, int fd
)
128 for (i
= 0; i
< size
-1; i
++) {
129 n
= read (fd
, buffer
+i
, 1);
130 if (n
== -1 && errno
== EINTR
){
138 if (buffer
[i
] == '\n'){
148 free_bucket (void *data
)
150 struct connection
*bucket
= data
;
152 g_free(qhost(bucket
));
153 g_free(quser(bucket
));
155 g_free(qcdir(bucket
));
157 g_free(qhome(bucket
));
159 g_free(qupdir(bucket
));
160 if (bucket
->password
)
161 wipe_password (bucket
->password
);
162 linklist_destroy(qdcache(bucket
), dir_destructor
);
168 connection_destructor(void *data
)
170 connection_close (data
);
175 flush_all_directory(struct connection
*bucket
)
177 linklist_delete_all(qdcache(bucket
), dir_destructor
);
180 static void X_fill_names (vfs
*me
, void (*func
)(char *))
182 struct linklist
*lptr
;
184 struct connection
*bucket
;
186 if (!connections_list
)
188 lptr
= connections_list
;
190 if ((bucket
= lptr
->data
) != 0){
192 path_name
= g_strconcat ( X_myname
, quser (bucket
),
194 qcdir(bucket
), NULL
);
199 } while (lptr
!= connections_list
);
203 * makes BUCKET point to the connection bucket descriptor for PATH
204 * returns a malloced string with the pathname relative to BUCKET.
206 * path must _not_ contain initial /bla/#ftp:
209 s_get_path (struct connection
**bucket
, char *path
, char *name
)
211 char *user
, *host
, *remote_path
, *pass
;
215 if (strncmp (path
, name
, strlen (name
)))
216 return NULL
; /* Normal: consider cd /bla/#ftp */
218 if (!(path
= strstr (path
, name
)))
221 path
+= strlen (name
);
223 if (!(remote_path
= my_get_host_and_username (path
, &host
, &user
, &port
, &pass
)))
226 if ((*bucket
= open_link (host
, user
, port
, pass
)) == NULL
) {
227 g_free (remote_path
);
236 wipe_password (pass
);
241 /* NOTE: Usage of tildes is deprecated, consider:
242 * cd /#ftp:pavel@hobit
244 * And now: what do I want to do? Do I want to go to /home/pavel or to
245 * /#ftp:hobit/home/pavel? I think first has better sense...
248 int f
= !strcmp( remote_path
, "/~" );
249 if (f
|| !strncmp( remote_path
, "/~/", 3 )) {
251 s
= concat_dir_and_file( qhome (*bucket
), remote_path
+3-f
);
252 g_free (remote_path
);
260 ftpfs_flushdir (void)
262 force_expiration
= 1;
267 s_setctl (vfs
*me
, char *path
, int ctlop
, char *arg
)
270 case MCCTL_REMOVELOCALCOPY
:
271 return remove_temp_file (path
);
273 case MCCTL_FORGET_ABOUT
:
280 static struct direntry
*
281 _get_file_entry(struct connection
*bucket
, char *file_name
,
285 struct direntry
*ent
;
286 struct linklist
*file_list
, *lptr
;
290 p
= strrchr(file_name
, '/');
293 dcache
= retrieve_dir(bucket
, *file_name
? file_name
: "/", op
& DO_RESOLVE_SYMLINK
);
296 file_list
= dcache
->file_list
;
300 for (lptr
= file_list
->next
; lptr
!= file_list
; lptr
= lptr
->next
) {
302 if (strcmp(p
, ent
->name
) == 0) {
303 if (S_ISLNK(ent
->s
.st_mode
) && (op
& DO_RESOLVE_SYMLINK
)) {
304 if (ent
->l_stat
== NULL
) ERRNOR (ENOENT
, NULL
);
305 if (S_ISLNK(ent
->l_stat
->st_mode
)) ERRNOR (ELOOP
, NULL
);
307 if (ent
&& (op
& DO_OPEN
)) {
310 fmode
= S_ISLNK(ent
->s
.st_mode
)
311 ? ent
->l_stat
->st_mode
314 ERRNOR (EISDIR
, NULL
);
316 ERRNOR (EPERM
, NULL
);
317 if ((flags
& O_EXCL
) && (flags
& O_CREAT
))
318 ERRNOR (EEXIST
, NULL
);
319 if (ent
->remote_filename
== NULL
)
320 if (!(ent
->remote_filename
= g_strdup(file_name
)))
321 ERRNOR (ENOMEM
, NULL
);
322 if (ent
->local_filename
== NULL
||
323 !ent
->local_stat
.st_mtime
||
324 stat (ent
->local_filename
, &sb
) < 0 ||
325 sb
.st_mtime
!= ent
->local_stat
.st_mtime
) {
328 if (ent
->local_filename
){
329 g_free (ent
->local_filename
);
330 ent
->local_filename
= NULL
;
332 if (flags
& O_TRUNC
) {
333 ent
->local_filename
= tempnam (NULL
, X
"fs");
334 if (ent
->local_filename
== NULL
) ERRNOR (ENOMEM
, NULL
);
335 handle
= open(ent
->local_filename
, O_CREAT
| O_TRUNC
| O_RDWR
| O_EXCL
, 0600);
336 if (handle
< 0) ERRNOR (EIO
, NULL
);
338 if (stat (ent
->local_filename
, &ent
->local_stat
) < 0)
339 ent
->local_stat
.st_mtime
= 0;
342 if (IS_LINEAR(flags
)) {
343 ent
->local_is_temp
= 0;
344 ent
->local_filename
= NULL
;
345 ent
->linear_state
= LS_LINEAR_CLOSED
;
348 if (!retrieve_file(ent
))
352 else if (flags
& O_TRUNC
) {
353 truncate(ent
->local_filename
, 0);
359 if ((op
& DO_OPEN
) && (flags
& O_CREAT
)) {
362 ent
= g_new (struct direntry
, 1);
363 if (ent
== NULL
) ERRNOR (ENOMEM
, NULL
);
364 ent
->freshly_created
= 0;
366 ent
->linkname
= NULL
;
368 ent
->bucket
= bucket
;
369 ent
->name
= g_strdup(p
);
370 ent
->remote_filename
= g_strdup(file_name
);
371 ent
->local_filename
= tempnam (NULL
, X
"fs");
372 if (!ent
->name
|| !ent
->remote_filename
|| !ent
->local_filename
) {
373 direntry_destructor(ent
);
374 ERRNOR (ENOMEM
, NULL
);
376 handle
= open (ent
->local_filename
, O_CREAT
| O_EXCL
| O_RDWR
| O_TRUNC
, 0700);
381 fstat(handle
, &ent
->s
);
384 /* This is very wrong - like this a zero length file will be always created
385 and usually preclude uploading anything more desirable */
386 #if defined(UPLOAD_ZERO_LENGTH_FILE)
387 if (!store_file(ent
)) goto error
;
390 if (!linklist_insert(file_list
, ent
)) {
394 ent
->freshly_created
= 1;
397 else ERRNOR (ENOENT
, NULL
);
399 direntry_destructor(ent
);
404 /* this just free's the local temp file. I don't know if the
405 remote file can be used after this without crashing - paul
406 psheer@obsidian.co.za psheer@icon.co.za */
408 remove_temp_file (char *file_name
)
411 struct connection
*bucket
;
412 struct direntry
*ent
;
413 struct linklist
*file_list
, *lptr
;
416 if (!(file_name
= get_path (&bucket
, file_name
)))
418 p
= strrchr (file_name
, '/');
421 dcache
= retrieve_dir (bucket
, *file_name
? file_name
: "/", 0);
424 file_list
= dcache
->file_list
;
428 for (lptr
= file_list
->next
; lptr
!= file_list
; lptr
= lptr
->next
) {
430 if (strcmp (p
, ent
->name
) == 0) {
431 if (ent
->local_filename
) {
432 unlink (ent
->local_filename
);
433 g_free (ent
->local_filename
);
434 ent
->local_filename
= NULL
;
442 static struct direntry
*
443 get_file_entry(char *path
, int op
, int flags
)
445 struct connection
*bucket
;
449 if (!(remote_path
= get_path (&bucket
, path
)))
451 fe
= _get_file_entry(bucket
, remote_path
, op
,
455 if (op
& DO_FREE_RESOURCE
)
456 vfs_add_noncurrent_stamps (&vfs_X_ops
, (vfsid
) bucket
, NULL
);
462 #define OPT_IGNORE_ERROR 2
464 static int normal_flush
= 1;
466 void X_hint_reread(int reread
)
478 unsigned int has_changed
:1;
483 static void *s_open (vfs
*me
, char *file
, int flags
, int mode
)
488 fp
= g_new (struct filp
, 1);
489 if (fp
== NULL
) ERRNOR (ENOMEM
, NULL
);
490 fe
= get_file_entry(file
, DO_OPEN
| DO_RESOLVE_SYMLINK
, flags
);
495 fe
->linear_state
= IS_LINEAR(flags
);
496 if (!fe
->linear_state
) {
497 fp
->local_handle
= open(fe
->local_filename
, flags
, mode
);
498 if (fp
->local_handle
< 0) {
500 ERRNOR (errno
, NULL
);
502 } else fp
->local_handle
= -1;
503 #ifdef UPLOAD_ZERO_LENGTH_FILE
504 fp
->has_changed
= fe
->freshly_created
;
514 static int s_read (void *data
, char *buffer
, int count
)
520 if (fp
->fe
->linear_state
== LS_LINEAR_CLOSED
) {
521 print_vfs_message (_("Starting linear transfer..."));
522 if (!linear_start (fp
->fe
, 0))
526 if (fp
->fe
->linear_state
== LS_LINEAR_CLOSED
)
527 vfs_die ("linear_start() did not set linear_state!");
529 if (fp
->fe
->linear_state
== LS_LINEAR_OPEN
)
530 return linear_read (fp
->fe
, buffer
, count
);
532 n
= read (fp
->local_handle
, buffer
, count
);
538 static int s_write (void *data
, char *buf
, int nbyte
)
540 struct filp
*fp
= data
;
543 if (fp
->fe
->linear_state
)
544 vfs_die ("You may not write to linear file");
545 n
= write (fp
->local_handle
, buf
, nbyte
);
552 static int s_close (void *data
)
554 struct filp
*fp
= data
;
557 if (fp
->has_changed
) {
558 if (!store_file(fp
->fe
))
561 flush_all_directory(fp
->fe
->bucket
);
563 if (fp
->fe
->linear_state
== LS_LINEAR_OPEN
)
564 linear_close(fp
->fe
);
565 if (fp
->local_handle
>= 0)
566 close(fp
->local_handle
);
567 qlock(fp
->fe
->bucket
)--;
568 direntry_destructor(fp
->fe
);
573 static int s_errno (vfs
*me
)
580 * On some operating systems (Slowaris 2 for example)
581 * the d_name member is just a char long (nice trick that break everything),
582 * so we need to set up some space for the filename.
586 #ifdef NEED_EXTRA_DIRENT_BUFFER
587 char extra_buffer
[MC_MAXPATHLEN
];
589 struct linklist
*pos
;
593 /* Possible FIXME: what happens if one directory is opened twice ? */
595 static void *s_opendir (vfs
*me
, char *dirname
)
597 struct connection
*bucket
;
599 struct my_dirent
*dirp
;
601 if (!(remote_path
= get_path (&bucket
, dirname
)))
603 dirp
= g_new (struct my_dirent
, 1);
608 dirp
->dcache
= retrieve_dir(bucket
, remote_path
, 1);
609 if (dirp
->dcache
== NULL
)
611 dirp
->pos
= dirp
->dcache
->file_list
->next
;
613 dirp
->dcache
->count
++;
616 vfs_add_noncurrent_stamps (&vfs_X_ops
, (vfsid
) bucket
, NULL
);
622 static void *s_readdir (void *data
)
625 struct my_dirent
*dirp
= data
;
627 if (dirp
->pos
== dirp
->dcache
->file_list
)
629 fe
= dirp
->pos
->data
;
630 strcpy (&(dirp
->dent
.d_name
[0]), fe
->name
);
631 #ifndef DIRENT_LENGTH_COMPUTED
632 dirp
->d_namlen
= strlen (dirp
->d_name
);
634 dirp
->pos
= dirp
->pos
->next
;
635 return (void *) &dirp
->dent
;
638 static int s_telldir (void *data
)
640 struct my_dirent
*dirp
= data
;
641 struct linklist
*pos
;
644 pos
= dirp
->dcache
->file_list
->next
;
645 while( pos
!=dirp
->dcache
->file_list
) {
646 if (pos
== dirp
->pos
)
654 static void s_seekdir (void *data
, int pos
)
656 struct my_dirent
*dirp
= data
;
659 dirp
->pos
= dirp
->dcache
->file_list
->next
;
660 for (i
=0; i
<pos
; i
++)
664 static int s_closedir (void *info
)
666 struct my_dirent
*dirp
= info
;
667 dir_destructor(dirp
->dcache
);
672 static int s_lstat (vfs
*me
, char *path
, struct stat
*buf
)
676 fe
= get_file_entry(path
, DO_FREE_RESOURCE
, 0);
685 static int s_stat (vfs
*me
, char *path
, struct stat
*buf
)
689 fe
= get_file_entry(path
, DO_RESOLVE_SYMLINK
| DO_FREE_RESOURCE
, 0);
691 if (!S_ISLNK(fe
->s
.st_mode
))
701 static int s_fstat (void *data
, struct stat
*buf
)
703 struct filp
*fp
= data
;
705 if (!S_ISLNK(fp
->fe
->s
.st_mode
))
708 *buf
= *fp
->fe
->l_stat
;
712 static int s_readlink (vfs
*me
, char *path
, char *buf
, int size
)
716 fe
= get_file_entry(path
, DO_FREE_RESOURCE
, 0);
719 if (!S_ISLNK(fe
->s
.st_mode
)) ERRNOR (EINVAL
, -1);
720 if (fe
->linkname
== NULL
) ERRNOR (EACCES
, -1);
721 if (strlen(fe
->linkname
) >= size
) ERRNOR (ERANGE
, -1);
722 strncpy(buf
, fe
->linkname
, size
);
723 return strlen(fe
->linkname
);
726 static int s_chdir (vfs
*me
, char *path
)
729 struct connection
*bucket
;
731 if (!(remote_path
= get_path(&bucket
, path
)))
734 g_free(qcdir(bucket
));
735 qcdir(bucket
) = remote_path
;
736 bucket
->cwd_defered
= 1;
738 vfs_add_noncurrent_stamps (&vfs_X_ops
, (vfsid
) bucket
, NULL
);
742 static int s_lseek (void *data
, off_t offset
, int whence
)
744 struct filp
*fp
= data
;
746 if (fp
->fe
->linear_state
== LS_LINEAR_OPEN
)
747 vfs_die ("You promissed not to seek!");
748 if (fp
->fe
->linear_state
== LS_LINEAR_CLOSED
) {
749 print_vfs_message (_("Preparing reget..."));
750 if (whence
!= SEEK_SET
)
751 vfs_die ("You may not do such seek on linear file");
752 if (!linear_start (fp
->fe
, offset
))
756 return lseek(fp
->local_handle
, offset
, whence
);
759 static vfsid
s_getid (vfs
*me
, char *p
, struct vfs_stamping
**parent
)
761 struct connection
*bucket
;
764 *parent
= NULL
; /* We are not enclosed in any other fs */
766 if (!(remote_path
= get_path (&bucket
, p
)))
770 return (vfsid
) bucket
;
774 static int s_nothingisopen (vfsid id
)
776 return qlock((struct connection
*)id
) == 0;
779 static void s_free (vfsid id
)
781 struct connection
*bucket
= (struct connection
*) id
;
783 connection_destructor(bucket
);
784 linklist_delete(connections_list
, bucket
);
787 static char *s_getlocalcopy (vfs
*me
, char *path
)
789 struct filp
*fp
= (struct filp
*) s_open (me
, path
, O_RDONLY
, 0);
794 if (fp
->fe
->local_filename
== NULL
) {
795 s_close ((void *) fp
);
798 p
= g_strdup (fp
->fe
->local_filename
);
799 qlock(fp
->fe
->bucket
)++;
801 s_close ((void *) fp
);
805 static int s_ungetlocalcopy (vfs
*me
, char *path
, char *local
, int has_changed
)
807 struct filp
*fp
= (struct filp
*) s_open (me
, path
, O_WRONLY
, 0);
811 if (!strcmp (fp
->fe
->local_filename
, local
)) {
812 fp
->has_changed
= has_changed
;
813 qlock(fp
->fe
->bucket
)--;
814 direntry_destructor(fp
->fe
);
815 s_close ((void *) fp
);
817 /* Should not happen */
818 s_close ((void *) fp
);
819 mc_def_ungetlocalcopy (me
, path
, local
, has_changed
);
827 linklist_destroy(connections_list
, connection_destructor
);
828 connections_list
= NULL
;
834 static int retrieve_file(struct direntry
*fe
)
836 /* If you want reget, you'll have to open file with O_LINEAR */
840 int stat_size
= fe
->s
.st_size
;
842 if (fe
->local_filename
)
844 if (!(fe
->local_filename
= tempnam (NULL
, X
))) ERRNOR (ENOMEM
, 0);
845 fe
->local_is_temp
= 1;
847 local_handle
= open(fe
->local_filename
, O_RDWR
| O_CREAT
| O_TRUNC
| O_EXCL
, 0600);
848 if (local_handle
== -1) {
853 if (!linear_start (fe
, 0))
856 /* Clear the interrupt status */
857 enable_interrupt_key ();
860 if ((n
= linear_read(fe
, buffer
, sizeof(buffer
))) < 0)
866 vfs_print_stats (X
, _("Getting file"), fe
->remote_filename
, total
, stat_size
);
868 while (write(local_handle
, buffer
, n
) < 0) {
869 if (errno
== EINTR
) {
870 if (got_interrupt()) {
882 disable_interrupt_key();
885 if (stat (fe
->local_filename
, &fe
->local_stat
) < 0)
886 fe
->local_stat
.st_mtime
= 0;
893 disable_interrupt_key();
895 unlink(fe
->local_filename
);
897 g_free(fe
->local_filename
);
898 fe
->local_filename
= NULL
;