8316304: (fs) Add support for BasicFileAttributes.creationTime() for Linux
Reviewed-by: stuefe, alanb, bpb, mli
This commit is contained in:
parent
77d40ce166
commit
0275efac88
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2008, 2023, 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
|
||||
@ -25,10 +25,15 @@
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.attribute.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.Set;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.nio.file.attribute.GroupPrincipal;
|
||||
import java.nio.file.attribute.PosixFileAttributes;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.nio.file.attribute.UserPrincipal;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Unix implementation of PosixFileAttributes.
|
||||
@ -52,6 +57,7 @@ class UnixFileAttributes
|
||||
private long st_ctime_sec;
|
||||
private long st_ctime_nsec;
|
||||
private long st_birthtime_sec;
|
||||
private long st_birthtime_nsec;
|
||||
|
||||
// created lazily
|
||||
private volatile UserPrincipal owner;
|
||||
@ -158,7 +164,7 @@ class UnixFileAttributes
|
||||
@Override
|
||||
public FileTime creationTime() {
|
||||
if (UnixNativeDispatcher.birthtimeSupported()) {
|
||||
return FileTime.from(st_birthtime_sec, TimeUnit.SECONDS);
|
||||
return toFileTime(st_birthtime_sec, st_birthtime_nsec);
|
||||
} else {
|
||||
// return last modified when birth time not supported
|
||||
return lastModifiedTime();
|
||||
|
@ -51,6 +51,7 @@
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/sysmacros.h> // makedev macros
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(_AIX)
|
||||
@ -71,6 +72,98 @@
|
||||
#define readdir64 readdir
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
// Account for the case where we compile on a system without statx
|
||||
// support. We still want to ensure we can call statx at runtime
|
||||
// if the runtime glibc version supports it (>= 2.28). We do this
|
||||
// by defining binary compatible statx structs in this file and
|
||||
// not relying on included headers.
|
||||
|
||||
#ifndef __GLIBC__
|
||||
// Alpine doesn't know these types, define them
|
||||
typedef unsigned int __uint32_t;
|
||||
typedef unsigned short __uint16_t;
|
||||
typedef unsigned long int __uint64_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Timestamp structure for the timestamps in struct statx.
|
||||
*/
|
||||
struct my_statx_timestamp {
|
||||
int64_t tv_sec;
|
||||
__uint32_t tv_nsec;
|
||||
int32_t __reserved;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct statx used by statx system call on >= glibc 2.28
|
||||
* systems
|
||||
*/
|
||||
struct my_statx
|
||||
{
|
||||
__uint32_t stx_mask;
|
||||
__uint32_t stx_blksize;
|
||||
__uint64_t stx_attributes;
|
||||
__uint32_t stx_nlink;
|
||||
__uint32_t stx_uid;
|
||||
__uint32_t stx_gid;
|
||||
__uint16_t stx_mode;
|
||||
__uint16_t __statx_pad1[1];
|
||||
__uint64_t stx_ino;
|
||||
__uint64_t stx_size;
|
||||
__uint64_t stx_blocks;
|
||||
__uint64_t stx_attributes_mask;
|
||||
struct my_statx_timestamp stx_atime;
|
||||
struct my_statx_timestamp stx_btime;
|
||||
struct my_statx_timestamp stx_ctime;
|
||||
struct my_statx_timestamp stx_mtime;
|
||||
__uint32_t stx_rdev_major;
|
||||
__uint32_t stx_rdev_minor;
|
||||
__uint32_t stx_dev_major;
|
||||
__uint32_t stx_dev_minor;
|
||||
__uint64_t __statx_pad2[14];
|
||||
};
|
||||
|
||||
// statx masks, flags, constants
|
||||
|
||||
#ifndef AT_SYMLINK_NOFOLLOW
|
||||
#define AT_SYMLINK_NOFOLLOW 0x100
|
||||
#endif
|
||||
|
||||
#ifndef AT_STATX_SYNC_AS_STAT
|
||||
#define AT_STATX_SYNC_AS_STAT 0x0000
|
||||
#endif
|
||||
|
||||
#ifndef AT_EMPTY_PATH
|
||||
#define AT_EMPTY_PATH 0x1000
|
||||
#endif
|
||||
|
||||
#ifndef STATX_BASIC_STATS
|
||||
#define STATX_BASIC_STATS 0x000007ffU
|
||||
#endif
|
||||
|
||||
#ifndef STATX_BTIME
|
||||
#define STATX_BTIME 0x00000800U
|
||||
#endif
|
||||
|
||||
#ifndef STATX_ALL
|
||||
#define STATX_ALL (STATX_BTIME | STATX_BASIC_STATS)
|
||||
#endif
|
||||
|
||||
#ifndef AT_FDCWD
|
||||
#define AT_FDCWD -100
|
||||
#endif
|
||||
|
||||
#ifndef RTLD_DEFAULT
|
||||
#define RTLD_DEFAULT RTLD_LOCAL
|
||||
#endif
|
||||
|
||||
#define NO_FOLLOW_SYMLINK 1
|
||||
#define FOLLOW_SYMLINK 0
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
|
||||
#include "jni.h"
|
||||
#include "jni_util.h"
|
||||
#include "jlong.h"
|
||||
@ -117,9 +210,12 @@ static jfieldID attrs_st_mtime_nsec;
|
||||
static jfieldID attrs_st_ctime_sec;
|
||||
static jfieldID attrs_st_ctime_nsec;
|
||||
|
||||
#ifdef _DARWIN_FEATURE_64_BIT_INODE
|
||||
#if defined(_DARWIN_FEATURE_64_BIT_INODE) || defined(__linux__)
|
||||
static jfieldID attrs_st_birthtime_sec;
|
||||
#endif
|
||||
#if defined(__linux__) // Linux has nsec granularity if supported
|
||||
static jfieldID attrs_st_birthtime_nsec;
|
||||
#endif
|
||||
|
||||
static jfieldID attrs_f_frsize;
|
||||
static jfieldID attrs_f_blocks;
|
||||
@ -143,6 +239,10 @@ typedef int futimesat_func(int, const char *, const struct timeval *);
|
||||
typedef int futimens_func(int, const struct timespec *);
|
||||
typedef int lutimes_func(const char *, const struct timeval *);
|
||||
typedef DIR* fdopendir_func(int);
|
||||
#if defined(__linux__)
|
||||
typedef int statx_func(int dirfd, const char *restrict pathname, int flags,
|
||||
unsigned int mask, struct my_statx *restrict statxbuf);
|
||||
#endif
|
||||
|
||||
static openat64_func* my_openat64_func = NULL;
|
||||
static fstatat64_func* my_fstatat64_func = NULL;
|
||||
@ -152,6 +252,9 @@ static futimesat_func* my_futimesat_func = NULL;
|
||||
static futimens_func* my_futimens_func = NULL;
|
||||
static lutimes_func* my_lutimes_func = NULL;
|
||||
static fdopendir_func* my_fdopendir_func = NULL;
|
||||
#if defined(__linux__)
|
||||
static statx_func* my_statx_func = NULL;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* fstatat missing from glibc on Linux.
|
||||
@ -177,6 +280,13 @@ static int fstatat64_wrapper(int dfd, const char *path,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
static int statx_wrapper(int dirfd, const char *restrict pathname, int flags,
|
||||
unsigned int mask, struct my_statx *restrict statxbuf) {
|
||||
return (*my_statx_func)(dirfd, pathname, flags, mask, statxbuf);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Call this to throw an internal UnixException when a system/library
|
||||
* call fails
|
||||
@ -229,10 +339,14 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this)
|
||||
attrs_st_ctime_nsec = (*env)->GetFieldID(env, clazz, "st_ctime_nsec", "J");
|
||||
CHECK_NULL_RETURN(attrs_st_ctime_nsec, 0);
|
||||
|
||||
#ifdef _DARWIN_FEATURE_64_BIT_INODE
|
||||
#if defined(_DARWIN_FEATURE_64_BIT_INODE) || defined(__linux__)
|
||||
attrs_st_birthtime_sec = (*env)->GetFieldID(env, clazz, "st_birthtime_sec", "J");
|
||||
CHECK_NULL_RETURN(attrs_st_birthtime_sec, 0);
|
||||
#endif
|
||||
#if defined (__linux__) // Linux has nsec granularity
|
||||
attrs_st_birthtime_nsec = (*env)->GetFieldID(env, clazz, "st_birthtime_nsec", "J");
|
||||
CHECK_NULL_RETURN(attrs_st_birthtime_nsec, 0);
|
||||
#endif
|
||||
|
||||
clazz = (*env)->FindClass(env, "sun/nio/fs/UnixFileStoreAttributes");
|
||||
CHECK_NULL_RETURN(clazz, 0);
|
||||
@ -314,6 +428,12 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this)
|
||||
#ifdef _DARWIN_FEATURE_64_BIT_INODE
|
||||
capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_BIRTHTIME;
|
||||
#endif
|
||||
#if defined(__linux__)
|
||||
my_statx_func = (statx_func*) dlsym(RTLD_DEFAULT, "statx");
|
||||
if (my_statx_func != NULL) {
|
||||
capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_BIRTHTIME;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* supports extended attributes */
|
||||
|
||||
@ -490,10 +610,37 @@ Java_sun_nio_fs_UnixNativeDispatcher_write0(JNIEnv* env, jclass this, jint fd,
|
||||
return (jint)n;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
/**
|
||||
* Copy statx members into sun.nio.fs.UnixFileAttributes
|
||||
*/
|
||||
static void copy_statx_attributes(JNIEnv* env, struct my_statx* buf, jobject attrs) {
|
||||
(*env)->SetIntField(env, attrs, attrs_st_mode, (jint)buf->stx_mode);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_ino, (jlong)buf->stx_ino);
|
||||
(*env)->SetIntField(env, attrs, attrs_st_nlink, (jint)buf->stx_nlink);
|
||||
(*env)->SetIntField(env, attrs, attrs_st_uid, (jint)buf->stx_uid);
|
||||
(*env)->SetIntField(env, attrs, attrs_st_gid, (jint)buf->stx_gid);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_size, (jlong)buf->stx_size);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_atime_sec, (jlong)buf->stx_atime.tv_sec);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_mtime_sec, (jlong)buf->stx_mtime.tv_sec);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_ctime_sec, (jlong)buf->stx_ctime.tv_sec);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_birthtime_sec, (jlong)buf->stx_btime.tv_sec);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_birthtime_nsec, (jlong)buf->stx_btime.tv_nsec);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_atime_nsec, (jlong)buf->stx_atime.tv_nsec);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_mtime_nsec, (jlong)buf->stx_mtime.tv_nsec);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_ctime_nsec, (jlong)buf->stx_ctime.tv_nsec);
|
||||
// convert statx major:minor to dev_t using makedev
|
||||
dev_t dev = makedev(buf->stx_dev_major, buf->stx_dev_minor);
|
||||
dev_t rdev = makedev(buf->stx_rdev_major, buf->stx_rdev_minor);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_dev, (jlong)dev);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_rdev, (jlong)rdev);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Copy stat64 members into sun.nio.fs.UnixFileAttributes
|
||||
*/
|
||||
static void prepAttributes(JNIEnv* env, struct stat64* buf, jobject attrs) {
|
||||
static void copy_stat64_attributes(JNIEnv* env, struct stat64* buf, jobject attrs) {
|
||||
(*env)->SetIntField(env, attrs, attrs_st_mode, (jint)buf->st_mode);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_ino, (jlong)buf->st_ino);
|
||||
(*env)->SetLongField(env, attrs, attrs_st_dev, (jlong)buf->st_dev);
|
||||
@ -508,6 +655,7 @@ static void prepAttributes(JNIEnv* env, struct stat64* buf, jobject attrs) {
|
||||
|
||||
#ifdef _DARWIN_FEATURE_64_BIT_INODE
|
||||
(*env)->SetLongField(env, attrs, attrs_st_birthtime_sec, (jlong)buf->st_birthtime);
|
||||
// rely on default value of 0 for st_birthtime_nsec field on Darwin
|
||||
#endif
|
||||
|
||||
#ifndef MACOSX
|
||||
@ -528,10 +676,25 @@ Java_sun_nio_fs_UnixNativeDispatcher_stat0(JNIEnv* env, jclass this,
|
||||
int err;
|
||||
struct stat64 buf;
|
||||
const char* path = (const char*)jlong_to_ptr(pathAddress);
|
||||
#if defined(__linux__)
|
||||
struct my_statx statx_buf;
|
||||
int flags = AT_STATX_SYNC_AS_STAT;
|
||||
unsigned int mask = STATX_ALL;
|
||||
|
||||
if (my_statx_func != NULL) {
|
||||
// Prefer statx over stat64 on Linux if it's available
|
||||
RESTARTABLE(statx_wrapper(AT_FDCWD, path, flags, mask, &statx_buf), err);
|
||||
if (err == 0) {
|
||||
copy_statx_attributes(env, &statx_buf, attrs);
|
||||
return 0;
|
||||
} else {
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
RESTARTABLE(stat64(path, &buf), err);
|
||||
if (err == 0) {
|
||||
prepAttributes(env, &buf, attrs);
|
||||
copy_stat64_attributes(env, &buf, attrs);
|
||||
return 0;
|
||||
} else {
|
||||
return errno;
|
||||
@ -545,12 +708,28 @@ Java_sun_nio_fs_UnixNativeDispatcher_lstat0(JNIEnv* env, jclass this,
|
||||
int err;
|
||||
struct stat64 buf;
|
||||
const char* path = (const char*)jlong_to_ptr(pathAddress);
|
||||
#if defined(__linux__)
|
||||
struct my_statx statx_buf;
|
||||
int flags = AT_STATX_SYNC_AS_STAT | AT_SYMLINK_NOFOLLOW;
|
||||
unsigned int mask = STATX_ALL;
|
||||
|
||||
if (my_statx_func != NULL) {
|
||||
// Prefer statx over stat64 on Linux if it's available
|
||||
RESTARTABLE(statx_wrapper(AT_FDCWD, path, flags, mask, &statx_buf), err);
|
||||
if (err == 0) {
|
||||
copy_statx_attributes(env, &statx_buf, attrs);
|
||||
} else {
|
||||
throwUnixException(env, errno);
|
||||
}
|
||||
// statx was available, so return now
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
RESTARTABLE(lstat64(path, &buf), err);
|
||||
if (err == -1) {
|
||||
throwUnixException(env, errno);
|
||||
} else {
|
||||
prepAttributes(env, &buf, attrs);
|
||||
copy_stat64_attributes(env, &buf, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -560,12 +739,29 @@ Java_sun_nio_fs_UnixNativeDispatcher_fstat0(JNIEnv* env, jclass this, jint fd,
|
||||
{
|
||||
int err;
|
||||
struct stat64 buf;
|
||||
#if defined(__linux__)
|
||||
struct my_statx statx_buf;
|
||||
int flags = AT_EMPTY_PATH | AT_STATX_SYNC_AS_STAT;
|
||||
unsigned int mask = STATX_ALL;
|
||||
|
||||
if (my_statx_func != NULL) {
|
||||
// statx supports FD use via dirfd iff pathname is an empty string and the
|
||||
// AT_EMPTY_PATH flag is specified in flags
|
||||
RESTARTABLE(statx_wrapper((int)fd, "", flags, mask, &statx_buf), err);
|
||||
if (err == 0) {
|
||||
copy_statx_attributes(env, &statx_buf, attrs);
|
||||
} else {
|
||||
throwUnixException(env, errno);
|
||||
}
|
||||
// statx was available, so return now
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
RESTARTABLE(fstat64((int)fd, &buf), err);
|
||||
if (err == -1) {
|
||||
throwUnixException(env, errno);
|
||||
} else {
|
||||
prepAttributes(env, &buf, attrs);
|
||||
copy_stat64_attributes(env, &buf, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -576,6 +772,26 @@ Java_sun_nio_fs_UnixNativeDispatcher_fstatat0(JNIEnv* env, jclass this, jint dfd
|
||||
int err;
|
||||
struct stat64 buf;
|
||||
const char* path = (const char*)jlong_to_ptr(pathAddress);
|
||||
#if defined(__linux__)
|
||||
struct my_statx statx_buf;
|
||||
int flags = AT_STATX_SYNC_AS_STAT;
|
||||
unsigned int mask = STATX_ALL;
|
||||
|
||||
if (my_statx_func != NULL) {
|
||||
// Prefer statx over stat64 on Linux if it's available
|
||||
if (((int)flag & AT_SYMLINK_NOFOLLOW) > 0) { // flag set in java code
|
||||
flags |= AT_SYMLINK_NOFOLLOW;
|
||||
}
|
||||
RESTARTABLE(statx_wrapper((int)dfd, path, flags, mask, &statx_buf), err);
|
||||
if (err == 0) {
|
||||
copy_statx_attributes(env, &statx_buf, attrs);
|
||||
} else {
|
||||
throwUnixException(env, errno);
|
||||
}
|
||||
// statx was available, so return now
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (my_fstatat64_func == NULL) {
|
||||
JNU_ThrowInternalError(env, "should not reach here");
|
||||
@ -585,7 +801,7 @@ Java_sun_nio_fs_UnixNativeDispatcher_fstatat0(JNIEnv* env, jclass this, jint dfd
|
||||
if (err == -1) {
|
||||
throwUnixException(env, errno);
|
||||
} else {
|
||||
prepAttributes(env, &buf, attrs);
|
||||
copy_stat64_attributes(env, &buf, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 8011536 8151430
|
||||
* @bug 8011536 8151430 8316304
|
||||
* @summary Basic test for creationTime attribute on platforms/file systems
|
||||
* that support it.
|
||||
* @library ../.. /test/lib
|
||||
@ -30,6 +30,7 @@
|
||||
* @run main CreationTime
|
||||
*/
|
||||
|
||||
import java.lang.foreign.Linker;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.attribute.*;
|
||||
@ -88,7 +89,13 @@ public class CreationTime {
|
||||
supportsCreationTimeRead = true;
|
||||
supportsCreationTimeWrite = true;
|
||||
}
|
||||
} else if (Platform.isLinux()) {
|
||||
// Creation time read depends on statx system call support
|
||||
supportsCreationTimeRead = Linker.nativeLinker().defaultLookup().find("statx").isPresent();
|
||||
// Creation time updates are not supported on Linux
|
||||
supportsCreationTimeWrite = false;
|
||||
}
|
||||
System.out.println("supportsCreationTimeRead == " + supportsCreationTimeRead);
|
||||
|
||||
/**
|
||||
* If the creation-time attribute is supported then change the file's
|
||||
|
Loading…
Reference in New Issue
Block a user