8011536: (fs) BasicFileAttributes.creationTime() should return birth time (mac)
Reviewed-by: chegar
--- a/jdk/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java Tue Apr 16 13:51:53 2013 -0400
+++ b/jdk/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java Thu Apr 18 11:13:18 2013 +0100
@@ -147,11 +147,11 @@
* 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
--- a/jdk/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java Tue Apr 16 13:51:53 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java Thu Apr 18 11:13:18 2013 +0100
@@ -84,7 +84,7 @@
}
continue;
}
- if (option == LinkOption.NOFOLLOW_LINKS && supportsNoFollowLinks()) {
+ if (option == LinkOption.NOFOLLOW_LINKS && O_NOFOLLOW != 0) {
flags.noFollowLinks = true;
continue;
}
@@ -218,7 +218,7 @@
// 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");
--- a/jdk/src/solaris/classes/sun/nio/fs/UnixCopyFile.java Tue Apr 16 13:51:53 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/fs/UnixCopyFile.java Thu Apr 18 11:13:18 2013 +0100
@@ -189,7 +189,7 @@
// 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 @@
// 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);
--- a/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java Tue Apr 16 13:51:53 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java Thu Apr 18 11:13:18 2013 +0100
@@ -73,6 +73,8 @@
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 @@
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 @@
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);
}
--- a/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java Tue Apr 16 13:51:53 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java Thu Apr 18 11:13:18 2013 +0100
@@ -51,6 +51,7 @@
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 @@
@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
--- a/jdk/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java Tue Apr 16 13:51:53 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java Thu Apr 18 11:13:18 2013 +0100
@@ -394,9 +394,9 @@
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);
--- a/jdk/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java Tue Apr 16 13:51:53 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java Thu Apr 18 11:13:18 2013 +0100
@@ -537,30 +537,42 @@
*/
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;
}
- // initialize syscalls and fieldIDs
- private static native int init();
+ /**
+ * Supports file birth (creation) time attribute
+ */
+ static boolean birthtimeSupported() {
+ return (capabilities & SUPPORTS_BIRTHTIME) != 0;
+ }
- // flags returned by init to indicate capabilities
- private static final int HAS_AT_SYSCALLS = 0x1;
-
+ private static native int init();
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();
}
}
--- a/jdk/src/solaris/classes/sun/nio/fs/UnixPath.java Tue Apr 16 13:51:53 2013 -0400
+++ b/jdk/src/solaris/classes/sun/nio/fs/UnixPath.java Thu Apr 18 11:13:18 2013 +0100
@@ -769,7 +769,7 @@
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;
}
--- a/jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c Tue Apr 16 13:51:53 2013 -0400
+++ b/jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c Thu Apr 18 11:13:18 2013 +0100
@@ -97,6 +97,10 @@
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 @@
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 @@
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 @@
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 @@
(*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);
--- a/jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java Tue Apr 16 13:51:53 2013 -0400
+++ b/jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java Thu Apr 18 11:13:18 2013 +0100
@@ -70,22 +70,16 @@
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),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java Thu Apr 18 11:13:18 2013 +0100
@@ -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);
+ }
+ }
+}