src.org.python.modules.posix.PosixModule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jython Show documentation
Show all versions of jython Show documentation
Jython is an implementation of the high-level, dynamic, object-oriented
language Python written in 100% Pure Java, and seamlessly integrated with
the Java platform. It thus allows you to run Python on any Java platform.
/* Copyright (c) Jython Developers */
package org.python.modules.posix;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
import java.nio.channels.Pipe;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.NotLinkException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.DosFileAttributes;
import java.nio.file.attribute.FileTime;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import jnr.constants.Constant;
import jnr.constants.platform.Errno;
import jnr.constants.platform.Sysconf;
import jnr.posix.FileStat;
import jnr.posix.POSIX;
import jnr.posix.POSIXFactory;
import jnr.posix.POSIXHandler;
import jnr.posix.Times;
import jnr.posix.WindowsRawFileStat;
import jnr.posix.util.FieldAccess;
import jnr.posix.windows.CommonFileInformation;
import org.python.core.BufferProtocol;
import org.python.core.ClassDictInit;
import org.python.core.Py;
import org.python.core.PyBUF;
import org.python.core.PyBuffer;
import org.python.core.PyBuiltinFunctionNarrow;
import org.python.core.PyDictionary;
import org.python.core.PyException;
import org.python.core.PyFile;
import org.python.core.PyFloat;
import org.python.core.PyJavaType;
import org.python.core.PyList;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PySystemState;
import org.python.core.PyTuple;
import org.python.core.PyUnicode;
import org.python.core.Untraversable;
import org.python.core.imp;
import org.python.core.io.FileIO;
import org.python.core.io.IOBase;
import org.python.core.io.RawIOBase;
import org.python.core.io.StreamIO;
import org.python.core.util.StringUtil;
/**
* The posix/nt module, depending on the platform.
*/
public class PosixModule implements ClassDictInit {
public static final PyString __doc__ = new PyString(
"This module provides access to operating system functionality that is\n" +
"standardized by the C Standard and the POSIX standard (a thinly\n" +
"disguised Unix interface). Refer to the library manual and\n" +
"corresponding Unix manual entries for more information on calls.");
/** Current OS information. */
private static final OS os = OS.getOS();
/** Platform specific POSIX services. */
private static final POSIXHandler posixHandler = new PythonPOSIXHandler();
private static final POSIX posix = POSIXFactory.getPOSIX(posixHandler, true);
/** os.open flags. */
private static final int O_RDONLY = 0x0;
private static final int O_WRONLY = 0x1;
private static final int O_RDWR = 0x2;
private static final int O_APPEND = 0x8;
private static final int O_SYNC = 0x80;
private static final int O_CREAT = 0x200;
private static final int O_TRUNC = 0x400;
private static final int O_EXCL = 0x800;
/** os.access constants. */
private static final int F_OK = 0;
private static final int X_OK = 1 << 0;
private static final int W_OK = 1 << 1;
private static final int R_OK = 1 << 2;
/** Lazily initialized singleton source for urandom. */
private static class UrandomSource {
static final SecureRandom INSTANCE = new SecureRandom();
}
public static void classDictInit(PyObject dict) {
// only expose the open flags we support
dict.__setitem__("O_RDONLY", Py.newInteger(O_RDONLY));
dict.__setitem__("O_WRONLY", Py.newInteger(O_WRONLY));
dict.__setitem__("O_RDWR", Py.newInteger(O_RDWR));
dict.__setitem__("O_APPEND", Py.newInteger(O_APPEND));
dict.__setitem__("O_SYNC", Py.newInteger(O_SYNC));
dict.__setitem__("O_CREAT", Py.newInteger(O_CREAT));
dict.__setitem__("O_TRUNC", Py.newInteger(O_TRUNC));
dict.__setitem__("O_EXCL", Py.newInteger(O_EXCL));
// os.access flags
dict.__setitem__("F_OK", Py.newInteger(F_OK));
dict.__setitem__("X_OK", Py.newInteger(X_OK));
dict.__setitem__("W_OK", Py.newInteger(W_OK));
dict.__setitem__("R_OK", Py.newInteger(R_OK));
// Successful termination
dict.__setitem__("EX_OK", Py.Zero);
// SecurityManager may restrict access to native implementation,
// so use Java-only implementation as necessary
boolean nativePosix = false;
try {
nativePosix = posix.isNative();
dict.__setitem__("_native_posix", Py.newBoolean(nativePosix));
dict.__setitem__("_posix_impl", Py.java2py(posix));
} catch (SecurityException ex) {}
dict.__setitem__("environ", getEnviron());
dict.__setitem__("error", Py.OSError);
dict.__setitem__("stat_result", PyStatResult.TYPE);
// Faster call paths, because __call__ is defined
dict.__setitem__("fstat", new FstatFunction());
if (os == OS.NT) {
WindowsStatFunction stat = new WindowsStatFunction();
dict.__setitem__("lstat", stat);
dict.__setitem__("stat", stat);
} else {
dict.__setitem__("lstat", new LstatFunction());
dict.__setitem__("stat", new StatFunction());
}
// Hide from Python
Hider.hideFunctions(PosixModule.class, dict, os, nativePosix);
dict.__setitem__("classDictInit", null);
dict.__setitem__("__init__", null);
dict.__setitem__("getPOSIX", null);
dict.__setitem__("getOSName", null);
dict.__setitem__("badFD", null);
// Hide __doc__s
PyList keys = (PyList)dict.invoke("keys");
for (Iterator> it = keys.listIterator(); it.hasNext();) {
String key = (String)it.next();
if (key.startsWith("__doc__")) {
it.remove();
dict.__setitem__(key, null);
}
}
dict.__setitem__("__all__", keys);
dict.__setitem__("__name__", new PyString(os.getModuleName()));
dict.__setitem__("__doc__", __doc__);
}
// Combine Java FileDescriptor objects with Posix int file descriptors in one representation.
// Unfortunate ugliness!
private static class FDUnion {
volatile int intFD;
final FileDescriptor javaFD;
FDUnion(int fd) {
intFD = fd;
javaFD = null;
}
FDUnion(FileDescriptor fd) {
intFD = -1;
javaFD = fd;
}
boolean isIntFD() {
return intFD != -1;
}
int getIntFD() {
return getIntFD(true);
}
int getIntFD(boolean checkFD) {
if (intFD == -1) {
if (!(javaFD instanceof FileDescriptor)) {
throw Py.OSError(Errno.EBADF);
}
try {
Field fdField = FieldAccess.getProtectedField(FileDescriptor.class, "fd");
intFD = fdField.getInt(javaFD);
} catch (SecurityException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (NullPointerException e) {}
}
if (checkFD) {
if (intFD == -1) {
throw Py.OSError(Errno.EBADF);
} else {
posix.fstat(intFD); // side effect of checking if this a good FD or not
}
}
return intFD;
}
@Override
public String toString() {
return "FDUnion(int=" + intFD + ", java=" + javaFD + ")";
}
}
private static FDUnion getFD(PyObject fdObj) {
if (fdObj.isInteger()) {
int intFd = fdObj.asInt();
switch (intFd) {
case -1:
break;
case 0:
return new FDUnion(FileDescriptor.in);
case 1:
return new FDUnion(FileDescriptor.out);
case 2:
return new FDUnion(FileDescriptor.err);
default:
return new FDUnion(intFd);
}
}
Object tojava = fdObj.__tojava__(FileDescriptor.class);
if (tojava != Py.NoConversion) {
return new FDUnion((FileDescriptor) tojava);
}
tojava = fdObj.__tojava__(FileIO.class);
if (tojava != Py.NoConversion) {
return new FDUnion(((FileIO)tojava).getFD());
}
throw Py.TypeError("an integer or Java/Jython file descriptor is required");
}
public static PyString __doc___exit = new PyString(
"_exit(status)\n\n" +
"Exit to the system with specified status, without normal exit processing.");
public static void _exit() {
_exit(0);
}
public static void _exit(int status) {
System.exit(status);
}
public static PyString __doc__access = new PyString(
"access(path, mode) -> True if granted, False otherwise\n\n" +
"Use the real uid/gid to test for access to a path. Note that most\n" +
"operations will use the effective uid/gid, therefore this routine can\n" +
"be used in a suid/sgid environment to test if the invoking user has the\n" +
"specified access to the path. The mode argument can be F_OK to test\n" +
"existence, or the inclusive-OR of R_OK, W_OK, and X_OK.");
public static boolean access(PyObject path, int mode) {
File file = absolutePath(path).toFile();
boolean result = true;
if (!file.exists()) {
result = false;
}
if ((mode & R_OK) != 0 && !file.canRead()) {
result = false;
}
if ((mode & W_OK) != 0 && !file.canWrite()) {
result = false;
}
if ((mode & X_OK) != 0 && !file.canExecute()) {
// Previously Jython used JNR Posix, but this is unnecessary -
// File#canExecute uses the same code path
// http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6379654
result = false;
}
return result;
}
public static PyString __doc__chdir = new PyString(
"chdir(path)\n\n" +
"Change the current working directory to the specified path.");
public static void chdir(PyObject path) {
PySystemState sys = Py.getSystemState();
Path absolutePath = absolutePath(path);
// stat raises ENOENT for us if path doesn't exist
if (!basicstat(path, absolutePath).isDirectory()) {
throw Py.OSError(Errno.ENOTDIR, path);
}
if (os == OS.NT) {
// No symbolic links and preserve dos-like names (e.g. PROGRA~1)
sys.setCurrentWorkingDir(absolutePath.toString());
} else {
// Resolve symbolic links
try {
sys.setCurrentWorkingDir(absolutePath.toRealPath().toString());
} catch (IOException ioe) {
throw Py.OSError(ioe);
}
}
}
public static PyString __doc__chmod = new PyString(
"chmod(path, mode)\n\n" +
"Change the access permissions of a file.");
public static void chmod(PyObject path, int mode) {
if (os == OS.NT) {
try {
// We can only allow/deny write access (not read & execute)
boolean writable = (mode & FileStat.S_IWUSR) != 0;
File f = absolutePath(path).toFile();
if (!f.exists()) {
throw Py.OSError(Errno.ENOENT, path);
} else if (!f.isDirectory() && !f.setWritable(writable)) {
// The read-only flag of directories means "don't delete" not "you can't write"
throw Py.OSError(Errno.EPERM, path);
}
} catch (SecurityException ex) {
throw Py.OSError(Errno.EACCES, path);
}
} else if (posix.chmod(absolutePath(path).toString(), mode) < 0) {
throw errorFromErrno(path);
}
}
public static PyString __doc__chown = new PyString(
"chown(path, uid, gid)\n\n" +
"Change the owner and group id of path to the numeric uid and gid.");
@Hider.Hide(OS.NT)
public static void chown(PyObject path, int uid, int gid) {
if (posix.chown(absolutePath(path).toString(), uid, gid) < 0) {
throw errorFromErrno(path);
}
}
public static PyString __doc__close = new PyString(
"close(fd)\n\n" +
"Close a file descriptor (for low level IO).");
public static void close(PyObject fd) {
Object obj = fd.__tojava__(RawIOBase.class);
if (obj != Py.NoConversion) {
((RawIOBase)obj).close();
} else {
posix.close(getFD(fd).getIntFD());
}
}
@Hider.Hide(OS.NT)
public static void closerange(PyObject fd_lowObj, PyObject fd_highObj) {
int fd_low = getFD(fd_lowObj).getIntFD(false);
int fd_high = getFD(fd_highObj).getIntFD(false);
for (int i = fd_low; i < fd_high; i++) {
try {
posix.close(i);
} catch (Exception e) {}
}
}
// Disable dup support until it fully works with fdopen;
// this incomplete support currently breaks py.test
// public static PyObject dup(PyObject fd1) {
// return Py.newInteger(posix.dup(getFD(fd1).getIntFD()));
// }
//
// public static PyObject dup2(PyObject fd1, PyObject fd2) {
// return Py.newInteger(posix.dup2(getFD(fd1).getIntFD(), getFD(fd2).getIntFD()));
// }
public static PyString __doc__fdopen = new PyString(
"fdopen(fd [, mode='r' [, bufsize]]) -> file_object\n\n" +
"Return an open file object connected to a file descriptor.");
public static PyObject fdopen(PyObject fd) {
return fdopen(fd, "r");
}
public static PyObject fdopen(PyObject fd, String mode) {
return fdopen(fd, mode, -1);
}
public static PyObject fdopen(PyObject fd, String mode, int bufsize) {
if (mode.length() == 0 || !"rwa".contains("" + mode.charAt(0))) {
throw Py.ValueError(String.format("invalid file mode '%s'", mode));
}
Object javaobj = fd.__tojava__(RawIOBase.class);
if (javaobj == Py.NoConversion) {
getFD(fd).getIntFD();
throw Py.NotImplementedError("Integer file descriptors not currently supported for fdopen");
}
RawIOBase rawIO = (RawIOBase)javaobj;
if (rawIO.closed()) {
throw badFD();
}
try {
return new PyFile(rawIO, "", mode, bufsize);
} catch (PyException pye) {
if (!pye.match(Py.IOError)) {
throw pye;
}
throw Py.OSError(Errno.EINVAL);
}
}
public static PyString __doc__fdatasync = new PyString(
"fdatasync(fildes)\n\n" +
"force write of file with filedescriptor to disk.\n" +
"does not force update of metadata.");
@Hider.Hide(OS.NT)
public static void fdatasync(PyObject fd) {
Object javaobj = fd.__tojava__(RawIOBase.class);
if (javaobj != Py.NoConversion) {
fsync((RawIOBase)javaobj, false);
} else {
posix.fdatasync(getFD(fd).getIntFD());
}
}
public static PyString __doc__fsync = new PyString(
"fsync(fildes)\n\n" +
"force write of file with filedescriptor to disk.");
public static void fsync(PyObject fd) {
Object javaobj = fd.__tojava__(RawIOBase.class);
if (javaobj != Py.NoConversion) {
fsync((RawIOBase)javaobj, true);
} else {
posix.fsync(getFD(fd).getIntFD());
}
}
/**
* Internal fsync implementation.
*/
private static void fsync(RawIOBase rawIO, boolean metadata) {
rawIO.checkClosed();
Channel channel = rawIO.getChannel();
if (!(channel instanceof FileChannel)) {
throw Py.OSError(Errno.EINVAL);
}
try {
((FileChannel)channel).force(metadata);
} catch (ClosedChannelException cce) {
// In the rare case it's closed but the rawIO wasn't
throw Py.ValueError("I/O operation on closed file");
} catch (IOException ioe) {
throw Py.OSError(ioe);
}
}
public static PyString __doc__ftruncate = new PyString(
"ftruncate(fd, length)\n\n" +
"Truncate a file to a specified length.");
public static void ftruncate(PyObject fd, long length) {
Object javaobj = fd.__tojava__(RawIOBase.class);
if (javaobj != Py.NoConversion) {
try {
((RawIOBase) javaobj).truncate(length);
} catch (PyException pye) {
throw Py.OSError(Errno.EBADF);
}
} else {
posix.ftruncate(getFD(fd).getIntFD(), length);
}
}
public static PyString __doc__getcwd = new PyString(
"getcwd() -> path\n\n" +
"Return a string representing the current working directory.");
public static PyObject getcwd() {
// The return value is bytes in the file system encoding
return Py.fileSystemEncode(Py.getSystemState().getCurrentWorkingDir());
}
public static PyString __doc__getcwdu = new PyString(
"getcwd() -> path\n\n" +
"Return a unicode string representing the current working directory.");
public static PyObject getcwdu() {
return Py.newUnicode(Py.getSystemState().getCurrentWorkingDir());
}
public static PyString __doc__getegid = new PyString(
"getegid() -> egid\n\n" +
"Return the current process's effective group id.");
@Hider.Hide(OS.NT)
public static int getegid() {
return posix.getegid();
}
public static PyString __doc__geteuid = new PyString(
"geteuid() -> euid\n\n" +
"Return the current process's effective user id.");
@Hider.Hide(OS.NT)
public static int geteuid() {
return posix.geteuid();
}
public static PyString __doc__getgid = new PyString(
"getgid() -> gid\n\n" +
"Return the current process's group id.");
@Hider.Hide(value=OS.NT, posixImpl = Hider.PosixImpl.JAVA)
public static int getgid() {
return posix.getgid();
}
public static PyString __doc__getlogin = new PyString(
"getlogin() -> string\n\n" +
"Return the actual login name.");
@Hider.Hide(value=OS.NT, posixImpl = Hider.PosixImpl.JAVA)
public static PyObject getlogin() {
String login = posix.getlogin();
if (login == null) {
// recommend according to https://docs.python.org/2/library/os.html#os.getlogin
throw Py.OSError(
"getlogin OS call failed. Preferentially use os.getenv('LOGNAME') instead.");
}
return new PyString(login);
}
public static PyString __doc__getppid = new PyString(
"getppid() -> ppid\n\n" +
"Return the parent's process id.");
@Hider.Hide(value=OS.NT, posixImpl = Hider.PosixImpl.JAVA)
public static int getppid() {
return posix.getppid();
}
public static PyString __doc__getuid = new PyString(
"getuid() -> uid\n\n" +
"Return the current process's user id.");
@Hider.Hide(value=OS.NT, posixImpl = Hider.PosixImpl.JAVA)
public static int getuid() {
return posix.getuid();
}
public static PyString __doc__getpid = new PyString(
"getpid() -> pid\n\n" +
"Return the current process id");
@Hider.Hide(posixImpl = Hider.PosixImpl.JAVA)
public static int getpid() {
return posix.getpid();
}
public static PyString __doc__getpgrp = new PyString(
"getpgrp() -> pgrp\n\n" +
"Return the current process group id.");
@Hider.Hide(value=OS.NT, posixImpl = Hider.PosixImpl.JAVA)
public static int getpgrp() {
return posix.getpgrp();
}
public static PyString __doc__isatty = new PyString(
"isatty(fd) -> bool\n\n" +
"Return True if the file descriptor 'fd' is an open file descriptor\n" +
"connected to the slave end of a terminal.");
@Hider.Hide(posixImpl = Hider.PosixImpl.JAVA)
public static boolean isatty(PyObject fdObj) {
Object tojava = fdObj.__tojava__(IOBase.class);
if (tojava != Py.NoConversion) {
try {
return ((IOBase) tojava).isatty();
} catch (PyException pye) {
if (pye.match(Py.ValueError)) {
return false;
}
throw pye;
}
}
FDUnion fd = getFD(fdObj);
if (fd.javaFD != null) {
return posix.isatty(fd.javaFD);
}
try {
fd.getIntFD(); // evaluate for side effect of checking EBADF or raising TypeError
} catch (PyException pye) {
if (pye.match(Py.OSError)) {
return false;
}
throw pye;
}
throw Py.NotImplementedError(
"Integer file descriptor compatibility only "
+ "available for stdin, stdout and stderr (0-2)");
}
public static PyString __doc__kill = new PyString(
"kill(pid, sig)\n\n" +
"Kill a process with a signal.");
@Hider.Hide(value=OS.NT, posixImpl = Hider.PosixImpl.JAVA)
public static void kill(int pid, int sig) {
if (posix.kill(pid, sig) < 0) {
throw errorFromErrno();
}
}
public static PyString __doc__lchmod = new PyString(
"lchmod(path, mode)\n\n" +
"Change the access permissions of a file. If path is a symlink, this\n" +
"affects the link itself rather than the target.");
@Hider.Hide(value=OS.NT, posixImpl = Hider.PosixImpl.JAVA)
public static void lchmod(PyObject path, int mode) {
if (posix.lchmod(absolutePath(path).toString(), mode) < 0) {
throw errorFromErrno(path);
}
}
public static PyString __doc__lchown = new PyString(
"lchown(path, uid, gid)\n\n" +
"Change the owner and group id of path to the numeric uid and gid.\n" +
"This function will not follow symbolic links.");
@Hider.Hide(value=OS.NT, posixImpl = Hider.PosixImpl.JAVA)
public static void lchown(PyObject path, int uid, int gid) {
if (posix.lchown(absolutePath(path).toString(), uid, gid) < 0) {
throw errorFromErrno(path);
}
}
public static PyString __doc__link = new PyString(
"link(src, dst)\n\n" +
"Create a hard link to a file.");
@Hider.Hide(OS.NT)
public static void link(PyObject src, PyObject dst) {
try {
Files.createLink(Paths.get(asPath(dst)), Paths.get(asPath(src)));
} catch (FileAlreadyExistsException ex) {
throw Py.OSError(Errno.EEXIST);
} catch (NoSuchFileException ex) {
throw Py.OSError(Errno.ENOENT);
} catch (IOException ioe) {
System.err.println("Got this exception " + ioe);
throw Py.OSError(ioe);
} catch (SecurityException ex) {
throw Py.OSError(Errno.EACCES);
}
}
public static PyString __doc__listdir = new PyString(
"listdir(path) -> list_of_strings\n\n" +
"Return a list containing the names of the entries in the directory.\n\n" +
"path: path of directory to list\n\n" +
"The list is in arbitrary order. It does not include the special\n" +
"entries '.' and '..' even if they are present in the directory.");
public static PyList listdir(PyObject path) {
File file = absolutePath(path).toFile();
String[] names = file.list();
if (names == null) {
if (!file.isDirectory()) {
throw Py.OSError(Errno.ENOTDIR, path);
}
if (!file.canRead()) {
throw Py.OSError(Errno.EACCES, path);
}
throw Py.OSError("listdir(): an unknown error occurred: " + path);
}
// Return names as bytes or unicode according to the type of the original argument
PyList list = new PyList();
if (path instanceof PyUnicode) {
for (String name : names) {
list.append(Py.newUnicode(name));
}
} else {
for (String name : names) {
list.append(Py.fileSystemEncode(name));
}
}
return list;
}
public static PyString __doc__lseek = new PyString(
"lseek(fd, pos, how) -> newpos\n\n" +
"Set the current position of a file descriptor.");
public static long lseek(PyObject fd, long pos, int how) {
Object javaobj = fd.__tojava__(RawIOBase.class);
if (javaobj != Py.NoConversion) {
try {
return ((RawIOBase) javaobj).seek(pos, how);
} catch (PyException pye) {
throw badFD();
}
} else {
return posix.lseek(getFD(fd).getIntFD(), pos, how);
}
}
public static PyString __doc__mkdir = new PyString(
"mkdir(path [, mode=0777])\n\n" +
"Create a directory.");
public static void mkdir(PyObject path) {
mkdir(path, 0777);
}
public static void mkdir(PyObject path, int mode) {
if (os == OS.NT) {
try {
Path nioPath = absolutePath(path);
// Windows does not use any mode attributes in creating a directory;
// see the corresponding function in posixmodule.c, posix_mkdir;
Files.createDirectory(nioPath);
} catch (FileAlreadyExistsException ex) {
throw Py.OSError(Errno.EEXIST, path);
} catch (IOException ioe) {
throw Py.OSError(ioe);
} catch (SecurityException ex) {
throw Py.OSError(Errno.EACCES, path);
}
// Further work on mapping mode to PosixAttributes would have to be done
// for non Windows platforms. In addition, posix.mkdir would still be necessary
// for mode bits like stat.S_ISGID
} else if (posix.mkdir(absolutePath(path).toString(), mode) < 0) {
throw errorFromErrno(path);
}
}
public static PyString __doc__open = new PyString(
"open(filename, flag [, mode=0777]) -> fd\n\n" +
"Open a file (for low level IO).\n\n" +
"Note that the mode argument is not currently supported on Jython.");
public static FileIO open(PyObject path, int flag) {
return open(path, flag, 0777);
}
public static FileIO open(PyObject path, int flag, int mode) {
File file = absolutePath(path).toFile();
boolean reading = (flag & O_RDONLY) != 0;
boolean writing = (flag & O_WRONLY) != 0;
boolean updating = (flag & O_RDWR) != 0;
boolean creating = (flag & O_CREAT) != 0;
boolean appending = (flag & O_APPEND) != 0;
boolean truncating = (flag & O_TRUNC) != 0;
boolean exclusive = (flag & O_EXCL) != 0;
boolean sync = (flag & O_SYNC) != 0;
if (updating && writing) {
throw Py.OSError(Errno.EINVAL, path);
}
if (!creating && !file.exists()) {
throw Py.OSError(Errno.ENOENT, path);
}
if (!writing) {
if (updating) {
writing = true;
} else {
reading = true;
}
}
if (truncating && !writing) {
// Explicitly truncate, writing will truncate anyway
new FileIO((PyString) path, "w").close();
}
if (exclusive && creating) {
try {
if (!file.createNewFile()) {
throw Py.OSError(Errno.EEXIST, path);
}
} catch (IOException ioe) {
throw Py.OSError(ioe);
}
}
String fileIOMode = (reading ? "r" : "") + (!appending && writing ? "w" : "")
+ (appending && (writing || updating) ? "a" : "") + (updating ? "+" : "");
if (sync && (writing || updating)) {
try {
return new FileIO(new RandomAccessFile(file, "rws").getChannel(), fileIOMode);
} catch (FileNotFoundException fnfe) {
throw Py.OSError(file.isDirectory() ? Errno.EISDIR : Errno.ENOENT, path);
}
}
return new FileIO((PyString) path, fileIOMode);
}
public static PyString __doc__pipe = new PyString(
"pipe() -> (read_end, write_end)\n\n" +
"Create a pipe.");
public static PyTuple pipe() {
try {
Pipe pipe = Pipe.open();
StreamIO pipe_read = new StreamIO(pipe.source());
StreamIO pipe_write = new StreamIO(pipe.sink());
return new PyTuple(
PyJavaType.wrapJavaObject(pipe_read.fileno()),
PyJavaType.wrapJavaObject(pipe_write.fileno()));
} catch (IOException ioe) {
throw Py.OSError(ioe);
}
}
public static PyString __doc__popen = new PyString(
"popen(command [, mode='r' [, bufsize]]) -> pipe\n\n" +
"Open a pipe to/from a command returning a file object.");
public static PyObject popen(PyObject[] args, String[] kwds) {
// XXX: popen lives in posix in 2.x, but moves to os in 3.x. It's easier for us to
// keep it in os
// import os; return os.popen(*args, **kwargs)
return imp.load("os").__getattr__("popen").__call__(args, kwds);
}
public static PyString __doc__putenv = new PyString(
"putenv(key, value)\n\n" +
"Change or add an environment variable.");
public static void putenv(String key, String value) {
// XXX: Consider deprecating putenv/unsetenv
// import os; os.environ[key] = value
PyObject environ = imp.load("os").__getattr__("environ");
environ.__setitem__(key, new PyString(value));
}
public static PyString __doc__read = new PyString(
"read(fd, buffersize) -> string\n\n" +
"Read a file descriptor.");
public static PyObject read(PyObject fd, int buffersize) {
Object javaobj = fd.__tojava__(RawIOBase.class);
if (javaobj != Py.NoConversion) {
try {
return new PyString(StringUtil.fromBytes(((RawIOBase) javaobj).read(buffersize)));
} catch (PyException pye) {
throw badFD();
}
} else {
ByteBuffer buffer = ByteBuffer.allocate(buffersize);
posix.read(getFD(fd).getIntFD(), buffer, buffersize);
return new PyString(StringUtil.fromBytes(buffer));
}
}
public static PyString __doc__readlink = new PyString(
"readlink(path) -> path\n\n" +
"Return a string representing the path to which the symbolic link points.");
@Hider.Hide(OS.NT)
public static PyString readlink(PyObject path) {
try {
return Py.newStringOrUnicode(path, Files.readSymbolicLink(absolutePath(path)).toString());
} catch (NotLinkException ex) {
throw Py.OSError(Errno.EINVAL, path);
} catch (NoSuchFileException ex) {
throw Py.OSError(Errno.ENOENT, path);
} catch (IOException ioe) {
throw Py.OSError(ioe);
} catch (SecurityException ex) {
throw Py.OSError(Errno.EACCES, path);
}
}
public static PyString __doc__remove = new PyString(
"remove(path)\n\n" +
"Remove a file (same as unlink(path)).");
public static void remove(PyObject path) {
unlink(path);
}
public static PyString __doc__rename = new PyString(
"rename(old, new)\n\n" +
"Rename a file or directory.");
public static void rename(PyObject oldpath, PyObject newpath) {
if (!(absolutePath(oldpath).toFile().renameTo(absolutePath(newpath).toFile()))) {
PyObject args = new PyTuple(Py.Zero, new PyString("Couldn't rename file"));
throw new PyException(Py.OSError, args);
}
}
public static PyString __doc__rmdir = new PyString(
"rmdir(path)\n\n" +
"Remove a directory.");
public static void rmdir(PyObject path) {
File file = absolutePath(path).toFile();
if (!file.exists()) {
throw Py.OSError(Errno.ENOENT, path);
} else if (!file.isDirectory()) {
throw Py.OSError(Errno.ENOTDIR, path);
} else if (!file.delete()) {
PyObject args = new PyTuple(Py.Zero, new PyString("Couldn't delete directory"),
path);
throw new PyException(Py.OSError, args);
}
}
public static PyString __doc__setpgrp = new PyString(
"setpgrp()\n\n" +
"Make this process a session leader.");
@Hider.Hide(value=OS.NT, posixImpl = Hider.PosixImpl.JAVA)
public static void setpgrp() {
if (posix.setpgrp(0, 0) < 0) {
throw errorFromErrno();
}
}
public static PyString __doc__setsid = new PyString(
"setsid()\n\n" +
"Call the system call setsid().");
@Hider.Hide(value=OS.NT, posixImpl = Hider.PosixImpl.JAVA)
public static void setsid() {
if (posix.setsid() < 0) {
throw errorFromErrno();
}
}
public static PyString __doc__strerror = new PyString(
"strerror(code) -> string\n\n" +
"Translate an error code to a message string.");
public static PyObject strerror(int code) {
Constant errno = Errno.valueOf(code);
if (errno == Errno.__UNKNOWN_CONSTANT__) {
return new PyString("Unknown error: " + code);
}
if (errno.name() == errno.toString()) {
// Fake constant or just lacks a description, fallback to Linux's
// XXX: have jnr-constants handle this fallback
errno = Enum.valueOf(jnr.constants.platform.linux.Errno.class,
errno.name());
}
return new PyString(errno.toString());
}
public static PyString __doc__symlink = new PyString(
"symlink(src, dst)\n\n" +
"Create a symbolic link pointing to src named dst.");
@Hider.Hide(OS.NT)
public static void symlink(PyObject src, PyObject dst) {
try {
Files.createSymbolicLink(Paths.get(asPath(dst)), Paths.get(asPath(src)));
} catch (FileAlreadyExistsException ex) {
throw Py.OSError(Errno.EEXIST);
} catch (IOException ioe) {
throw Py.OSError(ioe);
} catch (SecurityException ex) {
throw Py.OSError(Errno.EACCES);
}
}
private static PyFloat ratio(long num, long div) {
return Py.newFloat(((double)num)/((double)div));
}
public static PyString __doc__times = new PyString(
"times() -> (utime, stime, cutime, cstime, elapsed_time)\n\n" +
"Return a tuple of floating point numbers indicating process times.");
@Hider.Hide(posixImpl = Hider.PosixImpl.JAVA)
public static PyTuple times() {
Times times = posix.times();
long CLK_TCK = Sysconf._SC_CLK_TCK.longValue();
return new PyTuple(
ratio(times.utime(), CLK_TCK),
ratio(times.stime(), CLK_TCK),
ratio(times.cutime(), CLK_TCK),
ratio(times.cstime(), CLK_TCK),
ratio(ManagementFactory.getRuntimeMXBean().getUptime(), 1000)
);
}
public static PyString __doc__umask = new PyString(
"umask(new_mask) -> old_mask\n\n" +
"Set the current numeric umask and return the previous umask.");
@Hider.Hide(posixImpl = Hider.PosixImpl.JAVA)
public static int umask(int mask) {
return posix.umask(mask);
}
public static PyString __doc__uname = new PyString(
"uname() -> (sysname, nodename, release, version, machine)\n\n" +
"Return a tuple identifying the current operating system.");
/* Obtaining uname values via exec is expensive, so we cache here.
* Cache is placed here near uname, because it is needed exclusively by uname.
*/
private static PyTuple uname_cache = null;
/**
* Resembles CPython's uname with the addition that we also attempt a
* Windows-equivalent variant on win-systems. Implementation overview:
*
* Original/CPython (POSIX only):
* (uname -s, uname -n, uname -r, uname -v, uname -m)
*
* This version (non-Windows):
* (property os.name, InetAddress.getLocalHost().getHostName(), property os.version, uname -v, uname -m)
*
* Adjustments on OSX:
* Normalize "Mac OS X" to "Darwin",
* uname -r instead of property os.version
*
* Fallbacks:
* nodename/uname -n: exec uname -n
* version/uname -v: ""
* machine/uname -m: property os.arch
*
* This version (Windows):
* (reproduces platform.uname behavior on Windows)
* ("Windows", InetAddress.getLocalHost().getHostName(), property os.name: part after "Windows",
* cmd.exe /C ver: part after "Version", env PROCESSOR_ARCHITECTURE)
*
* Fallback for nodename/uname -n on Windows:
* - env USERDOMAIN
* - exec hostname
*
* For machine-entry on Windows this is a simplified description.
* It is actually mapped to typical uname -m values as follows (pseudo-code):
*
* PROCESSOR_ARCHITECTURE = x86 and PROCESSOR_ARCHITEW6432 undefined: "x86"
* else if PROCESSOR_ARCHITECTURE = x86: PROCESSOR_ARCHITEW6432
* else: PROCESSOR_ARCHITECTURE
*
* Potential flaws:
* - could be a 32-bit machine, but actually not i686
* - user might need to discriminate AMD64 from EM64T
*
* In the rare case that your application is sensitive to one of these flaws you shouldn't be
* using our uname-hack for Windows, but directly look at PROCESSOR_ARCHITECTURE and friends.
*
* @return PyTuple containing sysname, nodename, release, version, machine
*/
public static PyTuple uname() {
if (uname_cache == null) {
// First call: have to construct the result.
String sysname = System.getProperty("os.name");
String sysrelease, nodename, machine;
boolean win = false;
if (sysname.equals("Mac OS X")) {
sysname = "Darwin";
sysrelease = Py.getCommandResult("uname", "-r");
} else if (sysname.startsWith("Windows")) {
sysrelease = sysname.length() > 7 ? sysname.substring(8)
: System.getProperty("os.version", "");
sysname = "Windows";
win = true;
} else {
sysrelease = System.getProperty("os.version", "");
}
try {
nodename = java.net.InetAddress.getLocalHost().getHostName();
} catch (Exception e) {
// If that fails, try the shell.
if (win) {
nodename = Py.getenv("USERDOMAIN", "");
if (nodename.isEmpty()) {
nodename = Py.getCommandResult("hostname");
}
} else {
nodename = Py.getCommandResult("uname", "-n");
}
}
String sysver = PySystemState.getSystemVersionString();
if (win) {
// Check if 32-bit process on a 64 bit machine (compare platform.py)
machine = Py.getenv("PROCESSOR_ARCHITEW6432", "");
if (machine.isEmpty()) {
// Otherwise, this contains the value (or we default to null)
machine = Py.getenv("PROCESSOR_ARCHITECTURE", "");
}
} else {
machine = Py.getCommandResult("uname", "-m");
}
if (machine.isEmpty()) {
machine = System.getProperty("os.arch", "");
if (machine.equals("amd64")) {
// 64-bit processor presents as x86_64 on Linux and AMD64 on Windows.
machine = win ? "AMD64" : "x86_64";
} else if (machine.equals("x86")) {
machine = "i686";
}
}
uname_cache = new PyTuple(new PyObject[] {
Py.fileSystemEncode(sysname),
Py.fileSystemEncode(nodename),
Py.fileSystemEncode(sysrelease),
Py.fileSystemEncode(sysver),
Py.fileSystemEncode(machine)},
false);
}
return uname_cache;
}
public static PyString __doc__unlink = new PyString("unlink(path)\n\n"
+ "Remove a file (same as remove(path)).");
public static void unlink(PyObject path) {
Path nioPath = absolutePath(path);
try {
if (Files.isDirectory(nioPath, LinkOption.NOFOLLOW_LINKS)) {
throw Py.OSError(Errno.EISDIR, path);
} else if (!Files.deleteIfExists(nioPath)) {
// Something went wrong, does stat raise an error?
basicstat(path, nioPath);
// It exists, do we not have permissions?
if (!Files.isWritable(nioPath)) {
throw Py.OSError(Errno.EACCES, path);
}
throw Py.OSError("unlink(): an unknown error occurred: " + nioPath.toString());
}
} catch (IOException ex) {
PyException pyError = Py.OSError("unlink(): an unknown error occurred: " + nioPath.toString());
pyError.initCause(ex);
throw pyError;
}
}
public static PyString __doc__utime = new PyString(
"utime(path, (atime, mtime))\n" +
"utime(path, None)\n\n" +
"Set the access and modified time of the file to the given values. If the\n" +
"second form is used, set the access and modified times to the current time.");
public static void utime(PyObject path, PyObject times) {
FileTime atime;
FileTime mtime;
if (times == Py.None) {
// FIXME dynamically bind to java.time.Instant, available in Java 8,
// to potentially get higher resolution (nanosecond) time
atime = mtime = FileTime.from(
System.currentTimeMillis(), TimeUnit.MILLISECONDS);
} else if (times instanceof PyTuple && times.__len__() == 2) {
atime = getFileTime(times.__getitem__(0));
mtime = getFileTime(times.__getitem__(1));
} else {
throw Py.TypeError("utime() arg 2 must be a tuple (atime, mtime)");
}
try {
Files.getFileAttributeView(absolutePath(path), BasicFileAttributeView.class).
setTimes(mtime, atime, null);
} catch (NoSuchFileException ex) {
throw Py.OSError(Errno.ENOENT, path);
} catch (IOException ioe) {
throw Py.OSError(ioe);
} catch (SecurityException ex) {
throw Py.OSError(Errno.EACCES, path);
}
}
private static FileTime getFileTime(PyObject seconds) {
try {
return FileTime.from(
(long) (seconds.__float__().asDouble() * 1e6),
TimeUnit.MICROSECONDS);
} catch (PyException pye) {
if (!pye.match(Py.AttributeError)) {
throw pye;
} else {
throw Py.TypeError("an integer or float is required");
}
}
}
public static PyString __doc__wait = new PyString(
"wait() -> (pid, status)\n\n" +
"Wait for completion of a child process.");
@Hider.Hide(value=OS.NT, posixImpl = Hider.PosixImpl.JAVA)
public static PyObject wait$() {
int[] status = new int[1];
int pid = posix.wait(status);
if (pid < 0) {
throw errorFromErrno();
}
return new PyTuple(Py.newInteger(pid), Py.newInteger(status[0]));
}
public static PyString __doc__waitpid = new PyString(
"wait() -> (pid, status)\n\n" +
"Wait for completion of a child process.");
@Hider.Hide(posixImpl = Hider.PosixImpl.JAVA)
public static PyObject waitpid(int pid, int options) {
int[] status = new int[1];
pid = posix.waitpid(pid, status, options);
if (pid < 0) {
throw errorFromErrno();
}
return new PyTuple(Py.newInteger(pid), Py.newInteger(status[0]));
}
public static PyString __doc__write = new PyString(
"write(fd, string) -> byteswritten\n\n" +
"Write a string to a file descriptor.");
public static int write(PyObject fd, PyObject bytes) {
try (PyBuffer buf = ((BufferProtocol) bytes).getBuffer(PyBUF.SIMPLE)) {
// Get a ByteBuffer of that data, setting the position and limit to the real data.
ByteBuffer bb = buf.getNIOByteBuffer();
Object javaobj = fd.__tojava__(RawIOBase.class);
if (javaobj != Py.NoConversion) {
try {
return ((RawIOBase) javaobj).write(bb);
} catch (PyException pye) {
throw badFD();
}
} else {
return posix.write(getFD(fd).getIntFD(), bb, bb.position());
}
} catch (ClassCastException e) {
throw Py.TypeError(
"write() argument 2 must be string or buffer, not " + bytes.getType());
}
}
public static PyString __doc__unsetenv = new PyString(
"unsetenv(key)\n\n" +
"Delete an environment variable.");
public static void unsetenv(String key) {
// import os; try: del os.environ[key]; except KeyError: pass
PyObject environ = imp.load("os").__getattr__("environ");
try {
environ.__delitem__(key);
} catch (PyException pye) {
if (!pye.match(Py.KeyError)) {
throw pye;
}
}
}
public static PyString __doc__urandom = new PyString(
"urandom(n) -> str\n\n" +
"Return a string of n random bytes suitable for cryptographic use.");
public static PyObject urandom(int n) {
byte[] buf = new byte[n];
UrandomSource.INSTANCE.nextBytes(buf);
return new PyString(StringUtil.fromBytes(buf));
}
/**
* Helper function for the subprocess module, returns the potential shell commands for
* this OS.
*
* @return a tuple of lists of command line arguments. E.g. (['/bin/sh', '-c'])
*/
public static PyObject _get_shell_commands() {
String[][] commands = os.getShellCommands();
PyObject[] commandsTup = new PyObject[commands.length];
int i = 0;
for (String[] command : commands) {
PyList args = new PyList();
for (String arg : command) {
args.append(new PyString(arg));
}
commandsTup[i++] = args;
}
return new PyTuple(commandsTup);
}
/**
* Initialize the environ dict from System.getenv. environ may be empty when the
* security policy doesn't grant us access.
*/
private static PyObject getEnviron() {
PyObject environ = new PyDictionary();
Map env;
try {
env = System.getenv();
} catch (SecurityException se) {
return environ;
}
for (Map.Entry entry : env.entrySet()) {
// The shell restricts names to a subset of ASCII and values are encoded byte strings.
environ.__setitem__(
Py.newString(entry.getKey()),
Py.fileSystemEncode(entry.getValue()));
}
return environ;
}
/**
* Return a path as a String from a PyObject, which must be str
or
* unicode
. If the path is a str
(that is, bytes
), it is
* interpreted into Unicode using the file system encoding.
*
* @param path a PyObject, raising a TypeError if an invalid path type
* @return a String path
*/
private static String asPath(PyObject path) {
return Py.fileSystemDecode(path);
}
/**
* Return the absolute, normalised form of path, equivalent to Python os.path.abspath(), except
* that it is an error for pathObj to be an empty string or unacceptable in the file system.
*
* @param pathObj a PyObject, raising a TypeError if an invalid path type
* @return an absolute path String
*/
private static Path absolutePath(PyObject pathObj) {
String pathStr = asPath(pathObj);
if (pathStr.equals("")) {
// Returning current working directory would be wrong in our context (chdir, etc.).
throw Py.OSError(Errno.ENOENT, pathObj);
}
try {
Path path = Paths.get(pathStr);
// Relative path: augment from current working directory.
path = Paths.get(Py.getSystemState().getCurrentWorkingDir()).resolve(path);
// In case of a root different from cwd, resolve does not guarantee absolute.
path = path.toAbsolutePath();
// Strip redundant navigation a/b/../c -> a/c
path = path.normalize();
// Prevent trailing slash (possibly Java bug), except when '/' or C:\
pathStr = path.toString();
if (pathStr.endsWith(path.getFileSystem().getSeparator()) && path.getNameCount()>0) {
path = Paths.get(pathStr.substring(0, pathStr.length()-1));
}
return path;
} catch (java.nio.file.InvalidPathException ex) {
/*
* Thrown on Windows for paths like foo/bar/, where is the literal text,
* not a metavariable :) NOTE: CPython, Windows throws the Windows-specific internal
* error WindowsError [Error 123], but it seems excessive to duplicate this error
* hierarchy.
*/
throw Py.OSError(Errno.EINVAL, pathObj);
}
}
private static PyException badFD() {
return Py.OSError(Errno.EBADF);
}
private static PyException errorFromErrno() {
return Py.OSError(Errno.valueOf(posix.errno()));
}
private static PyException errorFromErrno(PyObject path) {
return Py.OSError(Errno.valueOf(posix.errno()), path);
}
public static POSIX getPOSIX() {
return posix;
}
public static String getOSName() {
return os.getModuleName();
}
private static void checkTrailingSlash(PyObject path, Map attributes) {
Boolean isDirectory = (Boolean) attributes.get("isDirectory");
if (isDirectory != null && !isDirectory.booleanValue()) {
String pathStr = path.toString();
if (pathStr.endsWith(File.separator) || pathStr.endsWith("/.")) {
throw Py.OSError(Errno.ENOTDIR, path);
}
}
}
private static BasicFileAttributes basicstat(PyObject path, Path absolutePath) {
try {
BasicFileAttributes attributes = Files.readAttributes(absolutePath, BasicFileAttributes.class);
if (!attributes.isDirectory()) {
String pathStr = path.toString();
if (pathStr.endsWith(File.separator) || pathStr.endsWith("/")) {
throw Py.OSError(Errno.ENOTDIR, path);
}
}
return attributes;
} catch (NoSuchFileException ex) {
throw Py.OSError(Errno.ENOENT, path);
} catch (IOException ioe) {
throw Py.OSError(Errno.EBADF, path);
} catch (SecurityException ex) {
throw Py.OSError(Errno.EACCES, path);
}
}
@Untraversable
static class LstatFunction extends PyBuiltinFunctionNarrow {
LstatFunction() {
super("lstat", 1, 1,
"lstat(path) -> stat result\n\n" +
"Like stat(path), but do not follow symbolic links.");
}
@Override
public PyObject __call__(PyObject path) {
Path absolutePath = absolutePath(path);
try {
Map attributes = Files.readAttributes(
absolutePath, "unix:*", LinkOption.NOFOLLOW_LINKS);
Boolean isSymbolicLink = (Boolean) attributes.get("isSymbolicLink");
if (isSymbolicLink != null && isSymbolicLink.booleanValue() && path.toString().endsWith("/")) {
// Chase the symbolic link, but do not follow further - this is a special case for lstat
Path symlink = Files.readSymbolicLink(absolutePath);
symlink = absolutePath.getParent().resolve(symlink);
attributes = Files.readAttributes(
symlink, "unix:*", LinkOption.NOFOLLOW_LINKS);
} else {
checkTrailingSlash(path, attributes);
}
return PyStatResult.fromUnixFileAttributes(attributes);
} catch (NoSuchFileException ex) {
throw Py.OSError(Errno.ENOENT, path);
} catch (IOException ioe) {
throw Py.OSError(Errno.EBADF, path);
} catch (SecurityException ex) {
throw Py.OSError(Errno.EACCES, path);
}
}
}
@Untraversable
static class StatFunction extends PyBuiltinFunctionNarrow {
StatFunction() {
super("stat", 1, 1,
"stat(path) -> stat result\n\n" +
"Perform a stat system call on the given path.\n\n" +
"Note that some platforms may return only a small subset of the\n" +
"standard fields");
}
@Override
public PyObject __call__(PyObject path) {
Path absolutePath = absolutePath(path);
try {
Map attributes = Files.readAttributes(absolutePath, "unix:*");
checkTrailingSlash(path, attributes);
return PyStatResult.fromUnixFileAttributes(attributes);
} catch (NoSuchFileException ex) {
throw Py.OSError(Errno.ENOENT, path);
} catch (IOException ioe) {
throw Py.OSError(Errno.EBADF, path);
} catch (SecurityException ex) {
throw Py.OSError(Errno.EACCES, path);
}
}
}
// Follows the approach taken by posixmodule.c for a Windows specific stat;
// in particular this is driven by the fact that Windows CRT does not properly handle
// daylight savings time in timestamps.
//
// Another advantage is setting the st_mode the same as CPython would return.
@Untraversable
static class WindowsStatFunction extends PyBuiltinFunctionNarrow {
WindowsStatFunction() {
super("stat", 1, 1,
"stat(path) -> stat result\n\n" +
"Perform a stat system call on the given path.\n\n" +
"Note that some platforms may return only a small subset of the\n" +
"standard fields"); // like this one!
}
private final static int _S_IFDIR = 0x4000;
private final static int _S_IFREG = 0x8000;
static int attributes_to_mode(DosFileAttributes attr) {
int m = 0;
if (attr.isDirectory()) {
m |= _S_IFDIR | 0111; /* IFEXEC for user,group,other */
} else {
m |= _S_IFREG;
}
if (attr.isReadOnly()) {
m |= 0444;
} else {
m |= 0666;
}
return m;
}
@Override
public PyObject __call__(PyObject path) {
Path absolutePath = absolutePath(path);
try {
DosFileAttributes attributes = Files.readAttributes(absolutePath, DosFileAttributes.class);
if (!attributes.isDirectory()) {
String pathStr = path.toString();
if (pathStr.endsWith(File.separator) || pathStr.endsWith("/")) {
throw Py.OSError(Errno.ENOTDIR, path);
}
}
int mode = attributes_to_mode(attributes);
String extension = com.google.common.io.Files.getFileExtension(absolutePath.toString());
if (extension.equals("bat") || extension.equals("cmd") || extension.equals("exe") || extension.equals("com")) {
mode |= 0111;
}
return PyStatResult.fromDosFileAttributes(mode, attributes);
} catch (NoSuchFileException ex) {
throw Py.OSError(Errno.ENOENT, path);
} catch (IOException ioe) {
throw Py.OSError(Errno.EBADF, path);
} catch (SecurityException ex) {
throw Py.OSError(Errno.EACCES, path);
}
}
}
@Untraversable
static class FstatFunction extends PyBuiltinFunctionNarrow {
FstatFunction() {
super("fstat", 1, 1,
"fstat(fd) -> stat result\\n\\nLike stat(), but for an open file descriptor.");
}
@Override
public PyObject __call__(PyObject fdObj) {
try {
FDUnion fd = getFD(fdObj);
FileStat stat;
if (os != OS.NT) {
if (fd.isIntFD()) {
stat = posix.fstat(fd.intFD);
} else {
stat = posix.fstat(fd.javaFD);
}
} else {
// FIXME: jnr-posix fstat work-around. See issue #2320.
stat = new WindowsRawFileStat2(posix, posixHandler);
if (posix.fstat(fd.javaFD, stat) < 0) {
throw Py.OSError(Errno.EBADF);
}
}
return PyStatResult.fromFileStat(stat);
} catch (PyException ex) {
throw Py.OSError(Errno.EBADF);
}
}
}
/*
* Extend the Windows stat object defined by jnr.posix, which in jnr-posix 2.0.4 is buggy to the
* extent that st_mode is always zero. Remarkably, it is possible to fix this by defining a
* cunning replacement.
*/
private static class WindowsRawFileStat2 extends WindowsRawFileStat {
public WindowsRawFileStat2(POSIX posix, POSIXHandler handler) {
super(posix, handler);
}
private int mode; // Replaces st_mode
@Override
public void setup(CommonFileInformation fileInfo) {
super.setup(fileInfo);
// CommonFileInformation gives us (DOS-style) file attributes, not access rights.
int attr = fileInfo.getFileAttributes();
int mode = ALL_READ;
if ((attr & CommonFileInformation.FILE_ATTRIBUTE_READONLY) == 0) {
// Writable: assume by all
mode |= ALL_WRITE;
}
if ((attr & CommonFileInformation.FILE_ATTRIBUTE_DIRECTORY) != 0) {
// Directory: assume by all can look things up in it.
mode |= S_IFDIR | S_IXUGO;
} else {
// Regular file
mode |= S_IFREG;
}
this.mode = mode;
}
@Override
public int mode() {
return mode;
}
}
}