Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8343785: (fs) Remove syscalls that set file times with microsecond precision #21989

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 39 additions & 37 deletions src/java.base/macosx/classes/sun/nio/fs/BsdFileAttributeViews.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2024, 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
Expand Down Expand Up @@ -29,12 +29,15 @@
import java.nio.file.attribute.FileTime;
import java.util.concurrent.TimeUnit;
import static sun.nio.fs.BsdNativeDispatcher.*;
import static sun.nio.fs.UnixNativeDispatcher.lutimes;
import static sun.nio.fs.UnixConstants.ELOOP;
import static sun.nio.fs.UnixConstants.ENXIO;
import static sun.nio.fs.UnixNativeDispatcher.futimens;
import static sun.nio.fs.UnixNativeDispatcher.utimensat;

class BsdFileAttributeViews {
//
// Use setattrlist(2) system call which can set creation, modification,
// and access times.
// Use the futimens(2)/utimensat(2) system calls to set the access and
// modification times, and setattrlist(2) to set the creation time.
//
private static void setTimes(UnixPath path, FileTime lastModifiedTime,
FileTime lastAccessTime, FileTime createTime,
Expand All @@ -50,31 +53,29 @@ private static void setTimes(UnixPath path, FileTime lastModifiedTime,
// permission check
path.checkWrite();

boolean useLutimes = false;
// use a file descriptor if possible to avoid a race due to accessing
// a path more than once as the file at that path could change.
// if path is a symlink, then the open should fail with ELOOP and
// the path will be used instead of the file descriptor.
int fd = -1;
try {
useLutimes = !followLinks &&
UnixFileAttributes.get(path, false).isSymbolicLink();
fd = path.openForAttributeAccess(followLinks);
} catch (UnixException x) {
x.rethrowAsIOException(path);
}

int fd = -1;
if (!useLutimes) {
try {
fd = path.openForAttributeAccess(followLinks);
} catch (UnixException x) {
if (!(x.errno() == ENXIO || (x.errno() == ELOOP))) {
x.rethrowAsIOException(path);
}
}

try {
// not all volumes support setattrlist(2), so set the last
// modified and last access times using futimens(2)/lutimes(3)
// modified and last access times use futimens(2)/utimensat(2)
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);
UnixFileAttributes attrs = fd >= 0 ?
UnixFileAttributes.get(fd) :
UnixFileAttributes.get(path, followLinks);
if (lastModifiedTime == null)
lastModifiedTime = attrs.lastModifiedTime();
if (lastAccessTime == null)
Expand All @@ -85,20 +86,21 @@ private static void setTimes(UnixPath path, FileTime lastModifiedTime,
}

// update times
TimeUnit timeUnit = useLutimes ?
TimeUnit.MICROSECONDS : TimeUnit.NANOSECONDS;
long modValue = lastModifiedTime.to(timeUnit);
long accessValue= lastAccessTime.to(timeUnit);
long modValue = lastModifiedTime.to(TimeUnit.NANOSECONDS);
long accessValue= lastAccessTime.to(TimeUnit.NANOSECONDS);

boolean retry = false;
int flags = followLinks ? 0 : UnixConstants.AT_SYMLINK_NOFOLLOW;
try {
if (useLutimes)
lutimes(path, accessValue, modValue);
else
if (fd >= 0)
futimens(fd, accessValue, modValue);
else
utimensat(UnixConstants.AT_FDCWD, path, accessValue,
modValue, flags);
} 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 futimens/utimensat 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;
Expand All @@ -110,34 +112,34 @@ private static void setTimes(UnixPath path, FileTime lastModifiedTime,
if (modValue < 0L) modValue = 0L;
if (accessValue < 0L) accessValue= 0L;
try {
if (useLutimes)
lutimes(path, accessValue, modValue);
else
if (fd >= 0)
futimens(fd, accessValue, modValue);
else
utimensat(UnixConstants.AT_FDCWD, path, accessValue,
modValue, flags);
} catch (UnixException x) {
x.rethrowAsIOException(path);
}
}
}

// set the creation time using setattrlist
// set the creation time using setattrlist(2)
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
if (fd >= 0)
fsetattrlist(fd, commonattr, 0L, 0L, createValue,
followLinks ? 0 : UnixConstants.FSOPT_NOFOLLOW);
followLinks ? 0 : UnixConstants.FSOPT_NOFOLLOW);
else
setattrlist(path, commonattr, 0L, 0L, createValue,
followLinks ? 0 : UnixConstants.FSOPT_NOFOLLOW);
} catch (UnixException x) {
x.rethrowAsIOException(path);
}
}
} finally {
if (!useLutimes)
close(fd, e -> null);
close(fd, e -> null);
}
}

