blob: cf8e2fb99c0cf577b2e1413dfa3c0057468c0443 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.unix;
16
Nathan Harmatad8b6ff22015-10-20 21:54:34 +000017import com.google.common.annotations.VisibleForTesting;
janakr9fc7cec2018-10-02 17:48:22 -070018import com.google.common.flogger.GoogleLogger;
jmmv50adefb2020-10-02 09:25:34 -070019import com.google.devtools.build.lib.jni.JniLoader;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import java.io.FileNotFoundException;
21import java.io.IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010022import java.util.logging.LogManager;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010023
24/**
25 * Utility methods for access to UNIX filesystem calls not exposed by the Java
26 * SDK. Exception messages are selected to be consistent with those generated
27 * by the java.io package where appropriate--see package javadoc for details.
28 */
Lukacs Berki7ecb2ce2016-01-26 15:40:42 +000029public final class NativePosixFiles {
Lukacs Berki7ecb2ce2016-01-26 15:40:42 +000030 private NativePosixFiles() {}
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010032 static {
33 if (!java.nio.charset.Charset.defaultCharset().name().equals("ISO-8859-1")) {
34 // Defer the Logger call, so we don't deadlock if this is called from Logger
35 // initialization code.
janakr2579c9a2021-06-22 10:06:14 -070036 new Thread(
37 () -> {
38 // wait (if necessary) until the logging system is initialized
39 synchronized (LogManager.getLogManager()) {
40 }
41 @SuppressWarnings("FloggerRequiredModifiers")
42 GoogleLogger logger = GoogleLogger.forEnclosingClass();
43 logger.atFine().log(
44 "WARNING: Default character set is not latin1; java.io.File and"
45 + " com.google.devtools.build.lib.unix.FilesystemUtils will represent"
46 + " some filenames differently.");
47 })
48 .start();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010049 }
jmmv50adefb2020-10-02 09:25:34 -070050 JniLoader.loadJni();
janakr2579c9a2021-06-22 10:06:14 -070051 initJNIClasses();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010052 }
53
54 /**
55 * Native wrapper around Linux readlink(2) call.
56 *
57 * @param path the file of interest
58 * @return the pathname to which the symbolic link 'path' links
59 * @throws IOException iff the readlink() call failed
60 */
61 public static native String readlink(String path) throws IOException;
62
63 /**
64 * Native wrapper around POSIX chmod(2) syscall: Changes the file access
65 * permissions of 'path' to 'mode'.
66 *
67 * @param path the file of interest
68 * @param mode the POSIX type and permission mode bits to set
69 * @throws IOException iff the chmod() call failed.
70 */
71 public static native void chmod(String path, int mode) throws IOException;
72
73 /**
74 * Native wrapper around POSIX symlink(2) syscall.
75 *
76 * @param oldpath the file to link to
77 * @param newpath the new path for the link
78 * @throws IOException iff the symlink() syscall failed.
79 */
80 public static native void symlink(String oldpath, String newpath)
81 throws IOException;
82
83 /**
Lukacs Berki546c2652016-01-26 08:46:08 +000084 * Native wrapper around POSIX link(2) syscall.
85 *
86 * @param oldpath the file to link to
87 * @param newpath the new path for the link
88 * @throws IOException iff the link() syscall failed.
89 */
Lukacs Berki213ae7a2016-01-26 14:27:34 +000090 public static native void link(String oldpath, String newpath) throws IOException;
Lukacs Berki546c2652016-01-26 08:46:08 +000091
92 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010093 * Native wrapper around POSIX stat(2) syscall.
94 *
95 * @param path the file to stat.
96 * @return a FileStatus instance containing the metadata.
97 * @throws IOException if the stat() syscall failed.
98 */
99 public static native FileStatus stat(String path) throws IOException;
100
101 /**
102 * Native wrapper around POSIX lstat(2) syscall.
103 *
104 * @param path the file to lstat.
105 * @return a FileStatus instance containing the metadata.
106 * @throws IOException if the lstat() syscall failed.
107 */
108 public static native FileStatus lstat(String path) throws IOException;
109
110 /**
111 * Native wrapper around POSIX stat(2) syscall.
112 *
113 * @param path the file to stat.
114 * @return an ErrnoFileStatus instance containing the metadata.
115 * If there was an error, the return value's hasError() method
116 * will return true, and all stat information is undefined.
117 */
118 public static native ErrnoFileStatus errnoStat(String path);
119
120 /**
121 * Native wrapper around POSIX lstat(2) syscall.
122 *
123 * @param path the file to lstat.
124 * @return an ErrnoFileStatus instance containing the metadata.
125 * If there was an error, the return value's hasError() method
126 * will return true, and all stat information is undefined.
127 */
128 public static native ErrnoFileStatus errnoLstat(String path);
129
130 /**
131 * Native wrapper around POSIX utime(2) syscall.
132 *
133 * Note: negative file times are interpreted as unsigned time_t.
134 *
135 * @param path the file whose times to change.
136 * @param now if true, ignore actime/modtime parameters and use current time.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100137 * @param modtime the file modification time in seconds since the UNIX epoch.
138 * @throws IOException if the utime() syscall failed.
139 */
Eric Fellheimer6bc040b2015-10-29 21:56:51 +0000140 public static native void utime(String path, boolean now, int modtime) throws IOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100141
142 /**
143 * Native wrapper around POSIX mkdir(2) syscall.
144 *
145 * Caveat: errno==EEXIST is mapped to the return value "false", not
146 * IOException. It requires an additional stat() to determine if mkdir
147 * failed because the directory already exists.
148 *
149 * @param path the directory to create.
150 * @param mode the mode with which to create the directory.
151 * @return true if the directory was successfully created; false if the
152 * system call returned EEXIST because some kind of a file (not necessarily
153 * a directory) already exists.
154 * @throws IOException if the mkdir() syscall failed for any other reason.
155 */
156 public static native boolean mkdir(String path, int mode)
157 throws IOException;
158
159 /**
ajurkowskicff2ea52021-06-30 18:44:17 -0700160 * Makes sure a writable directory exists at a given path. Returns whether a new directory was
161 * created.
162 *
163 * <p>Unlike {@link #mkdir}, it fails if a file/symlink at a given path already exists. If a
164 * directory is already present, it will make sure it is writable and return false.
165 */
166 public static native boolean mkdirWritable(String path);
167
168 /**
tomludecca2b2017-12-21 08:59:51 -0800169 * Implements (effectively) mkdir -p.
170 *
171 * @param path the directory to recursively create.
172 * @param mode the mode with which to create the directories.
173 * @throws IOException if the directory creation failed for any reason.
174 */
175 public static native void mkdirs(String path, int mode) throws IOException;
176
177 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100178 * Native wrapper around POSIX opendir(2)/readdir(3)/closedir(3) syscall.
179 *
180 * @param path the directory to read.
Googlerf815bf02022-03-09 12:44:59 -0800181 * @return the list of directory entries in the order they were returned by the system, excluding
182 * "." and "..".
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100183 * @throws IOException if the call to opendir failed for any reason.
184 */
185 public static String[] readdir(String path) throws IOException {
186 return readdir(path, ReadTypes.NONE).names;
187 }
188
189 /**
Googlerf815bf02022-03-09 12:44:59 -0800190 * Native wrapper around POSIX opendir(2)/readdir(3)/closedir(3) syscall.
191 *
192 * @param path the directory to read.
193 * @param readTypes How the types of individual entries should be returned. If {@code NONE}, the
194 * "types" field in the result will be null.
195 * @return a Dirents object, containing "names", the list of directory entries (excluding "." and
196 * "..") in the order they were returned by the system, and "types", an array of entry types
197 * (file, directory, etc) corresponding positionally to "names".
198 * @throws IOException if the call to opendir failed for any reason.
199 */
200 public static Dirents readdir(String path, ReadTypes readTypes) throws IOException {
201 // Passing enums to native code is possible, but onerous; we use a char instead.
202 return readdir(path, readTypes.getCode());
203 }
204
205 private static native Dirents readdir(String path, char typeCode) throws IOException;
206
207 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100208 * An enum for specifying now the types of the individual entries returned by
209 * {@link #readdir(String, ReadTypes)} is to be returned.
210 */
211 public enum ReadTypes {
212 NONE('n'), // Do not read types
213 NOFOLLOW('d'), // Do not follow symlinks
214 FOLLOW('f'); // Follow symlinks; never returns "SYMLINK" and returns "UNKNOWN" when dangling
215
216 private final char code;
217
218 private ReadTypes(char code) {
219 this.code = code;
220 }
221
222 private char getCode() {
223 return code;
224 }
225 }
226
227 /**
228 * A compound return type for readdir(), analogous to struct dirent[] in C. A low memory profile
229 * is critical for this class, as instances are expected to be kept around for caching for
230 * potentially a long time.
231 */
232 public static final class Dirents {
233
234 /**
235 * The type of the directory entry.
236 */
237 public enum Type {
238 FILE,
239 DIRECTORY,
240 SYMLINK,
241 UNKNOWN;
242
243 private static Type forChar(char c) {
244 if (c == 'f') {
245 return Type.FILE;
246 } else if (c == 'd') {
247 return Type.DIRECTORY;
248 } else if (c == 's') {
249 return Type.SYMLINK;
250 } else {
251 return Type.UNKNOWN;
252 }
253 }
254 }
255
256 /** The names of the entries in a directory. */
257 private final String[] names;
258 /**
259 * An optional (nullable) array of entry types, corresponding positionally
260 * to the "names" field. The types are:
261 * 'd': a subdirectory
262 * 'f': a regular file
263 * 's': a symlink (only returned with {@code NOFOLLOW})
264 * '?': anything else
265 * Note that unlike libc, this implementation of readdir() follows
266 * symlinks when determining these types.
267 *
268 * <p>This is intentionally a byte array rather than a array of enums to save memory.
269 */
270 private final byte[] types;
271
272 /** called from JNI */
273 public Dirents(String[] names, byte[] types) {
274 this.names = names;
275 this.types = types;
276 }
277
278 public int size() {
279 return names.length;
280 }
281
282 public boolean hasTypes() {
283 return types != null;
284 }
285
286 public String getName(int i) {
287 return names[i];
288 }
289
290 public Type getType(int i) {
291 return Type.forChar((char) types[i]);
292 }
293 }
294
295 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100296 * Native wrapper around POSIX rename(2) syscall.
297 *
298 * @param oldpath the source location.
299 * @param newpath the destination location.
300 * @throws IOException if the rename failed for any reason.
301 */
302 public static native void rename(String oldpath, String newpath)
303 throws IOException;
304
305 /**
306 * Native wrapper around POSIX remove(3) C library call.
307 *
308 * @param path the file or directory to remove.
309 * @return true iff the file was actually deleted by this call.
310 * @throws IOException if the remove failed, but the file was present prior to the call.
311 */
312 public static native boolean remove(String path) throws IOException;
313
Nathan Harmatad8b6ff22015-10-20 21:54:34 +0000314 /**
315 * Native wrapper around POSIX mkfifo(3) C library call.
316 *
317 * @param path the name of the pipe to create.
318 * @param mode the mode with which to create the pipe.
319 * @throws IOException if the mkfifo failed.
320 */
321 @VisibleForTesting
322 public static native void mkfifo(String path, int mode) throws IOException;
323
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100324 /********************************************************************
325 * *
326 * Linux extended file attributes *
327 * *
328 ********************************************************************/
329
330 /**
331 * Native wrapper around Linux getxattr(2) syscall.
332 *
333 * @param path the file whose extended attribute is to be returned.
334 * @param name the name of the extended attribute key.
335 * @return the value of the extended attribute associated with 'path', if
336 * any, or null if no such attribute is defined (ENODATA).
337 * @throws IOException if the call failed for any other reason.
338 */
339 public static native byte[] getxattr(String path, String name)
340 throws IOException;
341
342 /**
343 * Native wrapper around Linux lgetxattr(2) syscall. (Like getxattr, but
344 * does not follow symbolic links.)
345 *
346 * @param path the file whose extended attribute is to be returned.
347 * @param name the name of the extended attribute key.
348 * @return the value of the extended attribute associated with 'path', if
349 * any, or null if no such attribute is defined (ENODATA).
350 * @throws IOException if the call failed for any other reason.
351 */
352 public static native byte[] lgetxattr(String path, String name)
353 throws IOException;
354
355 /**
jmmvfac322b2019-03-21 07:53:51 -0700356 * Deletes all directory trees recursively beneath the given path, which is expected to be a
357 * directory. Does not remove the top directory.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100358 *
jmmvfac322b2019-03-21 07:53:51 -0700359 * @param dir the directory hierarchy to remove
360 * @throws IOException if the hierarchy cannot be removed successfully or if the given path is not
361 * a directory
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100362 */
jmmvfac322b2019-03-21 07:53:51 -0700363 public static native void deleteTreesBelow(String dir) throws IOException;
Benjamin Petersonbe9fbec2019-03-27 05:25:18 -0700364
365 /**
366 * Open a file descriptor for writing.
367 *
368 * <p>This is a low level API. The caller is responsible for calling {@link close} on the returned
369 * file descriptor.
370 *
371 * @param path file to open
372 * @param append whether to open is append mode
373 */
374 public static native int openWrite(String path, boolean append) throws FileNotFoundException;
375
376 /** Write a segment of data to a file descriptor. */
377 public static native int write(int fd, byte[] data, int off, int len) throws IOException;
378
379 /**
380 * Close a file descriptor. Additionally, accept and ignore an object; this can be used to keep a
381 * reference alive.
382 */
383 public static native int close(int fd, Object ignored) throws IOException;
janakr2579c9a2021-06-22 10:06:14 -0700384
385 private static native void initJNIClasses();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100386}