blob: d39131a6de2cd27fb398c2a26b054f7af4f42c7b [file]
/*
* 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
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.*;
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 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,
boolean followLinks) throws IOException
{
// null => don't change
if (lastModifiedTime == null && lastAccessTime == null &&
createTime == null) {
// no effect
return;
}
// 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 {
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 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 = fd >= 0 ?
UnixFileAttributes.get(fd) :
UnixFileAttributes.get(path, followLinks);
if (lastModifiedTime == null)
lastModifiedTime = attrs.lastModifiedTime();
if (lastAccessTime == null)
lastAccessTime = attrs.lastAccessTime();
} catch (UnixException x) {
x.rethrowAsIOException(path);
}
}
// update times
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 (fd >= 0)
futimens(fd, accessValue, modValue);
else
utimensat(UnixConstants.AT_FDCWD, path, accessValue,
modValue, flags);
} catch (UnixException x) {
// 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;
} else {
x.rethrowAsIOException(path);
}
}
if (retry) {
if (modValue < 0L) modValue = 0L;
if (accessValue < 0L) accessValue= 0L;
try {
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(2)
if (createTime != null) {
long createValue = createTime.to(TimeUnit.NANOSECONDS);
int commonattr = UnixConstants.ATTR_CMN_CRTIME;
try {
if (fd >= 0)
fsetattrlist(fd, commonattr, 0L, 0L, createValue,
followLinks ? 0 : UnixConstants.FSOPT_NOFOLLOW);
else
setattrlist(path, commonattr, 0L, 0L, createValue,
followLinks ? 0 : UnixConstants.FSOPT_NOFOLLOW);
} catch (UnixException x) {
x.rethrowAsIOException(path);
}
}
} finally {
close(fd, e -> null);
}
}
static class Basic extends UnixFileAttributeViews.Basic {
Basic(UnixPath file, boolean followLinks) {
super(file, followLinks);
}
@Override
public void setTimes(FileTime lastModifiedTime,
FileTime lastAccessTime,
FileTime createTime) throws IOException
{
BsdFileAttributeViews.setTimes(file, lastModifiedTime,
lastAccessTime, createTime,
followLinks);
}
}
static class Posix extends UnixFileAttributeViews.Posix {
Posix(UnixPath file, boolean followLinks) {
super(file, followLinks);
}
@Override
public void setTimes(FileTime lastModifiedTime,
FileTime lastAccessTime,
FileTime createTime) throws IOException
{
BsdFileAttributeViews.setTimes(file, lastModifiedTime,
lastAccessTime, createTime,
followLinks);
}
}
static class Unix extends UnixFileAttributeViews.Unix {
Unix(UnixPath file, boolean followLinks) {
super(file, followLinks);
}
@Override
public void setTimes(FileTime lastModifiedTime,
FileTime lastAccessTime,
FileTime createTime) throws IOException
{
BsdFileAttributeViews.setTimes(file, lastModifiedTime,
lastAccessTime, createTime,
followLinks);
}
}
static Basic createBasicView(UnixPath file, boolean followLinks) {
return new Basic(file, followLinks);
}
static Posix createPosixView(UnixPath file, boolean followLinks) {
return new Posix(file, followLinks);
}
static Unix createUnixView(UnixPath file, boolean followLinks) {
return new Unix(file, followLinks);
}
}