diff --git a/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java b/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java index 836b99e268e65be60e9aa1fc754c5d1dfa5e5b6a..2a8e2c9585bfaebbe6fb37c9a43a39be313c04e5 100644 --- a/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java +++ b/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java @@ -147,11 +147,11 @@ public interface BasicFileAttributeView * 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 --git a/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java b/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java index df00a728f2c980feaa72af2e70d2838c6359bad5..ec44ee1514ae4eee9e2e38d0aa9ce5a1579d57c5 100644 --- a/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java +++ b/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java @@ -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"); diff --git a/src/solaris/classes/sun/nio/fs/UnixCopyFile.java b/src/solaris/classes/sun/nio/fs/UnixCopyFile.java index c35133b202af7dfe712d06fa919ef99b55177c30..9c6e204414aa68a6965256af52de22572827cf57 100644 --- a/src/solaris/classes/sun/nio/fs/UnixCopyFile.java +++ b/src/solaris/classes/sun/nio/fs/UnixCopyFile.java @@ -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); diff --git a/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java b/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java index 3716e3fdef793f40b2a3034e21bb570e0e9b371c..c650758a6cfcab0a2c3f6c385b1339db243a59ad 100644 --- a/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java +++ b/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java @@ -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); } diff --git a/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java b/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java index b61b5d6e4a07e253e01b56c68ddb2730fe825ea0..47b61ac91810c382e3f89e75e0a5076294804607 100644 --- a/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java +++ b/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java @@ -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 diff --git a/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java b/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java index fc0f943a97a93fcf0ddf08c68696bff46b7314d4..553c4b2e8c81f2cbd8f77ee661ee8bfca9f339e6 100644 --- a/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java +++ b/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java @@ -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); diff --git a/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java b/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java index 5255c660dd1a4dc1c881e94980c7292c000e6e47..ec21e6df8efc5d0e11eb9b79f8623a8b7a213497 100644 --- a/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java +++ b/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java @@ -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; - static boolean supportsNoFollowLinks() { - return UnixConstants.O_NOFOLLOW != 0; + /** + * Supports openat and other *at calls. + */ + static boolean openatSupported() { + return (capabilities & SUPPORTS_OPENAT) != 0; } - // initialize syscalls and fieldIDs - private static native int init(); + /** + * Supports futimes or futimesat + */ + static boolean futimesSupported() { + return (capabilities & SUPPORTS_FUTIMES) != 0; + } - // flags returned by init to indicate capabilities - private static final int HAS_AT_SYSCALLS = 0x1; + /** + * Supports file birth (creation) time attribute + */ + static boolean birthtimeSupported() { + return (capabilities & SUPPORTS_BIRTHTIME) != 0; + } + 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 --git a/src/solaris/classes/sun/nio/fs/UnixPath.java b/src/solaris/classes/sun/nio/fs/UnixPath.java index b9410b4ee60090d891251749ee99fb59afe15afe..5738292027d1ea26be16b8981aab8ef039574c02 100644 --- a/src/solaris/classes/sun/nio/fs/UnixPath.java +++ b/src/solaris/classes/sun/nio/fs/UnixPath.java @@ -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; } diff --git a/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c b/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c index eb4698183df75f5a2595e7f3b7853e34975b59a0..8f408951e77bc124987de9ef45fe62ed886c2bef 100644 --- a/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c +++ b/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c @@ -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); diff --git a/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java b/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java index 40792dfde466557593cd43065786023a798f23fc..4459d071f24ddd415d0413a4dd7602c0f133df62 100644 --- a/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java +++ b/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java @@ -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), diff --git a/test/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java b/test/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java new file mode 100644 index 0000000000000000000000000000000000000000..16898feaec8a2747072b39ca4b3183d8d34efc41 --- /dev/null +++ b/test/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java @@ -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); + } + } +}