Added isCaseSensitive() and isCasePreserving() to FileSystem, plus implementations on windows and os x.

This commit is contained in:
Adam Murdoch
2014-08-02 14:23:28 +10:00
parent 71f3c43998
commit 2c53568577
10 changed files with 80 additions and 28 deletions

View File

@@ -245,6 +245,7 @@ You can run `$INSTALL_DIR/bin/native-platform-test` to run the test application.
* All: add diagnostics for terminal. * All: add diagnostics for terminal.
* All: version each native interface separately. * All: version each native interface separately.
* All: string names for errno values. * All: string names for errno values.
* Use `strerror()` to map errno to a string
* All: split into multiple projects. * All: split into multiple projects.
* Mac: use fully decomposed form for unicode file names on hfs+ filesystems. * Mac: use fully decomposed form for unicode file names on hfs+ filesystems.
* All: extend FileSystem to deal with removable media. * All: extend FileSystem to deal with removable media.
@@ -273,6 +274,11 @@ You can run `$INSTALL_DIR/bin/native-platform-test` to run the test application.
* Windows networking: http://msdn.microsoft.com/en-us/library/windows/desktop/ee663286(v=vs.85).aspx * Windows networking: http://msdn.microsoft.com/en-us/library/windows/desktop/ee663286(v=vs.85).aspx
* Windows ip functions: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366071(v=vs.85).aspx * Windows ip functions: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366071(v=vs.85).aspx
* Windows notification on change: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366329(v=vs.85).aspx * Windows notification on change: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366329(v=vs.85).aspx
* Expose information about memory size and usage:
* http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system
* Expose system monotonic clock, for timing:
* clock_gettime(CLOCK_MONOTONIC) on Linux
* mach_absolute_time() and mach_timebase_info() on OS X.
* Fire events when filesystems or network interfaces change in some way. * Fire events when filesystems or network interfaces change in some way.
* Fire events when terminal size changes. * Fire events when terminal size changes.
* Fire events when files change. * Fire events when files change.

View File

