7142921: (fs) Files.probeContentType reports a MIME type of "text/plain" on Ubuntu 11.04
authordxu
Tue, 04 Dec 2012 14:07:30 +0000
changeset 14702 111342b28e67
parent 14701 0c0578b802b1
child 14703 84efc7a1216e
7142921: (fs) Files.probeContentType reports a MIME type of "text/plain" on Ubuntu 11.04 7144997: (fs) Files.probeContentType returns null on Solaris 64-bit Reviewed-by: alanb, mduigou
jdk/make/java/nio/Makefile
jdk/make/java/nio/mapfile-linux
jdk/makefiles/CompileJavaClasses.gmk
jdk/makefiles/CompileNativeLibraries.gmk
jdk/makefiles/mapfiles/libnio/mapfile-linux
jdk/src/solaris/classes/sun/nio/fs/BsdFileSystemProvider.java
jdk/src/solaris/classes/sun/nio/fs/LinuxFileSystemProvider.java
jdk/src/solaris/classes/sun/nio/fs/MacOSXFileSystemProvider.java
jdk/src/solaris/classes/sun/nio/fs/MagicFileTypeDetector.java
jdk/src/solaris/classes/sun/nio/fs/MimeTypesFileTypeDetector.java
jdk/src/solaris/classes/sun/nio/fs/SolarisFileSystemProvider.java
jdk/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java
jdk/src/solaris/native/sun/nio/fs/MagicFileTypeDetector.c
--- a/jdk/make/java/nio/Makefile	Mon Dec 03 11:07:20 2012 -0500
+++ b/jdk/make/java/nio/Makefile	Tue Dec 04 14:07:30 2012 +0000
@@ -69,6 +69,7 @@
 	sun/nio/ch/UnixAsynchronousSocketChannelImpl.java \
 	\
 	sun/nio/fs/GnomeFileTypeDetector.java \
+	sun/nio/fs/MimeTypesFileTypeDetector.java \
 	sun/nio/fs/PollingWatchService.java \
 	sun/nio/fs/SolarisAclFileAttributeView.java \
 	sun/nio/fs/SolarisFileStore.java \
@@ -202,6 +203,8 @@
 	sun/nio/ch/UnixAsynchronousSocketChannelImpl.java \
 	\
 	sun/nio/fs/GnomeFileTypeDetector.java \
+	sun/nio/fs/MagicFileTypeDetector.java \
+	sun/nio/fs/MimeTypesFileTypeDetector.java \
 	sun/nio/fs/LinuxDosFileAttributeView.java \
 	sun/nio/fs/LinuxFileStore.java \
 	sun/nio/fs/LinuxFileSystem.java \
@@ -239,6 +242,7 @@
 	UnixAsynchronousSocketChannelImpl.c \
 	\
 	GnomeFileTypeDetector.c \
+	MagicFileTypeDetector.c \
 	LinuxNativeDispatcher.c \
 	LinuxWatchService.c \
 	UnixCopyFile.c \
@@ -254,6 +258,7 @@
 	sun/nio/ch/UnixAsynchronousSocketChannelImpl.java \
 	\
 	sun/nio/fs/GnomeFileTypeDetector.java \
+	sun/nio/fs/MagicFileTypeDetector.java \
 	sun/nio/fs/LinuxNativeDispatcher.java \
 	sun/nio/fs/LinuxWatchService.java \
 	sun/nio/fs/UnixCopyFile.java \
@@ -277,6 +282,7 @@
 	sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java \
 	sun/nio/ch/UnixAsynchronousSocketChannelImpl.java \
 	\
+	sun/nio/fs/MimeTypesFileTypeDetector.java \
 	sun/nio/fs/BsdFileStore.java \
 	sun/nio/fs/BsdFileSystem.java \
 	sun/nio/fs/BsdFileSystemProvider.java \
--- a/jdk/make/java/nio/mapfile-linux	Mon Dec 03 11:07:20 2012 -0500
+++ b/jdk/make/java/nio/mapfile-linux	Tue Dec 04 14:07:30 2012 +0000
@@ -130,6 +130,8 @@
 		Java_sun_nio_fs_GnomeFileTypeDetector_probeUsingGio;
 		Java_sun_nio_fs_GnomeFileTypeDetector_initializeGnomeVfs;
 		Java_sun_nio_fs_GnomeFileTypeDetector_probeUsingGnomeVfs;
