diff --git a/src/java.base/macosx/classes/sun/nio/fs/BsdFileAttributeViews.java b/src/java.base/macosx/classes/sun/nio/fs/BsdFileAttributeViews.java index 56e71ba033c..e071f25f22c 100644 --- a/src/java.base/macosx/classes/sun/nio/fs/BsdFileAttributeViews.java +++ b/src/java.base/macosx/classes/sun/nio/fs/BsdFileAttributeViews.java @@ -28,7 +28,8 @@ package sun.nio.fs; import java.io.IOException; import java.nio.file.attribute.FileTime; import java.util.concurrent.TimeUnit; -import static sun.nio.fs.BsdNativeDispatcher.setattrlist; +import static sun.nio.fs.BsdNativeDispatcher.*; +import static sun.nio.fs.UnixNativeDispatcher.lutimes; class BsdFileAttributeViews { // @@ -49,28 +50,94 @@ class BsdFileAttributeViews { // permission check path.checkWrite(); - int commonattr = 0; - long modValue = 0L; - if (lastModifiedTime != null) { - modValue = lastModifiedTime.to(TimeUnit.NANOSECONDS); - commonattr |= UnixConstants.ATTR_CMN_MODTIME; + boolean useLutimes = false; + try { + useLutimes = !followLinks && + UnixFileAttributes.get(path, false).isSymbolicLink(); + } catch (UnixException x) { + x.rethrowAsIOException(path); } - long accValue = 0L; - if (lastAccessTime != null) { - accValue = lastAccessTime.to(TimeUnit.NANOSECONDS); - commonattr |= UnixConstants.ATTR_CMN_ACCTIME; - } - long createValue = 0L; - if (createTime != null) { - createValue = createTime.to(TimeUnit.NANOSECONDS); - commonattr |= UnixConstants.ATTR_CMN_CRTIME; + + int fd = -1; + if (!useLutimes) { + try { + fd = path.openForAttributeAccess(followLinks); + } catch (UnixException x) { + x.rethrowAsIOException(path); + } } try { - setattrlist(path, commonattr, modValue, accValue, createValue, - followLinks ? 0 : UnixConstants.FSOPT_NOFOLLOW); - } catch (UnixException x) { - x.rethrowAsIOException(path); + // not all volumes support setattrlist(2), so set the last + // modified and last access times using futimens(2)/lutimes(3) + if (lastModifiedTime != null || lastAccessTime != null) { + // if not changing both attributes then need existing attributes + if (lastModifiedTime == null || lastAccessTime == null) { + try { + UnixFileAttributes attrs = UnixFileAttributes.get(fd); + if (lastModifiedTime == null) + lastModifiedTime = attrs.lastModifiedTime(); + if (lastAccessTime == null) + lastAccessTime = attrs.lastAccessTime(); + } catch (UnixException x) { + x.rethrowAsIOException(path); + } + } + + // update times + TimeUnit timeUnit = useLutimes ? + TimeUnit.MICROSECONDS : TimeUnit.NANOSECONDS; + long modValue = lastModifiedTime.to(timeUnit); + long accessValue= lastAccessTime.to(timeUnit); + + boolean retry = false; + try { + if (useLutimes) + lutimes(path, accessValue, modValue); + else + futimens(fd, accessValue, modValue); + } catch (UnixException x) { + // if futimens 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)) { + retry = true; + } else { + x.rethrowAsIOException(path); + } + } + if (retry) { + if (modValue < 0L) modValue = 0L; + if (accessValue < 0L) accessValue= 0L; + try { + if (useLutimes) + lutimes(path, accessValue, modValue); + else + futimens(fd, accessValue, modValue); + } catch (UnixException x) { + x.rethrowAsIOException(path); + } + } + } + + // set the creation time using setattrlist + if (createTime != null) { + long createValue = createTime.to(TimeUnit.NANOSECONDS); + int commonattr = UnixConstants.ATTR_CMN_CRTIME; + try { + if (useLutimes) + setattrlist(path, commonattr, 0L, 0L, createValue, + followLinks ? 0 : UnixConstants.FSOPT_NOFOLLOW); + else + fsetattrlist(fd, commonattr, 0L, 0L, createValue, + followLinks ? 0 : UnixConstants.FSOPT_NOFOLLOW); + } catch (UnixException x) { + x.rethrowAsIOException(path); + } + } + } finally { + if (!useLutimes) + close(fd, e -> null); } } diff --git a/src/java.base/macosx/classes/sun/nio/fs/BsdNativeDispatcher.java b/src/java.base/macosx/classes/sun/nio/fs/BsdNativeDispatcher.java index 0b6448ab42c..5a7de593c63 100644 --- a/src/java.base/macosx/classes/sun/nio/fs/BsdNativeDispatcher.java +++ b/src/java.base/macosx/classes/sun/nio/fs/BsdNativeDispatcher.java @@ -104,6 +104,27 @@ class BsdNativeDispatcher extends UnixNativeDispatcher { long createTime, long options) throws UnixException; + /** + * fsetattrlist(int fd, struct attrlist* attrList, void* attrBuf, + * size_t attrBufSize, unsigned long options) + */ + static void fsetattrlist(int fd, int commonattr, long modTime, + long accTime, long createTime, long options) + throws UnixException + { + long comp = Blocker.begin(); + try { + fsetattrlist0(fd, commonattr, modTime, accTime, + createTime, options); + } finally { + Blocker.end(comp); + } + } + private static native void fsetattrlist0(int fd, int commonattr, + long modTime, long accTime, + long createTime, long options) + throws UnixException; + // initialize field IDs private static native void initIDs(); diff --git a/src/java.base/macosx/native/libnio/fs/BsdNativeDispatcher.c b/src/java.base/macosx/native/libnio/fs/BsdNativeDispatcher.c index 325da794216..8776411be07 100644 --- a/src/java.base/macosx/native/libnio/fs/BsdNativeDispatcher.c +++ b/src/java.base/macosx/native/libnio/fs/BsdNativeDispatcher.c @@ -39,6 +39,7 @@ #define ISREADONLY MNT_RDONLY #endif #include +#include #include #include @@ -243,17 +244,11 @@ Java_sun_nio_fs_BsdNativeDispatcher_clonefile0(JNIEnv* env, jclass this, return 0; } -JNIEXPORT void JNICALL -Java_sun_nio_fs_BsdNativeDispatcher_setattrlist0(JNIEnv* env, jclass this, - jlong pathAddress, int commonattr, jlong modTime, jlong accTime, - jlong createTime, jlong options) +size_t initattrlist(jint commonattr, jlong modTime, jlong accTime, + jlong createTime, const int attrsize, char* buf, struct attrlist *attrList) { - const char* path = (const char*)jlong_to_ptr(pathAddress); - // attributes must align on 4-byte boundaries per the getattrlist(2) spec - const int attrsize = ((sizeof(struct timespec) + 3)/4)*4; - char buf[3*attrsize]; - int count = 0; + // attributes are ordered per the getattrlist(2) spec if ((commonattr & ATTR_CMN_CRTIME) != 0) { struct timespec* t = (struct timespec*)buf; @@ -274,12 +269,46 @@ Java_sun_nio_fs_BsdNativeDispatcher_setattrlist0(JNIEnv* env, jclass this, count++; } - struct attrlist attrList; - memset(&attrList, 0, sizeof(struct attrlist)); - attrList.bitmapcount = ATTR_BIT_MAP_COUNT; - attrList.commonattr = commonattr; + memset(attrList, 0, sizeof(struct attrlist)); + attrList->bitmapcount = ATTR_BIT_MAP_COUNT; + attrList->commonattr = commonattr; - if (setattrlist(path, &attrList, (void*)buf, count*attrsize, options) != 0) { + return count*attrsize; +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_BsdNativeDispatcher_setattrlist0(JNIEnv* env, jclass this, + jlong pathAddress, int commonattr, jlong modTime, jlong accTime, + jlong createTime, jlong options) +{ + const char* path = (const char*)jlong_to_ptr(pathAddress); + // attributes must align on 4-byte boundaries per the getattrlist(2) spec + const int attrsize = ((sizeof(struct timespec) + 3)/4)*4; + char buf[3*attrsize]; + + struct attrlist attrList; + size_t attrBufSize = initattrlist(commonattr, modTime, accTime, createTime, + attrsize, buf, &attrList); + + if (setattrlist(path, &attrList, (void*)buf, attrBufSize, options) != 0) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_BsdNativeDispatcher_fsetattrlist0(JNIEnv* env, jclass this, + jint fd, int commonattr, jlong modTime, jlong accTime, + jlong createTime, jlong options) +{ + // attributes must align on 4-byte boundaries per the getattrlist(2) spec + const int attrsize = ((sizeof(struct timespec) + 3)/4)*4; + char buf[3*attrsize]; + + struct attrlist attrList; + size_t attrBufSize = initattrlist(commonattr, modTime, accTime, createTime, + attrsize, buf, &attrList); + + if (fsetattrlist(fd, &attrList, (void*)buf, attrBufSize, options) != 0) { throwUnixException(env, errno); } } diff --git a/src/java.base/share/classes/java/nio/file/attribute/BasicFileAttributeView.java b/src/java.base/share/classes/java/nio/file/attribute/BasicFileAttributeView.java index 37508cfd200..97e6fb94cd3 100644 --- a/src/java.base/share/classes/java/nio/file/attribute/BasicFileAttributeView.java +++ b/src/java.base/share/classes/java/nio/file/attribute/BasicFileAttributeView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2022, 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 @@ -134,7 +134,7 @@ public interface BasicFileAttributeView * *

This method updates the file's timestamp attributes. The values are * converted to the epoch and precision supported by the file system. - * Converting from finer to coarser granularities result in precision loss. + * Converting from finer to coarser granularities results in precision loss. * The behavior of this method when attempting to set a timestamp that is * not supported or to a value that is outside the range supported by the * underlying file store is not defined. It may or not fail by throwing an diff --git a/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template b/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template index 459e2d7abd3..a8a521caf8e 100644 --- a/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template +++ b/src/java.base/unix/classes/sun/nio/fs/UnixConstants.java.template @@ -153,7 +153,7 @@ class UnixConstants { static final int PREFIX_CLONE_NOFOLLOW = CLONE_NOFOLLOW; static final int PREFIX_CLONE_NOOWNERCOPY = CLONE_NOOWNERCOPY; - // flags used with setattrlist + // flags used with fsetattrlist static final int PREFIX_ATTR_CMN_CRTIME = ATTR_CMN_CRTIME; static final int PREFIX_ATTR_CMN_MODTIME = ATTR_CMN_MODTIME; static final int PREFIX_ATTR_CMN_ACCTIME = ATTR_CMN_ACCTIME;