package jnr.posix;
import jnr.constants.platform.
OpenFlags;
import jnr.constants.platform.
Fcntl;
import jnr.constants.platform.
Errno;
import jnr.constants.platform.
WaitFlags;
import static jnr.constants.platform.
Errno.*;
import static jnr.constants.platform.windows.
LastError.*;
import jnr.ffi.
LastError;
import jnr.ffi.
Pointer;
import jnr.ffi.byref.
IntByReference;
import jnr.ffi.mapper.
FromNativeContext;
import java.io.
FileDescriptor;
import java.nio.
ByteBuffer;
import java.util.
HashMap;
import java.util.
Map;
import jnr.posix.util.
MethodName;
import jnr.posix.util.
Platform;
import jnr.posix.util.
WindowsHelpers;
import jnr.posix.windows.
CommonFileInformation;
import jnr.posix.windows.
WindowsByHandleFileInformation;
import jnr.posix.windows.
WindowsFileInformation;
import jnr.posix.windows.
WindowsFindData;
final public class
WindowsPOSIX extends
BaseNativePOSIX {
private final static int
FILE_TYPE_CHAR = 0x0002;
private final static
Map<
Integer,
Errno>
errorToErrnoMapper
= new
HashMap<
Integer,
Errno>();
static {
errorToErrnoMapper.
put(
ERROR_INVALID_FUNCTION.
value(),
EINVAL);
errorToErrnoMapper.
put(
ERROR_FILE_NOT_FOUND.
value(),
ENOENT);
errorToErrnoMapper.
put(
ERROR_PATH_NOT_FOUND.
value(),
ENOENT);
errorToErrnoMapper.
put(
ERROR_TOO_MANY_OPEN_FILES.
value(),
EMFILE);
errorToErrnoMapper.
put(
ERROR_ACCESS_DENIED.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_INVALID_HANDLE.
value(),
EBADF );
errorToErrnoMapper.
put(
ERROR_ARENA_TRASHED.
value(),
ENOMEM);
errorToErrnoMapper.
put(
ERROR_NOT_ENOUGH_MEMORY.
value(),
ENOMEM);
errorToErrnoMapper.
put(
ERROR_INVALID_BLOCK.
value(),
ENOMEM);
errorToErrnoMapper.
put(
ERROR_BAD_ENVIRONMENT.
value(),
E2BIG );
errorToErrnoMapper.
put(
ERROR_BAD_FORMAT.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_INVALID_ACCESS.
value(),
EINVAL);
errorToErrnoMapper.
put(
ERROR_INVALID_DATA.
value(),
EINVAL);
errorToErrnoMapper.
put(
ERROR_INVALID_DRIVE.
value(),
ENOENT);
errorToErrnoMapper.
put(
ERROR_CURRENT_DIRECTORY.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_NOT_SAME_DEVICE.
value(),
EXDEV );
errorToErrnoMapper.
put(
ERROR_NO_MORE_FILES.
value(),
ENOENT);
errorToErrnoMapper.
put(
ERROR_WRITE_PROTECT.
value(),
EROFS );
errorToErrnoMapper.
put(
ERROR_BAD_UNIT.
value(),
ENODEV);
errorToErrnoMapper.
put(
ERROR_NOT_READY.
value(),
ENXIO );
errorToErrnoMapper.
put(
ERROR_BAD_COMMAND.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_CRC.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_BAD_LENGTH.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_SEEK.
value(),
EIO);
errorToErrnoMapper.
put(
ERROR_NOT_DOS_DISK.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_SECTOR_NOT_FOUND.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_OUT_OF_PAPER.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_WRITE_FAULT.
value(),
EIO);
errorToErrnoMapper.
put(
ERROR_READ_FAULT.
value(),
EIO);
errorToErrnoMapper.
put(
ERROR_GEN_FAILURE.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_LOCK_VIOLATION.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_SHARING_VIOLATION.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_WRONG_DISK.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_SHARING_BUFFER_EXCEEDED.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_BAD_NETPATH.
value(),
ENOENT);
errorToErrnoMapper.
put(
ERROR_NETWORK_ACCESS_DENIED.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_BAD_NET_NAME.
value(),
ENOENT);
errorToErrnoMapper.
put(
ERROR_FILE_EXISTS.
value(),
EEXIST);
errorToErrnoMapper.
put(
ERROR_CANNOT_MAKE.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_FAIL_I24.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_INVALID_PARAMETER.
value(),
EINVAL);
errorToErrnoMapper.
put(
ERROR_NO_PROC_SLOTS.
value(),
EAGAIN);
errorToErrnoMapper.
put(
ERROR_DRIVE_LOCKED.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_BROKEN_PIPE.
value(),
EPIPE);
errorToErrnoMapper.
put(
ERROR_DISK_FULL.
value(),
ENOSPC);
errorToErrnoMapper.
put(
ERROR_INVALID_TARGET_HANDLE.
value(),
EBADF);
errorToErrnoMapper.
put(
ERROR_INVALID_HANDLE.
value(),
EINVAL);
errorToErrnoMapper.
put(
ERROR_WAIT_NO_CHILDREN.
value(),
ECHILD);
errorToErrnoMapper.
put(
ERROR_CHILD_NOT_COMPLETE.
value(),
ECHILD);
errorToErrnoMapper.
put(
ERROR_DIRECT_ACCESS_HANDLE.
value(),
EBADF);
errorToErrnoMapper.
put(
ERROR_NEGATIVE_SEEK.
value(),
EINVAL);
errorToErrnoMapper.
put(
ERROR_SEEK_ON_DEVICE.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_DIR_NOT_EMPTY.
value(),
ENOTEMPTY);
errorToErrnoMapper.
put(
ERROR_DIRECTORY.
value(),
ENOTDIR);
errorToErrnoMapper.
put(
ERROR_NOT_LOCKED.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_BAD_PATHNAME.
value(),
ENOENT);
errorToErrnoMapper.
put(
ERROR_MAX_THRDS_REACHED.
value(),
EAGAIN);
errorToErrnoMapper.
put(
ERROR_LOCK_FAILED.
value(),
EACCES);
errorToErrnoMapper.
put(
ERROR_ALREADY_EXISTS.
value(),
EEXIST);
errorToErrnoMapper.
put(
ERROR_INVALID_STARTING_CODESEG.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_INVALID_STACKSEG.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_INVALID_MODULETYPE.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_INVALID_EXE_SIGNATURE.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_EXE_MARKED_INVALID.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_BAD_EXE_FORMAT.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_ITERATED_DATA_EXCEEDS_64k.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_INVALID_MINALLOCSIZE.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_DYNLINK_FROM_INVALID_RING.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_IOPL_NOT_ENABLED.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_INVALID_SEGDPL.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_AUTODATASEG_EXCEEDS_64k.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_RING2SEG_MUST_BE_MOVABLE.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_RELOC_CHAIN_XEEDS_SEGLIM.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_INFLOOP_IN_RELOC_CHAIN.
value(),
ENOEXEC);
errorToErrnoMapper.
put(
ERROR_FILENAME_EXCED_RANGE.
value(),
ENOENT);
errorToErrnoMapper.
put(
ERROR_NESTING_NOT_ALLOWED.
value(),
EAGAIN);
// ERROR_PIPE_LOCAL (in MRI)
errorToErrnoMapper.
put(229,
EPIPE);
errorToErrnoMapper.
put(
ERROR_BAD_PIPE.
value(),
EPIPE);
errorToErrnoMapper.
put(
ERROR_PIPE_BUSY.
value(),
EAGAIN);
errorToErrnoMapper.
put(
ERROR_NO_DATA.
value(),
EPIPE);
errorToErrnoMapper.
put(
ERROR_PIPE_NOT_CONNECTED.
value(),
EPIPE);
errorToErrnoMapper.
put(
ERROR_OPERATION_ABORTED.
value(),
EINTR);
errorToErrnoMapper.
put(
ERROR_NOT_ENOUGH_QUOTA.
value(),
ENOMEM);
errorToErrnoMapper.
put(
ERROR_MOD_NOT_FOUND.
value(),
ENOENT);
errorToErrnoMapper.
put(
WSAENAMETOOLONG.
value(),
ENAMETOOLONG);
errorToErrnoMapper.
put(
WSAENOTEMPTY.
value(),
ENOTEMPTY);
errorToErrnoMapper.
put(
WSAEINTR.
value(),
EINTR);
errorToErrnoMapper.
put(
WSAEBADF.
value(),
EBADF);
errorToErrnoMapper.
put(
WSAEACCES.
value(),
EACCES);
errorToErrnoMapper.
put(
WSAEFAULT.
value(),
EFAULT);
errorToErrnoMapper.
put(
WSAEINVAL.
value(),
EINVAL);
errorToErrnoMapper.
put(
WSAEMFILE.
value(),
EMFILE);
}
private final
FileStat checkFdStat;
WindowsPOSIX(
LibCProvider libc,
POSIXHandler handler) {
super(
libc,
handler);
this.
checkFdStat = new
WindowsFileStat(this);
}
@
Override
public
FileStat allocateStat() {
return new
WindowsRawFileStat(this,
handler);
}
public
MsgHdr allocateMsgHdr() {
handler.
unimplementedError(
MethodName.
getCallerMethodName());
return null;
}
public
SocketMacros socketMacros() {
handler.
unimplementedError(
MethodName.
getCallerMethodName());
return null;
}
@
Override
public int
kill(int
pid, int
signal) {
handler.
unimplementedError("kill");
return -1;
}
@
Override
public int
kill(long
pid, int
signal) {
handler.
unimplementedError("kill");
return -1;
}
@
Override
public int
chmod(
String filename, int
mode) {
return
wlibc().
_wchmod(
WString.
path(
filename),
mode);
}
@
Override
public int
chdir(
String path) {
return
wlibc().
_wchdir(
WString.
path(
path));
}
@
Override
public int
chown(
String filename, int
user, int
group) {
return 0;
}
@
Override
public int
exec(
String path,
String[]
argv) {
if (
argv.length == 1) return
spawn(true,
argv[0], null,
path, null);
return
aspawn(true, null,
argv,
path, null);
}
@
Override
public
CharSequence crypt(
CharSequence key,
CharSequence salt) {
return
JavaLibCHelper.
crypt(
key,
salt);
}
@
Override
public byte[]
crypt(byte[]
key, byte[]
salt) {
return
JavaLibCHelper.
crypt(
key,
salt);
}
@
Override
public int
exec(
String path,
String[]
argv,
String[]
envp) {
if (
argv.length == 1) return
spawn(true,
argv[0], null,
path,
envp);
return
aspawn(true, null,
argv,
path,
envp);
}
@
Override
public int
execv(
String path,
String[]
argv) {
handler.
unimplementedError("egid");
return -1;
}
@
Override
public int
getegid() {
handler.
unimplementedError("egid");
return -1;
}
@
Override
public int
setegid(int
egid) {
handler.
unimplementedError("setegid");
return -1;
}
@
Override
public int
geteuid() {
return 0;
}
@
Override
public int
seteuid(int
euid) {
handler.
unimplementedError("seteuid");
return -1;
}
@
Override
public int
getuid() {
return 0;
}
@
Override
public int
setuid(int
uid) {
handler.
unimplementedError("setuid");
return -1;
}
@
Override
public int
getgid() {
return 0;
}
@
Override
public int
setgid(int
gid) {
handler.
unimplementedError("setgid");
return -1;
}
@
Override
public int
getpgid(int
pid) {
handler.
unimplementedError("getpgid");
return -1;
}
@
Override
public int
getpgid() {
handler.
unimplementedError("getpgid");
return -1;
}
@
Override
public int
setpgid(int
pid, int
pgid) {
handler.
unimplementedError("setpgid");
return -1;
}
@
Override
public int
getpriority(int
which, int
who) {
handler.
unimplementedError("getpriority");
return -1;
}
@
Override
public int
setpriority(int
which, int
who, int
prio) {
handler.
unimplementedError("setpriority");
return -1;
}
@
Override
public int
getpid(){
return
wlibc().
_getpid();
}
@
Override
public int
getppid() {
return 0;
}
@
Override
public int
lchmod(
String filename, int
mode) {
handler.
unimplementedError("lchmod");
return -1;
}
@
Override
public int
lchown(
String filename, int
user, int
group) {
handler.
unimplementedError("lchown");
return -1;
}
public
FileStat fstat(int
fd) {
WindowsFileStat stat = new
WindowsFileStat(this);
if (
fstat(
fd,
stat) < 0)
handler.
error(
Errno.
valueOf(
errno()), "fstat", "" +
fd);
return
stat;
}
@
Override
public int
fstat(
FileDescriptor fileDescriptor,
FileStat stat) {
WindowsByHandleFileInformation info = new
WindowsByHandleFileInformation(
getRuntime());
if (
wlibc().
GetFileInformationByHandle(
JavaLibCHelper.
gethandle(
fileDescriptor),
info) == 0) return -1;
((
WindowsRawFileStat)
stat).
setup(
info);
return 0;
}
@
Override
public
FileStat lstat(
String path) {
return
stat(
path);
}
@
Override
public int
lstat(
String path,
FileStat stat) {
return
stat(
path,
stat); // windows stat honors windows equiv of softlinks and dangling ones.
}
@
Override
public int
stat(
String path,
FileStat stat) {
WindowsFileInformation info = new
WindowsFileInformation(
getRuntime());
byte[]
wpath =
WString.
path(
path, true);
if (
wlibc().
GetFileAttributesExW(
wpath, 0,
info) != 0) {
((
WindowsRawFileStat)
stat).
setup(
path,
info);
} else {
int
e =
errno();
if (
e ==
ERROR_FILE_NOT_FOUND.
intValue() ||
e ==
ERROR_PATH_NOT_FOUND.
intValue()
||
e ==
ERROR_BAD_NETPATH.
intValue()) {
return -1;
}
return
findFirstFile(
path,
stat);
}
return 0;
}
// Public so we can test this via unit-testing. This makes me wish we had a whole different interface for
// windows APIs user32/kernel32 that this class could consume easily. We are clearly missing an abstraction
// or project here.
public int
findFirstFile(
String path,
FileStat stat) {
byte[]
wpath =
WString.
path(
path, true);
WindowsFindData findData = new
WindowsFindData(
getRuntime());
HANDLE handle =
wlibc().
FindFirstFileW(
wpath,
findData);
if (!
handle.
isValid()) return -1;
wlibc().
FindClose(
handle);
((
WindowsRawFileStat)
stat).
setup(
path,
findData);
return 0;
}
@
Override
public
String readlink(
String oldpath) {
handler.
unimplementedError("readlink");
return null;
}
@
Override
public
Pointer environ() {
return
getRuntime().
getMemoryManager().
newPointer(
wlibc().
_environ().
get());
}
@
Override
public int
setenv(
String envName,
String envValue, int
overwrite) {
if (
envName.
contains("=")) {
handler.
error(
EINVAL, "setenv",
envName);
return -1;
}
// FIXME: We do not have getenv implemented yet. So we are ignoring for now
// POSIX specified. Existence is success if overwrite is 0.
// if (overwrite == 0 && getenv(envName) != null) return 0;
if (!
wlibc().
SetEnvironmentVariableW(new
WString(
envName), new
WString(
envValue))) {
handler.
error(
EINVAL, "setenv",
envName);
return -1;
}
return 0;
}
@
Override
public int
umask(int
mask) {
return
wlibc().
_umask(
mask);
}
@
Override
public int
unsetenv(
String envName) {
if (!
wlibc().
SetEnvironmentVariableW(new
WString(
envName), null)) {
handler.
error(
EINVAL, "unsetenv",
envName);
return -1;
}
return 0;
}
private static final int
GENERIC_ALL = 0x10000000;
private static final int
GENERIC_READ = 0x80000000;
private static final int
GENERIC_WRITE = 0x40000000;
private static final int
GENERIC_EXECUTE = 0x2000000;
private static final int
FILE_SHARE_DELETE = 0x00000004;
private static final int
FILE_SHARE_READ = 0x00000001;
private static final int
FILE_SHARE_WRITE = 0x00000002;
private static final int
CREATE_ALWAYS = 2;
private static final int
CREATE_NEW = 1;
private static final int
OPEN_ALWAYS = 4;
private static final int
OPEN_EXISTING = 3;
private static final int
TRUNCATE_EXISTING = 5;
public static final int
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
@
Override
public int
utimes(
String path, long[]
atimeval, long[]
mtimeval) {
FileTime aTime =
timevalToFileTime(
atimeval);
FileTime mTime =
timevalToFileTime(
mtimeval);
return
setFileTime(
path,
aTime,
mTime);
}
@
Override
public int
utimensat(int
dirfd,
String path, long[]
atimespec, long[]
mtimespec, int
flag) {
FileTime aTime =
timespecToFileTime(
atimespec);
FileTime mTime =
timespecToFileTime(
mtimespec);
return
setFileTime(
path,
aTime,
mTime);
}
private
FileTime timevalToFileTime(long[]
timeval) {
if (
timeval == null) {
return
nullFileTime();
}
// timeval unit is (sec, microsec)
long
unixEpochIn100ns =
timeval[0] * 10000000 +
timeval[1] * 10;
return
unixTimeToFileTime(
unixEpochIn100ns);
}
private
FileTime timespecToFileTime(long[]
timespec) {
if (
timespec == null) {
return
nullFileTime();
}
// timespec unit is (sec, nanosec)
long
unixEpochIn100ns =
timespec[0] * 10000000 +
timespec[1] / 100;
return
unixTimeToFileTime(
unixEpochIn100ns);
}
private int
setFileTime(
String path,
FileTime aTime,
FileTime mTime) {
byte[]
wpath =
WindowsHelpers.
toWPath(
path);
HANDLE handle =
wlibc().
CreateFileW(
wpath,
GENERIC_WRITE,
FILE_SHARE_READ |
FILE_SHARE_WRITE,
null,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, 0);
if (!
handle.
isValid()) {
return -1; // TODO proper error handling
}
boolean
timeSet =
wlibc().
SetFileTime(
handle, null,
aTime,
mTime);
wlibc().
CloseHandle(
handle);
return
timeSet ? 0 : -1;
}
/**
*
* @param unixEpochIn100ns epoch time in 100-ns precision
* @return associated FILETIME structure
*/
private
FileTime unixTimeToFileTime(long
unixEpochIn100ns) {
// FILETIME is a 64-bit unsigned integer representing
// the number of 100-nanosecond intervals since January 1, 1601
// UNIX timestamp is number of seconds since January 1, 1970
// 116444736000000000 = 10_000_000 * 60 * 60 * 24 * 365 * 369 + 89 leap days
long
ft = 116444736000000000L +
unixEpochIn100ns;
//long ft = CommonFileInformation.asNanoSeconds(unixTimeSeconds);
FileTime fileTime = new
FileTime(
getRuntime());
fileTime.
dwLowDateTime.
set(
ft & 0xFFFFFFFFL);
fileTime.
dwHighDateTime.
set((
ft >> 32) & 0xFFFFFFFFL);
return
fileTime;
}
private
FileTime nullFileTime() {
FileTime fileTime = new
FileTime(
getRuntime());
fileTime.
dwLowDateTime.
set(0);
fileTime.
dwHighDateTime.
set(0);
return
fileTime;
}
@
Override
public int
wait(int[]
status) {
handler.
unimplementedError("wait");
return -1;
}
@
Override
public int
waitpid(int
pid, int[]
status, int
flags) {
if (
pid <= 0) {
handler.
unimplementedError("waitpid");
}
HANDLE h =
wlibc().
OpenProcess(
WindowsLibC.
PROCESS_QUERY_INFORMATION, 0,
pid);
if (
h == null) {
return -1; // TODO: Throw exception
}
// Block
if ((
flags &
WaitFlags.
WNOHANG.
intValue()) != 0) {
wlibc().
WaitForSingleObject(
h,
WindowsLibC.
INFINITE);
}
IntByReference exitCode = new
IntByReference();
wlibc().
GetExitCodeProcess(
h,
exitCode);
wlibc().
CloseHandle(
h);
int
code =
exitCode.
getValue();
if (
code == 259) {
return 0;
} else {
status[0] =
code;
return
pid;
}
}
@
Override
public int
waitpid(long
pid, int[]
status, int
flags) {
if (
pid >
Integer.
MAX_VALUE) {
throw new java.lang.
IllegalArgumentException("waitpid");
}
return
waitpid((int)
pid,
status,
flags);
}
@
Override
public
String getlogin() {
return
helper.
getlogin();
}
@
Override
public int
endgrent() {
return 0;
}
@
Override
public int
endpwent() {
return
helper.
endpwent();
}
@
Override
public
Group getgrent() {
return null;
}
@
Override
public
Passwd getpwent() {
return null;
}
@
Override
public
Group getgrgid(int
which) {
return null;
}
@
Override
public
Passwd getpwnam(
String which) {
return null;
}
@
Override
public
Group getgrnam(
String which) {
return null;
}
@
Override
public int
setgrent() {
return 0;
}
@
Override
public int
setpwent() {
return
helper.
setpwent();
}
@
Override
public
Passwd getpwuid(int
which) {
return null;
}
@
Override
public boolean
isatty(
FileDescriptor fd) {
HANDLE handle =
JavaLibCHelper.
gethandle(
fd);
int
type =
wlibc().
GetFileType(
handle);
return
type ==
FILE_TYPE_CHAR;
}
@
Override
public int
isatty(int
fd) {
HANDLE handle =
JavaLibCHelper.
gethandle(
fd);
int
type =
wlibc().
GetFileType(
handle);
return
type ==
FILE_TYPE_CHAR ? 1 : 0;
}
@
Override
public int
mkdir(
String path, int
mode) {
WString widePath =
WString.
path(
path);
int
res = -1;
if (
wlibc().
_wmkdir(
widePath) == 0) {
res =
wlibc().
_wchmod(
widePath,
mode);
}
if (
res < 0) {
int
errno =
errno();
handler.
error(
Errno.
valueOf(
errno), "mkdir",
path);
}
return
res;
}
// FIXME: Should this and other fields be part of constantine/jnr-constants?
static final int
FILE_ATTRIBUTE_READONLY = 1;
static final int
INVALID_FILE_ATTRIBUTES = -1;
/**
* The logic here is a bit strange and this copies MRI (Ruby) which may not be language
* agnostic, but windows (win7 and others) automatically mark folders as read-only when
* it contains other files and folders within it. This document explains more:
* http://support.microsoft.com/kb/326549
* I think the logic is based around idea that if you removed all other files it would
* be empty but will stay marked as read-only.
*
* @param path the path to remove
* @return 0 if successful, -1 if failed
*/
@
Override
public int
rmdir(
String path) {
WString pathW =
WString.
path(
path);
int
attr =
wlibc().
GetFileAttributesW(
pathW);
boolean
isReadOnly =
attr !=
INVALID_FILE_ATTRIBUTES && (
attr &
FILE_ATTRIBUTE_READONLY) != 0;
if (
isReadOnly)
wlibc().
SetFileAttributesW(
pathW,
attr & ~
FILE_ATTRIBUTE_READONLY);
if (!
wlibc().
RemoveDirectoryW(
pathW)) {
int
errno =
errno();
if (
isReadOnly)
wlibc().
SetFileAttributesW(
pathW,
attr &
FILE_ATTRIBUTE_READONLY);
handler.
error(
mapErrorToErrno(
errno), "rmdir",
path);
return -1;
}
return 0;
}
@
Override
public int
link(
String oldpath,
String newpath) {
boolean
linkCreated =
wlibc().
CreateHardLinkW(
WString.
path(
newpath),
WString.
path(
oldpath), null);
if (!
linkCreated) {
int
error =
errno();
handler.
error(
mapErrorToErrno(
error), "link",
oldpath + " or " +
newpath);
return
error;
} else {
return 0;
}
}
/**
* @param overlay is P_OVERLAY if true and P_NOWAIT if false
* @param program to be invoked
* @param argv is all args including argv0 being what is executed
* @param path is path to be searched when needed (delimited by ; on windows)
* @param envp is a set of KEY=VALUE strings to be set in the child process
* @return the pid
*/
public int
aspawn(boolean
overlay,
String program,
String[]
argv,
String path,
String[]
envp) {
try {
if (
argv.length == 0) return -1;
String[]
cmds =
WindowsHelpers.
processCommandArgs(this,
program,
argv,
path);
return
childResult(
createProcess("aspawn",
cmds[0],
cmds[1], null, null, null, null,
envp),
overlay);
} catch (
Exception e) {
return -1;
}
}
public int
pipe(int[]
fds) {
// TODO (nirvdrum 06-May-15) Maybe not hard-code the psize value. But figure out a sensible way to handle textmode.
return ((
WindowsLibC)
libc()).
_pipe(
fds, 512, 0);
}
public int
truncate(
CharSequence path, long
length) {
// Windows doesn't have a native truncate() equivalent, but it does have a native ftruncate() equivalent.
// In order to call the ftruncate() equivalent, we must convert a path to a FD. We do that by wrapping the
// ftruncate() call with open() and close().
// Permissions are ignored since we're not using O_CREAT.
int
fd =
libc().
open(
path,
OpenFlags.
O_WRONLY.
intValue(), 0);
if (
fd == -1) {
return -1;
}
if (
libc().
ftruncate(
fd,
length) == -1) {
return -1;
}
if (
libc().
close(
fd) == -1) {
return -1;
}
// truncate() returns 0 on success.
return 0;
}
public int
fcntlInt(int
fd,
Fcntl fcntl, int
arg) {
switch(
fcntl) {
case
F_GETFD: {
if (
checkFd(
fd) == -1) {
return -1;
} else {
// This is a gigantic hack. Indicate that Windows does not support close-on-exec.
return 0;
}
}
case
F_SETFD: {
if (
checkFd(
fd) == -1) {
return -1;
} else {
// This is a gigantic hack. Indicate that Windows does not support close-on-exec by no-oping.
return 0;
}
}
case
F_GETFL: {
if (
checkFd(
fd) == -1) {
return -1;
} else {
// TODO (nirvdrum 06-May-15): Look up the actual flags rather than optimistically hard-coding this set.
return
OpenFlags.
O_RDWR.
intValue();
}
}
default: {
handler.
unimplementedError("fcntl");
return -1;
}
}
}
private
WindowsLibC wlibc() {
return (
WindowsLibC)
libc();
}
/**
* @param overlay is P_OVERLAY if true and P_NOWAIT if false
* @param command full command string
* @param program program to be invoked
* @param path is path to be searched when needed (delimited by ; on windows)
* @param envp is a set of KEY=VALUE strings to be set in the child process
* @return the pid
*/
public int
spawn(boolean
overlay,
String command,
String program,
String path,
String[]
envp) {
if (
command == null) return -1;
String[]
cmds =
WindowsHelpers.
processCommandLine(this,
command,
program,
path);
return
childResult(
createProcess("spawn",
cmds[0],
cmds[1], null, null, null, null,
envp),
overlay);
}
private int
childResult(
WindowsChildRecord child, boolean
overlay) {
if (
child == null) return -1;
if (
overlay) {
IntByReference exitCode = new
IntByReference();
WindowsLibC libc = (
WindowsLibC)
libc();
HANDLE handle =
child.
getProcess();
libc.
WaitForSingleObject(
handle,
WindowsLibC.
INFINITE);
libc.
GetExitCodeProcess(
handle,
exitCode);
libc.
CloseHandle(
handle);
System.
exit(
exitCode.
getValue());
}
return
child.
getPid();
}
private static
Errno mapErrorToErrno(int
error) {
Errno errno =
errorToErrnoMapper.
get(
error);
if (
errno == null) {
errno =
__UNKNOWN_CONSTANT__;
}
return
errno;
}
private static final int
STARTF_USESTDHANDLES = 0x00000100;
// Used by spawn and aspawn (Note: See fixme below...envp not hooked up yet)
private
WindowsChildRecord createProcess(
String callingMethodName,
String command,
String program,
WindowsSecurityAttributes securityAttributes,
HANDLE input,
HANDLE output,
HANDLE error,
String[]
envp) {
if (
command == null &&
program == null) {
handler.
error(
EFAULT,
callingMethodName, "no command or program specified");
return null;
}
if (
securityAttributes == null) {
securityAttributes = new
WindowsSecurityAttributes(
getRuntime());
}
WindowsStartupInfo startupInfo = new
WindowsStartupInfo(
getRuntime());
startupInfo.
setFlags(
STARTF_USESTDHANDLES);
startupInfo.
setStandardInput(
input != null ?
input :
wlibc().
GetStdHandle(
WindowsLibC.
STD_INPUT_HANDLE));
startupInfo.
setStandardOutput(
output != null ?
output :
wlibc().
GetStdHandle(
WindowsLibC.
STD_OUTPUT_HANDLE));
startupInfo.
setStandardError(
error != null ?
input :
wlibc().
GetStdHandle(
WindowsLibC.
STD_ERROR_HANDLE));
int
creationFlags =
WindowsLibC.
NORMAL_PRIORITY_CLASS |
WindowsLibC.
CREATE_UNICODE_ENVIRONMENT;
WindowsProcessInformation processInformation = new
WindowsProcessInformation(
getRuntime());
// FIXME: Convert envp into useful wideEnv
Pointer wideEnv = null;
byte[]
programW =
WindowsHelpers.
toWString(
program);
byte[]
cwd =
WindowsHelpers.
toWString(
WindowsHelpers.
escapePath(
handler.
getCurrentWorkingDirectory().
toString()) +"\\");
ByteBuffer commandW =
ByteBuffer.
wrap(
WindowsHelpers.
toWString(
command));
boolean
returnValue =
wlibc().
CreateProcessW(
programW,
commandW,
securityAttributes,
securityAttributes,
securityAttributes.
getInheritHandle() ? 1: 0,
creationFlags,
wideEnv,
cwd,
startupInfo,
processInformation);
if (!
returnValue) return null;
wlibc().
CloseHandle(
processInformation.
getThread());
// TODO: On winnt reverse sign of pid
return new
WindowsChildRecord(
processInformation.
getProcess(),
processInformation.
getPid());
}
private int
checkFd(int
fd) {
// There might be a lighter-weight check, but we basically want to
// make sure the FD is valid and the effective user can access it,
// since we need to simulate fcntl semantics.
return
libc().
fstat(
fd,
checkFdStat);
}
public static final
PointerConverter PASSWD = new
PointerConverter() {
public
Object fromNative(
Object arg,
FromNativeContext ctx) {
throw new
RuntimeException("no support for native passwd");
}
};
public int
mkfifo(
String filename, int
mode) {
handler.
unimplementedError("mkfifo");
return -1;
}
public
Timeval allocateTimeval() {
return new
DefaultNativeTimeval(
getRuntime());
}
// TODO: Replace with Win32 calls. See jnr/jnr-posix#98.
public int
gettimeofday(
Timeval tv) {
long
currentMillis =
System.
currentTimeMillis();
tv.
sec(
currentMillis / 1000);
tv.
usec(
currentMillis % 1000 * 1000);
return 0;
}
}