+		Java_sun_nio_fs_MagicFileTypeDetector_initialize0;
+		Java_sun_nio_fs_MagicFileTypeDetector_probe0;
 		Java_sun_nio_fs_LinuxWatchService_eventSize;
 		Java_sun_nio_fs_LinuxWatchService_eventOffsets;
 		Java_sun_nio_fs_LinuxWatchService_inotifyInit;
--- a/jdk/makefiles/CompileJavaClasses.gmk	Mon Dec 03 11:07:20 2012 -0500
+++ b/jdk/makefiles/CompileJavaClasses.gmk	Tue Dec 04 14:07:30 2012 +0000
@@ -121,6 +121,7 @@
 	     sun/nio/fs/LinuxFileStore.java \
 	     sun/nio/fs/LinuxFileSystem.java \
 	     sun/nio/fs/LinuxFileSystemProvider.java \
+	     sun/nio/fs/MagicFileTypeDetector.java \
 	     sun/nio/fs/LinuxNativeDispatcher.java \
 	     sun/nio/fs/LinuxUserDefinedFileAttributeView.java \
 	     sun/nio/fs/LinuxWatchService.java
--- a/jdk/makefiles/CompileNativeLibraries.gmk	Mon Dec 03 11:07:20 2012 -0500
+++ b/jdk/makefiles/CompileNativeLibraries.gmk	Tue Dec 04 14:07:30 2012 +0000
@@ -1897,6 +1897,7 @@
 		UnixAsynchronousServerSocketChannelImpl.c \
 		UnixAsynchronousSocketChannelImpl.c \
 		GnomeFileTypeDetector.c \
+		MagicFileTypeDetector.c \
 		LinuxNativeDispatcher.c \
 		LinuxWatchService.c \
 		UnixCopyFile.c \
--- a/jdk/makefiles/mapfiles/libnio/mapfile-linux	Mon Dec 03 11:07:20 2012 -0500
+++ b/jdk/makefiles/mapfiles/libnio/mapfile-linux	Tue Dec 04 14:07:30 2012 +0000
@@ -130,6 +130,8 @@
 		Java_sun_nio_fs_GnomeFileTypeDetector_probeUsingGio;
 		Java_sun_nio_fs_GnomeFileTypeDetector_initializeGnomeVfs;
 		Java_sun_nio_fs_GnomeFileTypeDetector_probeUsingGnomeVfs;
+		Java_sun_nio_fs_MagicFileTypeDetector_initialize0;
+		Java_sun_nio_fs_MagicFileTypeDetector_probe0;
 		Java_sun_nio_fs_LinuxWatchService_eventSize;
 		Java_sun_nio_fs_LinuxWatchService_eventOffsets;
 		Java_sun_nio_fs_LinuxWatchService_inotifyInit;
--- a/jdk/src/solaris/classes/sun/nio/fs/BsdFileSystemProvider.java	Mon Dec 03 11:07:20 2012 -0500
+++ b/jdk/src/solaris/classes/sun/nio/fs/BsdFileSystemProvider.java	Tue Dec 04 14:07:30 2012 +0000
@@ -25,8 +25,6 @@
 
 package sun.nio.fs;
 
