8356493: (fs) SecureDirectoryStream missing some capabilities

This commit is contained in:
Brian Burkhalter 2025-08-26 13:15:52 -07:00
parent c755345177
commit 8667bca08f
6 changed files with 662 additions and 12 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -135,6 +135,182 @@ public interface SecureDirectoryStream<T>
FileAttribute<?>... attrs)
throws IOException;
/**
* Creates a new and empty file, failing if the file already exists.
*
* <p>This method works in a similar manner to {@linkplain Files#createFile
* Files.createFile}. If the {@code path} parameter is an {@linkplain
* Path#isAbsolute absolute} path then it locates the file to create. If
* the parameter is a relative path then it is located relative to this
* open directory.
*
* <p> The {@code attrs} parameter is optional with effects as specified
* for {@linkplain Files#createFile Files.createFile}.
*
* @param path
* the path of the file to create
* @param attrs
* an optional list of file attributes to set atomically when
* creating the file
*
* @return the file
*
* @throws ClosedDirectoryStreamException
* if the directory stream is closed
* @throws UnsupportedOperationException
* if the array contains an attribute that cannot be set atomically
* when creating the directory
* @throws FileAlreadyExistsException
* if a file could not otherwise be created because a file of
* that name already exists <i>(optional specific exception)</i>
* @throws IOException
* if an I/O error occurs or the parent directory does not exist
*
* @since 26
*/
Path createFile(Path path, FileAttribute<?>... attrs)
throws IOException;
/**
* Creates a new directory, failing if a file of that name already exists.
*
* <p>This method works in a similar manner to {@linkplain
* Files#createDirectory Files.createDirectory}. If the {@code path}
* parameter is an {@linkplain Path#isAbsolute absolute} path then it
* locates the directory to create. If the parameter is a relative path
* then it is located relative to this open directory.
*
* <p> The {@code attrs} parameter is optional with effects as specified
* for {@linkplain Files#createDirectory Files.createDirectory}.
*
* @param dir
* the path of the directory to create
* @param attrs
* an optional list of file attributes to set atomically when
* creating the directory
*
* @return the directory
*
* @throws ClosedDirectoryStreamException
* if the directory stream is closed
* @throws UnsupportedOperationException
* if the array contains an attribute that cannot be set atomically
* when creating the directory
* @throws FileAlreadyExistsException
* if a directory could not otherwise be created because a file of
* that name already exists <i>(optional specific exception)</i>
* @throws IOException
* if an I/O error occurs or the parent directory does not exist
*
* @since 26
*/
Path createDirectory(Path dir, FileAttribute<?>... attrs)
throws IOException;
/**
* Creates a new link (directory entry) for an existing file <i>(optional
* operation)</i>.
*
* <p>This method works in a similar manner to {@linkplain Files#createLink
* Files.createLink}. If the {@code link} parameter is an {@link
* Path#isAbsolute absolute} path then it locates the link file. If the
* {@code link} parameter is a relative path then it is located relative to
* this open directory. If the {@code existing} parameter is an absolute
* path then it locates the target file (the {@code targetdir} parameter is
* ignored). If the {@code existing} parameter is a relative path it is
* located relative to the open directory identified by the {@code
* targetdir} parameter.
*
* @param link
* the link (directory entry) to create
* @param targetdir
* the destination directory
* @param existing
* a path to an existing file
*
* @return the path to the link (directory entry)
*
* @throws ClosedDirectoryStreamException
* if the directory stream is closed
* @throws UnsupportedOperationException
* if the implementation does not support adding an existing file
* to a directory
* @throws FileAlreadyExistsException
* if the entry could not otherwise be created because a file of
* that name already exists <i>(optional specific exception)</i>
* @throws NoSuchFileException
* if the file specified by the combination of {@code targetdir}
* and {@code existing} does not exist
* @throws IOException
* if an I/O error occurs
*
* @since 26
*/
Path createLink(T link, SecureDirectoryStream<T> targetdir, T existing)
throws IOException;
/**
* Creates a symbolic link to a target <i>(optional operation)</i>.
*
* <p>This method works in a similar manner to {@linkplain Files#createSymbolicLink
* Files.createSymbolicLink}. If the {@code link} parameter is an {@link
* Path#isAbsolute absolute} path then it locates the link file. If the
* {@code link} parameter is a relative path then it is located relative to
* this open directory. The {@code target} parameter is the target of the
* link and behaves as specified for {@linkplain Files#createSymbolicLink
* Files.createSymbolicLink}.
*
* @param link
* the path of the symbolic link to create
* @param target
* the target of the symbolic link
* @param attrs
* the array of attributes to set atomically when creating the
* symbolic link
*
* @return the path to the symbolic link
*
* @throws ClosedDirectoryStreamException
* if the directory stream is closed
* @throws UnsupportedOperationException
* if the implementation does not support symbolic links or the
* array contains an attribute that cannot be set atomically when
* creating the symbolic link
* @throws FileAlreadyExistsException
* if a file with the name already exists <i>(optional specific
* exception)</i>
* @throws IOException
* if an I/O error occurs
*/
Path createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs)
throws IOException;
/**
* Reads the target of a symbolic link <i>(optional operation)</i>.
*
* <p>This method works in a similar manner to {@linkplain Files#readSymbolicLink
* Files.readSymbolicLink}. If the {@code link} parameter is an {@link
* Path#isAbsolute absolute} path then it locates the link file. If the
* {@code link} parameter is a relative path then it is located relative to
* this open directory.
*
* @param link
* the path to the symbolic link
*
* @return a {@code Path} object representing the target of the link
*
* @throws ClosedDirectoryStreamException
* if the directory stream is closed
* @throws UnsupportedOperationException
* if the implementation does not support symbolic links
* @throws NotLinkException
* if the target could otherwise not be read because the file
* is not a symbolic link <i>(optional specific exception)</i>
* @throws IOException
* if an I/O error occurs
*/
Path readSymbolicLink(Path link) throws IOException;
/**
* Deletes a file.
*
@ -185,8 +361,8 @@ public interface SecureDirectoryStream<T>
/**
* Move a file from this directory to another directory.
*
* <p> This method works in a similar manner to {@link Files#move move}
* method when the {@link StandardCopyOption#ATOMIC_MOVE ATOMIC_MOVE} option
* <p> This method works in a similar manner to {@link Files#move Files.move}
* when the {@link StandardCopyOption#ATOMIC_MOVE ATOMIC_MOVE} option
* is specified. That is, this method moves a file as an atomic file system
* operation. If the {@code srcpath} parameter is an {@link Path#isAbsolute
* absolute} path then it locates the source file. If the parameter is a

View File

@ -358,10 +358,10 @@ public abstract class UnixFileSystemProvider
if (attrs == null) {
break;
}
UnixFileKey fileKey = attrs.fileKey();
if (!attrs.isSymbolicLink()) {
break;
}
UnixFileKey fileKey = attrs.fileKey();
if (!fileKeys.add(fileKey)) {
throw new UnixException(ELOOP);
}

View File

@ -133,6 +133,21 @@ class UnixNativeDispatcher {
private static native void link0(long existingAddress, long newAddress)
throws UnixException;
/**
* linkat(int fd1, const char *name1, int fd2, const char *name2, int flag)
*/
static void linkat(int dfd1, UnixPath path1, int dfd2, UnixPath path2)
throws UnixException
{
try (NativeBuffer buffer1 = copyToNativeBuffer(path1);
NativeBuffer buffer2 = copyToNativeBuffer(path2)) {
linkat0(dfd1, buffer1.address(), dfd2, buffer2.address());
}
}
private static native void linkat0(int dfd1, long addr1,
int dfd2, long addr2)
throws UnixException;
/**
* unlink(const char* path)
*/
@ -199,6 +214,19 @@ class UnixNativeDispatcher {
}
private static native void mkdir0(long pathAddress, int mode) throws UnixException;
/**
* mkdirat(int dfd, const char *path, mode_t mode)
*/
static void mkdirat(int dfd, UnixPath path, int mode)
throws UnixException
{
try (NativeBuffer buffer = copyToNativeBuffer(path)) {
mkdirat0(dfd, buffer.address(), mode);
}
}
private static native void mkdirat0(int dfd, long pathAddress, int mode)
throws UnixException;
/**
* rmdir(const char* path)
*/
@ -221,6 +249,18 @@ class UnixNativeDispatcher {
}
private static native byte[] readlink0(long pathAddress) throws UnixException;
/**
* readlinkat(int fd, const char* path, char* buf, size_t bufsize)
*
* @return link target
*/
static byte[] readlinkat(int fd, UnixPath path) throws UnixException {
try (NativeBuffer buffer = copyToNativeBuffer(path)) {
return readlinkat0(fd, buffer.address());
}
}
private static native byte[] readlinkat0(int fd, long pathAddress) throws UnixException;
/**
* realpath(const char* path, char* resolved_name)
*
@ -245,6 +285,20 @@ class UnixNativeDispatcher {
private static native void symlink0(long name1, long name2)
throws UnixException;
/**
* symlinkat(const char* name1, int fd, const char* name2)
*/
static void symlinkat(byte[] name1, int fd, UnixPath name2)
throws UnixException
{
try (NativeBuffer targetBuffer = NativeBuffers.asNativeBuffer(name1);
NativeBuffer linkBuffer = copyToNativeBuffer(name2)) {
symlinkat0(targetBuffer.address(), fd, linkBuffer.address());
}
}
private static native void symlinkat0(long name1, int dfd, long name2)
throws UnixException;
/**
* stat(const char* path, struct stat* buf)
*/

