preface

We know that different operating systems have their own file systems, and there are many differences between these file systems. Java, because it is cross-platform, has to deal with the differences between these file systems in order to provide a unified portal.

About the FileSystem class

The JDK abstracts a FileSystem to represent a FileSystem. Different operating systems use this class to implement their own file systems. For example, Windows NT/2000 uses WinNTFileSystem. In the UNIX-like operating system, UnixFileSystem is used.

One thing to note is that WinNTFileSystem and UnixFileSystem are not in the same JDK, which means they are separate. You can only find WinNTFileSystem in the Windows JDK. Find the UnixFileSystem in the Linux JDK, as well as other operating systems that have their own file system implementation classes.

Here is divided into two series of analysis of JDK implementation classes for two (Windows and Linux) operating system, the Windows operating system, corresponding to the WinNTFileSystem class. Due to its long length, FileSystem of JDK for Different operating systems (Windows) is divided into the first, middle and second part.

Inheritance structure

--java.lang.Object
  --java.io.FileSystem
    --java.io.WinNTFileSystemCopy the code

CreateFileExclusively method

This method is used to create a file, and the local method logic is,

  1. Converts the path to a wide character format.
  2. Check whether the path reserves the device name for the system.
  3. The CreateFileW function is called to create the file, using the CREATE_NEW mode, only if the file does not exist.
  4. If the file already exists, try not to throw an exception but return false. The process also attempts to read the file’s properties. If the file fails, an IO exception is thrown.
public native boolean createFileExclusively(String path)
            throws IOException;

JNIEXPORT jboolean JNICALL
Java_java_io_WinNTFileSystem_createFileExclusively(JNIEnv *env, jclass cls,
                                                   jstring path)
{
    HANDLE h = NULL;
    WCHAR *pathbuf = pathToNTPath(env, path, JNI_FALSE);
    if (pathbuf == NULL)
        return JNI_FALSE;
    if (isReservedDeviceNameW(pathbuf)) {
        free(pathbuf);
        return JNI_FALSE;
    }
    h = CreateFileW(
        pathbuf,                             
        GENERIC_READ | GENERIC_WRITE,        
        FILE_SHARE_READ | FILE_SHARE_WRITE,   
        NULL,                                 
        CREATE_NEW,                           
        FILE_ATTRIBUTE_NORMAL |
            FILE_FLAG_OPEN_REPARSE_POINT,    
        NULL);

    if (h == INVALID_HANDLE_VALUE) {
        DWORD error = GetLastError();
        if((error ! = ERROR_FILE_EXISTS) && (error ! = ERROR_ALREADY_EXISTS)) { DWORD a = GetFileAttributesW(pathbuf);if (a == INVALID_FILE_ATTRIBUTES) {
                SetLastError(error);
                JNU_ThrowIOExceptionWithLastError(env, "Could not open file");
            }
        }
        free(pathbuf);
        return JNI_FALSE;
    }
    free(pathbuf);
    CloseHandle(h);
    return JNI_TRUE;
}Copy the code

The list method

This method is used to list all files and directories under the specified directory. The local method handles the following logic:

  1. To obtainjava/lang/StringClass object and check that it cannot be NULL.
  2. Gets the path of the File object.
  3. Reallocate the space based on the path length and copy the path to search_path.
  4. GetFileAttributesW gets the file attributes for the specified path, returning INVALID_FILE_ATTRIBUTES if it is a directory or NULL if it is a directory.
  5. Remove any extra space at the end.
  6. Add at the end of the path*orA \ *.
  7. The first file is retrieved using the FindFirstFileW function.
  8. We then use the while loop and the FindNextFileW function to continuously fetch the next file and place the resulting file name in the string array, and the file name cannot be.and..
  9. Returns an array of filenames, whose initial length is 16. If the array exceeds this length, the string array object is created twice as long as the original array, and the original array is copied into the new array.
public native String[] list(File f);