Expand Down
66 changes: 17 additions & 49 deletions src/java.base/unix/classes/sun/nio/fs/UnixFileAttributeViews.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,42 +72,25 @@ public void setTimes(FileTime lastModifiedTime,
// permission check
file.checkWrite();

boolean haveFd = false;
boolean useFutimes = false;
boolean useFutimens = false;
boolean useLutimes = false;
boolean useUtimensat = false;
// use a file descriptor if possible to avoid a race due to
// accessing a path more than once as the file at that path could
// change.
// if path is a symlink, then the open should fail with ELOOP and
// the path will be used instead of the file descriptor.
int fd = -1;
try {
if (!followLinks) {
// these path-based syscalls also work if following links
if (!(useUtimensat = utimensatSupported())) {
useLutimes = lutimesSupported();
}
}
if (!useUtimensat && !useLutimes) {
fd = file.openForAttributeAccess(followLinks);
if (fd != -1) {
haveFd = true;
if (!(useFutimens = futimensSupported())) {
useFutimes = futimesSupported();
}
}
}
fd = file.openForAttributeAccess(followLinks);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This throws if O_NOFOLLOW is not supported. Are there any platforms where this is possible?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe some old AIX versions. But they are no longer supported. We require 7.2.

} catch (UnixException x) {
if (!(x.errno() == ENXIO ||
(x.errno() == ELOOP && (useUtimensat || useLutimes)))) {
if (!(x.errno() == ENXIO || (x.errno() == ELOOP))) {
x.rethrowAsIOException(file);
}
}

try {
// assert followLinks || !UnixFileAttributes.get(fd).isSymbolicLink();

// if not changing both attributes then need existing attributes
if (lastModifiedTime == null || lastAccessTime == null) {
try {
UnixFileAttributes attrs = haveFd ?
UnixFileAttributes attrs = fd >= 0 ?
UnixFileAttributes.get(fd) :
UnixFileAttributes.get(file, followLinks);
if (lastModifiedTime == null)
Expand All @@ -120,28 +103,20 @@ public void setTimes(FileTime lastModifiedTime,
}

// update times
TimeUnit timeUnit = (useFutimens || useUtimensat) ?
TimeUnit.NANOSECONDS : TimeUnit.MICROSECONDS;
long modValue = lastModifiedTime.to(timeUnit);
long accessValue= lastAccessTime.to(timeUnit);
long modValue = lastModifiedTime.to(TimeUnit.NANOSECONDS);
long accessValue= lastAccessTime.to(TimeUnit.NANOSECONDS);

boolean retry = false;
try {
if (useFutimens) {
if (fd >= 0)
futimens(fd, accessValue, modValue);
} else if (useFutimes) {
futimes(fd, accessValue, modValue);
} else if (useLutimes) {
lutimes(file, accessValue, modValue);
} else if (useUtimensat) {
else
utimensat(AT_FDCWD, file, accessValue, modValue,
followLinks ? 0 : AT_SYMLINK_NOFOLLOW);
} else {
utimes(file, accessValue, modValue);
}
} catch (UnixException x) {
// 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 utimensat fails with EINVAL and one/both of
// the times is negative then we adjust the value to the
// epoch and retry.
if (x.errno() == EINVAL &&
(modValue < 0L || accessValue < 0L)) {
retry = true;
Expand All @@ -153,18 +128,11 @@ public void setTimes(FileTime lastModifiedTime,
if (modValue < 0L) modValue = 0L;
if (accessValue < 0L) accessValue= 0L;
try {
if (useFutimens) {
if (fd >= 0)
futimens(fd, accessValue, modValue);
} else if (useFutimes) {
futimes(fd, accessValue, modValue);
} else if (useLutimes) {
lutimes(file, accessValue, modValue);
} else if (useUtimensat) {
else
utimensat(AT_FDCWD, file, accessValue, modValue,
followLinks ? 0 : AT_SYMLINK_NOFOLLOW);
} else {
utimes(file, accessValue, modValue);
}
} catch (UnixException x) {
x.rethrowAsIOException(file);
}
Expand Down
33 changes: 14 additions & 19 deletions src/java.base/unix/classes/sun/nio/fs/UnixFileSystem.java
Original file line number Diff line number Diff line change
Expand Up @@ -577,14 +577,14 @@ private void copyDirectory(UnixPath source,
// copy time stamps last
if (flags.copyBasicAttributes) {
try {
if (dfd >= 0 && futimesSupported()) {
futimes(dfd,
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
if (dfd >= 0) {
futimens(dfd,
attrs.lastAccessTime().to(TimeUnit.NANOSECONDS),
attrs.lastModifiedTime().to(TimeUnit.NANOSECONDS));
} else {
utimes(target,
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
utimensat(AT_FDCWD, target,
attrs.lastAccessTime().to(TimeUnit.NANOSECONDS),
attrs.lastModifiedTime().to(TimeUnit.NANOSECONDS), 0);
}
} catch (UnixException x) {
// unable to set times
Expand Down Expand Up @@ -727,15 +727,9 @@ void copyFile(UnixPath source,
// copy time attributes
if (flags.copyBasicAttributes) {
try {
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));
}
futimens(fo,
attrs.lastAccessTime().to(TimeUnit.NANOSECONDS),
attrs.lastModifiedTime().to(TimeUnit.NANOSECONDS));
} catch (UnixException x) {
if (flags.failIfUnableToCopyBasic)
x.rethrowAsIOException(target);
Expand Down Expand Up @@ -814,9 +808,10 @@ private void copySpecial(UnixPath source,
}
if (flags.copyBasicAttributes) {
try {
utimes(target,
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
utimensat(AT_FDCWD, target,
attrs.lastAccessTime().to(TimeUnit.NANOSECONDS),
attrs.lastModifiedTime().to(TimeUnit.NANOSECONDS),
0);
} catch (UnixException x) {
if (flags.failIfUnableToCopyBasic)
x.rethrowAsIOException(target);
Expand Down
Loading