-import java.nio.file.*;
-import java.nio.file.attribute.*;
 import java.io.IOException;
 
 /**
--- a/jdk/src/solaris/classes/sun/nio/fs/LinuxFileSystemProvider.java	Mon Dec 03 11:07:20 2012 -0500
+++ b/jdk/src/solaris/classes/sun/nio/fs/LinuxFileSystemProvider.java	Tue Dec 04 14:07:30 2012 +0000
@@ -29,6 +29,8 @@
 import java.nio.file.attribute.*;
 import java.nio.file.spi.FileTypeDetector;
 import java.io.IOException;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
 
 /**
  * Linux implementation of FileSystemProvider
@@ -100,6 +102,13 @@
 
     @Override
     FileTypeDetector getFileTypeDetector() {
-        return new GnomeFileTypeDetector();
+        Path userMimeTypes = Paths.get(AccessController.doPrivileged(
+            new GetPropertyAction("user.home")), ".mime.types");
+        Path etcMimeTypes = Paths.get("/etc/mime.types");
+
+        return chain(new GnomeFileTypeDetector(),
+                     new MimeTypesFileTypeDetector(userMimeTypes),
+                     new MimeTypesFileTypeDetector(etcMimeTypes),
+                     new MagicFileTypeDetector());
     }
 }
--- a/jdk/src/solaris/classes/sun/nio/fs/MacOSXFileSystemProvider.java	Mon Dec 03 11:07:20 2012 -0500
+++ b/jdk/src/solaris/classes/sun/nio/fs/MacOSXFileSystemProvider.java	Tue Dec 04 14:07:30 2012 +0000
@@ -25,9 +25,11 @@
 
 package sun.nio.fs;
 
-import java.nio.file.*;
-import java.nio.file.attribute.*;
-import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.spi.FileTypeDetector;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
 
 /**
  * MacOSX implementation of FileSystemProvider
@@ -42,4 +44,11 @@
     MacOSXFileSystem newFileSystem(String dir) {
         return new MacOSXFileSystem(this, dir);
     }
+
+    @Override
+    FileTypeDetector getFileTypeDetector() {
+        Path userMimeTypes = Paths.get(AccessController.doPrivileged(
+            new GetPropertyAction("user.home")), ".mime.types");
+        return new MimeTypesFileTypeDetector(userMimeTypes);
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/fs/MagicFileTypeDetector.java	Tue Dec 04 14:07:30 2012 +0000
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * File type detector that uses the libmagic to guess the MIME type of a file.
+ */
+
+class MagicFileTypeDetector extends AbstractFileTypeDetector {
+
+    private static final String UNKNOW_MIME_TYPE = "application/octet-stream";
+
+    // true if libmagic is available and successfully loaded
+    private final boolean libmagicAvailable;
+
+    public MagicFileTypeDetector() {
+        libmagicAvailable = initialize0();
+    }
+
+    @Override
+    protected String implProbeContentType(Path obj) throws IOException {
+        if (!libmagicAvailable || !(obj instanceof UnixPath))
+            return null;
+
+        UnixPath path = (UnixPath) obj;
+        path.checkRead();
+
+        NativeBuffer buffer = NativeBuffers.asNativeBuffer(path.getByteArrayForSysCalls());
+        try {
+            byte[] type = probe0(buffer.address());
+            String mimeType = (type == null) ? null : new String(type);
+            return UNKNOW_MIME_TYPE.equals(mimeType) ? null : mimeType;
+        } finally {
+            buffer.release();
+        }
+    }
+
+    private static native boolean initialize0();
+
+    private static native byte[] probe0(long pathAddress);
+
+    static {
+        AccessController.doPrivileged(new PrivilegedAction<Void>() {
+            @Override
+            public Void run() {
+                System.loadLibrary("nio");
+                return null;
+            }
+        });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/classes/sun/nio/fs/MimeTypesFileTypeDetector.java	Tue Dec 04 14:07:30 2012 +0000
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * File type detector that uses a file extension to look up its MIME type
+ * based on a mime.types file.
+ */
+
+class MimeTypesFileTypeDetector extends AbstractFileTypeDetector {
+
+    // path to mime.types file
+    private final Path mimeTypesFile;
+
+    // map of extension to MIME type
+    private Map<String,String> mimeTypeMap;
+
+    // set to true when file loaded
+    private volatile boolean loaded = false;
+
+    public MimeTypesFileTypeDetector(Path filePath) {
+        mimeTypesFile = filePath;
+    }
+
+    @Override
+    protected String implProbeContentType(Path path) {
+        Path fn = path.getFileName();
+        if (fn == null)
+            return null;  // no file name
+
+        String ext = getExtension(fn.toString());
+        if (ext.isEmpty())
+            return null;  // no extension
+
+        loadMimeTypes();
+        if (mimeTypeMap == null || mimeTypeMap.isEmpty())
+            return null;
+
+        // Case-sensitive search
+        String mimeType;
+        do {
+            mimeType = mimeTypeMap.get(ext);
+            if (mimeType == null)
+                ext = getExtension(ext);
+        } while (mimeType == null && !ext.isEmpty());
+
+        return mimeType;
+    }
+
+    // Get the extension of a file name.
+    private static String getExtension(String name) {
+        String ext = "";
+        if (name != null && !name.isEmpty()) {
+            int dot = name.indexOf('.');
+            if ((dot >= 0) && (dot < name.length() - 1)) {
+                ext = name.substring(dot + 1);
+            }
+        }
+        return ext;
+    }
+
+    /**
+     * Parse the mime types file, and store the type-extension mappings into
+     * mimeTypeMap. The mime types file is not loaded until the first probe
+     * to achieve the lazy initialization. It adopts double-checked locking
+     * optimization to reduce the locking overhead.
+     */
+    private void loadMimeTypes() {
+        if (!loaded) {
+            synchronized (this) {
+                if (!loaded) {
+                    List<String> lines = AccessController.doPrivileged(
+                        new PrivilegedAction<List<String>>() {
+                            @Override
+                            public List<String> run() {
+                                try {
+                                    return Files.readAllLines(mimeTypesFile,
+                                                              Charset.defaultCharset());
+                                } catch (IOException ignore) {
+                                    return Collections.emptyList();
+                                }
+                            }
+                        });
+
+                    mimeTypeMap = new HashMap<>(lines.size());
+                    String entry = "";
+                    for (String line : lines) {
+                        entry += line;
+                        if (entry.endsWith("\\")) {
+                            entry = entry.substring(0, entry.length() - 1);
+                            continue;
+                        }
+                        parseMimeEntry(entry);
+                        entry = "";
+                    }
+                    if (!entry.isEmpty()) {
+                        parseMimeEntry(entry);
+                    }
+                    loaded = true;
+                }
+            }
+        }
+    }
+
+    /**
+     * Parse a mime-types entry, which can have the following formats.
+     * 1) Simple space-delimited format
+     * image/jpeg   jpeg jpg jpe JPG
+     *
+     * 2) Netscape key-value pair format
+     * type=application/x-java-jnlp-file desc="Java Web Start" exts="jnlp"
+     * or
+     * type=text/html exts=htm,html
+     */
+    private void parseMimeEntry(String entry) {
+        entry = entry.trim();
+        if (entry.isEmpty() || entry.charAt(0) == '#')
+            return;
+
+        entry = entry.replaceAll("\\s*#.*", "");
+        int equalIdx = entry.indexOf('=');
+        if (equalIdx > 0) {
+            // Parse a mime-types command having the key-value pair format
+            final String TYPEEQUAL = "type=";
+            String typeRegex = "\\b" + TYPEEQUAL +
+                    "(\"\\p{Graph}+?/\\p{Graph}+?\"|\\p{Graph}+/\\p{Graph}+\\b)";
+            Pattern typePattern = Pattern.compile(typeRegex);
+            Matcher typeMatcher = typePattern.matcher(entry);
+
+            if (typeMatcher.find()) {
+                String type = typeMatcher.group().substring(TYPEEQUAL.length());
+                if (type.charAt(0) == '"') {
+                    type = type.substring(1, type.length() - 1);
+                }
+
+                final String EXTEQUAL = "exts=";
+                String extRegex = "\\b" + EXTEQUAL +
+                        "(\"[\\p{Graph}|\\p{Blank}]+?\"|\\p{Graph}+\\b)";
+                Pattern extPattern = Pattern.compile(extRegex);
+                Matcher extMatcher = extPattern.matcher(entry);
+
+                if (extMatcher.find()) {
+                    String exts =
+                            extMatcher.group().substring(EXTEQUAL.length());
+                    if (exts.charAt(0) == '"') {
+                        exts = exts.substring(1, exts.length() - 1);
+                    }
+                    String[] extList = exts.split("[\\p{Blank}|\\p{Punct}]+");
+                    for (String ext : extList) {
+                        putIfAbsent(ext, type);
+                    }
+                }
+            }
+        } else {
+            // Parse a mime-types command having the space-delimited format
+            String[] elements = entry.split("\\s+");
+            int i = 1;
+            while (i < elements.length) {
+                putIfAbsent(elements[i++], elements[0]);
+            }
+        }
+    }
+
+    private void putIfAbsent(String key, String value) {
+        if (key != null && !key.isEmpty() &&
+            value != null && !value.isEmpty() &&
+            !mimeTypeMap.containsKey(key))
+        {
+            mimeTypeMap.put(key, value);
+        }
+    }
+}
--- a/jdk/src/solaris/classes/sun/nio/fs/SolarisFileSystemProvider.java	Mon Dec 03 11:07:20 2012 -0500
+++ b/jdk/src/solaris/classes/sun/nio/fs/SolarisFileSystemProvider.java	Tue Dec 04 14:07:30 2012 +0000
@@ -29,6 +29,8 @@
 import java.nio.file.attribute.*;
 import java.nio.file.spi.FileTypeDetector;
 import java.io.IOException;
+import java.security.AccessController;
+import sun.security.action.GetPropertyAction;
 
 /**
  * Solaris implementation of FileSystemProvider
@@ -83,6 +85,12 @@
 
     @Override
     FileTypeDetector getFileTypeDetector() {
-        return new GnomeFileTypeDetector();
+        Path userMimeTypes = Paths.get(AccessController.doPrivileged(
+            new GetPropertyAction("user.home")), ".mime.types");
+        Path etcMimeTypes = Paths.get("/etc/mime.types");
+
+        return chain(new GnomeFileTypeDetector(),
+                     new MimeTypesFileTypeDetector(userMimeTypes),
+                     new MimeTypesFileTypeDetector(etcMimeTypes));
     }
 }
--- a/jdk/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java	Mon Dec 03 11:07:20 2012 -0500
+++ b/jdk/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java	Tue Dec 04 14:07:30 2012 +0000
@@ -509,4 +509,24 @@
         };
     }
 
+    /**
+     * Returns a {@code FileTypeDetector} that chains the given array of file
+     * type detectors. When the {@code implProbeContentType} method is invoked
+     * then each of the detectors is invoked in turn, the result from the
+     * first to detect the file type is returned.
+     */
+    final FileTypeDetector chain(final AbstractFileTypeDetector... detectors) {
+        return new AbstractFileTypeDetector() {
+            @Override
+            protected String implProbeContentType(Path file) throws IOException {
+                for (AbstractFileTypeDetector detector : detectors) {
+                    String result = detector.implProbeContentType(file);
+                    if (result != null && !result.isEmpty()) {
+                        return result;
+                    }
+                }
+                return null;
+            }
+        };
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/solaris/native/sun/nio/fs/MagicFileTypeDetector.c	Tue Dec 04 14:07:30 2012 +0000
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "jlong.h"
+
+#include <dlfcn.h>
+#include <string.h>
+
+#define MAGIC_MIME_TYPE 0x000010 /* Return the MIME type */
+
+typedef struct magic_set magic_t;
+
+typedef magic_t* (*magic_open_func)(int flags);
+typedef int (*magic_load_func)(magic_t* cookie, const char* filename);
+typedef const char* (*magic_file_func)(magic_t* cookie, const char* filename);
+typedef void (*magic_close_func)(magic_t* cookie);
+
+static void* magic_handle;
+static magic_open_func magic_open;
+static magic_load_func magic_load;
+static magic_file_func magic_file;
+static magic_close_func magic_close;
+
+#include "sun_nio_fs_MagicFileTypeDetector.h"
+
+JNIEXPORT jboolean JNICALL
+Java_sun_nio_fs_MagicFileTypeDetector_initialize0
+    (JNIEnv* env, jclass this)
+{
+    magic_handle = dlopen("libmagic.so", RTLD_LAZY);
+    if (magic_handle == NULL) {
+        magic_handle = dlopen("libmagic.so.1", RTLD_LAZY);
+        if (magic_handle == NULL) {
+            return JNI_FALSE;
+        }
+    }
+
+    magic_open = (magic_open_func)dlsym(magic_handle, "magic_open");
+
+    magic_load = (magic_load_func)dlsym(magic_handle, "magic_load");
+
+    magic_file = (magic_file_func)dlsym(magic_handle, "magic_file");
+
+    magic_close = (magic_close_func)dlsym(magic_handle, "magic_close");
+
+    if (magic_open == NULL ||
+        magic_load == NULL ||
+        magic_file == NULL ||
+        magic_close == NULL)
+    {
+        dlclose(magic_handle);
+        return JNI_FALSE;
+    }
+
+    return JNI_TRUE;
+}
+
+JNIEXPORT jbyteArray JNICALL
+Java_sun_nio_fs_MagicFileTypeDetector_probe0
+    (JNIEnv* env, jclass this, jlong pathAddress)
+{
+    char* path = (char*)jlong_to_ptr(pathAddress);
+    magic_t* cookie;
+    jbyteArray result = NULL;
+
+    cookie = (*magic_open)(MAGIC_MIME_TYPE);
+
+    if (cookie != NULL) {
+        if ((*magic_load)(cookie, NULL) != -1) {
+            const char* type = (*magic_file)(cookie, path);
+            if (type != NULL) {
+                jsize len = strlen(type);
+                result = (*env)->NewByteArray(env, len);
+                if (result != NULL) {
+                    (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)type);
+                }
+            }
+        }
+        (*magic_close)(cookie);
+    }
+
+    return result;
+}