blob: 10339fa51d809109fd6bcbb7b51b8899b7b3a4e3 [file] [log] [blame]
// 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)));
}
}