View File

@ -27,11 +27,35 @@ package sun.nio.fs;
import java.io.IOException;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.util.*;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.ClosedDirectoryStreamException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.LinkOption;
import java.nio.file.NotDirectoryException;
import java.nio.file.NotLinkException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.ProviderMismatchException;
import java.nio.file.SecureDirectoryStream;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.attribute.FileOwnerAttributeView;
import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.GroupPrincipal;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.UserPrincipal;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static java.nio.file.StandardOpenOption.CREATE_NEW;
import static java.nio.file.StandardOpenOption.WRITE;
import static sun.nio.fs.UnixNativeDispatcher.*;
import static sun.nio.fs.UnixConstants.*;
@ -154,6 +178,129 @@ class UnixSecureDirectoryStream
}
}
@Override
public Path createFile(Path path, FileAttribute<?>... attrs)
throws IOException
{
newByteChannel(path, Set.of(CREATE_NEW, WRITE), attrs).close();
return path;
}
@Override
public Path createDirectory(Path dir, FileAttribute<?>... attrs)
throws IOException
{
UnixPath file = getName(dir);
int mode = UnixFileModeAttribute
.toUnixMode(UnixFileModeAttribute.ALL_PERMISSIONS, attrs);
ds.readLock().lock();
try {
if (!ds.isOpen())
throw new ClosedDirectoryStreamException();
try {
mkdirat(dfd, file, mode);
} catch (UnixException x) {
if (x.errno() == EISDIR)
throw new FileAlreadyExistsException(file.toString());
x.rethrowAsIOException(file);
return null; // keep compiler happy
}
} finally {
ds.readLock().unlock();
}
return dir;
}
@Override
public Path createLink(Path link, SecureDirectoryStream<Path> dir,
Path target)
throws IOException
{
UnixPath linkpath = UnixPath.toUnixPath(link);
UnixPath targetpath = UnixPath.toUnixPath(target);
ds.readLock().lock();
try {
if (!ds.isOpen())
throw new ClosedDirectoryStreamException();
try {
UnixSecureDirectoryStream that = (UnixSecureDirectoryStream)dir;
linkat(that.dfd, targetpath, this.dfd, linkpath);
} catch (UnixException x) {
x.rethrowAsIOException(linkpath, targetpath);
return null; // keep compiler happy
}
} finally {
ds.readLock().unlock();
}
return link;
}
@Override
public Path createSymbolicLink(Path link, Path target,
FileAttribute<?>... attrs)
throws IOException
{
UnixPath linkpath = UnixPath.toUnixPath(link);
UnixPath targetpath = UnixPath.toUnixPath(target);
// no attributes supported when creating links
if (attrs.length > 0) {
UnixFileModeAttribute.toUnixMode(0, attrs); // may throw NPE or UOE
throw new UnsupportedOperationException("Initial file attributes" +
" not supported when creating symbolic link");
}
ds.readLock().lock();
try {
if (!ds.isOpen())
throw new ClosedDirectoryStreamException();
try {
symlinkat(targetpath.asByteArray(), dfd, linkpath);
} catch (UnixException x) {
x.rethrowAsIOException(linkpath, targetpath);
return null; // keep compiler happy
}
} finally {
ds.readLock().unlock();
}
return link;
}
@Override
public Path readSymbolicLink(Path link) throws IOException {
UnixPath linkpath = getName(link);
ds.readLock().lock();
try {
if (!ds.isOpen())
throw new ClosedDirectoryStreamException();
try {
UnixFileAttributes attrs =
UnixFileAttributes.getIfExists(linkpath, false);
if (attrs != null && !attrs.isSymbolicLink())
throw new NotLinkException(linkpath.toString());
} catch (UnixException x) {
x.rethrowAsIOException(linkpath);
}
try {
byte[] target = readlinkat(dfd, linkpath);
return new UnixPath(linkpath.getFileSystem(), target);
} catch (UnixException x) {
x.rethrowAsIOException(linkpath);
return null; // keep compiler happy
}
} finally {
ds.readLock().unlock();
}
}
/**
* Deletes file/directory in this directory. Works in a race-free manner
* when invoked with flags.

View File

@ -989,6 +989,18 @@ Java_sun_nio_fs_UnixNativeDispatcher_mkdir0(JNIEnv* env, jclass this,
}
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_mkdirat0(JNIEnv* env, jclass this,
jint dfd, jlong pathAddress, jint mode)
{
const char* path = (const char*)jlong_to_ptr(pathAddress);
/* EINTR not listed as a possible error */
if (mkdirat(dfd, path, (mode_t)mode) == -1) {
throwUnixException(env, errno);
}
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_rmdir0(JNIEnv* env, jclass this,
jlong pathAddress)
@ -1015,6 +1027,20 @@ Java_sun_nio_fs_UnixNativeDispatcher_link0(JNIEnv* env, jclass this,
}
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_linkat0(JNIEnv* env, jclass this,
int dfd1, long addr1,
int dfd2, long addr2)
{
int err;
const char* name1 = (const char*)jlong_to_ptr(addr1);
const char* name2 = (const char*)jlong_to_ptr(addr2);
RESTARTABLE(linkat(dfd1, name1, dfd2, name2, AT_SYMLINK_FOLLOW), err);
if (err == -1) {
throwUnixException(env, errno);
}
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_unlink0(JNIEnv* env, jclass this,
@ -1089,6 +1115,19 @@ Java_sun_nio_fs_UnixNativeDispatcher_symlink0(JNIEnv* env, jclass this,
}
}
JNIEXPORT void JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_symlinkat0(JNIEnv* env, jclass this,
jlong targetAddress, jint dfd, jlong linkAddress)
{
const char* target = (const char*)jlong_to_ptr(targetAddress);
const char* link = (const char*)jlong_to_ptr(linkAddress);
/* EINTR not listed as a possible error */
if (symlinkat(target, dfd, link) == -1) {
throwUnixException(env, errno);
}
}
JNIEXPORT jbyteArray JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_readlink0(JNIEnv* env, jclass this,
jlong pathAddress)
@ -1119,6 +1158,36 @@ Java_sun_nio_fs_UnixNativeDispatcher_readlink0(JNIEnv* env, jclass this,
return result;
}
JNIEXPORT jbyteArray JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_readlinkat0(JNIEnv* env, jclass this,
jint fd, jlong pathAddress)
{
jbyteArray result = NULL;
char target[PATH_MAX+1];
const char* path = (const char*)jlong_to_ptr(pathAddress);
/* EINTR not listed as a possible error */
int n = readlinkat(fd, path, target, sizeof(target));
if (n == -1) {
throwUnixException(env, errno);
} else {
jsize len;
if (n == sizeof(target)) {
/* Traditionally readlink(2) should not return more than */
/* PATH_MAX bytes (no terminating null byte is appended). */
throwUnixException(env, ENAMETOOLONG);
return NULL;
}
target[n] = '\0';
len = (jsize)strlen(target);
result = (*env)->NewByteArray(env, len);
if (result != NULL) {
(*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)target);
}
}
return result;
}
JNIEXPORT jbyteArray JNICALL
Java_sun_nio_fs_UnixNativeDispatcher_realpath0(JNIEnv* env, jclass this,
jlong pathAddress)

