GSConnect / gnome-shell-extension-gsconnect

KDE Connect implementation for GNOME
GNU General Public License v2.0
3.2k stars 259 forks source link

Mount problem #478

Closed cbr9 closed 4 years ago

cbr9 commented 5 years ago

So I just installed GSConnect and everything works perfect, except for the mount option. I am able to mount my phone, but after some time it unmounts automatically, often when I'm trying to transfer some files from my phone to my pc. If this is the case (I'm copying files) I get "error splicing file: input/output error", which I guess is just the consequence of the phone being unmounted. I have all the dependencies installed. Any ideas?

ferdnyc commented 5 years ago

@cbr9 I've had some of the same issues, and unfortunately haven't completely found a solution yet. The only thing I can tell you (which admittedly isn't very helpful) is that GSConnect only creates the SFTP mount of the phone's filesystem — it doesn't have any role in maintaining the connection once the phone's mounted. (It can also unmount the device on command, but if the connection drops without you requesting an unmount then GSConnect isn't the one dropping the mount.) The disconnects are either coming from the Gnome Gio filesystem layer, or from the Android side.

The latter is far more likely, since Android's aggressive powersave will try very hard to halt the KDE Connect app's resource consumption when it isn't being interacted with as the foreground app. Meaning, whenever the screen shuts off, or locks, or a different app is switched to in the foreground, etc. Depending on the manufacturer of your device and the Android version it's running, there will be different rules for how aggressive this management is.

One thing you can try is to make sure KDE Connect is in the exception whitelist for whatever power-saving / resource-management features the OS provides. (On my Samsung Galaxy phone, that's Samsung's hyper-aggressive Device Maintenance app. Unfortunately, whitelisting KDE Connect there did not completely clear up connection drops for me.) SFTP is unfortunately not a passive network protocol that's designed to weather intermittent connectivity lapses; it expects an active, always-on connection of a type that's (intentionally) not well-supported in the design of the Android OS.

cbr9 commented 5 years ago

@ferdnyc that's a pity. I'll try that, but with no high hopes. Thank you for your answer anyways :)

ferdnyc commented 5 years ago

@cbr9 Oh, right, I almost forgot one other suggestion: The KDE Connect app has the "Show persistent notification" option in its Settings specifically because, in some Android builds, having a notification visible causes the app to be granted access to background resources when it might otherwise be put to sleep. It couldn't hurt to try switching that on, and see if it helps with connection stability.

cbr9 commented 5 years ago

@ferdnyc that option cannot be unchecked, it says it's required since Android 8.0.

ferdnyc commented 5 years ago

@cbr9 Aha, interesting!

andyholmes commented 5 years ago

I'm going to close this one, since I don't think it has anything specifically to do with GSConnect or anything that can be fixed on our side. At some point we may want to add a wiki page for Android app specific problems/tips, although that probably should be on the KDE Connect wiki instead.

MatthewYounkins commented 5 years ago

I have the same problem - apparently my phone is turning off the connection, and nothing I've tried (turning battery saver off, allowing notifications, not allowing notifications) seems to help. I'm transferring files and every ~10 minutes I need to re-establish the connection.

Any other suggestions? Apparently not? A different operating system on the phone?

ferdnyc commented 5 years ago

@MatthewYounkins The only thing I can think of is to report the issue in the KDE Connect bugzilla. (Or search for existing reports, to see what's been covered already.) If it's the device that's causing the dropped connections, there may be something more that can be done on the app side ­— an additional Android permission that can be requested, perhaps — but it would have to be a KDE Connect fix.

heqro commented 4 years ago

Conversely, I have been extensively using KDE Connect with no unmounting issues, but GSConnect seems to give me the same issues posted in this very post.

I'm not certain if this exact issue is the right one to talk about this particular problem, but I seem to have the same issue the OP discusses.

andyholmes commented 4 years ago

If this is only happening in GSConnect, my guess is it's because KDE Connect uses sshfs with the reconnect option.

We replaced sshfs in GSConnect with GVfs some time ago, because it integrates with the desktop better and we had numerous reports from users who were confused they had to to install additonal software for this feature.

If this is a short-coming in GVfs that would be the correct place to fix this problem, but there are number of other reasons this could happen. The KDE Connect protocol hot-swaps socket connections frequently and it's unclear how that might affect this plugin. Without further debugging it's pretty hard to know.

heqro commented 4 years ago

If anything, I've so far been able to "recover" the mounted device on Nautilus by just bookmarking its location. Instead of getting the "missing folder" error message, it just appeared after a handful of seconds waiting. I'm not certain if it's any useful for you (I hope it is).

andyholmes commented 4 years ago

That sounds more like the SFTP server is just changing addresses than disconnecting.

ferdnyc commented 4 years ago

^ Mmm, I don't think so, as then the bookmark would be invalid. It gets bookmarked by IP (and port#), so those parameters must remain the same. I have noticed that Gvfs seems pretty disconnect-happy — or more accurately, doesn't seem interested in reconnecting automatically. Whichever end is responsible for the disconnect (most likely it's the Android end), it seems to happen after just a few minutes, and then has to be manually reestablished every time.

That's the kind of thing I was referring to, way back when, about how Gvfs' support for SFTP mounts is kind of dated and old-school. Gvfs wants the remote filesystem mount to represent an active network connection, even when idle. Whereas the model for cloud-based services (especially on mobile) hews more towards a local cache, backed by a dynamically-managed link that's transparently brought up and down as needed (and is only up when it's needed).

andyholmes commented 4 years ago

More what I mean is this: https://github.com/andyholmes/gnome-shell-extension-gsconnect/blob/ec38bf073f87aa345e04439240a381e9c7f3338f/src/service/plugins/sftp.js#L180-L184

Coupled with this: https://github.com/andyholmes/gnome-shell-extension-gsconnect/blob/ec38bf073f87aa345e04439240a381e9c7f3338f/src/service/plugins/sftp.js#L202-L216

The problem with the SFTP plugin (on both sides) is it's super flaky and non-deterministic. We generally have to assume that if we're handed a new IP/Port pair that the old one is defunct. In practice those old ports are often still open and accepting connections, we'll just never be told they (still) exist. We also make no attempt ourself to reconnect these connections.

The behaviour of device (packet) connections is not well documented, but they're basically hot-swapped any time either side sends a broadcast; which is a lot. Strictly speaking, when the device connection changes the remote side should disconnect SFTP connections (since it is a new "session" and a new session password is generated for SFTP).

Unfortunately there's upwards of a 30s keepalive for device connections, so it's often the case that Device A thinks the connection is being replaced without interruption while Device B recognizes the connection has been broken for some time. Depending on which side that is, the Android app may regenerate the SFTP session or GSConnect may request a new one.

If it happens you are using private key auth though, you can totally just keep connecting to that port and you don't even need the password. Bookmarking probably just means you have some reference to the fact that port exists or maybe it even convinces Gvfs to keep the connection alive. The reason this probably works with sshfs is because it's reconnecting automatically to the same IP/Port independent of what KDE Connect is telling it to do.

ferdnyc commented 4 years ago

Ah, OK. I think I have a better handle on some of that now.

Unfortunately there's upwards of a 30s keepalive for device connections,

If I'm reading their desktop code right, it can actually be more like 90 seconds, at least from that end — they set ServerAliveInterval=30 in the sshfs options to enable unresponsive-server pings over the encrypted link, but leave the ServerAliveCountMax alone, which according to the ssh_config(5) man page will leave it at the default 3. And then, as you said, they also enable automatic reconnects.

i see they do some other interesting things using sshfs that unfortunately Gvfs doesn't support. Like they just forcibly disable the known hosts file entirely and set StrictHostKeyChecking=no, instead of fiddling with known hosts, .

Strictly speaking, when the device connection changes the remote side should disconnect SFTP connections (since it is a new "session" and a new session password is generated for SFTP).

I wonder. Reading the Android server code, they say that it's a single-session password. (Actually, now I don't even see anywhere it says that, here, though I thought I saw it in some other code.) But the password is only generated on server start(), and then not again. A session ID is passed to authenticate() calls, but seems to be ignored completely.

And their use of Device (to generate the keypair for pubkey auth) looks more like a persistent paired-device identifier.

(That's from skimming, probably inaccurately, their SimpleSftpServer.java code:

    void init(Context context, Device device) throws GeneralSecurityException {
// --  8<  --
        //Reuse this device keys for the ssh connection as well
        final KeyPair keyPair;
        PrivateKey privateKey = RsaHelper.getPrivateKey(context);
        PublicKey publicKey = RsaHelper.getPublicKey(context);
        keyPair = new KeyPair(publicKey, privateKey);
// --  8<  --
        keyAuth.deviceKey = device.certificate.getPublicKey();
        sshd.setPublickeyAuthenticator(keyAuth);
    public boolean start(List<SftpPlugin.StorageInfo> storageInfoList) {
        if (!started) {
            fileSystemFactory.initRoots(storageInfoList);
            passwordAuth.password = RandomHelper.randomString(28);
    static class SimplePasswordAuthenticator implements PasswordAuthenticator {
        String password;

        @Override
        public boolean authenticate(String user, String password, ServerSession session) {
            return user.equals(SimpleSftpServer.USER) && password.equals(this.password);
        }
    }
andyholmes commented 4 years ago

Yeah, then something funky is happening on the Android side. Without much work I can get this to happen:

$ gio mount -l
<snip>
Mount(0): 192.168.0.68 -> sftp://192.168.0.68:1748/
  Type: GDaemonMount
Mount(1): 192.168.0.68 -> sftp://192.168.0.68:1747/
  Type: GDaemonMount

Screenshot from 2020-03-05 16-27-00

Strictly speaking this shouldn't be possible, because the Sftp server should only be stopped if the plugin is destroyed and if it's not stopped it shouldn't grab a new port. It's possible the server is not properly shutting down, since it's set to started = false before the shutdown is attempted:

SimpleSftpServer.java#L129-L136

public void stop() {
    try {
        started = false;
        sshd.stop(true);
    } catch (Exception e) {
        Log.e("SFTP", "Exception while stopping the server", e);
    }
}

In any case, the result seems to be that sshfs is covering up the fact that the Android app is not properly shutting down ports/servers. I guess we could take advantage of that too by using the same regex to match existing mounts and adopt them, instead of considering them "stale" and unmounting them.

Whether that's a good idea or not though, I'm not sure of since I'm unfamiliar with when plugins are actually destroyed in the Android app. That's the only time the SFTP server should be stopped, but I seem to recall Albert saying plugins are only constructed once.

ferdnyc commented 4 years ago

It could even be a concurrency issue, perhaps. You'd want two servers to be able to run concurrently on different ports, to service two different paired devices both connected at once. But there's nothing there that I can see (mutexes or synchronization points) which would protect against two servers running for the same device, either. Or that would ensure the first server is stopped, before starting a second.

(I don't think so, anyway. I'm on my phone and discussing code I don't know well from memory, which is probably stupid.)

andyholmes commented 4 years ago

(I don't think so, anyway. I'm on my phone and discussing code I don't know well from memory, which is probably stupid.)

Yeah, I never do that :angel: #CodeInBed

Anyways, I've been playing with this a bit today and maybe this is the way to (re: my previous comment). There are some catches, but I think the catches might actually be a boon.

We can just watch the global GVolumeMonitor for ::mount-added and ::mount-removed instead of looping through the GVolumeMonitor after a successful mount operation for the specific mount we were told exists. Conversely, before we even attempt a mount operation, we can loop for an existing mount that matches sftp://host:[1739-1764] and just adopt it.

The catch here, is we're basically ignoring the info packet (except for the port when we need it) so we can't really rely on the list of directories. But with the way SAF works, I'm thinking our shell menu should just open the root URI and let the file manager do the mounting/unmounting vis GVfs. This also means I can drop that submenu stuff in the Shell, which is more inline with the "Integrate with GNOME" motto.

ferdnyc commented 4 years ago

I gave the remote mount a try again today, for the first time in a while — just from an enduser perspective.

I'm starting to think they may be using the ServerAlive and auto-reconnect features of sshfs to paper over a lot of connection flakiness coming from the Android side. No idea whether that's their server implementation, or (more likely) just the nature of the beast when running services from user-space app code on Android. But, using Gvfs, all of that flakiness is unfortunately laid bare.

The Gio mount for the Android filesystem goes up and down constantly. It will bounce in a matter of seconds after becoming idle. (With the mount entry in Nautilus' sidebar flickering every time, and open windows to any directory on the device filesystem getting thrown back to the local Home.)

And even when it's actively in use, that's not enough to completely protect it from drops or resets. I had a gio tree sftp://[host]:[port] running in a shell just now, and it slogged through a bunch of the remote tree OK, but eventually it reached a point where the SFTP mount dropped off entirely. (Probably where my phone screenlocked, though I can't be sure because I have it set so it won't even turn off the screen while I have it plugged in to charge.)

Which caused the gio tree run to end with a quick barrage of failures:

$ gio tree sftp://192.168.12.102:1739
sftp://192.168.12.102:1739/
`-- primary
    |-- Alarms
    |-- Android
    |   |-- data
    |   |   |-- air.com.bartbonte.yellow
    |   |   |   `-- files
    |   |   |-- com.adobe.reader
    |   |   |   `-- files
    |   |   |       |-- Download
    |   |   |       `-- Downloads
    |   |   |-- com.alphainventor.filemanager
    |   |   |   |-- cache
    |   |   |   |   |-- archive-edit
    |   |   |   |   |-- archive-tmp
    |   |   |   |   |   |-- 1579631850952
[ -- 8< -- ]
    |   |   |-- net.darksky.darksky
    |   |   |       [The specified location is not mounted]
    |   |   |-- org.connectbot
    |   |   |       [The specified location is not mounted]
    |   |   |-- org.mozilla.firefox
    |   |   |       [The specified location is not mounted]
    |   |   `-- ru.andr7e.sensortest
    |   |           [The specified location is not mounted]
    |   |-- media
    |   |       [The specified location is not mounted]
    |   `-- obb
    |           [The specified location is not mounted]
    |-- DCIM
    |       [The specified location is not mounted]
    |-- Documents
    |       [The specified location is not mounted]
    |-- Download
    |       [The specified location is not mounted]
    |-- Movies
    |       [The specified location is not mounted]
    |-- Music
    |       [The specified location is not mounted]
    |-- Notifications
    |       [The specified location is not mounted]
    |-- Pictures
    |       [The specified location is not mounted]
    |-- Samsung
    |       [The specified location is not mounted]
    `-- smvvm
            [The specified location is not mounted]

Never even made it through the Android/data directory.

Which kind of sucks, though not even as hard as the constant flakiness when idle. That really sucks, because it makes the SFTP service pretty unusable... and I'm not sure there's really anything we can do about that, with Gvfs driving things. It doesn't appear to support a mode of operation that would be closer to the way KDE Connect configures sshfs: One where it would not react to any network connection disruption, even a momentary one, as a complete disconnection of the remote filesystem — causing it to invalidate the mount, as well as any active browsing sessions referring to it.

andyholmes commented 4 years ago

Oops, I didn't see your comment hours ago for some reason. I've just a WIP branch as sftp-refactor which seems to improve the situation somewhat.

At least there are fewer cases where we implicitly disconnect the remote volume like when the device packet connection breaks or if we encounter an existing mount. I haven't actually done any file transfer testing, but this might allow Gvfs to reconnect gracefully. I haven't looked at the Gvfs SFTP backend in over year though, so I'm not sure if it will do that.

EDIT: I think this will merge gracefully over master or gnome-3-36, or at least that's what I tried for. I guess I should think about just merging what's in gnome-3-36 pretty soon here anyways.

andyholmes commented 4 years ago

I've rebased the above branch on master, so it probably needs to be rebased on gnome-3-34 for users that haven't updated yet.

The bad news is there seems to be a significant flaw with this approach. kdeconnect-android seems to rely on the remote client to explicitly disconnect, otherwise it may never drop ports. Either that or it really just doesn't ever drop or reuse ports of it's own accord as I keep hitting this on the Android side:

03-08 17:01:14.529 24362   889 E SftpServer: No more ports available

This also results in any other transfers failing as all ports become owned and leaked by the SFTP server :/

alexanderadam commented 4 years ago

I'm also having issues.

kdeconnect-android seems to rely on the remote client to explicitly disconnect, otherwise it may never drop ports.

Is gsconnect explicitly doing this before the computer is going to suspend / hibernation or disconnecting/changing networks?

PS: Thank you so much for your work! GSConnect feels pretty great!

andyholmes commented 4 years ago

Is gsconnect explicitly doing this before the computer is going to suspend / hibernation or disconnecting/changing networks?

Currently GSConnect does this whenever we mount a new location we're told is available by Android. In #831 a different approach is taken, but this patch needs testing before I'll merge it.

pranav083 commented 3 years ago

Yeah, then something funky is happening on the Android side. Without much work I can get this to happen:

$ gio mount -l
<snip>
Mount(0): 192.168.0.68 -> sftp://192.168.0.68:1748/
  Type: GDaemonMount
Mount(1): 192.168.0.68 -> sftp://192.168.0.68:1747/
  Type: GDaemonMount

Screenshot from 2020-03-05 16-27-00

Strictly speaking this shouldn't be possible, because the Sftp server should only be stopped if the plugin is destroyed and if it's not stopped it shouldn't grab a new port. It's possible the server is not properly shutting down, since it's set to started = false before the shutdown is attempted:

SimpleSftpServer.java#L129-L136

public void stop() {
    try {
        started = false;
        sshd.stop(true);
    } catch (Exception e) {
        Log.e("SFTP", "Exception while stopping the server", e);
    }
}

In any case, the result seems to be that sshfs is covering up the fact that the Android app is not properly shutting down ports/servers. I guess we could take advantage of that too by using the same regex to match existing mounts and adopt them, instead of considering them "stale" and unmounting them.

Whether that's a good idea or not though, I'm not sure of since I'm unfamiliar with when plugins are actually destroyed in the Android app. That's the only time the SFTP server should be stopped, but I seem to recall Albert saying plugins are only constructed once.

I am facing the same problem on GSConnect v44 but when i ran the given command gio mount -lin reply it gets the system connected automatically. I did not able to know what exactly had happened and how to fix the problem permanently.
As i am able to send file from my system to mobile normally but form the android side i was not to send any file. And on my system the mount option doesnot have any effect (as it does not show any thing).