mirror of https://github.com/termux/termux-app
415 lines
12 KiB
Java
415 lines
12 KiB
Java
/*
|
|
* Copyright (c) 2008, 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. 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 com.termux.shared.file.filesystem;
|
|
|
|
import android.os.Build;
|
|
import android.system.StructStat;
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import java.io.File;
|
|
import java.io.FileDescriptor;
|
|
import java.io.IOException;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.Set;
|
|
import java.util.HashSet;
|
|
|
|
/**
|
|
* Unix implementation of PosixFileAttributes.
|
|
* https://cs.android.com/android/platform/superproject/+/android-11.0.0_r3:libcore/ojluni/src/main/java/sun/nio/fs/UnixFileAttributes.java
|
|
*/
|
|
|
|
public class FileAttributes {
|
|
private String filePath;
|
|
private FileDescriptor fileDescriptor;
|
|
|
|
private int st_mode;
|
|
private long st_ino;
|
|
private long st_dev;
|
|
private long st_rdev;
|
|
private long st_nlink;
|
|
private int st_uid;
|
|
private int st_gid;
|
|
private long st_size;
|
|
private long st_blksize;
|
|
private long st_blocks;
|
|
private long st_atime_sec;
|
|
private long st_atime_nsec;
|
|
private long st_mtime_sec;
|
|
private long st_mtime_nsec;
|
|
private long st_ctime_sec;
|
|
private long st_ctime_nsec;
|
|
|
|
// created lazily
|
|
private volatile String owner;
|
|
private volatile String group;
|
|
private volatile FileKey key;
|
|
|
|
private FileAttributes(String filePath) {
|
|
this.filePath = filePath;
|
|
}
|
|
|
|
private FileAttributes(FileDescriptor fileDescriptor) {
|
|
this.fileDescriptor = fileDescriptor;
|
|
}
|
|
|
|
// get the FileAttributes for a given file
|
|
public static FileAttributes get(String filePath, boolean followLinks) throws IOException {
|
|
FileAttributes fileAttributes;
|
|
|
|
if (filePath == null || filePath.isEmpty())
|
|
fileAttributes = new FileAttributes((String) null);
|
|
else
|
|
fileAttributes = new FileAttributes(new File(filePath).getAbsolutePath());
|
|
|
|
if (followLinks) {
|
|
NativeDispatcher.stat(filePath, fileAttributes);
|
|
} else {
|
|
NativeDispatcher.lstat(filePath, fileAttributes);
|
|
}
|
|
|
|
// Logger.logDebug(fileAttributes.toString());
|
|
|
|
return fileAttributes;
|
|
}
|
|
|
|
// get the FileAttributes for an open file
|
|
public static FileAttributes get(FileDescriptor fileDescriptor) throws IOException {
|
|
FileAttributes fileAttributes = new FileAttributes(fileDescriptor);
|
|
NativeDispatcher.fstat(fileDescriptor, fileAttributes);
|
|
return fileAttributes;
|
|
}
|
|
|
|
public String file() {
|
|
if(filePath != null)
|
|
return filePath;
|
|
else if(fileDescriptor != null)
|
|
return fileDescriptor.toString();
|
|
else
|
|
return null;
|
|
}
|
|
|
|
// package-private
|
|
public boolean isSameFile(FileAttributes attrs) {
|
|
return ((st_ino == attrs.st_ino) && (st_dev == attrs.st_dev));
|
|
}
|
|
|
|
// package-private
|
|
public int mode() {
|
|
return st_mode;
|
|
}
|
|
|
|
public long blksize() {
|
|
return st_blksize;
|
|
}
|
|
|
|
public long blocks() {
|
|
return st_blocks;
|
|
}
|
|
|
|
public long ino() {
|
|
return st_ino;
|
|
}
|
|
|
|
public long dev() {
|
|
return st_dev;
|
|
}
|
|
|
|
public long rdev() {
|
|
return st_rdev;
|
|
}
|
|
|
|
public long nlink() {
|
|
return st_nlink;
|
|
}
|
|
|
|
public int uid() {
|
|
return st_uid;
|
|
}
|
|
|
|
public int gid() {
|
|
return st_gid;
|
|
}
|
|
|
|
private static FileTime toFileTime(long sec, long nsec) {
|
|
if (nsec == 0) {
|
|
return FileTime.from(sec, TimeUnit.SECONDS);
|
|
} else {
|
|
// truncate to microseconds to avoid overflow with timestamps
|
|
// way out into the future. We can re-visit this if FileTime
|
|
// is updated to define a from(secs,nsecs) method.
|
|
long micro = sec * 1000000L + nsec / 1000L;
|
|
return FileTime.from(micro, TimeUnit.MICROSECONDS);
|
|
}
|
|
}
|
|
|
|
public FileTime lastAccessTime() {
|
|
return toFileTime(st_atime_sec, st_atime_nsec);
|
|
}
|
|
|
|
public FileTime lastModifiedTime() {
|
|
return toFileTime(st_mtime_sec, st_mtime_nsec);
|
|
}
|
|
|
|
public FileTime lastChangeTime() {
|
|
return toFileTime(st_ctime_sec, st_ctime_nsec);
|
|
}
|
|
|
|
public FileTime creationTime() {
|
|
return lastModifiedTime();
|
|
}
|
|
|
|
public boolean isRegularFile() {
|
|
return ((st_mode & UnixConstants.S_IFMT) == UnixConstants.S_IFREG);
|
|
}
|
|
|
|
public boolean isDirectory() {
|
|
return ((st_mode & UnixConstants.S_IFMT) == UnixConstants.S_IFDIR);
|
|
}
|
|
|
|
public boolean isSymbolicLink() {
|
|
return ((st_mode & UnixConstants.S_IFMT) == UnixConstants.S_IFLNK);
|
|
}
|
|
|
|
public boolean isCharacter() {
|
|
return ((st_mode & UnixConstants.S_IFMT) == UnixConstants.S_IFCHR);
|
|
}
|
|
|
|
public boolean isFifo() {
|
|
return ((st_mode & UnixConstants.S_IFMT) == UnixConstants.S_IFIFO);
|
|
}
|
|
|
|
public boolean isBlock() {
|
|
return ((st_mode & UnixConstants.S_IFMT) == UnixConstants.S_IFBLK);
|
|
}
|
|
|
|
public boolean isOther() {
|
|
int type = st_mode & UnixConstants.S_IFMT;
|
|
return (type != UnixConstants.S_IFREG &&
|
|
type != UnixConstants.S_IFDIR &&
|
|
type != UnixConstants.S_IFLNK);
|
|
}
|
|
|
|
public boolean isDevice() {
|
|
int type = st_mode & UnixConstants.S_IFMT;
|
|
return (type == UnixConstants.S_IFCHR ||
|
|
type == UnixConstants.S_IFBLK ||
|
|
type == UnixConstants.S_IFIFO);
|
|
}
|
|
|
|
public long size() {
|
|
return st_size;
|
|
}
|
|
|
|
public FileKey fileKey() {
|
|
if (key == null) {
|
|
synchronized (this) {
|
|
if (key == null) {
|
|
key = new FileKey(st_dev, st_ino);
|
|
}
|
|
}
|
|
}
|
|
return key;
|
|
}
|
|
|
|
public String owner() {
|
|
if (owner == null) {
|
|
synchronized (this) {
|
|
if (owner == null) {
|
|
owner = Integer.toString(st_uid);
|
|
}
|
|
}
|
|
}
|
|
return owner;
|
|
}
|
|
|
|
public String group() {
|
|
if (group == null) {
|
|
synchronized (this) {
|
|
if (group == null) {
|
|
group = Integer.toString(st_gid);
|
|
}
|
|
}
|
|
}
|
|
return group;
|
|
}
|
|
|
|
public Set<FilePermission> permissions() {
|
|
int bits = (st_mode & UnixConstants.S_IAMB);
|
|
HashSet<FilePermission> perms = new HashSet<>();
|
|
|
|
if ((bits & UnixConstants.S_IRUSR) > 0)
|
|
perms.add(FilePermission.OWNER_READ);
|
|
if ((bits & UnixConstants.S_IWUSR) > 0)
|
|
perms.add(FilePermission.OWNER_WRITE);
|
|
if ((bits & UnixConstants.S_IXUSR) > 0)
|
|
perms.add(FilePermission.OWNER_EXECUTE);
|
|
|
|
if ((bits & UnixConstants.S_IRGRP) > 0)
|
|
perms.add(FilePermission.GROUP_READ);
|
|
if ((bits & UnixConstants.S_IWGRP) > 0)
|
|
perms.add(FilePermission.GROUP_WRITE);
|
|
if ((bits & UnixConstants.S_IXGRP) > 0)
|
|
perms.add(FilePermission.GROUP_EXECUTE);
|
|
|
|
if ((bits & UnixConstants.S_IROTH) > 0)
|
|
perms.add(FilePermission.OTHERS_READ);
|
|
if ((bits & UnixConstants.S_IWOTH) > 0)
|
|
perms.add(FilePermission.OTHERS_WRITE);
|
|
if ((bits & UnixConstants.S_IXOTH) > 0)
|
|
perms.add(FilePermission.OTHERS_EXECUTE);
|
|
|
|
return perms;
|
|
}
|
|
|
|
public void loadFromStructStat(StructStat structStat) {
|
|
this.st_mode = structStat.st_mode;
|
|
this.st_ino = structStat.st_ino;
|
|
this.st_dev = structStat.st_dev;
|
|
this.st_rdev = structStat.st_rdev;
|
|
this.st_nlink = structStat.st_nlink;
|
|
this.st_uid = structStat.st_uid;
|
|
this.st_gid = structStat.st_gid;
|
|
this.st_size = structStat.st_size;
|
|
this.st_blksize = structStat.st_blksize;
|
|
this.st_blocks = structStat.st_blocks;
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
|
this.st_atime_sec = structStat.st_atim.tv_sec;
|
|
this.st_atime_nsec = structStat.st_atim.tv_nsec;
|
|
this.st_mtime_sec = structStat.st_mtim.tv_sec;
|
|
this.st_mtime_nsec = structStat.st_mtim.tv_nsec;
|
|
this.st_ctime_sec = structStat.st_ctim.tv_sec;
|
|
this.st_ctime_nsec = structStat.st_ctim.tv_nsec;
|
|
} else {
|
|
this.st_atime_sec = structStat.st_atime;
|
|
this.st_atime_nsec = 0;
|
|
this.st_mtime_sec = structStat.st_mtime;
|
|
this.st_mtime_nsec = 0;
|
|
this.st_ctime_sec = structStat.st_ctime;
|
|
this.st_ctime_nsec = 0;
|
|
}
|
|
}
|
|
|
|
public String getFileString() {
|
|
return "File: `" + file() + "`";
|
|
}
|
|
|
|
public String getTypeString() {
|
|
return "Type: `" + FileTypes.getFileType(this).getName() + "`";
|
|
}
|
|
|
|
public String getSizeString() {
|
|
return "Size: `" + size() + "`";
|
|
}
|
|
|
|
public String getBlocksString() {
|
|
return "Blocks: `" + blocks() + "`";
|
|
}
|
|
|
|
public String getIOBlockString() {
|
|
return "IO Block: `" + blksize() + "`";
|
|
}
|
|
|
|
public String getDeviceString() {
|
|
return "Device: `" + Long.toHexString(st_dev) + "`";
|
|
}
|
|
|
|
public String getInodeString() {
|
|
return "Inode: `" + st_ino + "`";
|
|
}
|
|
|
|
public String getLinksString() {
|
|
return "Links: `" + nlink() + "`";
|
|
}
|
|
|
|
public String getDeviceTypeString() {
|
|
return "Device Type: `" + rdev() + "`";
|
|
}
|
|
|
|
public String getOwnerString() {
|
|
return "Owner: `" + owner() + "`";
|
|
}
|
|
|
|
public String getGroupString() {
|
|
return "Group: `" + group() + "`";
|
|
}
|
|
|
|
public String getPermissionString() {
|
|
return "Permissions: `" + FilePermissions.toString(permissions()) + "`";
|
|
}
|
|
|
|
public String getAccessTimeString() {
|
|
return "Access Time: `" + lastAccessTime() + "`";
|
|
}
|
|
|
|
public String getModifiedTimeString() {
|
|
return "Modified Time: `" + lastModifiedTime() + "`";
|
|
}
|
|
|
|
public String getChangeTimeString() {
|
|
return "Change Time: `" + lastChangeTime() + "`";
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public String toString() {
|
|
return getFileAttributesLogString(this);
|
|
}
|
|
|
|
public static String getFileAttributesLogString(final FileAttributes fileAttributes) {
|
|
if (fileAttributes == null) return "null";
|
|
|
|
StringBuilder logString = new StringBuilder();
|
|
|
|
logString.append(fileAttributes.getFileString());
|
|
|
|
logString.append("\n").append(fileAttributes.getTypeString());
|
|
|
|
logString.append("\n").append(fileAttributes.getSizeString());
|
|
logString.append("\n").append(fileAttributes.getBlocksString());
|
|
logString.append("\n").append(fileAttributes.getIOBlockString());
|
|
|
|
logString.append("\n").append(fileAttributes.getDeviceString());
|
|
logString.append("\n").append(fileAttributes.getInodeString());
|
|
logString.append("\n").append(fileAttributes.getLinksString());
|
|
|
|
if(fileAttributes.isBlock() || fileAttributes.isCharacter())
|
|
logString.append("\n").append(fileAttributes.getDeviceTypeString());
|
|
|
|
logString.append("\n").append(fileAttributes.getOwnerString());
|
|
logString.append("\n").append(fileAttributes.getGroupString());
|
|
logString.append("\n").append(fileAttributes.getPermissionString());
|
|
|
|
logString.append("\n").append(fileAttributes.getAccessTimeString());
|
|
logString.append("\n").append(fileAttributes.getModifiedTimeString());
|
|
logString.append("\n").append(fileAttributes.getChangeTimeString());
|
|
|
|
return logString.toString();
|
|
}
|
|
|
|
}
|