| // Copyright 2014 The Bazel Authors. All rights reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package com.google.devtools.build.lib.unix; | |
| import com.google.common.annotations.VisibleForTesting; | |
| import com.google.common.flogger.GoogleLogger; | |
| import com.google.common.hash.HashCode; | |
| import com.google.devtools.build.lib.UnixJniLoader; | |
| import java.io.FileNotFoundException; | |
| import java.io.IOException; | |
| import java.util.logging.LogManager; | |
| /** | |
| * Utility methods for access to UNIX filesystem calls not exposed by the Java | |
| * SDK. Exception messages are selected to be consistent with those generated | |
| * by the java.io package where appropriate--see package javadoc for details. | |
| */ | |
| public final class NativePosixFiles { | |
| private NativePosixFiles() {} | |
| static { | |
| if (!java.nio.charset.Charset.defaultCharset().name().equals("ISO-8859-1")) { | |
| // Defer the Logger call, so we don't deadlock if this is called from Logger | |
| // initialization code. | |
| new Thread() { | |
| @Override | |
| public void run() { | |
| // wait (if necessary) until the logging system is initialized | |
| synchronized (LogManager.getLogManager()) { | |
| } | |
| GoogleLogger.forEnclosingClass() | |
| .atFine() | |
| .log( | |
| "WARNING: Default character set is not latin1; java.io.File and " | |
| + "com.google.devtools.build.lib.unix.FilesystemUtils will represent some " | |
| + "filenames differently."); | |
| } | |
| }.start(); | |
| } | |
| UnixJniLoader.loadJni(); | |
| } | |
| /** | |
| * Native wrapper around Linux readlink(2) call. | |
| * | |
| * @param path the file of interest | |
| * @return the pathname to which the symbolic link 'path' links | |
| * @throws IOException iff the readlink() call failed | |
| */ | |
| public static native String readlink(String path) throws IOException; | |
| /** | |
| * Native wrapper around POSIX chmod(2) syscall: Changes the file access | |
| * permissions of 'path' to 'mode'. | |
| * | |
| * @param path the file of interest | |
| * @param mode the POSIX type and permission mode bits to set | |
| * @throws IOException iff the chmod() call failed. | |
| */ | |
| public static native void chmod(String path, int mode) throws IOException; | |
| /** | |
| * Native wrapper around POSIX symlink(2) syscall. | |
| * | |
| * @param oldpath the file to link to | |
| * @param newpath the new path for the link | |
| * @throws IOException iff the symlink() syscall failed. | |
| */ | |
| public static native void symlink(String oldpath, String newpath) | |
| throws IOException; | |
| /** | |
| * Native wrapper around POSIX link(2) syscall. | |
| * | |
| * @param oldpath the file to link to | |
| * @param newpath the new path for the link | |
| * @throws IOException iff the link() syscall failed. | |
| */ | |
| public static native void link(String oldpath, String newpath) throws IOException; | |
| /** | |
| * Native wrapper around POSIX stat(2) syscall. | |
| * | |
| * @param path the file to stat. | |
| * @return a FileStatus instance containing the metadata. | |
| * @throws IOException if the stat() syscall failed. | |
| */ | |
| public static native FileStatus stat(String path) throws IOException; | |
| /** | |
| * Native wrapper around POSIX lstat(2) syscall. | |
| * | |
| * @param path the file to lstat. | |
| * @return a FileStatus instance containing the metadata. | |
| * @throws IOException if the lstat() syscall failed. | |
| */ | |
| public static native FileStatus lstat(String path) throws IOException; | |
| /** | |
| * Native wrapper around POSIX stat(2) syscall. | |
| * | |
| * @param path the file to stat. | |
| * @return an ErrnoFileStatus instance containing the metadata. | |
| * If there was an error, the return value's hasError() method | |
| * will return true, and all stat information is undefined. | |
| */ | |
| public static native ErrnoFileStatus errnoStat(String path); | |
| /** | |
| * Native wrapper around POSIX lstat(2) syscall. | |
| * | |
| * @param path the file to lstat. | |
| * @return an ErrnoFileStatus instance containing the metadata. | |
| * If there was an error, the return value's hasError() method | |
| * will return true, and all stat information is undefined. | |
| */ | |
| public static native ErrnoFileStatus errnoLstat(String path); | |
| /** | |
| * Native wrapper around POSIX utime(2) syscall. | |
| * | |
| * Note: negative file times are interpreted as unsigned time_t. | |
| * | |
| * @param path the file whose times to change. | |
| * @param now if true, ignore actime/modtime parameters and use current time. | |
| * @param modtime the file modification time in seconds since the UNIX epoch. | |
| * @throws IOException if the utime() syscall failed. | |
| */ | |
| public static native void utime(String path, boolean now, int modtime) throws IOException; | |
| /** | |
| * Native wrapper around POSIX mkdir(2) syscall. | |
| * | |
| * Caveat: errno==EEXIST is mapped to the return value "false", not | |
| * IOException. It requires an additional stat() to determine if mkdir | |
| * failed because the directory already exists. | |
| * | |
| * @param path the directory to create. | |
| * @param mode the mode with which to create the directory. | |
| * @return true if the directory was successfully created; false if the | |
| * system call returned EEXIST because some kind of a file (not necessarily | |
| * a directory) already exists. | |
| * @throws IOException if the mkdir() syscall failed for any other reason. | |
| */ | |
| public static native boolean mkdir(String path, int mode) | |
| throws IOException; | |
| /** | |
| * Implements (effectively) mkdir -p. | |
| * | |
| * @param path the directory to recursively create. | |
| * @param mode the mode with which to create the directories. | |
| * @throws IOException if the directory creation failed for any reason. | |
| */ | |
| public static native void mkdirs(String path, int mode) throws IOException; | |
| /** | |
| * Native wrapper around POSIX opendir(2)/readdir(3)/closedir(3) syscall. | |
| * | |
| * @param path the directory to read. | |
| * @return the list of directory entries in the order they were returned by | |
| * the system, excluding "." and "..". | |
| * @throws IOException if the call to opendir failed for any reason. | |
| */ | |
| public static String[] readdir(String path) throws IOException { | |
| return readdir(path, ReadTypes.NONE).names; | |
| } | |
| /** | |
| * An enum for specifying now the types of the individual entries returned by | |
| * {@link #readdir(String, ReadTypes)} is to be returned. | |
| */ | |
| public enum ReadTypes { | |
| NONE('n'), // Do not read types | |
| NOFOLLOW('d'), // Do not follow symlinks | |
| FOLLOW('f'); // Follow symlinks; never returns "SYMLINK" and returns "UNKNOWN" when dangling | |
| private final char code; | |
| private ReadTypes(char code) { | |
| this.code = code; | |
| } | |
| private char getCode() { | |
| return code; | |
| } | |
| } | |
| /** | |
| * A compound return type for readdir(), analogous to struct dirent[] in C. A low memory profile | |
| * is critical for this class, as instances are expected to be kept around for caching for | |
| * potentially a long time. | |
| */ | |
| public static final class Dirents { | |
| /** | |
| * The type of the directory entry. | |
| */ | |
| public enum Type { | |
| FILE, | |
| DIRECTORY, | |
| SYMLINK, | |
| UNKNOWN; | |
| private static Type forChar(char c) { | |
| if (c == 'f') { | |
| return Type.FILE; | |
| } else if (c == 'd') { | |
| return Type.DIRECTORY; | |
| } else if (c == 's') { | |
| return Type.SYMLINK; | |
| } else { | |
| return Type.UNKNOWN; | |
| } | |
| } | |
| } | |
| /** The names of the entries in a directory. */ | |
| private final String[] names; | |
| /** | |
| * An optional (nullable) array of entry types, corresponding positionally | |
| * to the "names" field. The types are: | |
| * 'd': a subdirectory | |
| * 'f': a regular file | |
| * 's': a symlink (only returned with {@code NOFOLLOW}) | |
| * '?': anything else | |
| * Note that unlike libc, this implementation of readdir() follows | |
| * symlinks when determining these types. | |
| * | |
| * <p>This is intentionally a byte array rather than a array of enums to save memory. | |
| */ | |
| private final byte[] types; | |
| /** called from JNI */ | |
| public Dirents(String[] names, byte[] types) { | |
| this.names = names; | |
| this.types = types; | |
| } | |
| public int size() { | |
| return names.length; | |
| } | |
| public boolean hasTypes() { | |
| return types != null; | |
| } | |
| public String getName(int i) { | |
| return names[i]; | |
| } | |
| public Type getType(int i) { | |
| return Type.forChar((char) types[i]); | |
| } | |
| } | |
| /** | |
| * Native wrapper around POSIX opendir(2)/readdir(3)/closedir(3) syscall. | |
| * | |
| * @param path the directory to read. | |
| * @param readTypes How the types of individual entries should be returned. If {@code NONE}, | |
| * the "types" field in the result will be null. | |
| * @return a Dirents object, containing "names", the list of directory entries | |
| * (excluding "." and "..") in the order they were returned by the system, | |
| * and "types", an array of entry types (file, directory, etc) corresponding | |
| * positionally to "names". | |
| * @throws IOException if the call to opendir failed for any reason. | |
| */ | |
| public static Dirents readdir(String path, ReadTypes readTypes) throws IOException { | |
| // Passing enums to native code is possible, but onerous; we use a char instead. | |
| return readdir(path, readTypes.getCode()); | |
| } | |
| private static native Dirents readdir(String path, char typeCode) | |
| throws IOException; | |
| /** | |
| * Native wrapper around POSIX rename(2) syscall. | |
| * | |
| * @param oldpath the source location. | |
| * @param newpath the destination location. | |
| * @throws IOException if the rename failed for any reason. | |
| */ | |
| public static native void rename(String oldpath, String newpath) | |
| throws IOException; | |
| /** | |
| * Native wrapper around POSIX remove(3) C library call. | |
| * | |
| * @param path the file or directory to remove. | |
| * @return true iff the file was actually deleted by this call. | |
| * @throws IOException if the remove failed, but the file was present prior to the call. | |
| */ | |
| public static native boolean remove(String path) throws IOException; | |
| /** | |
| * Native wrapper around POSIX mkfifo(3) C library call. | |
| * | |
| * @param path the name of the pipe to create. | |
| * @param mode the mode with which to create the pipe. | |
| * @throws IOException if the mkfifo failed. | |
| */ | |
| @VisibleForTesting | |
| public static native void mkfifo(String path, int mode) throws IOException; | |
| /******************************************************************** | |
| * * | |
| * Linux extended file attributes * | |
| * * | |
| ********************************************************************/ | |
| /** | |
| * Native wrapper around Linux getxattr(2) syscall. | |
| * | |
| * @param path the file whose extended attribute is to be returned. | |
| * @param name the name of the extended attribute key. | |
| * @return the value of the extended attribute associated with 'path', if | |
| * any, or null if no such attribute is defined (ENODATA). | |
| * @throws IOException if the call failed for any other reason. | |
| */ | |
| public static native byte[] getxattr(String path, String name) | |
| throws IOException; | |
| /** | |
| * Native wrapper around Linux lgetxattr(2) syscall. (Like getxattr, but | |
| * does not follow symbolic links.) | |
| * | |
| * @param path the file whose extended attribute is to be returned. | |
| * @param name the name of the extended attribute key. | |
| * @return the value of the extended attribute associated with 'path', if | |
| * any, or null if no such attribute is defined (ENODATA). | |
| * @throws IOException if the call failed for any other reason. | |
| */ | |
| public static native byte[] lgetxattr(String path, String name) | |
| throws IOException; | |
| /** | |
| * Returns the MD5 digest of the specified file, following symbolic links. | |
| * | |
| * @param path the file whose MD5 digest is required. | |
| * @return the MD5 digest, as a 16-byte array. | |
| * @throws IOException if the call failed for any reason. | |
| */ | |
| static native byte[] md5sumAsBytes(String path) throws IOException; | |
| /** | |
| * Returns the MD5 digest of the specified file, following symbolic links. | |
| * | |
| * @param path the file whose MD5 digest is required. | |
| * @return the MD5 digest, as a {@link HashCode} | |
| * @throws IOException if the call failed for any reason. | |
| */ | |
| public static HashCode md5sum(String path) throws IOException { | |
| return HashCode.fromBytes(md5sumAsBytes(path)); | |
| } | |
| /** | |
| * Deletes all directory trees recursively beneath the given path, which is expected to be a | |
| * directory. Does not remove the top directory. | |
| * | |
| * @param dir the directory hierarchy to remove | |
| * @throws IOException if the hierarchy cannot be removed successfully or if the given path is not | |
| * a directory | |
| */ | |
| public static native void deleteTreesBelow(String dir) throws IOException; | |
| /** | |
| * Open a file descriptor for writing. | |
| * | |
| * <p>This is a low level API. The caller is responsible for calling {@link close} on the returned | |
| * file descriptor. | |
| * | |
| * @param path file to open | |
| * @param append whether to open is append mode | |
| */ | |
| public static native int openWrite(String path, boolean append) throws FileNotFoundException; | |
| /** Write a segment of data to a file descriptor. */ | |
| public static native int write(int fd, byte[] data, int off, int len) throws IOException; | |
| /** | |
| * Close a file descriptor. Additionally, accept and ignore an object; this can be used to keep a | |
| * reference alive. | |
| */ | |
| public static native int close(int fd, Object ignored) throws IOException; | |
| } |