Skip to content

Commit

Permalink
Merge pull request #395 from lmagyar/pr-refactor-filesystem
Browse files Browse the repository at this point in the history
File-system refactor - plus minor bugfixes
  • Loading branch information
wolpi authored Oct 6, 2024
2 parents 8eeeb27 + 493d45e commit f0a38c6
Show file tree
Hide file tree
Showing 45 changed files with 1,017 additions and 901 deletions.
75 changes: 28 additions & 47 deletions primitiveFTPd/src/org/primftpd/filesystem/AbstractFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,30 @@
import java.util.HashMap;
import java.util.Map;

public abstract class AbstractFile {
public abstract class AbstractFile<TFileSystemView extends AbstractFileSystemView> {

protected final Logger logger = LoggerFactory.getLogger(getClass());

protected boolean isDirectory;
protected String absPath;
private final AbstractFileSystemView fileSystemView;

protected String absPath;
protected String name;
protected long lastModified;
protected long size;
protected boolean readable;
protected boolean exists;

protected final PftpdService pftpdService;

public AbstractFile(
TFileSystemView fileSystemView,
String absPath,
String name,
long lastModified,
long size,
boolean readable,
boolean exists,
boolean isDirectory,
PftpdService pftpdService) {
String name) {
this.fileSystemView = fileSystemView;
this.absPath = absPath;
this.name = name;
this.lastModified = lastModified;
this.size = size;
this.readable = readable;
this.exists = exists;
this.isDirectory = isDirectory;
this.pftpdService = pftpdService;
}

protected final TFileSystemView getFileSystemView() {
return (TFileSystemView)fileSystemView;
}

protected final PftpdService getPftpdService() {
return fileSystemView.getPftpdService();
}

public abstract String getClientIp();
Expand All @@ -58,7 +50,7 @@ public void postClientActionError(String error) {
}

public void postClientAction(ClientActionEvent.ClientAction clientAction, String error) {
pftpdService.postClientAction(
getPftpdService().postClientAction(
getClientActionStorage(),
clientAction,
getClientIp(),
Expand All @@ -76,30 +68,15 @@ public String getName() {
return name != null ? name : "<unknown>";
}

public boolean isDirectory() {
logger.trace("[{}] isDirectory() -> {}", name, isDirectory);
return isDirectory;
}
public abstract boolean isDirectory();

public boolean doesExist() {
logger.trace("[{}] doesExist() -> {}", name, exists);
return exists;
}
public abstract boolean doesExist();

public boolean isReadable() {
logger.trace("[{}] isReadable() -> {}", name, readable);
return readable;
}
public abstract boolean isReadable();

public long getLastModified() {
logger.trace("[{}] getLastModified() -> {}", name, lastModified);
return lastModified;
}
public abstract long getLastModified();

public long getSize() {
logger.trace("[{}] getSize() -> {}", name, size);
return size;
}
public abstract long getSize();

public abstract boolean isFile();

Expand All @@ -113,6 +90,8 @@ public long getSize() {

public abstract boolean delete();

public abstract boolean move(AbstractFile destination);

public abstract OutputStream createOutputStream(long offset) throws IOException;

public abstract InputStream createInputStream(long offset) throws IOException;
Expand Down Expand Up @@ -199,16 +178,18 @@ public Map<SshFile.Attribute, Object> getAttributes(boolean followLinks)
}

public boolean isExecutable() {
logger.trace("[{}] isExecutable()", name);
// return directories as executable in order to allow to enter them
// at least we tell clients that they can try
return isDirectory;
boolean result = isDirectory();
logger.trace("[{}] isExecutable() -> {}", name, result);
return result;
}

public boolean create() throws IOException {
logger.trace("[{}] create()", name);
// called e.g. when uploading a new file
return true;
boolean result = true;
logger.trace("[{}] create() -> {}", name, result);
return result;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.primftpd.filesystem;

import org.primftpd.services.PftpdService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class AbstractFileSystemView {

protected final Logger logger = LoggerFactory.getLogger(getClass());

protected final PftpdService pftpdService;

public AbstractFileSystemView(PftpdService pftpdService) {
this.pftpdService = pftpdService;
}

public final PftpdService getPftpdService() {
return pftpdService;
}
}
93 changes: 60 additions & 33 deletions primitiveFTPd/src/org/primftpd/filesystem/FsFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@
import java.util.Map;
import java.util.Set;

public abstract class FsFile<T> extends AbstractFile {
public abstract class FsFile<TMina, TFileSystemView extends FsFileSystemView> extends AbstractFile<TFileSystemView> {

protected final File file;
protected final FsFileSystemView fileSystemView;
protected final boolean injectedDirectory;
protected final boolean isInjectedDirectory;

private final static Map<String, String[]> DIRECTORY_INJECTIONS;
static {
Expand All @@ -49,33 +48,30 @@ public abstract class FsFile<T> extends AbstractFile {
INJECTIONS_AND_CHILDREN = Collections.unmodifiableSet(tmp);
}

public FsFile(File file, PftpdService pftpdService, FsFileSystemView fileSystemView) {
public FsFile(TFileSystemView fileSystemView, File file) {
super(
fileSystemView,
file.getAbsolutePath(),
file.getName(),
fileSystemView.getCorrectedTime(file.getAbsolutePath(), file.lastModified()),
file.length(),
file.canRead(),
file.exists(),
file.isDirectory(),
pftpdService);
file.getName());
this.file = file;
this.name = file.getName();
this.fileSystemView = fileSystemView;
this.injectedDirectory = file.isDirectory() && INJECTIONS_AND_CHILDREN.contains(file.getAbsolutePath());
this.isInjectedDirectory = file.isDirectory() && INJECTIONS_AND_CHILDREN.contains(file.getAbsolutePath());
}

protected final MediaScannerClient getMediaScannerClient() {
return getFileSystemView().getMediaScannerClient();
}

protected abstract T createFile(File file, PftpdService pftpdService);
protected abstract TMina createFile(File file);

@Override
public ClientActionEvent.Storage getClientActionStorage() {
return ClientActionEvent.Storage.FS;
}

public boolean isFile() {
boolean isFile = file.isFile();
logger.trace("[{}] isFile() -> {}", name, isFile);
return isFile;
public boolean isDirectory() {
boolean result = file.isDirectory();
logger.trace("[{}] isDirectory() -> {}", name, result);
return result;
}

public boolean doesExist() {
Expand Down Expand Up @@ -107,9 +103,27 @@ public boolean doesExist() {
}

public boolean isReadable() {
boolean canRead = injectedDirectory || file.canRead();
logger.trace("[{}] isReadable() -> {}", name, canRead);
return canRead;
boolean result = isInjectedDirectory || file.canRead();
logger.trace("[{}] isReadable() -> {}", name, result);
return result;
}

public long getLastModified() {
long result = getFileSystemView().getCorrectedTime(file.getAbsolutePath(), file.lastModified());
logger.trace("[{}] getLastModified() -> {}", name, result);
return result;
}

public long getSize() {
long result = file.length();
logger.trace("[{}] getSize() -> {}", name, result);
return result;
}

public boolean isFile() {
boolean result = file.isFile();
logger.trace("[{}] isFile() -> {}", name, result);
return result;
}

public boolean isWritable() {
Expand All @@ -126,7 +140,7 @@ public boolean isWritable() {
}

// file does not exist, probably an upload of a new file, check parent
// must be done in loop as some clients to not issue mkdir commands
// must be done in loop as some clients do not issue mkdir commands
// like filezilla
File parent = file.getParentFile();
while (parent != null) {
Expand All @@ -146,33 +160,43 @@ public boolean isRemovable() {

public boolean setLastModified(long time) {
logger.trace("[{}] setLastModified({})", name, Long.valueOf(time));
long correctedTime = fileSystemView.getCorrectedTime(absPath, time);
long correctedTime = getFileSystemView().getCorrectedTime(file.getAbsolutePath(), time);
return file.setLastModified(correctedTime);
}

public boolean mkdir() {
logger.trace("[{}] mkdir()", name);
postClientAction(ClientActionEvent.ClientAction.CREATE_DIR);
return file.mkdir();
// may be necessary to create dirs
// some clients do not issue mkdir commands like filezilla
// see isWritable()
return file.mkdirs();
}

public boolean delete() {
logger.trace("[{}] delete()", name);
postClientAction(ClientActionEvent.ClientAction.DELETE);
return file.delete();
boolean success = file.delete();
if (success) {
getMediaScannerClient().scanFile(file.getAbsolutePath());
}
return success;
}

public boolean move(FsFile<T> destination) {
public boolean move(AbstractFile destination) {
logger.trace("[{}] move({})", name, destination.getAbsolutePath());
postClientAction(ClientActionEvent.ClientAction.RENAME);
boolean success = file.renameTo(new File(destination.getAbsolutePath()));
if (success) {
Utils.mediaScanFile(pftpdService.getContext(), getAbsolutePath());
// remove old file location
getMediaScannerClient().scanFile(file.getAbsolutePath());
// add new file location
getMediaScannerClient().scanFile(destination.getAbsolutePath());
}
return success;
}

public List<T> listFiles() {
public List<TMina> listFiles() {
logger.trace("[{}] listFiles()", name);
postClientAction(ClientActionEvent.ClientAction.LIST_DIR);
File[] filesArray = file.listFiles();
Expand All @@ -190,9 +214,9 @@ public List<T> listFiles() {
}

if (filesArray != null) {
List<T> files = new ArrayList<>(filesArray.length);
List<TMina> files = new ArrayList<>(filesArray.length);
for (File file : filesArray) {
files.add(createFile(file, pftpdService));
files.add(createFile(file));
}
return files;
}
Expand All @@ -205,10 +229,13 @@ public OutputStream createOutputStream(long offset) throws IOException {
postClientAction(ClientActionEvent.ClientAction.UPLOAD);

// may be necessary to create dirs
// some clients do not issue mkdir commands like filezilla
// see isWritable()
File parent = file.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
if (!parent.mkdirs()) {
throw new IOException(String.format("Failed to create parent folder(s) '%s'", file.getAbsolutePath()));
}
}

// now create out stream
Expand Down Expand Up @@ -236,7 +263,7 @@ public void close() throws IOException {
@Override
public void close() throws IOException {
super.close();
Utils.mediaScanFile(pftpdService.getContext(), getAbsolutePath());
getMediaScannerClient().scanFile(file.getAbsolutePath());
}
};
}
Expand Down
22 changes: 13 additions & 9 deletions primitiveFTPd/src/org/primftpd/filesystem/FsFileSystemView.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,36 @@

import java.io.File;

public abstract class FsFileSystemView<T extends FsFile<X>, X> {
public abstract class FsFileSystemView<TFile extends FsFile<TMina, ? extends FsFileSystemView>, TMina> extends AbstractFileSystemView {

protected final Logger logger = LoggerFactory.getLogger(getClass());
private final MediaScannerClient mediaScannerClient;
private final String safVolumePath;
private final int safTimeResolution;
protected final PftpdService pftpdService;

protected abstract T createFile(File file, PftpdService pftpdService);
protected abstract TFile createFile(File file);

protected abstract String absolute(String file);

public FsFileSystemView(Context context, Uri safStartUrl, PftpdService pftpdService) {
public FsFileSystemView(PftpdService pftpdService, Uri safStartUrl) {
super(pftpdService);
this.mediaScannerClient = new MediaScannerClient(pftpdService.getContext());
this.safTimeResolution = StorageManagerUtil.getFilesystemTimeResolutionForTreeUri(safStartUrl);
this.safVolumePath = safTimeResolution != 1 ? StorageManagerUtil.getVolumePathFromTreeUri(safStartUrl, context) : null;
this.pftpdService = pftpdService;
this.safVolumePath = safTimeResolution != 1 ? StorageManagerUtil.getVolumePathFromTreeUri(safStartUrl, pftpdService.getContext()) : null;
}

public final MediaScannerClient getMediaScannerClient() {
return mediaScannerClient;
}

public long getCorrectedTime(String abs, long time) {
int timeResolution = safVolumePath != null && abs.startsWith(safVolumePath) ? safTimeResolution : 1;
return (time / timeResolution) * timeResolution;
}

public T getFile(String file) {
public TFile getFile(String file) {
logger.trace("getFile({})", file);
String abs = absolute(file);
logger.trace(" getFile(abs: {})", abs);
return createFile(new File(abs), pftpdService);
return createFile(new File(abs));
}
}
Loading

0 comments on commit f0a38c6

Please sign in to comment.