@@ -21,10 +21,19 @@
#include "native.h" #include "native.h"
#include "generic.h" #include "generic.h"
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/ucred.h> #include <sys/ucred.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/attr.h>
#include <unistd.h>
#include <errno.h>
typedef struct vol_caps_buf {
u_int32_t size;
vol_capabilities_attr_t caps;
} vol_caps_buf_t;
/* /*
* File system functions * File system functions
@@ -46,14 +55,38 @@ Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileS
} }
jclass info_class = env->GetObjectClass(info); jclass info_class = env->GetObjectClass(info);
jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V"); jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZ)V");
for (int i = 0; i < fs_count; i++) { for (int i = 0; i < fs_count; i++) {
struct attrlist alist;
memset(&alist, 0, sizeof(alist));
alist.bitmapcount = ATTR_BIT_MAP_COUNT;
alist.volattr = ATTR_VOL_CAPABILITIES | ATTR_VOL_INFO;
vol_caps_buf_t buffer;
// getattrlist requires the path to the actual mount point.
int err = getattrlist(buf[i].f_mntonname, &alist, &buffer, sizeof(buffer), 0);
if (err != 0) {
mark_failed_with_errno(env, "could not determine file system attributes", result);
break;
}
jboolean caseSensitive = JNI_TRUE;
jboolean casePreserving = JNI_TRUE;
if (alist.volattr & ATTR_VOL_CAPABILITIES) {
if ((buffer.caps.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_SENSITIVE)) {
caseSensitive = (buffer.caps.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_SENSITIVE) != 0;
}
if ((buffer.caps.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_PRESERVING)) {
casePreserving = (buffer.caps.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_CASE_PRESERVING) != 0;
}
}
jstring mount_point = char_to_java(env, buf[i].f_mntonname, result); jstring mount_point = char_to_java(env, buf[i].f_mntonname, result);
jstring file_system_type = char_to_java(env, buf[i].f_fstypename, result); jstring file_system_type = char_to_java(env, buf[i].f_fstypename, result);
jstring device_name = char_to_java(env, buf[i].f_mntfromname, result); jstring device_name = char_to_java(env, buf[i].f_mntfromname, result);
jboolean remote = (buf[i].f_flags & MNT_LOCAL) == 0; jboolean remote = (buf[i].f_flags & MNT_LOCAL) == 0;
env->CallVoidMethod(info, method, mount_point, file_system_type, device_name, remote); env->CallVoidMethod(info, method, mount_point, file_system_type, device_name, remote, caseSensitive, casePreserving);
} }
free(buf); free(buf);
} }

View File

@@ -38,13 +38,13 @@ Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileS
struct mntent mount_info; struct mntent mount_info;
jclass info_class = env->GetObjectClass(info); jclass info_class = env->GetObjectClass(info);
jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V"); jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZ)V");
while (getmntent_r(fp, &mount_info, buf, sizeof(buf)) != NULL) { while (getmntent_r(fp, &mount_info, buf, sizeof(buf)) != NULL) {
jstring mount_point = char_to_java(env, mount_info.mnt_dir, result); jstring mount_point = char_to_java(env, mount_info.mnt_dir, result);
jstring file_system_type = char_to_java(env, mount_info.mnt_type, result); jstring file_system_type = char_to_java(env, mount_info.mnt_type, result);
jstring device_name = char_to_java(env, mount_info.mnt_fsname, result); jstring device_name = char_to_java(env, mount_info.mnt_fsname, result);
env->CallVoidMethod(info, method, mount_point, file_system_type, device_name, JNI_FALSE); env->CallVoidMethod(info, method, mount_point, file_system_type, device_name, JNI_FALSE, JNI_TRUE, JNI_TRUE);
} }
endmntent(fp); endmntent(fp);

View File

@@ -168,9 +168,8 @@ Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_setEnvironme
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileSystems(JNIEnv *env, jclass target, jobject info, jobject result) { Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileSystems(JNIEnv *env, jclass target, jobject info, jobject result) {
jclass info_class = env->GetObjectClass(info); jclass info_class = env->GetObjectClass(info);
jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V"); jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZ)V");
wprintf(L"Drives:\n");
DWORD required = GetLogicalDriveStringsW(0, NULL); DWORD required = GetLogicalDriveStringsW(0, NULL);
if (required == 0) { if (required == 0) {
mark_failed_with_errno(env, "could not determine logical drive buffer size", result); mark_failed_with_errno(env, "could not determine logical drive buffer size", result);
@@ -186,11 +185,8 @@ Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileS
} else { } else {
wchar_t* cur = buffer; wchar_t* cur = buffer;
for (;cur[0] != L'\0'; cur += wcslen(cur) + 1) { for (;cur[0] != L'\0'; cur += wcslen(cur) + 1) {
wprintf(L" DRIVE: %s\n", cur);
DWORD type = GetDriveTypeW(cur); DWORD type = GetDriveTypeW(cur);
jboolean remote = type == DRIVE_REMOTE; jboolean remote = type == DRIVE_REMOTE;
wprintf(L" DRIVE TYPE: %d\n", type);
// chop off trailing '\' // chop off trailing '\'
size_t len = wcslen(cur); size_t len = wcslen(cur);
@@ -204,7 +200,6 @@ Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileS
mark_failed_with_errno(env, "could not map device for logical drive", result); mark_failed_with_errno(env, "could not map device for logical drive", result);
break; break;
} }
wprintf(L" DEVICE: %s\n", deviceName);
cur[len-1] = L'\\'; cur[len-1] = L'\\';
DWORD available = 1; DWORD available = 1;
@@ -221,29 +216,21 @@ Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileS
NULL, 0, // no output buffer NULL, 0, // no output buffer
&cbBytesReturned, // # bytes returned &cbBytesReturned, // # bytes returned
(LPOVERLAPPED) NULL); // synchronous I/O (LPOVERLAPPED) NULL); // synchronous I/O
if (bSuccess) { if (!bSuccess) {
wprintf(L" AVAILABLE: yes\n");
} else if (GetLastError() == ERROR_NOT_READY) {
available = 0; available = 0;
wprintf(L" AVAILABLE: no\n");
} else {
available = 0;
wprintf(L" AVAILABLE: unknown: %d\n", GetLastError());
} }
CloseHandle(hDevice); CloseHandle(hDevice);
} }
} }
jboolean casePreserving = JNI_TRUE;
if (available) { if (available) {
DWORD flags; DWORD flags;
if (GetVolumeInformationW(cur, NULL, 0, NULL, NULL, &flags, fileSystemName, MAX_PATH+1) == 0) { if (GetVolumeInformationW(cur, NULL, 0, NULL, NULL, &flags, fileSystemName, MAX_PATH+1) == 0) {
mark_failed_with_errno(env, "could not get volume information", result); mark_failed_with_errno(env, "could not get volume information", result);
break; break;
} }
wprintf(L" PRESERVE CASE: %s\n", (flags & FILE_CASE_PRESERVED_NAMES) != 0 ? L"yes" : L"no"); casePreserving = (flags & FILE_CASE_PRESERVED_NAMES) != 0;
wprintf(L" CASE SENSITIVE: %s\n", (flags & FILE_CASE_SENSITIVE_SEARCH) != 0 ? L"yes" : L"no");
wprintf(L" READ ONLY: %s\n", (flags & FILE_READ_ONLY_VOLUME) != 0 ? L"yes" : L"no");
wprintf(L" FS TYPE: %s\n", fileSystemName);
} else { } else {
if (type == DRIVE_CDROM) { if (type == DRIVE_CDROM) {
swprintf(fileSystemName, MAX_PATH+1, L"cdrom"); swprintf(fileSystemName, MAX_PATH+1, L"cdrom");
@@ -256,7 +243,7 @@ Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileS
wchar_to_java(env, cur, wcslen(cur), result), wchar_to_java(env, cur, wcslen(cur), result),
wchar_to_java(env, fileSystemName, wcslen(fileSystemName), result), wchar_to_java(env, fileSystemName, wcslen(fileSystemName), result),
wchar_to_java(env, deviceName, wcslen(deviceName), result), wchar_to_java(env, deviceName, wcslen(deviceName), result),
remote); remote, JNI_FALSE, casePreserving);
} }
} }

View File

@@ -41,6 +41,18 @@ public interface FileSystem {
@ThreadSafe @ThreadSafe
boolean isRemote(); boolean isRemote();
/**
* Returns true if this file system is performance case sensitive searches.
*/
@ThreadSafe
boolean isCaseSensitive();
/**
* Returns true if this file system preserves file name case.
*/
@ThreadSafe
boolean isCasePreserving();
/** /**
* Returns the operating system specific name for this file system. * Returns the operating system specific name for this file system.
*/ */

