Skip to content

Commit

Permalink
Merge pull request #205 from Xavron/scoped_storage_simplified
Browse files Browse the repository at this point in the history
scoped storage fixes and improvements
  • Loading branch information
ppareit authored Jul 19, 2024
2 parents 6a94272 + 6588c49 commit 2a33961
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 456 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ private void tryToUpgradeToScopedStorage(Uri treeUri) {
if (!Util.useScopedStorage()) {
DocumentFile df = FileUtil.getDocumentFileFromUri(treeUri);
if (df == null) return;
final String a11Path = FileUtil.getUriStoragePathFullFromDocumentFile(df, "");
final String a11Path = FileUtil.getFileTypePathFromDocumentFile(df);
if (a11Path == null) return;
File root = new File(a11Path);
if (!root.canRead() || !root.canWrite()) {
Expand All @@ -370,7 +370,7 @@ private void scopedStorageChrootOverride(Uri treeUri) {
if (Util.useScopedStorage()) {
DocumentFile df = FileUtil.getDocumentFileFromUri(treeUri);
if (df == null) return;
final String scopedStoragePath = FileUtil.getUriStoragePathFullFromDocumentFile(df, "");
final String scopedStoragePath = FileUtil.getFileTypePathFromDocumentFile(df);
if (scopedStoragePath == null) return;
List<FtpUser> userList = FsSettings.getUsers();
for (int i = 0; i < userList.size(); i++) {
Expand Down
42 changes: 28 additions & 14 deletions app/src/main/java/be/ppareit/swiftp/server/CmdAbstractStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,21 +85,32 @@ public void doStorOrAppe(String param, boolean append) {
}
}

if (!storeFile.exists()) {
if (Util.useScopedStorage()) {
final String mime = "application/octet-stream";
//final URI fileUri = storeFile.toURI();
//final URL url = fileUri.toURL();
//mime = url.openConnection().getContentType(); // sometimes makes exe's into html files
docStoreFile = FileUtil.mkfile(storeFile, App.getAppContext(), mime);
if(docStoreFile == null){
errString = "451 Couldn't open file \"" + param + "\" aka \""
+ storeFile.getCanonicalPath() + "\" for writing\r\n";
if (Util.useScopedStorage()) {
final String mime = "application/octet-stream";
// Fix: REST needs check of exists here. sessionThread.offset must be checked
// as correct before using it as it can be wrong during connection issues. As
// it is not at this time, do not use it here.
if (!append && !storeFile.exists()) {
if (storeFile.exists()) {
errString = "451 Couldn't truncate file\r\n";
break storing;
}
docStoreFile = FileUtil.mkfile(storeFile, mime);
} else {
FileUtil.mkfile(storeFile, App.getAppContext());
docStoreFile = FileUtil.getDocumentFile(storeFile.getPath());
}
if (docStoreFile == null || !docStoreFile.exists()) {
errString = "451 Couldn't open file \"" + param + "\" aka \""
+ storeFile.getCanonicalPath() + "\" for writing\r\n";
break storing;
}
// Fix: Do not allow this situation to pass.
if (sessionThread.offset > 0 && docStoreFile.length() == 0) {
errString = "554 restart failure: bad offset\r\n";
break storing;
}
} else {
FileUtil.mkfile(storeFile, App.getAppContext());
}

if (docStoreFile != null) {
Expand Down Expand Up @@ -200,14 +211,17 @@ public void doStorOrAppe(String param, boolean append) {
}
}
try {
if (out != null) {
out.close();
}
if (os != null) {
os.close();
}
} catch (IOException ignored) {
}
try {
if (out != null) {
out.close();
}
} catch (IOException ignored) {
}

if (errString != null) {
Cat.i("STOR error: " + errString.trim());
Expand Down
25 changes: 8 additions & 17 deletions app/src/main/java/be/ppareit/swiftp/server/CmdDELE.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@

package be.ppareit.swiftp.server;

import java.io.File;

import android.util.Log;

import androidx.documentfile.provider.DocumentFile;

import java.io.File;

import be.ppareit.swiftp.App;
import be.ppareit.swiftp.MediaUpdater;
import be.ppareit.swiftp.Util;
import be.ppareit.swiftp.utils.FileUtil;
import be.ppareit.swiftp.MediaUpdater;

public class CmdDELE extends FtpCmd implements Runnable {
private static final String TAG = CmdDELE.class.getSimpleName();
Expand All @@ -48,31 +48,22 @@ public void run() {
sessionThread.getWorkingDir(), param);

if (Util.useScopedStorage()) {
String clientPath;
final String sfPath = storeFile.getPath();
if (sfPath.contains(File.separator)) {
clientPath = sfPath.substring(0, sfPath.lastIndexOf(File.separator));
} else {
clientPath = sfPath;
}
DocumentFile docStoreFile = FileUtil.getDocumentFileWithParamScopedStorage(File.separator +
param, null, clientPath);
tryToDelete(new FileUtil.Gen(docStoreFile), clientPath);
DocumentFile docStoreFile = FileUtil.getDocumentFile(storeFile.getPath());
tryToDelete(new FileUtil.Gen(docStoreFile));
return;
}

tryToDelete(new FileUtil.Gen(storeFile), param);
tryToDelete(new FileUtil.Gen(storeFile));
}

private void tryToDelete(FileUtil.Gen storeFile, String param) {
private void tryToDelete(FileUtil.Gen storeFile) {
String errString = null;
if (storeFile == null || storeFile.getOb() == null) {
errString = "550 Invalid name or chroot violation\r\n";
} else {
final boolean isDocumentFile = storeFile.getOb() instanceof DocumentFile;
final boolean isFile = !isDocumentFile;
final String path = FileUtil.getScopedClientPath(param, null, null);
if ((isDocumentFile && violatesChroot((DocumentFile) storeFile.getOb(), path))
if ((isDocumentFile && violatesChroot((DocumentFile) storeFile.getOb()))
|| (isFile && violatesChroot((File) storeFile.getOb()))) {
errString = "550 Invalid name or chroot violation\r\n";
} else if (storeFile.isDirectory()) {
Expand Down
45 changes: 32 additions & 13 deletions app/src/main/java/be/ppareit/swiftp/server/CmdHASH.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package be.ppareit.swiftp.server;

import androidx.documentfile.provider.DocumentFile;

import net.vrallev.android.cat.Cat;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import be.ppareit.swiftp.App;
import be.ppareit.swiftp.utils.FileUtil;


/**
* CmdHASH provides a method to verify the integrity of a transferred file or to compare two files
Expand All @@ -34,45 +40,55 @@ public void run() {
{
fileToHash = inputPathToChrootedFile(sessionThread.getChrootDir(),
sessionThread.getWorkingDir(), param);
if (violatesChroot(fileToHash)) {

FileUtil.Gen gen = FileUtil.createGenFromFile(fileToHash);
final boolean isDocumentFile = gen.getOb() instanceof DocumentFile;
final boolean isFile = !isDocumentFile;

if (isFile && violatesChroot((File) gen.getOb())
|| isDocumentFile && violatesChroot((DocumentFile) gen.getOb())) {
errString = "550 Invalid name or chroot violation\r\n";
break mainblock;
} else if (fileToHash.isDirectory()) {
} else if (gen.isDirectory()) {
Cat.d("Ignoring HASH for directory");
errString = "553 Can't HASH a directory\r\n";
break mainblock;
} else if (!fileToHash.exists()) {
Cat.d("Can't HASH nonexistent file: " + fileToHash.getAbsolutePath());
} else if (!gen.exists()) {
Cat.d("Can't HASH nonexistent file: " + gen.getAbsolutePath());
errString = "550 File does not exist\r\n";
break mainblock;
} else if (!fileToHash.canRead()) {
} else if (!gen.canRead()) {
Cat.i("Failed HASH permission (canRead() is false)");
errString = "556 No read permissions\r\n";
break mainblock;
}

FileInputStream in = null;
InputStream is = null;
try {
String algorithm = sessionThread.getHashingAlgorithm();
MessageDigest md = MessageDigest.getInstance(algorithm);
byte[] buffer = new byte[SessionThread.DATA_CHUNK_SIZE];
in = new FileInputStream(fileToHash);
if (isFile) in = new FileInputStream((File) gen.getOb());
else
is = App.getAppContext().getContentResolver().openInputStream(((DocumentFile) gen.getOb()).getUri());

long offset = 0L;
long endPosition = fileToHash.length() - 1;
long endPosition = gen.length() - 1;
if (sessionThread.offset >= 0) {
offset = sessionThread.offset;
if (offset <= sessionThread.endPosition
&& sessionThread.endPosition <= fileToHash.length() - 1) {
&& sessionThread.endPosition <= gen.length() - 1) {
endPosition = sessionThread.endPosition;
}
}

// This is not a range but length (Range 0-0 would still read 0th byte), so +1
long bytesToRead = endPosition - offset + 1;
int bytesRead;
in.skip(offset);
while ((bytesRead = in.read(buffer)) != -1) {
if (isFile) in.skip(offset);
else is.skip(offset);
while ((bytesRead = isFile ? in.read(buffer) : is.read(buffer)) != -1) {
if (bytesRead > bytesToRead) {
md.update(buffer, 0, (int) bytesToRead);
break;
Expand Down Expand Up @@ -101,8 +117,11 @@ public void run() {
break mainblock;
} finally {
try {
if (in != null)
in.close();
if (in != null) in.close();
} catch (IOException ignore) {
}
try {
if (is != null) is.close();
} catch (IOException ignore) {
}
}
Expand All @@ -112,4 +131,4 @@ public void run() {
}
Cat.d("HASH done");
}
}
}
17 changes: 6 additions & 11 deletions app/src/main/java/be/ppareit/swiftp/server/CmdLIST.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,15 @@

package be.ppareit.swiftp.server;

import android.net.Uri;

import androidx.documentfile.provider.DocumentFile;

import net.vrallev.android.cat.Cat;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import androidx.documentfile.provider.DocumentFile;

import net.vrallev.android.cat.Cat;

import be.ppareit.swiftp.Util;
import be.ppareit.swiftp.utils.FileUtil;

Expand Down Expand Up @@ -68,9 +66,7 @@ public void run() {
if (param.equals("")) {
fileToList = sessionThread.getWorkingDir();
if (Util.useScopedStorage()) {
final String clientPath = fileToList.getPath();
Uri uri = FileUtil.getFullCWDUri("", clientPath);
docFileToList = FileUtil.getDocumentFileFromUri(uri);
docFileToList = FileUtil.getDocumentFile(fileToList.getPath());
}
} else {
if (param.contains("*")) {
Expand All @@ -79,8 +75,7 @@ public void run() {
}
fileToList = new File(sessionThread.getWorkingDir(), param);
if (Util.useScopedStorage()) {
final String clientPath = fileToList.getPath();
docFileToList = FileUtil.getDocumentFileWithParamScopedStorage(param, "", clientPath);
docFileToList = FileUtil.getDocumentFile(fileToList.getPath());
}
if (violatesChroot(fileToList)) {
// sd card should be eg /storage/xxx/
Expand Down
30 changes: 10 additions & 20 deletions app/src/main/java/be/ppareit/swiftp/server/CmdRETR.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,15 @@ public void run() {

DocumentFile docFileToRetr = null;
if (Util.useScopedStorage()) {
String clientPath;
final String ftrPath = fileToRetr.getPath();
if (ftrPath.contains(File.separator)) {
clientPath = ftrPath.substring(0, ftrPath.lastIndexOf(File.separator));
} else {
clientPath = ftrPath;
}
docFileToRetr = FileUtil.getDocumentFileWithParamScopedStorage(param, null, clientPath);
docFileToRetr = FileUtil.getDocumentFile(fileToRetr.getPath());
if (docFileToRetr == null) {
errString = "550 File does not exist\r\n";
break mainblock;
}
if (!docFileToRetr.exists()) {
errString = "550 File does not exist\r\n";
break mainblock;
}
}

FileUtil.Gen gen;
Expand Down Expand Up @@ -114,7 +111,8 @@ public void run() {
long bytesToRead = endPosition - offset + 1;
if (Util.useScopedStorage()) is.skip(offset);
else in.skip(offset);
while ((bytesRead = (Util.useScopedStorage() ? is.read(buffer) : in.read(buffer))) != -1) {
final boolean scoped = Util.useScopedStorage();
while ((bytesRead = (scoped ? is.read(buffer) : in.read(buffer))) != -1) {
boolean success;
if (bytesRead > bytesToRead) {
success = sessionThread.sendViaDataSocket(buffer, 0, (int) bytesToRead);
Expand All @@ -138,7 +136,8 @@ public void run() {
}
// We have to convert all solitary \n to \r\n
boolean lastBufEndedWithCR = false;
while ((bytesRead = (Util.useScopedStorage() ? is.read(buffer) : in.read(buffer))) != -1) {
final boolean scoped = Util.useScopedStorage();
while ((bytesRead = (scoped ? is.read(buffer) : in.read(buffer))) != -1) {
int startPos = 0, endPos = 0;
byte[] crnBuf = {'\r', '\n'};
for (endPos = 0; endPos < bytesRead; endPos++) {
Expand Down Expand Up @@ -207,16 +206,7 @@ private String validate(FileUtil.Gen fileToRetr, String param) {
final boolean isDocumentFile = fileToRetr.getOb() instanceof DocumentFile;
final boolean isFile = !isDocumentFile;

String clientPath;
String path = null;
if (isDocumentFile) {
clientPath = param;
if (!param.contains(File.separator)) clientPath = "";
else if (!param.endsWith(File.separator)) clientPath = param.substring(0, param
.lastIndexOf(File.separator));
path = FileUtil.getScopedClientPath(clientPath, null, null);
}
if ((isDocumentFile && violatesChroot((DocumentFile) fileToRetr.getOb(), path))
if ((isDocumentFile && violatesChroot((DocumentFile) fileToRetr.getOb()))
|| (isFile && violatesChroot((File) fileToRetr.getOb()))) {
errString = "550 Invalid name or chroot violation\r\n";
} else if (fileToRetr.isDirectory()) {
Expand Down
Loading

0 comments on commit 2a33961

Please sign in to comment.