mirror of
https://github.com/rclone/rclone.git
synced 2025-01-24 12:56:36 +02:00
sftp: add About support - fixes #3107
This adds support for using About with SFTP remotes. This works by calling the df command remotely.
This commit is contained in:
parent
3d475dc0ee
commit
49d7b0d278
@ -14,6 +14,7 @@ import (
|
|||||||
"os/user"
|
"os/user"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -813,6 +814,47 @@ func (f *Fs) Hashes() hash.Set {
|
|||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// About gets usage stats
|
||||||
|
func (f *Fs) About() (*fs.Usage, error) {
|
||||||
|
c, err := f.getSftpConnection()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "About get SFTP connection")
|
||||||
|
}
|
||||||
|
session, err := c.sshClient.NewSession()
|
||||||
|
f.putSftpConnection(&c, err)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "About put SFTP connection")
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
session.Stdout = &stdout
|
||||||
|
session.Stderr = &stderr
|
||||||
|
escapedPath := shellEscape(f.root)
|
||||||
|
if f.opt.PathOverride != "" {
|
||||||
|
escapedPath = shellEscape(path.Join(f.opt.PathOverride, f.root))
|
||||||
|
}
|
||||||
|
if len(escapedPath) == 0 {
|
||||||
|
escapedPath = "/"
|
||||||
|
}
|
||||||
|
err = session.Run("df -k " + escapedPath)
|
||||||
|
if err != nil {
|
||||||
|
_ = session.Close()
|
||||||
|
return nil, errors.Wrap(err, "About invocation of df failed. Your remote may not support about.")
|
||||||
|
}
|
||||||
|
_ = session.Close()
|
||||||
|
|
||||||
|
usageTotal, usageUsed, usageAvail := parseUsage(stdout.Bytes())
|
||||||
|
if usageTotal < 0 || usageUsed < 0 || usageAvail < 0 {
|
||||||
|
return nil, errors.Wrap(err, "About failed to parse information")
|
||||||
|
}
|
||||||
|
usage := &fs.Usage{
|
||||||
|
Total: fs.NewUsageValue(usageTotal),
|
||||||
|
Used: fs.NewUsageValue(usageUsed),
|
||||||
|
Free: fs.NewUsageValue(usageAvail),
|
||||||
|
}
|
||||||
|
return usage, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Fs is the filesystem this remote sftp file object is located within
|
// Fs is the filesystem this remote sftp file object is located within
|
||||||
func (o *Object) Fs() fs.Info {
|
func (o *Object) Fs() fs.Info {
|
||||||
return o.fs
|
return o.fs
|
||||||
@ -903,6 +945,34 @@ func parseHash(bytes []byte) string {
|
|||||||
return strings.Split(string(bytes), " ")[0] // Split at hash / filename separator
|
return strings.Split(string(bytes), " ")[0] // Split at hash / filename separator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parses the byte array output from the SSH session
|
||||||
|
// returned by an invocation of df into
|
||||||
|
// the disk size, used space, and avaliable space on the disk, in that order.
|
||||||
|
// Only works when `df` has output info on only one disk
|
||||||
|
func parseUsage(bytes []byte) (int64, int64, int64) {
|
||||||
|
lines := strings.Split(string(bytes), "\n")
|
||||||
|
if len(lines) < 2 {
|
||||||
|
return -1, -1, -1
|
||||||
|
}
|
||||||
|
split := strings.Fields(lines[1])
|
||||||
|
if len(split) < 6 {
|
||||||
|
return -1, -1, -1
|
||||||
|
}
|
||||||
|
spaceTotal, err := strconv.ParseInt(split[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, -1
|
||||||
|
}
|
||||||
|
spaceUsed, err := strconv.ParseInt(split[2], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, -1
|
||||||
|
}
|
||||||
|
spaceAvail, err := strconv.ParseInt(split[3], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, -1
|
||||||
|
}
|
||||||
|
return spaceTotal * 1024, spaceUsed * 1024, spaceAvail * 1024
|
||||||
|
}
|
||||||
|
|
||||||
// Size returns the size in bytes of the remote sftp file
|
// Size returns the size in bytes of the remote sftp file
|
||||||
func (o *Object) Size() int64 {
|
func (o *Object) Size() int64 {
|
||||||
return o.size
|
return o.size
|
||||||
|
@ -35,3 +35,17 @@ func TestParseHash(t *testing.T) {
|
|||||||
assert.Equal(t, test.checksum, got, fmt.Sprintf("Test %d sshOutput = %q", i, test.sshOutput))
|
assert.Equal(t, test.checksum, got, fmt.Sprintf("Test %d sshOutput = %q", i, test.sshOutput))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseUsage(t *testing.T) {
|
||||||
|
for i, test := range []struct {
|
||||||
|
sshOutput string
|
||||||
|
usage [3]int64
|
||||||
|
}{
|
||||||
|
{"Filesystem 1K-blocks Used Available Use% Mounted on\n/dev/root 91283092 81111888 10154820 89% /", [3]int64{93473886208, 83058573312, 10398535680}},
|
||||||
|
{"Filesystem 1K-blocks Used Available Use% Mounted on\ntmpfs 818256 1636 816620 1% /run", [3]int64{837894144, 1675264, 836218880}},
|
||||||
|
{"Filesystem 1024-blocks Used Available Capacity iused ifree %iused Mounted on\n/dev/disk0s2 244277768 94454848 149566920 39% 997820 4293969459 0% /", [3]int64{250140434432, 96721764352, 153156526080}},
|
||||||
|
} {
|
||||||
|
gotSpaceTotal, gotSpaceUsed, gotSpaceAvail := parseUsage([]byte(test.sshOutput))
|
||||||
|
assert.Equal(t, test.usage, [3]int64{gotSpaceTotal, gotSpaceUsed, gotSpaceAvail}, fmt.Sprintf("Test %d sshOutput = %q", i, test.sshOutput))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -149,7 +149,7 @@ operations more efficient.
|
|||||||
| Openstack Swift | Yes † | Yes | No | No | No | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | Yes |
|
| Openstack Swift | Yes † | Yes | No | No | No | Yes | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | Yes |
|
||||||
| pCloud | Yes | Yes | Yes | Yes | Yes | No | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | Yes |
|
| pCloud | Yes | Yes | Yes | Yes | Yes | No | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | Yes |
|
||||||
| QingStor | No | Yes | No | No | No | Yes | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | No |
|
| QingStor | No | Yes | No | No | No | Yes | No | No [#2178](https://github.com/ncw/rclone/issues/2178) | No |
|
||||||
| SFTP | No | No | Yes | Yes | No | No | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | No |
|
| SFTP | No | No | Yes | Yes | No | No | Yes | No [#2178](https://github.com/ncw/rclone/issues/2178) | Yes |
|
||||||
| WebDAV | Yes | Yes | Yes | Yes | No | No | Yes ‡ | No [#2178](https://github.com/ncw/rclone/issues/2178) | Yes |
|
| WebDAV | Yes | Yes | Yes | Yes | No | No | Yes ‡ | No [#2178](https://github.com/ncw/rclone/issues/2178) | Yes |
|
||||||
| Yandex Disk | Yes | Yes | Yes | Yes | Yes | No | Yes | Yes | Yes |
|
| Yandex Disk | Yes | Yes | Yes | Yes | Yes | No | Yes | Yes | Yes |
|
||||||
| The local filesystem | Yes | No | Yes | Yes | No | No | Yes | No | Yes |
|
| The local filesystem | Yes | No | Yes | Yes | No | No | Yes | No | Yes |
|
||||||
|
Loading…
x
Reference in New Issue
Block a user