JNIEXPORT jobjectArray JNICALL
Java_java_io_WinNTFileSystem_list(JNIEnv *env, jobject this, jobject file)
{
    WCHAR *search_path;
    HANDLE handle;
    WIN32_FIND_DATAW find_data;
    int len, maxlen;
    jobjectArray rv, old;
    DWORD fattr;
    jstring name;
    jclass str_class;
    WCHAR *pathbuf;

    str_class = JNU_ClassString(env);
    CHECK_NULL_RETURN(str_class, NULL);

    pathbuf = fileToNTPath(env, file, ids.path);
    if (pathbuf == NULL)
        return NULL;
    search_path = (WCHAR*)malloc(2*wcslen(pathbuf) + 6);
    if (search_path == 0) {
        free (pathbuf);
        errno = ENOMEM;
        JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
        return NULL;
    }
    wcscpy(search_path, pathbuf);
    free(pathbuf);
    fattr = GetFileAttributesW(search_path);
    if (fattr == INVALID_FILE_ATTRIBUTES) {
        free(search_path);
        return NULL;
    } else if ((fattr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
        free(search_path);
        return NULL;
    }

    len = (int)wcslen(search_path);
    while (search_path[len-1] == L' ') {
        len--;
    }
    search_path[len] = 0;

    if ((search_path[0] == L'\ \' && search_path[1] == L'\ 0') ||
        (search_path[1] == L':'
        && (search_path[2] == L'\ 0'
        || (search_path[2] == L'\ \' && search_path[3] == L'\ 0')))) {
        wcscat(search_path, L"*");
    } else {
        wcscat(search_path, L"\ \ *");
    }

    handle = FindFirstFileW(search_path, &find_data);
    free(search_path);
    if (handle == INVALID_HANDLE_VALUE) {
        if(GetLastError() ! = ERROR_FILE_NOT_FOUND) {return NULL;
        } else {
            rv = (*env)->NewObjectArray(env, 0, str_class, NULL);
            return rv;
        }
    }

    len = 0;
    maxlen = 16;
    rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
    if (rv == NULL) 
        return NULL;
    do {
        if(! wcscmp(find_data.cFileName, L".") | |! wcscmp(find_data.cFileName, L".."))
           continue;
        name = (*env)->NewString(env, find_data.cFileName,
                                 (jsize)wcslen(find_data.cFileName));
        if (name == NULL)
            return NULL; 
        if (len == maxlen) {
            old = rv;
            rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
            if (rv == NULL || JNU_CopyObjectArray(env, rv, old, len) < 0)
                return NULL; 
            (*env)->DeleteLocalRef(env, old);
        }
        (*env)->SetObjectArrayElement(env, rv, len++, name);
        (*env)->DeleteLocalRef(env, name);

    } while (FindNextFileW(handle, &find_data));

    if(GetLastError() ! = ERROR_NO_MORE_FILES)return NULL; 
    FindClose(handle);

    if (len < maxlen) {
        old = rv;
        rv = (*env)->NewObjectArray(env, len, str_class, NULL);
        if (rv == NULL)
            return NULL; 
        if (JNU_CopyObjectArray(env, rv, old, len) < 0)
            return NULL; 
    }
    return rv;
}Copy the code

CreateDirectory method

This method is used to create a directory. The local method is as simple as getting the path of the File object and calling CreateDirectoryW to create the directory.

public native boolean createDirectory(File f);

JNIEXPORT jboolean JNICALL
Java_java_io_WinNTFileSystem_createDirectory(JNIEnv *env, jobject this,
                                             jobject file)
{
    BOOL h = FALSE;
    WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
    if (pathbuf == NULL) {
        return JNI_FALSE;
    }
    h = CreateDirectoryW(pathbuf, NULL);
    free(pathbuf);

    if (h == 0) {
        return JNI_FALSE;
    }

    return JNI_TRUE;
}Copy the code

SetLastModifiedTime method

This method is used to set the last modification time of a File or directory. The local method is to obtain the path of the File object, open the specified File or directory with CreateFileW and set the last modification time with SetFileTime.

public native boolean setLastModifiedTime(File f, long time);

JNIEXPORT jboolean JNICALL
Java_java_io_WinNTFileSystem_setLastModifiedTime(JNIEnv *env, jobject this,
                                                 jobject file, jlong time)
{
    jboolean rv = JNI_FALSE;
    WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
    HANDLE h;
    if (pathbuf == NULL)
        return JNI_FALSE;
    h = CreateFileW(pathbuf,
                    FILE_WRITE_ATTRIBUTES,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    FILE_FLAG_BACKUP_SEMANTICS,
                    0);
    if(h ! = INVALID_HANDLE_VALUE) { LARGE_INTEGER modTime; FILETIME t; modTime.QuadPart = (time + 11644473600000L) * 10000L; t.dwLowDateTime = (DWORD)modTime.LowPart; t.dwHighDateTime = (DWORD)modTime.HighPart;if (SetFileTime(h, NULL, NULL, &t)) {
            rv = JNI_TRUE;
        }
        CloseHandle(h);
    }
    free(pathbuf);

    return rv;
}Copy the code

SetReadOnly method

This method is used to make the specified file read only. The local method logic is,

  1. Gets the path of the File object.
  2. Get file attributes using the GetFileAttributesW function.
  3. If the file is a hyperlink or shortcut, the final path is retrieved, and then the file attributes are retrieved using the GetFileAttributesW function.
  4. If the file is not a directory, the SetFileAttributesW function sets the file to read-only, and the corresponding identifier is FILE_ATTRIBUTE_READONLY.
public native boolean setReadOnly(File f);

JNIEXPORT jboolean JNICALL
Java_java_io_WinNTFileSystem_setReadOnly(JNIEnv *env, jobject this,
                                         jobject file)
{
    jboolean rv = JNI_FALSE;
    DWORD a;
    WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
    if (pathbuf == NULL)
        return JNI_FALSE;
    a = GetFileAttributesW(pathbuf);

    if((a ! = INVALID_FILE_ATTRIBUTES) && ((a & FILE_ATTRIBUTE_REPARSE_POINT) ! = 0)) { WCHAR *fp = getFinalPath(env, pathbuf);if (fp == NULL) {
            a = INVALID_FILE_ATTRIBUTES;
        } else{ free(pathbuf); pathbuf = fp; a = GetFileAttributesW(pathbuf); }}if((a ! = INVALID_FILE_ATTRIBUTES) && ((a & FILE_ATTRIBUTE_DIRECTORY) == 0)) {if (SetFileAttributesW(pathbuf, a | FILE_ATTRIBUTE_READONLY))
            rv = JNI_TRUE;
    }
    free(pathbuf);
    return rv;
}Copy the code

The delete method

This method is used to delete the specified path of a File object by clearing the standard path cache and the standard path prefix cache, and then calling the local method delete0 to perform the deletion operation.

public boolean delete(File f) {
        cache.clear();
        prefixCache.clear();
        return delete0(f);
    }

private native boolean delete0(File f);Copy the code

The local method retrieves the path of the File object and then calls removeFileOrDirectory to remove the directory or File. The logic of removeFileOrDirectory is to set the specified path file or directory to FILE_ATTRIBUTE_NORMAL, and then use GetFileAttributesW to retrieve the file attributes. Finally, call RemoveDirectoryW to remove the directory if the specified path is a directory, or DeleteFileW to delete the file if it is a file.

JNIEXPORT jboolean JNICALL
Java_java_io_WinNTFileSystem_delete0(JNIEnv *env, jobject this, jobject file)
{
    jboolean rv = JNI_FALSE;
    WCHAR *pathbuf = fileToNTPath(env, file, ids.path);
    if (pathbuf == NULL) {
        return JNI_FALSE;
    }
    if (removeFileOrDirectory(pathbuf) == 0) {
        rv = JNI_TRUE;
    }
    free(pathbuf);
    return rv;
}

static int
removeFileOrDirectory(const jchar *path)
{
    DWORD a;

    SetFileAttributesW(path, FILE_ATTRIBUTE_NORMAL);
    a = GetFileAttributesW(path);
    if (a == INVALID_FILE_ATTRIBUTES) {
        return 1;
    } else if (a & FILE_ATTRIBUTE_DIRECTORY) {
        return! RemoveDirectoryW(path); }else {
        return !DeleteFileW(path);
    }
}Copy the code

Rename method

This method is used to rename files by clearing out both the standard path cache and the standard path prefix cache, and then calling the local method rename0 to perform the renaming operation.

public boolean rename(File f1, File f2) {
        cache.clear();
        prefixCache.clear();
        return rename0(f1, f2);
    }

private native boolean rename0(File f1, File f2);Copy the code

The local methods first get the original file path and the renamed file path, and then use the _wrename function to rename the file.

JNIEXPORT jboolean JNICALL
Java_java_io_WinNTFileSystem_rename0(JNIEnv *env, jobject this, jobject from,
                                     jobject to)
{

    jboolean rv = JNI_FALSE;
    WCHAR *frompath = fileToNTPath(env, from, ids.path);
    WCHAR *topath = fileToNTPath(env, to, ids.path);
    if(frompath ! = NULL && topath ! = NULL && _wrename(frompath, topath) == 0) { rv = JNI_TRUE; } free(frompath); free(topath);return rv;
}Copy the code

The access method

This method is used to check whether the specified path file or directory is readable. This is mainly a permission check at the JVM level, so the SecurityManager SecurityManager is used to check.

private boolean access(String path) {
        try {
            SecurityManager security = System.getSecurityManager();
            if(security ! = null) security.checkRead(path);return true;
        } catch (SecurityException x) {
            return false; }}Copy the code

ListRoots method

This method is used to get an array of the root file objects of the available file systems. The logic goes like this,

  1. All root files are first retrieved using the listRoots0 local method.
  2. The listRoots0 method is very simple, which is to get the logical drive character of the operating system directly using the GetLogicalDrives function. Note that GetLogicalDrives returns an int, so how does it represent the drive character? Each bit indicates whether A logical drive exists. For example, if the first bit is “1”, it indicates that drive “A:” exists. If the second bit is “1”, it indicates that drive “B:” exists.
  3. After you get all the drive characters, check the access of the drive through a for loop, remove the unauthorized drives, and count a drive with n. Here you only need to loop 26 times, because the maximum is 26 uppercase letters.
  4. Instantiate a File array of size n.
  5. The authorized drive is iterated again 26 times, and a File object is instantiated according to the drive symbol and added to the File array.
  6. Returns the File array.
public File[] listRoots() {
        int ds = listRoots0();
        int n = 0;
        for (int i = 0; i < 26; i++) {
            if(((ds >> i) & 1) ! = 0) {if(! access((char)('A' + i) + ":" + slash))
                    ds &= ~(1 << i);
                else
                    n++;
            }
        }
        File[] fs = new File[n];
        int j = 0;
        char slash = this.slash;
        for (int i = 0; i < 26; i++) {
            if(((ds >> i) & 1) ! = 0) fs[j++] = new File((char)('A' + i) + ":" + slash);
        }
        return fs;
    }

private static native int listRoots0();

JNIEXPORT jint JNICALL
Java_java_io_WinNTFileSystem_listRoots0(JNIEnv *env, jclass ignored)
{
    return GetLogicalDrives();
}Copy the code

GetSpace method

SPACE_USABLE = 2 SPACE_TOTAL = 0 SPACE_FREE = 1 SPACE_USABLE = 2 SPACE_USABLE = 2 SPACE_USABLE = 0 SPACE_FREE = 1 SPACE_USABLE = 2 To query for the size of the root directory of a file, pass in the corresponding identifier, which is obtained using the getSpace0 local method.

public long getSpace(File f, int t) {
        if (f.exists()) {
            return getSpace0(f, t);
        }
        return 0;
    }

private native long getSpace0(File f, int t);Copy the code

The logic of the local method is,

  1. Gets the File or directory path of the File object.
  2. Use GetVolumePathNameW to obtain the root path of the specified path.
  3. The total space size, free space size, and free space size are retrieved using the GetDiskFreeSpaceExW function.
  4. Returns different metrics based on the passed identity, such asSPACE_TOTALReturns the total space size, and so on.
JNIEXPORT jlong JNICALL
Java_java_io_WinNTFileSystem_getSpace0(JNIEnv *env, jobject this,
                                       jobject file, jint t)
{
    WCHAR volname[MAX_PATH_LENGTH + 1];
    jlong rv = 0L;
    WCHAR *pathbuf = fileToNTPath(env, file, ids.path);

    if (GetVolumePathNameW(pathbuf, volname, MAX_PATH_LENGTH)) {
        ULARGE_INTEGER totalSpace, freeSpace, usableSpace;
        if (GetDiskFreeSpaceExW(volname, &usableSpace, &totalSpace, &freeSpace)) {
            switch(t) {
            case java_io_FileSystem_SPACE_TOTAL:
                rv = long_to_jlong(totalSpace.QuadPart);
                break;
            case java_io_FileSystem_SPACE_FREE:
                rv = long_to_jlong(freeSpace.QuadPart);
                break;
            case java_io_FileSystem_SPACE_USABLE:
                rv = long_to_jlong(usableSpace.QuadPart);
                break;
            default:
                assert(0);
            }
        }
    }

    free(pathbuf);
    return rv;
}Copy the code

GetNameMax method

This method is used to get the maximum file name length allowed by the system. Some processing is done before calling the getNameMax0 local method. If the path is absolute, get the root path and add \\.

public int getNameMax(String path) {
        String s = null;
        if(path ! = null) { File f = new File(path);if (f.isAbsolute()) {
                Path root = f.toPath().getRoot();
                if(root ! = null) { s = root.toString();if(! s.endsWith("\ \")) {
                        s = s + "\ \"; }}}}return getNameMax0(s);
    }

private native int getNameMax0(String path);Copy the code

The local method uses GetVolumeInformationW to get the maximum file name length allowed by the system, maxComponentLength

JNIEXPORT jint JNICALL
Java_java_io_WinNTFileSystem_getNameMax0(JNIEnv *env, jobject this,
                                         jstring pathname)
{
    BOOL res = 0;
    DWORD maxComponentLength;

    if (pathname == NULL) {
            res = GetVolumeInformationW(NULL,
                                        NULL,
                                        0,
                                        NULL,
                                        &maxComponentLength,
                                        NULL,
                                        NULL,
                                        0);
    } else {
        WITH_UNICODE_STRING(env, pathname, path) {
            res = GetVolumeInformationW(path,
                                        NULL,
                                        0,
                                        NULL,
                                        &maxComponentLength,
                                        NULL,
                                        NULL,
                                        0);
        } END_UNICODE_STRING(env, path);
    }

    if (res == 0) {
        JNU_ThrowIOExceptionWithLastError(env,
            "Could not get maximum component length");
    }

    return (jint)maxComponentLength;
}Copy the code

Compare method

This method is used to compare two File objects, essentially comparing the path strings directly.

public int compare(File f1, File f2) {
        return f1.getPath().compareToIgnoreCase(f2.getPath());
    }Copy the code

HashCode methods

This method is used to get the hash value of the File object, get the path of the File object, lower the string to lowercase, call the hashCode method of the string, and finally perform xor with 1234321 to get the hash value of the File.

public int hashCode(File f) {
        return f.getPath().toLowerCase(Locale.ENGLISH).hashCode() ^ 1234321;
    }Copy the code

Below is the advertisement and related reading

============= advertising time ===============

The public menu has been divided into “distributed”, “machine learning”, “deep learning”, “NLP”, “Java depth”, “Java concurrent core”, “JDK source”, “Tomcat kernel” and so on, there may be a suitable for your appetite.

My new book “Analysis of Tomcat kernel Design” has been sold on JINGdong, friends in need can buy. Thank you all.

Why to write “Analysis of Tomcat Kernel Design”

= = = = = = = = = = = = = = = = = = = = = = = = =

Related reading:

JDK FileSystem (Windows) for different operating systems

Welcome to:

Write the picture description here