mirror of
https://github.com/cryptomator/cryptomator.git
synced 2025-02-18 17:17:50 +00:00
reinitate workaround for MOUNT_WITHIN_PARENT
This commit is contained in:
parent
47a32893f0
commit
23060a8497
@ -0,0 +1,8 @@
|
||||
package org.cryptomator.common.mount;
|
||||
|
||||
public class MountPointPreparationException extends RuntimeException {
|
||||
|
||||
public MountPointPreparationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
package org.cryptomator.common.mount;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryNotEmptyException;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.NotDirectoryException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public final class MountWithinParentUtil {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Mounter.class);
|
||||
private static final String HIDEAWAY_PREFIX = ".~$";
|
||||
private static final String HIDEAWAY_SUFFIX = ".tmp";
|
||||
private static final String WIN_HIDDEN_ATTR = "dos:hidden";
|
||||
|
||||
private MountWithinParentUtil() {}
|
||||
|
||||
static void prepareParentNoMountPoint(Path mountPoint) throws MountPointPreparationException {
|
||||
Path hideaway = getHideaway(mountPoint);
|
||||
var mpExists = Files.exists(mountPoint, LinkOption.NOFOLLOW_LINKS);
|
||||
var hideExists = Files.exists(hideaway, LinkOption.NOFOLLOW_LINKS);
|
||||
|
||||
//TODO: possible improvement by just deleting an _empty_ hideaway
|
||||
if (mpExists && hideExists) { //both resources exist (whatever type)
|
||||
throw new MountPointPreparationException(new FileAlreadyExistsException(hideaway.toString()));
|
||||
} else if (!mpExists && !hideExists) { //neither mountpoint nor hideaway exist
|
||||
throw new MountPointPreparationException(new NoSuchFileException(mountPoint.toString()));
|
||||
} else if (!mpExists) { //only hideaway exists
|
||||
checkIsDirectory(hideaway);
|
||||
LOG.info("Mountpoint {} seems to be not properly cleaned up. Will be fixed on unmount.", mountPoint);
|
||||
try {
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MountPointPreparationException(e);
|
||||
}
|
||||
} else { //only mountpoint exists
|
||||
try {
|
||||
checkIsDirectory(mountPoint);
|
||||
checkIsEmpty(mountPoint);
|
||||
|
||||
Files.move(mountPoint, hideaway);
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
Files.setAttribute(hideaway, WIN_HIDDEN_ATTR, true, LinkOption.NOFOLLOW_LINKS);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new MountPointPreparationException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cleanup(Path mountPoint) {
|
||||
Path hideaway = getHideaway(mountPoint);
|
||||
try {
|
||||
waitForMountpointRestoration(mountPoint);
|
||||
Files.move(hideaway, mountPoint);
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
Files.setAttribute(mountPoint, WIN_HIDDEN_ATTR, false);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.error("Unable to restore hidden directory to mountpoint {}.", mountPoint, e);
|
||||
}
|
||||
}
|
||||
|
||||
//on Windows removing the mountpoint takes some time, so we poll for at most 3 seconds
|
||||
private static void waitForMountpointRestoration(Path mountPoint) throws FileAlreadyExistsException {
|
||||
int attempts = 0;
|
||||
while (!Files.notExists(mountPoint, LinkOption.NOFOLLOW_LINKS)) {
|
||||
attempts++;
|
||||
if (attempts >= 5) {
|
||||
throw new FileAlreadyExistsException("Timeout waiting for mountpoint cleanup for " + mountPoint + " .");
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(300);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new FileAlreadyExistsException("Interrupted before mountpoint " + mountPoint + " was cleared");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkIsDirectory(Path toCheck) throws MountPointPreparationException {
|
||||
if (!Files.isDirectory(toCheck, LinkOption.NOFOLLOW_LINKS)) {
|
||||
throw new MountPointPreparationException(new NotDirectoryException(toCheck.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkIsEmpty(Path toCheck) throws MountPointPreparationException, IOException {
|
||||
try (var dirStream = Files.list(toCheck)) {
|
||||
if (dirStream.findFirst().isPresent()) {
|
||||
throw new MountPointPreparationException(new DirectoryNotEmptyException(toCheck.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//visible for testing
|
||||
static Path getHideaway(Path mountPoint) {
|
||||
return mountPoint.resolveSibling(HIDEAWAY_PREFIX + mountPoint.getFileName().toString() + HIDEAWAY_SUFFIX);
|
||||
}
|
||||
|
||||
}
|
@ -50,7 +50,7 @@ public class Mounter {
|
||||
this.vaultSettings = vaultSettings;
|
||||
}
|
||||
|
||||
MadePreparations prepare() throws IOException {
|
||||
Runnable prepare() throws IOException {
|
||||
for (var capability : service.capabilities()) {
|
||||
switch (capability) {
|
||||
case FILE_SYSTEM_NAME -> builder.setFileSystemName("cryptoFs");
|
||||
@ -67,13 +67,13 @@ public class Mounter {
|
||||
return prepareMountPoint();
|
||||
}
|
||||
|
||||
private MadePreparations prepareMountPoint() throws IOException {
|
||||
private Runnable prepareMountPoint() throws IOException {
|
||||
Runnable cleanup = () -> {};
|
||||
var userChosenMountPoint = vaultSettings.getMountPoint();
|
||||
var defaultMountPointBase = env.getMountPointsDir().orElseThrow();
|
||||
var canMountToDriveLetter = service.hasCapability(MOUNT_AS_DRIVE_LETTER);
|
||||
var canMountToParent = service.hasCapability(MOUNT_WITHIN_EXISTING_PARENT);
|
||||
var canMountToDir = service.hasCapability(MOUNT_TO_EXISTING_DIR);
|
||||
boolean mountWithinCustomParent = false;
|
||||
|
||||
if (userChosenMountPoint == null) {
|
||||
if (service.hasCapability(MOUNT_TO_SYSTEM_CHOSEN_PATH)) {
|
||||
@ -89,9 +89,12 @@ public class Mounter {
|
||||
builder.setMountpoint(mountPoint);
|
||||
}
|
||||
} else {
|
||||
mountWithinCustomParent = canMountToParent && !canMountToDir;
|
||||
if (mountWithinCustomParent) {
|
||||
// TODO: move the mount point away in case of MOUNT_WITHIN_EXISTING_PARENT
|
||||
if (canMountToParent && !canMountToDir) {
|
||||
MountWithinParentUtil.prepareParentNoMountPoint(userChosenMountPoint);
|
||||
cleanup = () -> {
|
||||
System.out.println("CLEANUP");
|
||||
MountWithinParentUtil.cleanup(userChosenMountPoint);
|
||||
};
|
||||
}
|
||||
try {
|
||||
builder.setMountpoint(userChosenMountPoint);
|
||||
@ -104,17 +107,12 @@ public class Mounter {
|
||||
//mountpoint must exist
|
||||
throw new MountPointNotExistsException(e.getMessage());
|
||||
} else {
|
||||
//TODO: if (!canMountToDir && canMountToParent && !Files.notExists(userChosenMountPoint)) {
|
||||
throw new IllegalMountPointException(e.getMessage());
|
||||
}
|
||||
/*
|
||||
//TODO:
|
||||
if (!canMountToDir && canMountToParent && !Files.notExists(userChosenMountPoint)) {
|
||||
//parent must exist, mountpoint must not exist
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
return new MadePreparations(mountWithinCustomParent);
|
||||
return cleanup;
|
||||
}
|
||||
|
||||
}
|
||||
@ -123,22 +121,11 @@ public class Mounter {
|
||||
var mountService = this.mountServiceObservable.getValue().service();
|
||||
var builder = mountService.forFileSystem(cryptoFsRoot);
|
||||
var internal = new SettledMounter(mountService, builder, vaultSettings);
|
||||
var preps = internal.prepare();
|
||||
return new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), preps.mountWithinCustomParent);
|
||||
var cleanup = internal.prepare();
|
||||
return new MountHandle(builder.mount(), mountService.hasCapability(UNMOUNT_FORCED), cleanup);
|
||||
}
|
||||
|
||||
public void cleanup(MountHandle handle) {
|
||||
if(handle.mountWithinCustomParent) {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public record MountHandle(Mount mountObj, boolean supportsUnmountForced, boolean mountWithinCustomParent) {
|
||||
|
||||
}
|
||||
|
||||
private record MadePreparations(boolean mountWithinCustomParent) {
|
||||
public record MountHandle(Mount mountObj, boolean supportsUnmountForced, Runnable specialCleanup) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -173,9 +173,7 @@ public class Vault {
|
||||
|
||||
try {
|
||||
mountHandle.mountObj().close();
|
||||
if(mountHandle.mountWithinCustomParent()) {
|
||||
mounter.cleanup(mountHandle);
|
||||
}
|
||||
mountHandle.specialCleanup().run();
|
||||
} finally {
|
||||
destroyCryptoFileSystem();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user