jnr.posix.JavaFileStat Maven / Gradle / Ivy
package jnr.posix;
import java.io.File;
import java.io.IOException;
import jnr.posix.POSIXHandler.WARNING_ID;
public final class JavaFileStat implements FileStat {
private final POSIXHandler handler;
private final POSIX posix;
short st_mode;
int st_blksize;
long st_size;
int st_ctime;
int st_mtime;
public JavaFileStat(POSIX posix, POSIXHandler handler) {
this.handler = handler;
this.posix = posix;
}
public void setup(String path) {
// ENEBO: This was originally JRubyFile, but I believe we use JRuby file for normalization
// of paths. So we should be safe.
File file = new JavaSecuredFile(path);
// Limitation: We cannot determine, so always return 4096 (better than blowing up)
st_blksize = 4096;
st_mode = calculateMode(file, st_mode);
st_size = file.length();
// Parent file last modified will only represent when something was added or removed.
// This is not correct, but it is better than nothing and does work in one common use
// case.
st_mtime = (int) (file.lastModified() / 1000);
if (file.getParentFile() != null) {
st_ctime = (int) (file.getParentFile().lastModified() / 1000);
} else {
st_ctime = st_mtime;
}
}
private short calculateMode(File file, short st_mode) {
// implementation to lowest common denominator...
// Windows has no file mode, but C ruby returns either 0100444 or 0100644
if (file.canRead()) {
st_mode |= ALL_READ;
}
if (file.canWrite()) {
st_mode |= ALL_WRITE;
st_mode &= ~(S_IWGRP | S_IWOTH);
}
if (file.isDirectory()) {
st_mode |= S_IFDIR;
} else if (file.isFile()) {
st_mode |= S_IFREG;
}
try {
st_mode = calculateSymlink(file, st_mode);
} catch (IOException e) {
// Not sure we can do much in this case...
}
return st_mode;
}
private short calculateSymlink(File file, short st_mode) throws IOException {
if (file.getAbsoluteFile().getParentFile() == null) {
return st_mode;
}
File absoluteParent = file.getAbsoluteFile().getParentFile();
File canonicalParent = absoluteParent.getCanonicalFile();
if (canonicalParent.getAbsolutePath().equals(absoluteParent.getAbsolutePath())) {
// parent doesn't change when canonicalized, compare absolute and canonical file directly
if (!file.getAbsolutePath().equalsIgnoreCase(file.getCanonicalPath())) {
st_mode |= S_IFLNK;
return st_mode;
}
}
// directory itself has symlinks (canonical != absolute), so build new path with canonical parent and compare
file = new JavaSecuredFile(canonicalParent.getAbsolutePath() + "/" + file.getName());
if (!file.getAbsolutePath().equalsIgnoreCase(file.getCanonicalPath())) {
st_mode |= S_IFLNK;
}
return st_mode;
}
/**
* Limitation: Java has no access time support, so we return mtime as the next best thing.
*/
public long atime() {
return st_mtime;
}
public long blocks() {
handler.unimplementedError("stat.st_blocks");
return -1;
}
public long blockSize() {
return st_blksize;
}
public long ctime() {
return st_ctime;
}
public long dev() {
handler.unimplementedError("stat.st_dev");
return -1;
}
public String ftype() {
if (isFile()) {
return "file";
} else if (isDirectory()) {
return "directory";
}
return "unknown";
}
public int gid() {
handler.unimplementedError("stat.st_gid");
return -1;
}
public boolean groupMember(int gid) {
return posix.getgid() == gid || posix.getegid() == gid;
}
/**
* Limitation: We have no pure-java way of getting inode. webrick needs this defined to work.
*/
public long ino() {
return 0;
}
public boolean isBlockDev() {
handler.unimplementedError("block device detection");
return false;
}
/**
* Limitation: [see JRUBY-1516] We just pick more likely value. This is a little scary.
*/
public boolean isCharDev() {
return false;
}
public boolean isDirectory() {
return (mode() & S_IFDIR) != 0;
}
public boolean isEmpty() {
return st_size() == 0;
}
// At least one major library depends on this method existing.
public boolean isExecutable() {
handler.warn(WARNING_ID.DUMMY_VALUE_USED, "executable? does not in this environment and will return a dummy value", "executable");
return true;
}
// At least one major library depends on this method existing.
public boolean isExecutableReal() {
handler.warn(WARNING_ID.DUMMY_VALUE_USED, "executable_real? does not work in this environmnt and will return a dummy value", "executable_real");
return true;
}
public boolean isFifo() {
handler.unimplementedError("fifo file detection");
return false;
}
public boolean isFile() {
return (mode() & S_IFREG) != 0;
}
public boolean isGroupOwned() {
return groupMember(gid());
}
public boolean isIdentical(FileStat other) {
handler.unimplementedError("identical file detection");
return false;
}
public boolean isNamedPipe() {
handler.unimplementedError("piped file detection");
return false;
}
public boolean isOwned() {
return posix.geteuid() == uid();
}
public boolean isROwned() {
return posix.getuid() == uid();
}
public boolean isReadable() {
int mode = mode();
if ((mode & S_IRUSR) != 0) return true;
if ((mode & S_IRGRP) != 0) return true;
if ((mode & S_IROTH) != 0) return true;
return false;
}
// We do both readable and readable_real through the same method because
public boolean isReadableReal() {
return isReadable();
}
public boolean isSymlink() {
return (mode() & S_IFLNK) == S_IFLNK;
}
public boolean isWritable() {
int mode = mode();
if ((mode & S_IWUSR) != 0) return true;
if ((mode & S_IWGRP) != 0) return true;
if ((mode & S_IWOTH) != 0) return true;
return false;
}
// We do both readable and readable_real through the same method because
// in our java process effective and real userid will always be the same.
public boolean isWritableReal() {
return isWritable();
}
public boolean isSetgid() {
handler.unimplementedError("setgid detection");
return false;
}
public boolean isSetuid() {
handler.unimplementedError("setuid detection");
return false;
}
public boolean isSocket() {
handler.unimplementedError("socket file type detection");
return false;
}
public boolean isSticky() {
handler.unimplementedError("sticky bit detection");
return false;
}
public int major(long dev) {
handler.unimplementedError("major device");
return -1;
}
public int minor(long dev) {
handler.unimplementedError("minor device");
return -1;
}
public int mode() {
return st_mode & 0xffff;
}
public long mtime() {
return st_mtime;
}
public int nlink() {
handler.unimplementedError("stat.nlink");
return -1;
}
public long rdev() {
handler.unimplementedError("stat.rdev");
return -1;
}
public long st_size() {
return st_size;
}
// Limitation: We have no pure-java way of getting uid. RubyZip needs this defined to work.
public int uid() {
return -1;
}
}