View File

@@ -25,12 +25,16 @@ public class DefaultFileSystem implements FileSystem {
private final String fileSystemType; private final String fileSystemType;
private final String deviceName; private final String deviceName;
private final boolean remote; private final boolean remote;
private final boolean caseSensitive;
private final boolean casePreserving;
public DefaultFileSystem(File mountPoint, String fileSystemType, String deviceName, boolean remote) { public DefaultFileSystem(File mountPoint, String fileSystemType, String deviceName, boolean remote, boolean caseSensitive, boolean casePreserving) {
this.mountPoint = mountPoint; this.mountPoint = mountPoint;
this.fileSystemType = fileSystemType; this.fileSystemType = fileSystemType;
this.deviceName = deviceName; this.deviceName = deviceName;
this.remote = remote; this.remote = remote;
this.caseSensitive = caseSensitive;
this.casePreserving = casePreserving;
} }
public String getDeviceName() { public String getDeviceName() {
@@ -48,4 +52,12 @@ public class DefaultFileSystem implements FileSystem {
public boolean isRemote() { public boolean isRemote() {
return remote; return remote;
} }
public boolean isCaseSensitive() {
return caseSensitive;
}
public boolean isCasePreserving() {
return casePreserving;
}
} }

View File

@@ -25,7 +25,7 @@ import java.util.List;
public class FileSystemList { public class FileSystemList {
public final List<FileSystem> fileSystems = new ArrayList<FileSystem>(); public final List<FileSystem> fileSystems = new ArrayList<FileSystem>();
public void add(String mountPoint, String fileSystemName, String deviceName, boolean remote) { public void add(String mountPoint, String fileSystemName, String deviceName, boolean remote, boolean caseSensitive, boolean casePreserving) {
fileSystems.add(new DefaultFileSystem(new File(mountPoint), fileSystemName, deviceName, remote)); fileSystems.add(new DefaultFileSystem(new File(mountPoint), fileSystemName, deviceName, remote, caseSensitive, casePreserving));
} }
} }

View File

@@ -20,7 +20,7 @@ import net.rubygrapefruit.platform.internal.FunctionResult;
import net.rubygrapefruit.platform.internal.MutableSystemInfo; import net.rubygrapefruit.platform.internal.MutableSystemInfo;
public class NativeLibraryFunctions { public class NativeLibraryFunctions {
public static final int VERSION = 19; public static final int VERSION = 20;
public static native int getVersion(); public static native int getVersion();

View File

@@ -23,7 +23,7 @@
extern "C" { extern "C" {
#endif #endif
#define NATIVE_VERSION 19 #define NATIVE_VERSION 20
/* /*
* Marks the given result as failed, using the given error message * Marks the given result as failed, using the given error message

View File

@@ -57,7 +57,9 @@ public class Main {
FileSystems fileSystems = Native.get(FileSystems.class); FileSystems fileSystems = Native.get(FileSystems.class);
System.out.println("* File systems: "); System.out.println("* File systems: ");
for (FileSystem fileSystem : fileSystems.getFileSystems()) { for (FileSystem fileSystem : fileSystems.getFileSystems()) {
System.out.println(" * " + fileSystem.getMountPoint() + " -> " + fileSystem.getDeviceName() + " (" + fileSystem.getFileSystemType() + (fileSystem.isRemote() ? " remote" : " local") + ")"); System.out.println(String.format(" * %s -> %s (type: %s %s, case sensitive: %s, case preserving: %s)",
fileSystem.getMountPoint(), fileSystem.getDeviceName(), fileSystem.getFileSystemType(),
fileSystem.isRemote() ? "remote" : "local", fileSystem.isCaseSensitive(), fileSystem.isCasePreserving()));
} }
Terminals terminals = Native.get(Terminals.class); Terminals terminals = Native.get(Terminals.class);