| // 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.devtools.build.lib.bugreport.BugReport; | |
| import com.google.devtools.build.lib.jni.JniLoader; | |
| import com.google.devtools.build.lib.vfs.FileStatus; | |
| import java.io.IOException; | |
| /** | |
| * 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. | |
| */ | |
| final class NativePosixFiles { | |
| private NativePosixFiles() {} | |
| static { | |
| JniLoader.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 | |
| */ | |
| 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. | |
| */ | |
| 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. | |
| */ | |
| 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. | |
| */ | |
| static native void link(String oldpath, String newpath) throws IOException; | |
| /** How stat() and lstat() should handle errors. */ | |
| enum StatErrorHandling { | |
| /** Always throw an exception. */ | |
| ALWAYS_THROW('a'), | |
| /** Throw an exception unless the error is ENOENT/ENOTDIR, in which case return null. */ | |
| THROW_UNLESS_NOT_FOUND('f'), | |
| /* Never throw an exception. Return null instead. */ | |
| NEVER_THROW('n'); | |
| private final char code; | |
| private StatErrorHandling(char code) { | |
| this.code = code; | |
| } | |
| private char getCode() { | |
| return code; | |
| } | |
| } | |
| static FileStatus stat(String path, StatErrorHandling errorHandling) throws IOException { | |
| return stat(path, errorHandling.getCode()); | |
| } | |
| /** | |
| * Native wrapper around POSIX stat(2) syscall. | |
| * | |
| * @param path the file to stat. | |
| * @param errorHandling how to handle errors. | |
| * @return a FileStatus instance containing the metadata. | |
| * @throws IOException if the stat() syscall failed. | |
| */ | |
| private static native FileStatus stat(String path, char errorHandling) throws IOException; | |
| static FileStatus lstat(String path, StatErrorHandling errorHandling) throws IOException { | |
| return lstat(path, errorHandling.getCode()); | |
| } | |
| /** | |
| * Native wrapper around POSIX lstat(2) syscall. | |
| * | |
| * @param path the file to lstat. | |
| * @param errorHandling how to handle errors. | |
| * @return a FileStatus instance containing the metadata. | |
| * @throws IOException if the lstat() syscall failed. | |
| */ | |
| private static native FileStatus lstat(String path, char errorHandling) throws IOException; | |
| /** | |
| * Native wrapper around POSIX utimensat(2) syscall. | |
| * | |
| * <p>Note that, even though utimensat(2) supports up to nanosecond precision, this interface only | |
| * allows millisecond precision, which is what Bazel uses internally. | |
| * | |
| * @param path the file whose modification time should be changed. | |
| * @param now if true, ignore {@code epochMilli} and use the current time. | |
| * @param epochMilli the file modification time in milliseconds since the UNIX epoch. | |
| * @throws IOException if the operation failed. | |
| */ | |
| static native void utimensat(String path, boolean now, long epochMilli) throws IOException; | |
| /** | |
| * Native wrapper around POSIX mkdir(2) syscall. | |
| * | |
| * <p>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. | |
| */ | |
| static native boolean mkdir(String path, int mode) throws IOException; | |
| /** | |
| * Native wrapper around POSIX opendir(2)/readdir(3)/closedir(3) syscalls. | |
| * | |
| * @param path the directory to read. | |
| * @return an array of {@link Dirent} objects, one for each directory entry, excluding {@code .} | |
| * and {@code ..}. | |
| * @throws IOException if the opendir(), readdir() or closedir() calls failed for any reason. | |
| */ | |
| static native Dirent[] readdir(String path) throws IOException; | |
| /** A directory entry and its corresponding type, as returned by readdir(). */ | |
| record Dirent(String name, Type type) { | |
| /** The type of the directory entry. */ | |
| enum Type { | |
| /** Regular file. */ | |
| FILE, | |
| /** Directory. */ | |
| DIRECTORY, | |
| /** Symbolic link. */ | |
| SYMLINK, | |
| /** Character special device. */ | |
| CHAR, | |
| /* Block special device. */ | |
| BLOCK, | |
| /** Named pipe. */ | |
| FIFO, | |
| /** Unix domain socket. */ | |
| SOCKET, | |
| /** Unknown type. */ | |
| UNKNOWN | |
| } | |
| } | |
| /** | |
| * 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. | |
| */ | |
| 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. | |
| */ | |
| 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 | |
| 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. | |
| */ | |
| 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. | |
| */ | |
| static native byte[] lgetxattr(String path, String name) throws IOException; | |
| /** | |
| * 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 | |
| */ | |
| static native void deleteTreesBelow(String dir) throws IOException; | |
| /** Logs a path string that does not have a Latin-1 coder. Called from JNI. */ | |
| private static void logBadPath(String path) { | |
| BugReport.sendNonFatalBugReport( | |
| new IllegalStateException("Path string does not have a Latin-1 coder: %s".formatted(path))); | |
| } | |
| } |