# HG changeset patch # User alanb # Date 1366279998 -3600 # Node ID e8991539c4d75b349e8b1110e128249395df376f # Parent c984ae5655cbfc6a8e75e36f37afd620a8546214 8011536: (fs) BasicFileAttributes.creationTime() should return birth time (mac) Reviewed-by: chegar diff -r c984ae5655cb -r e8991539c4d7 jdk/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.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. * *

Usage Example: - * Suppose we want to change a file's creation time. + * Suppose we want to change a file's last access time. *

      *    Path path = ...
      *    FileTime time = ...
-     *    Files.getFileAttributeView(path, BasicFileAttributeView.class).setTimes(null, null, time);
+     *    Files.getFileAttributeView(path, BasicFileAttributeView.class).setTimes(null, time, null);
      * 
* * @param lastModifiedTime diff -r c984ae5655cb -r e8991539c4d7 jdk/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java --- 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"); diff -r c984ae5655cb -r e8991539c4d7 jdk/src/solaris/classes/sun/nio/fs/UnixCopyFile.java --- 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); diff -r c984ae5655cb -r e8991539c4d7 jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java --- 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); } diff -r c984ae5655cb -r e8991539c4d7 jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java --- 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 diff -r c984ae5655cb -r e8991539c4d7 jdk/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java --- 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); diff -r c984ae5655cb -r e8991539c4d7 jdk/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java --- 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() { public Void run() { System.loadLibrary("nio"); return null; }}); - int flags = init(); - - hasAtSysCalls = (flags & HAS_AT_SYSCALLS) > 0; + capabilities = init(); } } diff -r c984ae5655cb -r e8991539c4d7 jdk/src/solaris/classes/sun/nio/fs/UnixPath.java --- 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; } diff -r c984ae5655cb -r e8991539c4d7 jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c --- 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); diff -r c984ae5655cb -r e8991539c4d7 jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java --- 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), diff -r c984ae5655cb -r e8991539c4d7 jdk/test/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java --- /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); + } + } +}