diff --git a/common/common.go b/common/common.go index 8bad34aa..d702cc7c 100644 --- a/common/common.go +++ b/common/common.go @@ -23,6 +23,7 @@ import ( "github.com/drakkan/sftpgo/logger" "github.com/drakkan/sftpgo/metrics" "github.com/drakkan/sftpgo/utils" + "github.com/drakkan/sftpgo/vfs" ) // constants @@ -149,6 +150,7 @@ func Initialize(c Configuration) error { } } } + vfs.SetTempPath(c.TempPath) return nil } @@ -335,6 +337,12 @@ type Configuration struct { // 2 means "ignore mode for cloud fs": requests for changing permissions and owner/group/time are // silently ignored for cloud based filesystem such as S3, GCS, Azure Blob SetstatMode int `json:"setstat_mode" mapstructure:"setstat_mode"` + // TempPath defines the path for temporary files such as those used for atomic uploads or file pipes. + // If you set this option you must make sure that the defined path exists, is accessible for writing + // by the user running SFTPGo, and is on the same filesystem as the users home directories otherwise + // the renaming for atomic uploads will become a copy and therefore may take a long time. + // The temporary files are not namespaced. The default is generally fine. Leave empty for the default. + TempPath string `json:"temp_path" mapstructure:"temp_path"` // Support for HAProxy PROXY protocol. // If you are running SFTPGo behind a proxy server such as HAProxy, AWS ELB or NGNIX, you can enable // the proxy protocol. It provides a convenient way to safely transport connection information diff --git a/config/config.go b/config/config.go index b31ce0f9..a7462bc6 100644 --- a/config/config.go +++ b/config/config.go @@ -115,6 +115,7 @@ func Init() { Hook: "", }, SetstatMode: 0, + TempPath: "", ProxyProtocol: 0, ProxyAllowed: []string{}, PostConnectHook: "", @@ -925,6 +926,7 @@ func setViperDefaults() { viper.SetDefault("common.actions.execute_sync", globalConf.Common.Actions.ExecuteSync) viper.SetDefault("common.actions.hook", globalConf.Common.Actions.Hook) viper.SetDefault("common.setstat_mode", globalConf.Common.SetstatMode) + viper.SetDefault("common.temp_path", globalConf.Common.TempPath) viper.SetDefault("common.proxy_protocol", globalConf.Common.ProxyProtocol) viper.SetDefault("common.proxy_allowed", globalConf.Common.ProxyAllowed) viper.SetDefault("common.post_connect_hook", globalConf.Common.PostConnectHook) diff --git a/docs/dare.md b/docs/dare.md index 12abc117..23d6febc 100644 --- a/docs/dare.md +++ b/docs/dare.md @@ -15,4 +15,5 @@ The encrypted filesystem has some limitations compared to the local, unencrypted - Resuming uploads is not supported. - Opening a file for both reading and writing at the same time is not supported and so clients that require advanced filesystem-like features such as `sshfs` are not supported too. - Truncate is not supported. +- Atomic uploads are not supported. - System commands such as `git` or `rsync` are not supported: they will store data unencrypted. diff --git a/docs/full-configuration.md b/docs/full-configuration.md index 4dd4381f..546ef22a 100644 --- a/docs/full-configuration.md +++ b/docs/full-configuration.md @@ -52,10 +52,11 @@ The configuration file contains the following sections: - `idle_timeout`, integer. Time in minutes after which an idle client will be disconnected. 0 means disabled. Default: 15 - `upload_mode` integer. 0 means standard: the files are uploaded directly to the requested path. 1 means atomic: files are uploaded to a temporary path and renamed to the requested path when the client ends the upload. Atomic mode avoids problems such as a web server that serves partial files when the files are being uploaded. In atomic mode, if there is an upload error, the temporary file is deleted and so the requested upload path will not contain a partial file. 2 means atomic with resume support: same as atomic but if there is an upload error, the temporary file is renamed to the requested path and not deleted. This way, a client can reconnect and resume the upload. - `actions`, struct. It contains the command to execute and/or the HTTP URL to notify and the trigger conditions. See [Custom Actions](./custom-actions.md) for more details - - `execute_on`, list of strings. Valid values are `download`, `upload`, `pre-delete`, `delete`, `rename`, `ssh_cmd`. Leave empty to disable actions. + - `execute_on`, list of strings. Valid values are `pre-download`, `download`, `pre-upload`, `upload`, `pre-delete`, `delete`, `rename`, `ssh_cmd`. Leave empty to disable actions. - `execute_sync`, list of strings. Actions to be performed synchronously. The `pre-delete` action is always executed synchronously while the other ones are asynchronous. Executing an action synchronously means that SFTPGo will not return a result code to the client (which is waiting for it) until your hook have completed its execution. Leave empty to execute only the `pre-delete` hook synchronously - `hook`, string. Absolute path to the command to execute or HTTP URL to notify. - `setstat_mode`, integer. 0 means "normal mode": requests for changing permissions, owner/group and access/modification times are executed. 1 means "ignore mode": requests for changing permissions, owner/group and access/modification times are silently ignored. 2 means "ignore mode for cloud based filesystems": requests for changing permissions, owner/group and access/modification times are silently ignored for cloud filesystems and executed for local filesystem. + - `temp_path`, string. Defines the path for temporary files such as those used for atomic uploads or file pipes. If you set this option you must make sure that the defined path exists, is accessible for writing by the user running SFTPGo, and is on the same filesystem as the users home directories otherwise the renaming for atomic uploads will become a copy and therefore may take a long time. The temporary files are not namespaced. The default is generally fine. Leave empty for the default. - `proxy_protocol`, integer. Support for [HAProxy PROXY protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt). If you are running SFTPGo behind a proxy server such as HAProxy, AWS ELB or NGNIX, you can enable the proxy protocol. It provides a convenient way to safely transport connection information such as a client's address across multiple layers of NAT or TCP proxies to get the real client IP address instead of the proxy IP. Both protocol versions 1 and 2 are supported. If the proxy protocol is enabled in SFTPGo then you have to enable the protocol in your proxy configuration too. For example, for HAProxy, add `send-proxy` or `send-proxy-v2` to each server configuration line. The following modes are supported: - 0, disabled - 1, enabled. Proxy header will be used and requests without proxy header will be accepted diff --git a/go.mod b/go.mod index dd5118d9..78e81623 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect github.com/alexedwards/argon2id v0.0.0-20210511081203-7d35d68092b8 - github.com/aws/aws-sdk-go v1.38.45 + github.com/aws/aws-sdk-go v1.38.49 github.com/cockroachdb/cockroach-go/v2 v2.1.1 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect github.com/eikenb/pipeat v0.0.0-20200430215831-470df5986b6d @@ -20,6 +20,7 @@ require ( github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-sql-driver/mysql v1.6.0 github.com/goccy/go-json v0.5.1 // indirect + github.com/google/go-cmp v0.5.6 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/grandcat/zeroconf v1.0.0 github.com/hashicorp/go-retryablehttp v0.7.0 @@ -56,11 +57,12 @@ require ( gocloud.dev v0.23.0 gocloud.dev/secrets/hashivault v0.23.0 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a - golang.org/x/net v0.0.0-20210521195947-fe42d452be8f - golang.org/x/sys v0.0.0-20210521203332-0cec03c779c1 + golang.org/x/net v0.0.0-20210525063256-abc453219eb5 + golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba + golang.org/x/tools v0.1.2 // indirect google.golang.org/api v0.47.0 - google.golang.org/genproto v0.0.0-20210521181308-5ccab8a35a9a // indirect + google.golang.org/genproto v0.0.0-20210524171403-669157292da3 // indirect google.golang.org/grpc v1.38.0 // indirect gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 @@ -70,5 +72,5 @@ require ( replace ( github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20210515063737-edf1d3b63536 - golang.org/x/net => github.com/drakkan/net v0.0.0-20210522080832-660347a239d0 + golang.org/x/net => github.com/drakkan/net v0.0.0-20210527130624-42a06f551c29 ) diff --git a/go.sum b/go.sum index ac2fda80..b8a72c93 100644 --- a/go.sum +++ b/go.sum @@ -127,8 +127,8 @@ github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.38.45 h1:pQmv1vT/voRAjENnPsT4WobFBgLwnODDFogrt2kXc7M= -github.com/aws/aws-sdk-go v1.38.45/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.38.49 h1:E31vxjCe6a5I+mJLmUGaZobiWmg9KdWaud9IfceYeYQ= +github.com/aws/aws-sdk-go v1.38.49/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -208,8 +208,8 @@ github.com/drakkan/crypto v0.0.0-20210515063737-edf1d3b63536 h1:3DJdhj83IA3NjlVz github.com/drakkan/crypto v0.0.0-20210515063737-edf1d3b63536/go.mod h1:M1JpE4lvRI5LLrE7yTCWfhbsy5rx3oZVjGZad4XMwWc= github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHPMtBLXhQmjaga91/DDjWk9jWA= github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU= -github.com/drakkan/net v0.0.0-20210522080832-660347a239d0 h1:4JyDDfo5GQZDDi2l/aU3k4582lxAXrouFIdXQc+kDko= -github.com/drakkan/net v0.0.0-20210522080832-660347a239d0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +github.com/drakkan/net v0.0.0-20210527130624-42a06f551c29 h1:T+pvNt3dz01gREP36oHu2wi7Q73NWuFF8S+BHmunrp8= +github.com/drakkan/net v0.0.0-20210527130624-42a06f551c29/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= @@ -349,8 +349,9 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-replayers/grpcreplay v1.0.0 h1:B5kVOzJ1hBgnevTgIWhSTatQ3608yu/2NnU0Ta1d0kY= github.com/google/go-replayers/grpcreplay v1.0.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE= github.com/google/go-replayers/httpreplay v0.1.2 h1:HCfx+dQzwN9XbGTHF8qJ+67WN8glL9FTWV5rraCJ/jU= @@ -1026,8 +1027,8 @@ golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210521203332-0cec03c779c1 h1:lCnv+lfrU9FRPGf8NeRuWAAPjNnema5WtBinMgs1fD8= -golang.org/x/sys v0.0.0-20210521203332-0cec03c779c1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea h1:+WiDlPBBaO+h9vPNZi8uJ3k4BkKQB7Iow3aqwHVA5hI= +golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1109,8 +1110,9 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1204,8 +1206,8 @@ google.golang.org/genproto v0.0.0-20210423144448-3a41ef94ed2b/go.mod h1:P3QM42oQ google.golang.org/genproto v0.0.0-20210429181445-86c259c2b4ab/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210506142907-4a47615972c2/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210521181308-5ccab8a35a9a h1:FaCiYXNZoBH/gnmVjMAHgOgdmpVVROBYOA+qCOHh6Hc= -google.golang.org/genproto v0.0.0-20210521181308-5ccab8a35a9a/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210524171403-669157292da3 h1:xFyh6GBb+NO1L0xqb978I3sBPQpk6FrKO0jJGRvdj/0= +google.golang.org/genproto v0.0.0-20210524171403-669157292da3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= diff --git a/sftpgo.json b/sftpgo.json index 659457e8..6944f950 100644 --- a/sftpgo.json +++ b/sftpgo.json @@ -8,6 +8,7 @@ "hook": "" }, "setstat_mode": 0, + "temp_path": "", "proxy_protocol": 0, "proxy_allowed": [], "startup_hook": "", diff --git a/vfs/azblobfs.go b/vfs/azblobfs.go index e3dd74e3..08ca05fd 100644 --- a/vfs/azblobfs.go +++ b/vfs/azblobfs.go @@ -54,7 +54,11 @@ func init() { // NewAzBlobFs returns an AzBlobFs object that allows to interact with Azure Blob storage func NewAzBlobFs(connectionID, localTempDir, mountPath string, config AzBlobFsConfig) (Fs, error) { if localTempDir == "" { - localTempDir = filepath.Clean(os.TempDir()) + if tempPath != "" { + localTempDir = tempPath + } else { + localTempDir = filepath.Clean(os.TempDir()) + } } fs := &AzureBlobFs{ connectionID: connectionID, diff --git a/vfs/cryptfs.go b/vfs/cryptfs.go index 5c8d4d4b..140d7b7f 100644 --- a/vfs/cryptfs.go +++ b/vfs/cryptfs.go @@ -27,7 +27,8 @@ const ( // CryptFs is a Fs implementation that allows to encrypts/decrypts local files type CryptFs struct { *OsFs - masterKey []byte + localTempDir string + masterKey []byte } // NewCryptFs returns a CryptFs object @@ -47,6 +48,11 @@ func NewCryptFs(connectionID, rootDir, mountPath string, config CryptFsConfig) ( }, masterKey: []byte(config.Passphrase.GetPayload()), } + if tempPath == "" { + fs.localTempDir = rootDir + } else { + fs.localTempDir = tempPath + } return fs, nil } @@ -66,7 +72,7 @@ func (fs *CryptFs) Open(name string, offset int64) (File, *pipeat.PipeReaderAt, f.Close() return nil, nil, nil, err } - r, w, err := pipeat.PipeInDir(fs.rootDir) + r, w, err := pipeat.PipeInDir(fs.localTempDir) if err != nil { f.Close() return nil, nil, nil, err @@ -157,7 +163,7 @@ func (fs *CryptFs) Create(name string, flag int) (File, *PipeWriter, func(), err f.Close() return nil, nil, nil, err } - r, w, err := pipeat.PipeInDir(fs.rootDir) + r, w, err := pipeat.PipeInDir(fs.localTempDir) if err != nil { f.Close() return nil, nil, nil, err diff --git a/vfs/gcsfs.go b/vfs/gcsfs.go index e06bddeb..c89a0f17 100644 --- a/vfs/gcsfs.go +++ b/vfs/gcsfs.go @@ -52,7 +52,11 @@ func init() { // NewGCSFs returns an GCSFs object that allows to interact with Google Cloud Storage func NewGCSFs(connectionID, localTempDir, mountPath string, config GCSFsConfig) (Fs, error) { if localTempDir == "" { - localTempDir = filepath.Clean(os.TempDir()) + if tempPath != "" { + localTempDir = tempPath + } else { + localTempDir = filepath.Clean(os.TempDir()) + } } var err error diff --git a/vfs/osfs.go b/vfs/osfs.go index 35d89214..d749a9f2 100644 --- a/vfs/osfs.go +++ b/vfs/osfs.go @@ -226,6 +226,9 @@ func (fs *OsFs) ScanRootDirContents() (int, int64, error) { // GetAtomicUploadPath returns the path to use for an atomic upload func (*OsFs) GetAtomicUploadPath(name string) string { dir := filepath.Dir(name) + if tempPath != "" { + dir = tempPath + } guid := xid.New().String() return filepath.Join(dir, ".sftpgo-upload."+guid+"."+filepath.Base(name)) } diff --git a/vfs/s3fs.go b/vfs/s3fs.go index 4feb09da..308575fc 100644 --- a/vfs/s3fs.go +++ b/vfs/s3fs.go @@ -48,7 +48,11 @@ func init() { // object storage func NewS3Fs(connectionID, localTempDir, mountPath string, config S3FsConfig) (Fs, error) { if localTempDir == "" { - localTempDir = filepath.Clean(os.TempDir()) + if tempPath != "" { + localTempDir = tempPath + } else { + localTempDir = filepath.Clean(os.TempDir()) + } } fs := &S3Fs{ connectionID: connectionID, diff --git a/vfs/sftpfs.go b/vfs/sftpfs.go index c851d3a8..27147d9e 100644 --- a/vfs/sftpfs.go +++ b/vfs/sftpfs.go @@ -175,7 +175,11 @@ type SFTPFs struct { // NewSFTPFs returns an SFTPFs object that allows to interact with an SFTP server func NewSFTPFs(connectionID, mountPath, localTempDir string, forbiddenSelfUsernames []string, config SFTPFsConfig) (Fs, error) { if localTempDir == "" { - localTempDir = filepath.Clean(os.TempDir()) + if tempPath != "" { + localTempDir = tempPath + } else { + localTempDir = filepath.Clean(os.TempDir()) + } } if err := config.Validate(); err != nil { return nil, err diff --git a/vfs/vfs.go b/vfs/vfs.go index 9a7920a8..13f2486a 100644 --- a/vfs/vfs.go +++ b/vfs/vfs.go @@ -30,6 +30,7 @@ var ( // ErrVfsUnsupported defines the error for an unsupported VFS operation ErrVfsUnsupported = errors.New("not supported") credentialsDirPath string + tempPath string sftpFingerprints []string ) @@ -43,6 +44,16 @@ func GetCredentialsDirPath() string { return credentialsDirPath } +// SetTempPath sets the path for temporary files +func SetTempPath(fsPath string) { + tempPath = fsPath +} + +// GetTempPath returns the path for temporary files +func GetTempPath() string { + return tempPath +} + // SetSFTPFingerprints sets the SFTP host key fingerprints func SetSFTPFingerprints(fp []string) { sftpFingerprints = fp