-
Notifications
You must be signed in to change notification settings - Fork 501
/
sshfs.c
4506 lines (3946 loc) · 105 KB
/
sshfs.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
SSH file system
Copyright (C) 2004 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPL.
See the file COPYING.
*/
#define _GNU_SOURCE /* avoid implicit declaration of *pt* functions */
#include "config.h"
#include <fuse.h>
#include <fuse_opt.h>
#if !defined(__CYGWIN__)
# include <fuse_lowlevel.h>
#endif
#ifdef __APPLE__
# include <fuse_darwin.h>
#endif
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#ifndef __APPLE__
# include <semaphore.h>
#endif
#include <pthread.h>
#include <netdb.h>
#include <signal.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <sys/mman.h>
#include <poll.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <glib.h>
#include <pwd.h>
#include <grp.h>
#include <limits.h>
#ifdef __APPLE__
# include <strings.h>
# include <libgen.h>
# include <darwin_compat.h>
#endif
#ifdef __linux__
# include <linux/vm_sockets.h>
#endif
#include "cache.h"
#ifndef MAP_LOCKED
# define MAP_LOCKED 0
#endif
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
# define MAP_ANONYMOUS MAP_ANON
#endif
#define SSH_FXP_INIT 1
#define SSH_FXP_VERSION 2
#define SSH_FXP_OPEN 3
#define SSH_FXP_CLOSE 4
#define SSH_FXP_READ 5
#define SSH_FXP_WRITE 6
#define SSH_FXP_LSTAT 7
#define SSH_FXP_FSTAT 8
#define SSH_FXP_SETSTAT 9
#define SSH_FXP_FSETSTAT 10
#define SSH_FXP_OPENDIR 11
#define SSH_FXP_READDIR 12
#define SSH_FXP_REMOVE 13
#define SSH_FXP_MKDIR 14
#define SSH_FXP_RMDIR 15
#define SSH_FXP_REALPATH 16
#define SSH_FXP_STAT 17
#define SSH_FXP_RENAME 18
#define SSH_FXP_READLINK 19
#define SSH_FXP_SYMLINK 20
#define SSH_FXP_STATUS 101
#define SSH_FXP_HANDLE 102
#define SSH_FXP_DATA 103
#define SSH_FXP_NAME 104
#define SSH_FXP_ATTRS 105
#define SSH_FXP_EXTENDED 200
#define SSH_FXP_EXTENDED_REPLY 201
#define SSH_FILEXFER_ATTR_SIZE 0x00000001
#define SSH_FILEXFER_ATTR_UIDGID 0x00000002
#define SSH_FILEXFER_ATTR_PERMISSIONS 0x00000004
#define SSH_FILEXFER_ATTR_ACMODTIME 0x00000008
#define SSH_FILEXFER_ATTR_EXTENDED 0x80000000
#define SSH_FX_OK 0
#define SSH_FX_EOF 1
#define SSH_FX_NO_SUCH_FILE 2
#define SSH_FX_PERMISSION_DENIED 3
#define SSH_FX_FAILURE 4
#define SSH_FX_BAD_MESSAGE 5
#define SSH_FX_NO_CONNECTION 6
#define SSH_FX_CONNECTION_LOST 7
#define SSH_FX_OP_UNSUPPORTED 8
#define SSH_FXF_READ 0x00000001
#define SSH_FXF_WRITE 0x00000002
#define SSH_FXF_APPEND 0x00000004
#define SSH_FXF_CREAT 0x00000008
#define SSH_FXF_TRUNC 0x00000010
#define SSH_FXF_EXCL 0x00000020
/* statvfs@openssh.com f_flag flags */
#define SSH2_FXE_STATVFS_ST_RDONLY 0x00000001
#define SSH2_FXE_STATVFS_ST_NOSUID 0x00000002
#define SFTP_EXT_POSIX_RENAME "posix-rename@openssh.com"
#define SFTP_EXT_STATVFS "statvfs@openssh.com"
#define SFTP_EXT_HARDLINK "hardlink@openssh.com"
#define SFTP_EXT_FSYNC "fsync@openssh.com"
#define PROTO_VERSION 3
#define MY_EOF 1
#define MAX_REPLY_LEN (1 << 17)
#define RENAME_TEMP_CHARS 8
#define SFTP_SERVER_PATH "/usr/lib/sftp-server"
/* Asynchronous readdir parameters */
#define READDIR_START 2
#define READDIR_MAX 32
#define MAX_PASSWORD 1024
/*
Handling of multiple SFTP connections
--------------------------------------
An SFTP server is free to return responses to outstanding requests in arbitrary
order. However, execution of requests may only be re-ordered and parallelized as long
as "the results in the responses will be the same as if [the client] had sent the
requests one at a time and waited for the response in each case".
(https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-6.1).
When using multiple connections, this requirement applies independently for each
connection. We therefore have to make sure in SSHFS that the way in which we distribute
requests between connections does not affect the responses that we get.
In general, this is a tricky problem to solve since for each incoming request we have
to determine which other in-flight requests may interact with it, and then either
transmit the request through the same connection or (if there are multiple connections
involved) wait for the other requests to complete. This means that e.g. a readdir
request would have to block on most other activity in the same directory, eliminating a
major advantage of using multiple connections.
In practice, we can luckily take advantage of the knowledge that most FUSE requests are
the result of (synchronous) syscalls from userspace that will block until the
corresponding FUSE response has been sent.
If -o sshfs_sync is used, SSHFS always waits for the SFTP server response before
returning a FUSE response to userspace. If userspace makes concurrent system calls,
there is no ordering guarantee in the first place, so we do not have to worry about
(re-)ordering within SSHFS either.
For requests that originate in the kernel (rather than userspace), the situation is
slightly different. Transmission of FUSE requests and responses is decoupled (there are
no synchronous calls) and there is no formal specification that defines if reordering
is permitted. However, the Linux kernel seems to avoid submitting any concurrent
requests that would give different results depending on execution order and (as of
kernel 4.20 with writeback caching disabled) the only kind of kernel originated
requests are read() requests for read-ahead. Since libfuse internally uses multiple
threads, SSHFS does not necessarily receive requests in the order in which they were
sent by the kernel. Unless there is a major bug in FUSE, there is therefore no need to
worry about correct sequencing of such calls even when using multiple SFTP connections.
If -o sshfs_sync is *not* used, then write() syscalls will return to userspace before
SSHFS has received responses from the SFTP server. If userspace then issues a second
syscall related to the same file (and only one connection is in-use), SFTP ordering
guarantees will ensure that the response takes into account the preceding writes. If
multiple connections are in use, this has to be ensured by SSHFS instead.
The easiest way to do so would be to bind specific SFTP connections to file
handles. Unfortunately, not all requests for the same dentry are guaranteed to come
from the same file handle and some requests may come without any file handle. We
therefore maintain a separate mapping from currently open files to SFTP connections. If
a request comes in for a path contained in sshfs.conntab and its result could be
changed by a pending write() operation, it will always be executed with the
associated SFTP connection.
There are additional subtleties for requests that affect multiple paths. For example,
if both source and destination of a rename() request are currently open, which
connection should be used?
This problem is again hard in general, but solvable since we only have to worry about
the effects of pending write() calls. For rename() and link(), it does not matter if a
pending write is executed before or after the operation. For readdir(), it is possible
that a pending write() will change the length of the file. However, SSHFS currently
does not return attribute information for readdir(), so this does not pose problems
either. Should SSHFS implement a readdirplus() handler (which provides file names and
attributes) this is a problem that will need to be solved.
*/
#ifdef __APPLE__
static char sshfs_program_path[PATH_MAX] = { 0 };
#endif /* __APPLE__ */
struct conn {
pthread_mutex_t lock_write;
int processing_thread_started;
int rfd;
int wfd;
int connver;
int req_count;
int dir_count;
int file_count;
};
struct buffer {
uint8_t *p;
size_t len;
size_t size;
};
struct dir_handle {
struct buffer buf;
struct conn *conn;
};
struct list_head {
struct list_head *prev;
struct list_head *next;
};
struct request;
typedef void (*request_func)(struct request *);
struct request {
unsigned int want_reply;
sem_t ready;
uint8_t reply_type;
uint32_t id;
int replied;
int error;
struct buffer reply;
struct timeval start;
void *data;
request_func end_func;
size_t len;
struct list_head list;
struct conn *conn;
};
struct sshfs_io {
int num_reqs;
pthread_cond_t finished;
int error;
};
struct read_req {
struct sshfs_io *sio;
struct list_head list;
struct buffer data;
size_t size;
ssize_t res;
};
struct read_chunk {
off_t offset;
size_t size;
int refs;
long modifver;
struct list_head reqs;
struct sshfs_io sio;
};
struct sshfs_file {
struct buffer handle;
struct list_head write_reqs;
pthread_cond_t write_finished;
int write_error;
struct read_chunk *readahead;
off_t next_pos;
int is_seq;
struct conn *conn;
int connver;
int modifver;
};
struct conntab_entry {
unsigned refcount;
struct conn *conn;
};
struct sshfs {
char *directport;
char *ssh_command;
char *sftp_server;
struct fuse_args ssh_args;
char *workarounds;
int rename_workaround;
int renamexdev_workaround;
int truncate_workaround;
int buflimit_workaround;
int unrel_append;
int fstat_workaround;
int createmode_workaround;
int transform_symlinks;
int follow_symlinks;
int no_check_root;
int detect_uid;
int idmap;
int nomap;
int disable_hardlink;
int dir_cache;
int show_version;
int show_help;
int singlethread;
char *mountpoint;
char *uid_file;
char *gid_file;
GHashTable *uid_map;
GHashTable *gid_map;
GHashTable *r_uid_map;
GHashTable *r_gid_map;
unsigned max_read;
unsigned max_write;
unsigned ssh_ver;
int sync_write;
int sync_read;
int sync_readdir;
int direct_io;
int debug;
int verbose;
int foreground;
int reconnect;
int delay_connect;
int passive;
char *host;
char *base_path;
GHashTable *reqtab;
GHashTable *conntab;
pthread_mutex_t lock;
unsigned int randseed;
int max_conns;
char *vsock;
struct conn *conns;
int ptyfd;
int ptypassivefd;
int connvers;
int server_version;
unsigned remote_uid;
unsigned local_uid;
unsigned remote_gid;
unsigned local_gid;
int remote_uid_detected;
unsigned blksize;
char *progname;
long modifver;
unsigned outstanding_len;
unsigned max_outstanding_len;
pthread_cond_t outstanding_cond;
int password_stdin;
char *password;
int ext_posix_rename;
int ext_statvfs;
int ext_hardlink;
int ext_fsync;
struct fuse_operations *op;
/* statistics */
uint64_t bytes_sent;
uint64_t bytes_received;
uint64_t num_sent;
uint64_t num_received;
unsigned int min_rtt;
unsigned int max_rtt;
uint64_t total_rtt;
unsigned int num_connect;
};
static struct sshfs sshfs;
static const char *ssh_opts[] = {
"AddressFamily",
"BatchMode",
"BindAddress",
"BindInterface",
"CertificateFile",
"ChallengeResponseAuthentication",
"CheckHostIP",
"Cipher",
"Ciphers",
"Compression",
"CompressionLevel",
"ConnectionAttempts",
"ConnectTimeout",
"ControlMaster",
"ControlPath",
"ControlPersist",
"FingerprintHash",
"GlobalKnownHostsFile",
"GSSAPIAuthentication",
"GSSAPIDelegateCredentials",
"HostbasedAuthentication",
"HostbasedKeyTypes",
"HostKeyAlgorithms",
"HostKeyAlias",
"HostName",
"IdentitiesOnly",
"IdentityFile",
"IdentityAgent",
"IPQoS",
"KbdInteractiveAuthentication",
"KbdInteractiveDevices",
"KexAlgorithms",
"LocalCommand",
"LogLevel",
"MACs",
"NoHostAuthenticationForLocalhost",
"NumberOfPasswordPrompts",
"PasswordAuthentication",
"PermitLocalCommand",
"PKCS11Provider",
"Port",
"PreferredAuthentications",
"ProxyCommand",
"ProxyJump",
"ProxyUseFdpass",
"PubkeyAcceptedKeyTypes",
"PubkeyAuthentication",
"RekeyLimit",
"RevokedHostKeys",
"RhostsRSAAuthentication",
"RSAAuthentication",
"ServerAliveCountMax",
"ServerAliveInterval",
"SmartcardDevice",
"StrictHostKeyChecking",
"TCPKeepAlive",
"UpdateHostKeys",
"UsePrivilegedPort",
"UserKnownHostsFile",
"VerifyHostKeyDNS",
"VisualHostKey",
NULL,
};
enum {
KEY_PORT,
KEY_COMPRESS,
KEY_CONFIGFILE,
};
enum {
IDMAP_NONE,
IDMAP_USER,
IDMAP_FILE,
};
enum {
NOMAP_IGNORE,
NOMAP_ERROR,
};
#define SSHFS_OPT(t, p, v) { t, offsetof(struct sshfs, p), v }
static struct fuse_opt sshfs_opts[] = {
SSHFS_OPT("directport=%s", directport, 0),
SSHFS_OPT("ssh_command=%s", ssh_command, 0),
SSHFS_OPT("sftp_server=%s", sftp_server, 0),
SSHFS_OPT("max_read=%u", max_read, 0),
SSHFS_OPT("max_write=%u", max_write, 0),
SSHFS_OPT("ssh_protocol=%u", ssh_ver, 0),
SSHFS_OPT("-1", ssh_ver, 1),
SSHFS_OPT("workaround=%s", workarounds, 0),
SSHFS_OPT("idmap=none", idmap, IDMAP_NONE),
SSHFS_OPT("idmap=user", idmap, IDMAP_USER),
SSHFS_OPT("idmap=file", idmap, IDMAP_FILE),
SSHFS_OPT("uidfile=%s", uid_file, 0),
SSHFS_OPT("gidfile=%s", gid_file, 0),
SSHFS_OPT("nomap=ignore", nomap, NOMAP_IGNORE),
SSHFS_OPT("nomap=error", nomap, NOMAP_ERROR),
SSHFS_OPT("sshfs_sync", sync_write, 1),
SSHFS_OPT("no_readahead", sync_read, 1),
SSHFS_OPT("sync_readdir", sync_readdir, 1),
SSHFS_OPT("sshfs_debug", debug, 1),
SSHFS_OPT("sshfs_verbose", verbose, 1),
SSHFS_OPT("reconnect", reconnect, 1),
SSHFS_OPT("transform_symlinks", transform_symlinks, 1),
SSHFS_OPT("follow_symlinks", follow_symlinks, 1),
SSHFS_OPT("no_check_root", no_check_root, 1),
SSHFS_OPT("password_stdin", password_stdin, 1),
SSHFS_OPT("delay_connect", delay_connect, 1),
SSHFS_OPT("slave", passive, 1),
SSHFS_OPT("passive", passive, 1),
SSHFS_OPT("disable_hardlink", disable_hardlink, 1),
SSHFS_OPT("dir_cache=yes", dir_cache, 1),
SSHFS_OPT("dir_cache=no", dir_cache, 0),
SSHFS_OPT("direct_io", direct_io, 1),
SSHFS_OPT("max_conns=%u", max_conns, 1),
SSHFS_OPT("vsock=%s", vsock, 0),
SSHFS_OPT("-h", show_help, 1),
SSHFS_OPT("--help", show_help, 1),
SSHFS_OPT("-V", show_version, 1),
SSHFS_OPT("--version", show_version, 1),
SSHFS_OPT("-d", debug, 1),
SSHFS_OPT("debug", debug, 1),
SSHFS_OPT("-v", verbose, 1),
SSHFS_OPT("verbose", verbose, 1),
SSHFS_OPT("-f", foreground, 1),
SSHFS_OPT("-s", singlethread, 1),
FUSE_OPT_KEY("-p ", KEY_PORT),
FUSE_OPT_KEY("-C", KEY_COMPRESS),
FUSE_OPT_KEY("-F ", KEY_CONFIGFILE),
/* For backwards compatibility */
SSHFS_OPT("cache=yes", dir_cache, 1),
SSHFS_OPT("cache=no", dir_cache, 0),
FUSE_OPT_KEY("writeback_cache=no", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("unreliable_append", FUSE_OPT_KEY_DISCARD),
/* These may come in from /etc/fstab - we just ignore them */
FUSE_OPT_KEY("auto", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("noauto", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("user", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("nouser", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("users", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("_netdev", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_END
};
static struct fuse_opt workaround_opts[] = {
SSHFS_OPT("none", rename_workaround, 0),
SSHFS_OPT("none", truncate_workaround, 0),
SSHFS_OPT("none", buflimit_workaround, 0),
SSHFS_OPT("none", fstat_workaround, 0),
SSHFS_OPT("rename", rename_workaround, 1),
SSHFS_OPT("norename", rename_workaround, 0),
SSHFS_OPT("renamexdev", renamexdev_workaround, 1),
SSHFS_OPT("norenamexdev", renamexdev_workaround, 0),
SSHFS_OPT("truncate", truncate_workaround, 1),
SSHFS_OPT("notruncate", truncate_workaround, 0),
SSHFS_OPT("buflimit", buflimit_workaround, 1),
SSHFS_OPT("nobuflimit", buflimit_workaround, 0),
SSHFS_OPT("fstat", fstat_workaround, 1),
SSHFS_OPT("nofstat", fstat_workaround, 0),
SSHFS_OPT("createmode", createmode_workaround, 1),
SSHFS_OPT("nocreatemode", createmode_workaround, 0),
FUSE_OPT_END
};
#define DEBUG(format, args...) \
do { if (sshfs.debug) fprintf(stderr, format, args); } while(0)
static const char *type_name(uint8_t type)
{
switch(type) {
case SSH_FXP_INIT: return "INIT";
case SSH_FXP_VERSION: return "VERSION";
case SSH_FXP_OPEN: return "OPEN";
case SSH_FXP_CLOSE: return "CLOSE";
case SSH_FXP_READ: return "READ";
case SSH_FXP_WRITE: return "WRITE";
case SSH_FXP_LSTAT: return "LSTAT";
case SSH_FXP_FSTAT: return "FSTAT";
case SSH_FXP_SETSTAT: return "SETSTAT";
case SSH_FXP_FSETSTAT: return "FSETSTAT";
case SSH_FXP_OPENDIR: return "OPENDIR";
case SSH_FXP_READDIR: return "READDIR";
case SSH_FXP_REMOVE: return "REMOVE";
case SSH_FXP_MKDIR: return "MKDIR";
case SSH_FXP_RMDIR: return "RMDIR";
case SSH_FXP_REALPATH: return "REALPATH";
case SSH_FXP_STAT: return "STAT";
case SSH_FXP_RENAME: return "RENAME";
case SSH_FXP_READLINK: return "READLINK";
case SSH_FXP_SYMLINK: return "SYMLINK";
case SSH_FXP_STATUS: return "STATUS";
case SSH_FXP_HANDLE: return "HANDLE";
case SSH_FXP_DATA: return "DATA";
case SSH_FXP_NAME: return "NAME";
case SSH_FXP_ATTRS: return "ATTRS";
case SSH_FXP_EXTENDED: return "EXTENDED";
case SSH_FXP_EXTENDED_REPLY: return "EXTENDED_REPLY";
default: return "???";
}
}
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
static void list_init(struct list_head *head)
{
head->next = head;
head->prev = head;
}
static void list_add(struct list_head *new, struct list_head *head)
{
struct list_head *prev = head;
struct list_head *next = head->next;
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
static void list_del(struct list_head *entry)
{
struct list_head *prev = entry->prev;
struct list_head *next = entry->next;
next->prev = prev;
prev->next = next;
}
static int list_empty(const struct list_head *head)
{
return head->next == head;
}
/* given a pointer to the uid/gid, and the mapping table, remap the
* uid/gid, if necessary */
static inline int translate_id(uint32_t *id, GHashTable *map)
{
gpointer id_p;
if (g_hash_table_lookup_extended(map, GUINT_TO_POINTER(*id), NULL, &id_p)) {
*id = GPOINTER_TO_UINT(id_p);
return 0;
}
switch (sshfs.nomap) {
case NOMAP_ERROR: return -1;
case NOMAP_IGNORE: return 0;
default:
fprintf(stderr, "internal error\n");
abort();
}
}
static inline void buf_init(struct buffer *buf, size_t size)
{
if (size) {
buf->p = (uint8_t *) malloc(size);
if (!buf->p) {
fprintf(stderr, "sshfs: memory allocation failed\n");
abort();
}
} else
buf->p = NULL;
buf->len = 0;
buf->size = size;
}
static inline void buf_free(struct buffer *buf)
{
free(buf->p);
}
static inline void buf_finish(struct buffer *buf)
{
buf->len = buf->size;
}
static inline void buf_clear(struct buffer *buf)
{
buf_free(buf);
buf_init(buf, 0);
}
static void buf_resize(struct buffer *buf, size_t len)
{
buf->size = (buf->len + len + 63) & ~31;
buf->p = (uint8_t *) realloc(buf->p, buf->size);
if (!buf->p) {
fprintf(stderr, "sshfs: memory allocation failed\n");
abort();
}
}
static inline void buf_check_add(struct buffer *buf, size_t len)
{
if (buf->len + len > buf->size)
buf_resize(buf, len);
}
#define _buf_add_mem(b, d, l) \
buf_check_add(b, l); \
memcpy(b->p + b->len, d, l); \
b->len += l;
static inline void buf_add_mem(struct buffer *buf, const void *data,
size_t len)
{
_buf_add_mem(buf, data, len);
}
static inline void buf_add_buf(struct buffer *buf, const struct buffer *bufa)
{
_buf_add_mem(buf, bufa->p, bufa->len);
}
static inline void buf_add_uint8(struct buffer *buf, uint8_t val)
{
_buf_add_mem(buf, &val, 1);
}
static inline void buf_add_uint32(struct buffer *buf, uint32_t val)
{
uint32_t nval = htonl(val);
_buf_add_mem(buf, &nval, 4);
}
static inline void buf_add_uint64(struct buffer *buf, uint64_t val)
{
buf_add_uint32(buf, val >> 32);
buf_add_uint32(buf, val & 0xffffffff);
}
static inline void buf_add_data(struct buffer *buf, const struct buffer *data)
{
buf_add_uint32(buf, data->len);
buf_add_mem(buf, data->p, data->len);
}
static inline void buf_add_string(struct buffer *buf, const char *str)
{
struct buffer data;
data.p = (uint8_t *) str;
data.len = strlen(str);
buf_add_data(buf, &data);
}
static inline void buf_add_path(struct buffer *buf, const char *path)
{
char *realpath;
if (sshfs.base_path[0]) {
if (path[1]) {
if (sshfs.base_path[strlen(sshfs.base_path)-1] != '/') {
realpath = g_strdup_printf("%s/%s",
sshfs.base_path,
path + 1);
} else {
realpath = g_strdup_printf("%s%s",
sshfs.base_path,
path + 1);
}
} else {
realpath = g_strdup(sshfs.base_path);
}
} else {
if (path[1])
realpath = g_strdup(path + 1);
else
realpath = g_strdup(".");
}
buf_add_string(buf, realpath);
g_free(realpath);
}
static int buf_check_get(struct buffer *buf, size_t len)
{
if (buf->len + len > buf->size) {
fprintf(stderr, "buffer too short\n");
return -1;
} else
return 0;
}
static inline int buf_get_mem(struct buffer *buf, void *data, size_t len)
{
if (buf_check_get(buf, len) == -1)
return -1;
memcpy(data, buf->p + buf->len, len);
buf->len += len;
return 0;
}
static inline int buf_get_uint8(struct buffer *buf, uint8_t *val)
{
return buf_get_mem(buf, val, 1);
}
static inline int buf_get_uint32(struct buffer *buf, uint32_t *val)
{
uint32_t nval;
if (buf_get_mem(buf, &nval, 4) == -1)
return -1;
*val = ntohl(nval);
return 0;
}
static inline int buf_get_uint64(struct buffer *buf, uint64_t *val)
{
uint32_t val1;
uint32_t val2;
if (buf_get_uint32(buf, &val1) == -1 ||
buf_get_uint32(buf, &val2) == -1) {
return -1;
}
*val = ((uint64_t) val1 << 32) + val2;
return 0;
}
static inline int buf_get_data(struct buffer *buf, struct buffer *data)
{
uint32_t len;
if (buf_get_uint32(buf, &len) == -1 || len > buf->size - buf->len)
return -1;
buf_init(data, len + 1);
data->size = len;
if (buf_get_mem(buf, data->p, data->size) == -1) {
buf_free(data);
return -1;
}
return 0;
}
static inline int buf_get_string(struct buffer *buf, char **str)
{
struct buffer data;
if (buf_get_data(buf, &data) == -1)
return -1;
data.p[data.size] = '\0';
*str = (char *) data.p;
return 0;
}
static int buf_get_attrs(struct buffer *buf, struct stat *stbuf, int *flagsp)
{
uint32_t flags;
uint64_t size = 0;
uint32_t uid = 0;
uint32_t gid = 0;
uint32_t atime = 0;
uint32_t mtime = 0;
uint32_t mode = S_IFREG | 0777;
if (buf_get_uint32(buf, &flags) == -1)
return -EIO;
if (flagsp)
*flagsp = flags;
if ((flags & SSH_FILEXFER_ATTR_SIZE) &&
buf_get_uint64(buf, &size) == -1)
return -EIO;
if ((flags & SSH_FILEXFER_ATTR_UIDGID) &&
(buf_get_uint32(buf, &uid) == -1 ||
buf_get_uint32(buf, &gid) == -1))
return -EIO;
if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
buf_get_uint32(buf, &mode) == -1)
return -EIO;
if ((flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
if (buf_get_uint32(buf, &atime) == -1 ||
buf_get_uint32(buf, &mtime) == -1)
return -EIO;
}
if ((flags & SSH_FILEXFER_ATTR_EXTENDED)) {
uint32_t extcount;
unsigned i;
if (buf_get_uint32(buf, &extcount) == -1)
return -EIO;
for (i = 0; i < extcount; i++) {
struct buffer tmp;
if (buf_get_data(buf, &tmp) == -1)
return -EIO;
buf_free(&tmp);
if (buf_get_data(buf, &tmp) == -1)
return -EIO;
buf_free(&tmp);
}
}
if (sshfs.remote_uid_detected) {
if (uid == sshfs.remote_uid)
uid = sshfs.local_uid;
if (gid == sshfs.remote_gid)
gid = sshfs.local_gid;
}
if (sshfs.idmap == IDMAP_FILE && sshfs.uid_map)
if (translate_id(&uid, sshfs.uid_map) == -1)
return -EPERM;
if (sshfs.idmap == IDMAP_FILE && sshfs.gid_map)
if (translate_id(&gid, sshfs.gid_map) == -1)
return -EPERM;
memset(stbuf, 0, sizeof(struct stat));
stbuf->st_mode = mode;
stbuf->st_nlink = 1;
stbuf->st_size = size;
if (sshfs.blksize) {
stbuf->st_blksize = sshfs.blksize;
stbuf->st_blocks = ((size + sshfs.blksize - 1) &
~((unsigned long long) sshfs.blksize - 1)) >> 9;
}
stbuf->st_uid = uid;
stbuf->st_gid = gid;
stbuf->st_atime = atime;
stbuf->st_ctime = stbuf->st_mtime = mtime;
return 0;
}
static int buf_get_statvfs(struct buffer *buf, struct statvfs *stbuf)
{
uint64_t bsize;
uint64_t frsize;
uint64_t blocks;
uint64_t bfree;
uint64_t bavail;
uint64_t files;
uint64_t ffree;
uint64_t favail;
uint64_t fsid;
uint64_t flag;
uint64_t namemax;
if (buf_get_uint64(buf, &bsize) == -1 ||
buf_get_uint64(buf, &frsize) == -1 ||
buf_get_uint64(buf, &blocks) == -1 ||
buf_get_uint64(buf, &bfree) == -1 ||
buf_get_uint64(buf, &bavail) == -1 ||
buf_get_uint64(buf, &files) == -1 ||
buf_get_uint64(buf, &ffree) == -1 ||
buf_get_uint64(buf, &favail) == -1 ||
buf_get_uint64(buf, &fsid) == -1 ||
buf_get_uint64(buf, &flag) == -1 ||
buf_get_uint64(buf, &namemax) == -1) {
return -1;
}
memset(stbuf, 0, sizeof(struct statvfs));
stbuf->f_bsize = bsize;
stbuf->f_frsize = frsize;
stbuf->f_blocks = blocks;
stbuf->f_bfree = bfree;
stbuf->f_bavail = bavail;
stbuf->f_files = files;
stbuf->f_ffree = ffree;
stbuf->f_favail = favail;
stbuf->f_namemax = namemax;
return 0;
}
static int buf_get_entries(struct buffer *buf, void *dbuf,
fuse_fill_dir_t filler)
{
uint32_t count;
unsigned i;
if (buf_get_uint32(buf, &count) == -1)
return -EIO;
for (i = 0; i < count; i++) {
int err = -1;
char *name;
char *longname;
struct stat stbuf;
if (buf_get_string(buf, &name) == -1)
return -EIO;
if (buf_get_string(buf, &longname) != -1) {
free(longname);
err = buf_get_attrs(buf, &stbuf, NULL);
if (!err) {
if (sshfs.follow_symlinks &&
S_ISLNK(stbuf.st_mode)) {
stbuf.st_mode = 0;
}
filler(dbuf, name, &stbuf, 0, 0);
}
}
free(name);
if (err)
return err;
}
return 0;
}
static void ssh_add_arg(const char *arg)
{
if (fuse_opt_add_arg(&sshfs.ssh_args, arg) == -1)