8011536: (fs) BasicFileAttributes.creationTime() should return birth time (mac)
authoralanb
Thu, 18 Apr 2013 11:13:18 +0100
changeset 17154 e8991539c4d7
parent 16929 c984ae5655cb
child 17155 69080fe3eb45
8011536: (fs) BasicFileAttributes.creationTime() should return birth time (mac) Reviewed-by: chegar
jdk/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java
jdk/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java
jdk/src/solaris/classes/sun/nio/fs/UnixCopyFile.java
jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java
jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java
jdk/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java
jdk/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java
jdk/src/solaris/classes/sun/nio/fs/UnixPath.java
jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c
jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java
jdk/test/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java
--- 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);
+        }
+    }
+}