View File

@ -22,7 +22,7 @@
*/
/* @test
* @bug 4313887 6838333 8343020 8357425
* @bug 4313887 6838333 8343020 8357425 8356493
* @summary Unit test for java.nio.file.SecureDirectoryStream
* @requires (os.family == "linux" | os.family == "mac" | os.family == "aix")
* @library .. /test/lib
@ -31,21 +31,24 @@
*/
import java.nio.file.*;
import static java.nio.file.Files.*;
import static java.nio.file.StandardOpenOption.*;
import static java.nio.file.LinkOption.*;
import java.nio.file.attribute.*;
import java.nio.channels.*;
import java.io.IOException;
import java.util.*;
import static java.nio.file.Files.*;
import static java.nio.file.StandardOpenOption.*;
import static java.nio.file.LinkOption.*;
import jdk.test.lib.Platform;
public class SecureDS {
static boolean supportsSymbolicLinks;
public static void main(String[] args) throws IOException {
Path dir = TestUtil.createTemporaryDirectory();
String cwd = System.getProperty("user.dir");
Path dir = TestUtil.createTemporaryDirectory(cwd);
System.err.println("Top level test directory: " + dir);
try {
DirectoryStream<Path> stream = newDirectoryStream(dir);
stream.close();
@ -164,6 +167,141 @@ public class SecureDS {
} catch (IOException x) { }
}
// Test: createFile
Path somefile = Path.of("somefile");
// - absolute -
Path absfile = dir.resolve(somefile);
stream.createFile(absfile);
assertTrue(exists(absfile));
assertTrue(isRegularFile(absfile));
try {
stream.createFile(absfile);
shouldNotGetHere();
} catch (FileAlreadyExistsException x) {
}
stream.deleteFile(absfile);
// - relative -
Path relfile = dir2.resolve(somefile);
stream.createFile(relfile);
assertTrue(exists(relfile));
assertTrue(isRegularFile(relfile));
try {
stream.createFile(relfile);
shouldNotGetHere();
} catch (FileAlreadyExistsException x) {
}
stream.deleteFile(relfile);
// Test: createDirectory
Path somedir = Path.of("somedir");
// - absolute -
Path absdir = dir.resolve(somedir);
stream.createDirectory(absdir);
assertTrue(exists(absdir));
assertTrue(isDirectory(absdir));
try {
stream.createDirectory(absdir);
shouldNotGetHere();
} catch (FileAlreadyExistsException x) {
}
stream.deleteDirectory(absdir);
// - relative -
Path reldir = dir2.resolve(somedir);
stream.createDirectory(somedir);
assertTrue(exists(reldir));
assertTrue(isDirectory(reldir));
try {
stream.createDirectory(reldir);
shouldNotGetHere();
} catch (FileAlreadyExistsException x) {
}
stream.deleteDirectory(reldir);
// Test: createLink
Path somehardlink = Path.of("somehardlink");
Path someexisting = Path.of("someexisting");
// - absolute -
Path abshardlink = dir.resolve(somehardlink);
Path absexisting = dir.resolve(someexisting);
createFile(absexisting);
String text1 = "I simply don't know what to say.";
writeString(absexisting, text1);
stream.createLink(abshardlink, stream, absexisting);
assertTrue(exists(abshardlink));
assertTrue(text1.equals(readString(abshardlink)));
try {
stream.createLink(abshardlink, stream, absexisting);
shouldNotGetHere();
} catch (FileAlreadyExistsException x) {
}
stream.deleteFile(absexisting);
try {
stream.createLink(abshardlink, stream, absexisting);
shouldNotGetHere();
} catch (NoSuchFileException x) {
}
stream.deleteFile(abshardlink);
// - relative -
Path relhardlink = dir2.resolve(somehardlink);
Path relexisting = dir2.resolve(someexisting);
createFile(relexisting);
String text2 = "I still don't know what to say.";
writeString(relexisting, text2);
stream.createLink(somehardlink, stream, someexisting);
assertTrue(exists(relhardlink));
assertTrue(text2.equals(readString(relhardlink)));
try {
stream.createLink(somehardlink, stream, someexisting);
shouldNotGetHere();
} catch (FileAlreadyExistsException x) {
}
stream.deleteFile(relexisting);
try {
stream.createLink(somehardlink, stream, someexisting);
shouldNotGetHere();
} catch (NoSuchFileException x) {
}
stream.deleteFile(relhardlink);
if (supportsSymbolicLinks) {
// Tests: createSymbolicLink and readSymbolicLink
Path target = dir.resolve(Path.of("target"));
assertTrue(exists(createFile(target)));
Path somesymlink = Path.of("somesymlink");
// - absolute -
Path abssymlink = dir.resolve(somesymlink);
stream.createSymbolicLink(abssymlink, target);
assertTrue(exists(abssymlink, NOFOLLOW_LINKS));
assertTrue(isSymbolicLink(abssymlink));
try {
stream.createSymbolicLink(abssymlink, target);
shouldNotGetHere();
} catch (FileAlreadyExistsException x) {
}
assertTrue(target.equals(stream.readSymbolicLink(abssymlink)));
try {
stream.readSymbolicLink(target);
shouldNotGetHere();
} catch (NotLinkException x) {
}
delete(target);
stream.deleteFile(abssymlink);
// - relative -
Path relsymlink = dir2.resolve(somesymlink);
stream.createSymbolicLink(somesymlink, target);
assertTrue(exists(relsymlink, NOFOLLOW_LINKS));
assertTrue(isSymbolicLink(relsymlink));
try {
stream.createSymbolicLink(relsymlink, target);
shouldNotGetHere();
} catch (FileAlreadyExistsException x) {
}
assertTrue(target.equals(stream.readSymbolicLink(relsymlink)));
stream.deleteFile(relsymlink);
}
// Test: delete
if (supportsSymbolicLinks) {
stream.deleteFile(link1Entry);
@ -346,6 +484,42 @@ public class SecureDS {
stream.newDirectoryStream(null);
shouldNotGetHere();
} catch (NullPointerException x) { }
try {
stream.createFile(null);
shouldNotGetHere();
} catch (NullPointerException x) { }
try {
stream.createDirectory(null);
shouldNotGetHere();
} catch (NullPointerException x) { }
Path link = Path.of("link");
try {
stream.createLink(null, stream, file);
shouldNotGetHere();
} catch (NullPointerException x) { }
try {
stream.createLink(link, null, file);
shouldNotGetHere();
} catch (NullPointerException x) { }
try {
stream.createLink(link, stream, null);
shouldNotGetHere();
} catch (NullPointerException x) { }
if (supportsSymbolicLinks) {
Path symlink = Path.of("symlink");
try {
stream.createSymbolicLink(null, file);
shouldNotGetHere();
} catch (NullPointerException x) { }
try {
stream.createSymbolicLink(symlink, null);
shouldNotGetHere();
} catch (NullPointerException x) { }
try {
stream.readSymbolicLink(null);
shouldNotGetHere();
} catch (NullPointerException x) { }
}
try {
stream.deleteFile(null);
shouldNotGetHere();
@ -372,6 +546,32 @@ public class SecureDS {
stream.move(file, stream, file);
shouldNotGetHere();
} catch (ClosedDirectoryStreamException x) { }
try {
stream.createFile(Path.of("nofile"));
shouldNotGetHere();
} catch (ClosedDirectoryStreamException x) { }
try {
stream.createDirectory(Path.of("nodir"));
shouldNotGetHere();
} catch (ClosedDirectoryStreamException x) { }
link = Path.of("nohardlink");
Path target = Path.of("nohardtarget");
try {
stream.createLink(link, null, target);
shouldNotGetHere();
} catch (ClosedDirectoryStreamException x) { }
if (supportsSymbolicLinks) {
link = Path.of("nosymlink");
target = Path.of("nosofttarget");
try {
stream.createSymbolicLink(link, target);
shouldNotGetHere();
} catch (ClosedDirectoryStreamException x) { }
try {
stream.readSymbolicLink(link);
shouldNotGetHere();
} catch (ClosedDirectoryStreamException x) { }
}
try {
stream.deleteFile(file);
shouldNotGetHere();
@ -385,6 +585,10 @@ public class SecureDS {
if (!b) throw new RuntimeException("Assertion failed");
}
static void assertFalse(boolean b) {
if (b) throw new RuntimeException("Assertion failed");
}
static void shouldNotGetHere() {
assertTrue(false);
}