8011536: (fs) BasicFileAttributes.creationTime() should return birth time (mac)
Reviewed-by: chegar
This commit is contained in:
parent
95fadac5c6
commit
6c1e93b7c6
@ -147,11 +147,11 @@ public interface BasicFileAttributeView
|
||||
* this method has no effect.
|
||||
*
|
||||
* <p> <b>Usage Example:</b>
|
||||
* Suppose we want to change a file's creation time.
|
||||
* Suppose we want to change a file's last access time.
|
||||
* <pre>
|
||||
* Path path = ...
|
||||
* FileTime time = ...
|
||||
* Files.getFileAttributeView(path, BasicFileAttributeView.class).setTimes(null, null, time);
|
||||
* Files.getFileAttributeView(path, BasicFileAttributeView.class).setTimes(null, time, null);
|
||||
* </pre>
|
||||
*
|
||||
* @param lastModifiedTime
|
||||
|
@ -84,7 +84,7 @@ class UnixChannelFactory {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (option == LinkOption.NOFOLLOW_LINKS && supportsNoFollowLinks()) {
|
||||
if (option == LinkOption.NOFOLLOW_LINKS && O_NOFOLLOW != 0) {
|
||||
flags.noFollowLinks = true;
|
||||
continue;
|
||||
}
|
||||
@ -218,7 +218,7 @@ class UnixChannelFactory {
|
||||
// follow links by default
|
||||
boolean followLinks = true;
|
||||
if (!flags.createNew && (flags.noFollowLinks || flags.deleteOnClose)) {
|
||||
if (flags.deleteOnClose && !supportsNoFollowLinks()) {
|
||||
if (flags.deleteOnClose && O_NOFOLLOW == 0) {
|
||||
try {
|
||||
if (UnixFileAttributes.get(path, false).isSymbolicLink())
|
||||
throw new UnixException("DELETE_ON_CLOSE specified and file is a symbolic link");
|
||||
|
@ -189,7 +189,7 @@ class UnixCopyFile {
|
||||
// copy time stamps last
|
||||
if (flags.copyBasicAttributes) {
|
||||
try {
|
||||
if (dfd >= 0) {
|
||||
if (dfd >= 0 && futimesSupported()) {
|
||||
futimes(dfd,
|
||||
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
|
||||
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
|
||||
@ -269,9 +269,15 @@ class UnixCopyFile {
|
||||
// copy time attributes
|
||||
if (flags.copyBasicAttributes) {
|
||||
try {
|
||||
futimes(fo,
|
||||
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
|
||||
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
|
||||
if (futimesSupported()) {
|
||||
futimes(fo,
|
||||
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
|
||||
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
|
||||
} else {
|
||||
utimes(target,
|
||||
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
|
||||
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
|
||||
}
|
||||
} catch (UnixException x) {
|
||||
if (flags.failIfUnableToCopyBasic)
|
||||
x.rethrowAsIOException(target);
|
||||
|
@ -73,6 +73,8 @@ class UnixFileAttributeViews {
|
||||
|
||||
int fd = file.openForAttributeAccess(followLinks);
|
||||
try {
|
||||
// assert followLinks || !UnixFileAttributes.get(fd).isSymbolicLink();
|
||||
|
||||
// if not changing both attributes then need existing attributes
|
||||
if (lastModifiedTime == null || lastAccessTime == null) {
|
||||
try {
|
||||
@ -92,9 +94,13 @@ class UnixFileAttributeViews {
|
||||
|
||||
boolean retry = false;
|
||||
try {
|
||||
futimes(fd, accessValue, modValue);
|
||||
if (futimesSupported()) {
|
||||
futimes(fd, accessValue, modValue);
|
||||
} else {
|
||||
utimes(file, accessValue, modValue);
|
||||
}
|
||||
} catch (UnixException x) {
|
||||
// if futimes fails with EINVAL and one/both of the times is
|
||||
// if futimes/utimes fails with EINVAL and one/both of the times is
|
||||
// negative then we adjust the value to the epoch and retry.
|
||||
if (x.errno() == UnixConstants.EINVAL &&
|
||||
(modValue < 0L || accessValue < 0L)) {
|
||||
@ -107,7 +113,11 @@ class UnixFileAttributeViews {
|
||||
if (modValue < 0L) modValue = 0L;
|
||||
if (accessValue < 0L) accessValue= 0L;
|
||||
try {
|
||||
futimes(fd, accessValue, modValue);
|
||||
if (futimesSupported()) {
|
||||
futimes(fd, accessValue, modValue);
|
||||
} else {
|
||||
utimes(file, accessValue, modValue);
|
||||
}
|
||||
} catch (UnixException x) {
|
||||
x.rethrowAsIOException(file);
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ class UnixFileAttributes
|
||||
private long st_mtime_nsec;
|
||||
private long st_ctime_sec;
|
||||
private long st_ctime_nsec;
|
||||
private long st_birthtime_sec;
|
||||
|
||||
// created lazily
|
||||
private volatile UserPrincipal owner;
|
||||
@ -139,7 +140,12 @@ class UnixFileAttributes
|
||||
|
||||
@Override
|
||||
public FileTime creationTime() {
|
||||
return lastModifiedTime();
|
||||
if (UnixNativeDispatcher.birthtimeSupported()) {
|
||||
return FileTime.from(st_birthtime_sec, TimeUnit.SECONDS);
|
||||
} else {
|
||||
// return last modified when birth time not supported
|
||||
return lastModifiedTime();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -394,9 +394,9 @@ public abstract class UnixFileSystemProvider
|
||||
if (filter == null)
|
||||
throw new NullPointerException();
|
||||
|
||||
// can't return SecureDirectoryStream on kernels that don't support
|
||||
// openat, etc.
|
||||
if (!supportsAtSysCalls() || !supportsNoFollowLinks()) {
|
||||
// can't return SecureDirectoryStream on kernels that don't support openat
|
||||
// or O_NOFOLLOW
|
||||
if (!openatSupported() || O_NOFOLLOW == 0) {
|
||||
try {
|
||||
long ptr = opendir(dir);
|
||||
return new UnixDirectoryStream(dir, ptr, filter);
|
||||
|
@ -537,30 +537,42 @@ class UnixNativeDispatcher {
|
||||
*/
|
||||
static native byte[] strerror(int errnum);
|
||||
|
||||
// indicates if openat, unlinkat, etc. is supported
|
||||
private static final boolean hasAtSysCalls;
|
||||
static boolean supportsAtSysCalls() {
|
||||
return hasAtSysCalls;
|
||||
/**
|
||||
* Capabilities
|
||||
*/
|
||||
private static final int SUPPORTS_OPENAT = 1 << 1; // syscalls
|
||||
private static final int SUPPORTS_FUTIMES = 1 << 2;
|
||||
private static final int SUPPORTS_BIRTHTIME = 1 << 16; // other features
|
||||
private static final int capabilities;
|
||||
|
||||
/**
|
||||
* Supports openat and other *at calls.
|
||||
*/
|
||||
static boolean openatSupported() {
|
||||
return (capabilities & SUPPORTS_OPENAT) != 0;
|
||||
}
|
||||
|
||||
static boolean supportsNoFollowLinks() {
|
||||
return UnixConstants.O_NOFOLLOW != 0;
|
||||
/**
|
||||
* Supports futimes or futimesat
|
||||
*/
|
||||
static boolean futimesSupported() {
|
||||
return (capabilities & SUPPORTS_FUTIMES) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports file birth (creation) time attribute
|
||||
*/
|
||||
static boolean birthtimeSupported() {
|
||||
return (capabilities & SUPPORTS_BIRTHTIME) != 0;
|
||||
}
|
||||
|
||||
// initialize syscalls and fieldIDs
|
||||
private static native int init();
|
||||
|
||||
// flags returned by init to indicate capabilities
|
||||
private static final int HAS_AT_SYSCALLS = 0x1;
|
||||
|
||||
static {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
public Void run() {
|
||||
System.loadLibrary("nio");
|
||||
return null;
|
||||
}});
|
||||
int flags = init();
|
||||
|
||||
hasAtSysCalls = (flags & HAS_AT_SYSCALLS) > 0;
|
||||
capabilities = init();
|
||||
}
|
||||
}
|
||||
|
@ -769,7 +769,7 @@ class UnixPath
|
||||
int openForAttributeAccess(boolean followLinks) throws IOException {
|
||||
int flags = O_RDONLY;
|
||||
if (!followLinks) {
|
||||
if (!supportsNoFollowLinks())
|
||||
if (O_NOFOLLOW == 0)
|
||||
throw new IOException("NOFOLLOW_LINKS is not supported on this platform");
|
||||
flags |= O_NOFOLLOW;
|
||||
}
|
||||
|
@ -97,6 +97,10 @@ static jfieldID attrs_st_mtime_nsec;
|
||||
static jfieldID attrs_st_ctime_sec;
|
||||
static jfieldID attrs_st_ctime_nsec;
|
||||
|
||||
#ifdef _DARWIN_FEATURE_64_BIT_INODE
|
||||
static jfieldID attrs_st_birthtime_sec;
|
||||
#endif
|
||||
|
||||
static jfieldID attrs_f_frsize;
|
||||
static jfieldID attrs_f_blocks;
|
||||
static jfieldID attrs_f_bfree;
|
||||
@ -171,7 +175,7 @@ static void throwUnixException(JNIEnv* env, int errnum) {
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this)
|
||||
{
|
||||
jint flags = 0;
|
||||
jint capabilities = 0;
|
||||
jclass clazz;
|
||||
|
||||
clazz = (*env)->FindClass(env, "sun/nio/fs/UnixFileAttributes");
|
||||
@ -193,6 +197,10 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this)
|
||||
attrs_st_ctime_sec = (*env)->GetFieldID(env, clazz, "st_ctime_sec", "J");
|
||||
attrs_st_ctime_nsec = (*env)->GetFieldID(env, clazz, "st_ctime_nsec", "J");
|
||||
|
||||
#ifdef _DARWIN_FEATURE_64_BIT_INODE
|
||||
attrs_st_birthtime_sec = (*env)->GetFieldID(env, clazz, "st_birthtime_sec", "J");
|
||||
#endif
|
||||
|
||||
clazz = (*env)->FindClass(env, "sun/nio/fs/UnixFileStoreAttributes");
|
||||
if (clazz == NULL) {
|
||||
return 0;
|
||||
@ -233,14 +241,31 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this)
|
||||
my_fstatat64_func = (fstatat64_func*)&fstatat64_wrapper;
|
||||
#endif
|
||||
|
||||
/* supports futimes or futimesat */
|
||||
|
||||
#ifdef _ALLBSD_SOURCE
|
||||
capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_FUTIMES;
|
||||
#else
|
||||
if (my_futimesat_func != NULL)
|
||||
capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_FUTIMES;
|
||||
#endif
|
||||
|
||||
/* supports openat, etc. */
|
||||
|
||||
if (my_openat64_func != NULL && my_fstatat64_func != NULL &&
|
||||
my_unlinkat_func != NULL && my_renameat_func != NULL &&
|
||||
my_futimesat_func != NULL && my_fdopendir_func != NULL)
|
||||
{
|
||||
flags |= sun_nio_fs_UnixNativeDispatcher_HAS_AT_SYSCALLS;
|
||||
capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_OPENAT;
|
||||
}
|
||||
|
||||
return flags;
|
||||
/* supports file birthtime */
|
||||
|
||||
#ifdef _DARWIN_FEATURE_64_BIT_INODE
|
||||
capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_BIRTHTIME;
|
||||
#endif
|
||||
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
@ -405,6 +430,10 @@ static void prepAttributes(JNIEnv* env, struct stat64* buf, jobject attrs) {
|
||||
(*env)->SetLongField(env, attrs, attrs_st_mtime_sec, (jlong)buf->st_mtime);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_ctime_sec, (jlong)buf->st_ctime);
|
||||
|
||||
#ifdef _DARWIN_FEATURE_64_BIT_INODE
|
||||
(*env)->SetLongField(env, attrs, attrs_st_birthtime_sec, (jlong)buf->st_birthtime);
|
||||
#endif
|
||||
|
||||
#if (_POSIX_C_SOURCE >= 200809L) || defined(__solaris__)
|
||||
(*env)->SetLongField(env, attrs, attrs_st_atime_nsec, (jlong)buf->st_atim.tv_nsec);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_mtime_nsec, (jlong)buf->st_mtim.tv_nsec);
|
||||
|
@ -70,22 +70,16 @@ public class Basic {
|
||||
check(f.lastModified()/1000 == attrs.lastModifiedTime().to(TimeUnit.SECONDS),
|
||||
"last-modified time should be the same");
|
||||
|
||||
// copy last-modified time and file create time from directory to file,
|
||||
// copy last-modified time from directory to file,
|
||||
// re-read attribtues, and check they match
|
||||
BasicFileAttributeView view =
|
||||
Files.getFileAttributeView(file, BasicFileAttributeView.class);
|
||||
BasicFileAttributes dirAttrs = Files.readAttributes(dir, BasicFileAttributes.class);
|
||||
view.setTimes(dirAttrs.lastModifiedTime(), null, null);
|
||||
if (dirAttrs.creationTime() != null) {
|
||||
view.setTimes(null, null, dirAttrs.creationTime());
|
||||
}
|
||||
|
||||
attrs = view.readAttributes();
|
||||
check(attrs.lastModifiedTime().equals(dirAttrs.lastModifiedTime()),
|
||||
"last-modified time should be equal");
|
||||
if (dirAttrs.creationTime() != null) {
|
||||
check(attrs.creationTime().equals(dirAttrs.creationTime()),
|
||||
"create time should be the same");
|
||||
}
|
||||
|
||||
// security tests
|
||||
check (!(attrs instanceof PosixFileAttributes),
|
||||
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 8011536
|
||||
* @summary Basic test for creationTime attribute on platforms/file systems
|
||||
* that support it.
|
||||
* @library ../..
|
||||
*/
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.attribute.*;
|
||||
import java.time.Instant;
|
||||
import java.io.IOException;
|
||||
|
||||
public class CreationTime {
|
||||
|
||||
private static final java.io.PrintStream err = System.err;
|
||||
|
||||
/**
|
||||
* Reads the creationTime attribute
|
||||
*/
|
||||
private static FileTime creationTime(Path file) throws IOException {
|
||||
return Files.readAttributes(file, BasicFileAttributes.class).creationTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the creationTime attribute
|
||||
*/
|
||||
private static void setCreationTime(Path file, FileTime time) throws IOException {
|
||||
BasicFileAttributeView view =
|
||||
Files.getFileAttributeView(file, BasicFileAttributeView.class);
|
||||
view.setTimes(null, null, time);
|
||||
}
|
||||
|
||||
static void test(Path top) throws IOException {
|
||||
Path file = Files.createFile(top.resolve("foo"));
|
||||
|
||||
/**
|
||||
* Check that creationTime reported
|
||||
*/
|
||||
FileTime creationTime = creationTime(file);
|
||||
Instant now = Instant.now();
|
||||
if (Math.abs(creationTime.toMillis()-now.toEpochMilli()) > 10000L) {
|
||||
err.println("File creation time reported as: " + creationTime);
|
||||
throw new RuntimeException("Expected to be close to: " + now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the creationTime attribute supported here?
|
||||
*/
|
||||
boolean supportsCreationTimeRead = false;
|
||||
boolean supportsCreationTimeWrite = false;
|
||||
String os = System.getProperty("os.name");
|
||||
if (os.contains("OS X") && Files.getFileStore(file).type().equals("hfs")) {
|
||||
supportsCreationTimeRead = true;
|
||||
} else if (os.startsWith("Windows")) {
|
||||
String type = Files.getFileStore(file).type();
|
||||
if (type.equals("NTFS") || type.equals("FAT")) {
|
||||
supportsCreationTimeRead = true;
|
||||
supportsCreationTimeWrite = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the creation-time attribute is supported then change the file's
|
||||
* last modified and check that it doesn't change the creation-time.
|
||||
*/
|
||||
if (supportsCreationTimeRead) {
|
||||
// change modified time by +1 hour
|
||||
Instant plusHour = Instant.now().plusSeconds(60L * 60L);
|
||||
Files.setLastModifiedTime(file, FileTime.from(plusHour));
|
||||
FileTime current = creationTime(file);
|
||||
if (!current.equals(creationTime))
|
||||
throw new RuntimeException("Creation time should not have changed");
|
||||
}
|
||||
|
||||
/**
|
||||
* If the creation-time attribute is supported and can be changed then
|
||||
* check that the change is effective.
|
||||
*/
|
||||
if (supportsCreationTimeWrite) {
|
||||
// change creation time by -1 hour
|
||||
Instant minusHour = Instant.now().minusSeconds(60L * 60L);
|
||||
creationTime = FileTime.from(minusHour);
|
||||
setCreationTime(file, creationTime);
|
||||
FileTime current = creationTime(file);
|
||||
if (Math.abs(creationTime.toMillis()-current.toMillis()) > 1000L)
|
||||
throw new RuntimeException("Creation time not changed");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
// create temporary directory to run tests
|
||||
Path dir = TestUtil.createTemporaryDirectory();
|
||||
try {
|
||||
test(dir);
|
||||
} finally {
|
||||
TestUtil.removeAll(dir);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user