Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public class JavaNIODeviceContext extends DiskDeviceContext {
// Large file size, require special processing for deletes/truncates
private long m_largeFileSize = DefaultLargeFileSize;

// Whether the underlying file system can be assumed to be case-insensitive
private boolean m_caseInsensitiveDisk = false;

/**
* Class constructor
*
Expand Down Expand Up @@ -144,6 +147,10 @@ else if ( m_trashDir.isFile())
m_trashDir = trashDir;
}

// Check if we can assume a case-insensitive file system
if (args.getChild("DiskIsCaseInsensitive") != null)
m_caseInsensitiveDisk = true;

// Check if debug output is enabled
if ( args.getChild( "Debug") != null)
setDebug( true);
Expand Down Expand Up @@ -195,4 +202,14 @@ protected final File getTrashFolder() {
protected final long getLargeFileSize() {
return m_largeFileSize;
}

/**
* Whether the underlying disk is assumed to be case-insensitive, allowing
* certain performance optimizations to be made
*
* @return boolean
*/
protected final boolean getDiskIsCaseInsensitive() {
return m_caseInsensitiveDisk;
}
}
165 changes: 84 additions & 81 deletions src/main/java/org/filesys/smb/server/disk/JavaNIODiskDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.attribute.FileTime;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.concurrent.Executor;
Expand Down Expand Up @@ -221,8 +222,8 @@ public NetworkFile createFile(SrvSession sess, TreeConnection tree, FileOpenPara
throws java.io.IOException {

// Get the full path for the new file
DeviceContext ctx = tree.getContext();
Path newPath = Paths.get( mapPath( ctx.getDeviceName(), params.getPath()));
JavaNIODeviceContext ctx = (JavaNIODeviceContext) tree.getContext();
Path newPath = Paths.get(mapPath(ctx.getDeviceName(), params.getPath(), ctx.getDiskIsCaseInsensitive()));

// Check if the file already exists
if ( Files.exists( newPath, LinkOption.NOFOLLOW_LINKS))
Expand Down Expand Up @@ -267,7 +268,7 @@ public void deleteDirectory(SrvSession sess, TreeConnection tree, String dir)
throws java.io.IOException {

// Get the full path for the directory
DeviceContext ctx = tree.getContext();
JavaNIODeviceContext ctx = (JavaNIODeviceContext) tree.getContext();
Path dirPath = Paths.get( FileName.buildPath(ctx.getDeviceName(), dir, null, java.io.File.separatorChar));

// Check if the directory exists, and it is a directory
Expand All @@ -286,7 +287,7 @@ public void deleteDirectory(SrvSession sess, TreeConnection tree, String dir)
else if ( Files.exists( dirPath) == false) {

// Map the path to a real path
String mappedPath = mapPath(ctx.getDeviceName(), dir);
String mappedPath = mapPath(ctx.getDeviceName(), dir, ctx.getDiskIsCaseInsensitive());

if (mappedPath != null) {

Expand Down Expand Up @@ -390,7 +391,7 @@ public void run() {
public FileStatus fileExists(SrvSession sess, TreeConnection tree, String name) {

// Get the full path for the file
DeviceContext ctx = tree.getContext();
JavaNIODeviceContext ctx = (JavaNIODeviceContext) tree.getContext();
Path filePath = Paths.get( FileName.buildPath(ctx.getDeviceName(), name, null, java.io.File.separatorChar));

if ( Files.exists( filePath, LinkOption.NOFOLLOW_LINKS)) {
Expand All @@ -404,7 +405,7 @@ public FileStatus fileExists(SrvSession sess, TreeConnection tree, String name)

// Map the path, and re-check
try {
String mappedPath = mapPath(ctx.getDeviceName(), name);
String mappedPath = mapPath(ctx.getDeviceName(), name, ctx.getDiskIsCaseInsensitive());

if ( mappedPath != null) {
filePath = Paths.get( mappedPath);
Expand Down Expand Up @@ -454,7 +455,7 @@ public FileInfo getFileInformation(SrvSession sess, TreeConnection tree, String
throws java.io.IOException {

// Get the full path for the file/directory
DeviceContext ctx = tree.getContext();
JavaNIODeviceContext ctx = (JavaNIODeviceContext) tree.getContext();
String path = FileName.buildPath(ctx.getDeviceName(), name, null, java.io.File.separatorChar);

// Build the file information for the file/directory
Expand All @@ -464,7 +465,7 @@ public FileInfo getFileInformation(SrvSession sess, TreeConnection tree, String
return info;

// Try and map the path to a real path
String mappedPath = mapPath(ctx.getDeviceName(), name);
String mappedPath = mapPath(ctx.getDeviceName(), name, ctx.getDiskIsCaseInsensitive());
if (mappedPath != null)
return buildFileInformation(mappedPath, name);

Expand Down Expand Up @@ -494,30 +495,33 @@ public boolean isReadOnly(SrvSession sess, DeviceContext ctx)
}

/**
* Map the input path to a real path, this may require changing the case of various parts of the
* path.
* Map the input path to a real path, this may require changing the case of
* various parts of the path.
*
* @param path Share relative path
* @param path Share relative path
* @param caseInsensitive boolean
* @return Real path on the local filesystem
* @exception FileNotFoundException The path could not be mapped to a real path.
* @exception PathNotFoundException Part of the path is not valid
*/
protected final String mapPath(String path)
protected final String mapPath(String path, boolean caseInsensitive)
throws java.io.FileNotFoundException, PathNotFoundException {
return mapPath("", path);
return mapPath("", path, caseInsensitive);
}

/**
* Map the input path to a real path, this may require changing the case of various parts of the
* path. The base path is not checked, it is assumed to exist.
* Map the input path to a real path, this may require changing the case of
* various parts of the path. The base path is not checked, it is assumed to
* exist.
*
* @param base String
* @param path String
* @param base String
* @param path String
* @param caseInsensitive boolean
* @return String
* @exception FileNotFoundException The path could not be mapped to a real path.
* @exception PathNotFoundException Part of the path is not valid
*/
protected final String mapPath(String base, String path)
protected final String mapPath(String base, String path, boolean caseInsensitive)
throws java.io.FileNotFoundException, PathNotFoundException {

// Split the path string into seperate directory components
Expand Down Expand Up @@ -557,10 +561,10 @@ protected final String mapPath(String base, String path)

int lastPos = pathStr.length();
idx = 0;
File lastDir = null;
Path lastDir = null;
if (base != null && base.length() > 0)
lastDir = new File(base);
File curDir = null;
lastDir = Path.of(base);
Path curDir = null;

while (idx < maxDir) {

Expand All @@ -569,47 +573,48 @@ protected final String mapPath(String base, String path)
pathStr.append(java.io.File.separator);

// Check if the current path exists
curDir = new File(pathStr.toString());
curDir = Path.of(pathStr.toString());

if (curDir.exists() == false) {
if (!Files.exists(curDir)) {

// Check if there is a previous directory to search
if (lastDir == null)
// (Unless the disk is case-insensitive, because in that case the file really
// doesn't exist)
if (lastDir == null || caseInsensitive)
throw new PathNotFoundException();

// Search the current path for a matching directory, the case may be different
String[] fileList = lastDir.list();
if (fileList == null || fileList.length == 0)
throw new PathNotFoundException();

int fidx = 0;
boolean foundPath = false;

while (fidx < fileList.length && foundPath == false) {

// Check if the current file name matches the required directory name
if (fileList[fidx].equalsIgnoreCase(dirs[idx])) {

// Use the current directory name
pathStr.setLength(lastPos);
pathStr.append(fileList[fidx]);
pathStr.append(java.io.File.separator);

// Check if the path is valid
curDir = new File(pathStr.toString());
if (curDir.exists()) {
foundPath = true;
break;
try (DirectoryStream<Path> lastDirStream = Files.newDirectoryStream(lastDir)) {
Iterator<Path> lastDirIter = lastDirStream.iterator();

boolean foundPath = false;

while (lastDirIter.hasNext() && foundPath == false) {
// Check if the current file name matches the required directory name
String candidateFile = lastDirIter.next().getFileName().toString();
if (candidateFile.equalsIgnoreCase(dirs[idx])) {

// Use the current directory name
pathStr.setLength(lastPos);
pathStr.append(candidateFile);
pathStr.append(java.io.File.separator);

// Check if the path is valid
curDir = Path.of(pathStr.toString());
if (Files.exists(curDir)) {
foundPath = true;
break;
}
}
}

// Update the file name index
fidx++;
// Check if we found the required directory
if (foundPath == false)
throw new PathNotFoundException();
}

// Check if we found the required directory
if (foundPath == false)
catch (IOException ex) {
throw new PathNotFoundException();
}
}

// Set the last valid directory file
Expand All @@ -626,37 +631,34 @@ protected final String mapPath(String base, String path)
if (path.endsWith( FileName.DOS_SEPERATOR_STR) == false) {

// Map the file name
String[] fileList = lastDir.list();
String fileName = dirs[dirs.length - 1];

// Check if the file list is valid, if not then the path is not valid
if (fileList == null)
throw new FileNotFoundException(path);
// If it's in fact a wildcard search, we can skip attempting to map a real file
if (!WildCard.containsWildcards(fileName)) {
Path targetFile = Path.of(pathStr.toString(), fileName);

// Search for the required file
idx = 0;
boolean foundFile = false;
// Search for the required file
boolean foundFile = Files.exists(targetFile);

while (idx < fileList.length && foundFile == false) {
if (fileList[idx].compareTo(fileName) == 0)
foundFile = true;
else
idx++;
}
// Check if we found the file name, if not then do a case insensitive search
// (Unless the disk is already assumed to be case-insensitive, in which case
// skip this step)
if (foundFile == false && !caseInsensitive) {
try (DirectoryStream<Path> lastDirStream = Files.newDirectoryStream(lastDir)) {
Iterator<Path> lastDirIter = lastDirStream.iterator();

// Check if we found the file name, if not then do a case insensitive search
if (foundFile == false) {
// Search again using a case insensitive search
while (lastDirIter.hasNext() && foundFile == false) {

// Search again using a case insensitive search
idx = 0;

while (idx < fileList.length && foundFile == false) {
if (fileList[idx].equalsIgnoreCase(fileName)) {
foundFile = true;
fileName = fileList[idx];
String candidateFile = lastDirIter.next().getFileName().toString();
if (candidateFile.equalsIgnoreCase(fileName)) {
foundFile = true;
fileName = candidateFile;
}
}
} catch (IOException ex) {
throw new PathNotFoundException();
}
else
idx++;
}
}

Expand Down Expand Up @@ -689,13 +691,13 @@ public NetworkFile openFile(SrvSession sess, TreeConnection tree, FileOpenParams
throws java.io.IOException {

// Create a Java network file
DeviceContext ctx = tree.getContext();
JavaNIODeviceContext ctx = (JavaNIODeviceContext) tree.getContext();
Path filePath = Paths.get( FileName.buildPath(ctx.getDeviceName(), params.getPath(), null, java.io.File.separatorChar));

if ( Files.exists( filePath) == false) {

// Try and map the file name string to a local path
String mappedPath = mapPath(ctx.getDeviceName(), params.getPath());
String mappedPath = mapPath(ctx.getDeviceName(), params.getPath(), ctx.getDiskIsCaseInsensitive());
if (mappedPath == null)
throw new java.io.FileNotFoundException(filePath.toString());

Expand Down Expand Up @@ -850,8 +852,8 @@ public void setFileInformation(SrvSession sess, TreeConnection tree, String name
if (info.hasSetFlag(FileInfo.SetModifyDate)) {

// Build the path to the file
DeviceContext ctx = tree.getContext();
Path filePath = Paths.get( mapPath( ctx.getDeviceName(), name));
JavaNIODeviceContext ctx = (JavaNIODeviceContext) tree.getContext();
Path filePath = Paths.get(mapPath(ctx.getDeviceName(), name, ctx.getDiskIsCaseInsensitive()));

// Update the file/folder modify date/time
Files.setLastModifiedTime( filePath, FileTime.fromMillis( info.getModifyDateTime()));
Expand All @@ -873,13 +875,14 @@ public SearchContext startSearch(SrvSession sess, TreeConnection tree, String se
throws java.io.FileNotFoundException {

// Create the full search path string
String path = FileName.buildPath(tree.getContext().getDeviceName(), null, searchPath, File.separatorChar);
JavaNIODeviceContext diskCtx = (JavaNIODeviceContext) tree.getContext();
String path = FileName.buildPath(diskCtx.getDeviceName(), null, searchPath, File.separatorChar);
JavaNIOSearchContext ctx = null;

try {

// Map the path, this may require changing the case on some or all path components
path = mapPath(path);
path = mapPath(path, diskCtx.getDiskIsCaseInsensitive());

// Split the search path to get the share relative path
String[] paths = FileName.splitPath(path, File.separatorChar);
Expand Down