#include <tlhelp32.h>
#include <io.h>
#include <signal.h>
+#include <winioctl.h>
/* #include "config.h" */
#include <float.h>
#include <time.h>
#include <sys/utime.h>
+#include <wchar.h>
#ifdef __GNUC__
/* Mingw32 defaults to globing command line
if (!ptr || stricmp(ptr+1, strip) != 0) {
/* ... but not if component matches m|5\.$patchlevel.*| */
if (!ptr || !(*strip == '5' && *(ptr+1) == '5'
- && strncmp(strip, base, baselen) == 0
- && strncmp(ptr+1, base, baselen) == 0))
+ && strnEQ(strip, base, baselen)
+ && strnEQ(ptr+1, base, baselen)))
{
*optr = '/';
ptr = optr;
sv1 = sv2;
} else if (sv2) {
dTHX;
- sv_catpv(sv1, ";");
+ sv_catpvs(sv1, ";");
sv_catsv(sv1, sv2);
}
int status;
int flag = P_WAIT;
int index = 0;
+ int eno;
PERL_ARGS_ASSERT_DO_ASPAWN;
(const char*)(really ? SvPV_nolen(really) : argv[0]),
(const char* const*)argv);
- if (status < 0 && (errno == ENOEXEC || errno == ENOENT)) {
+ if (status < 0 && (eno = errno, (eno == ENOEXEC || eno == ENOENT))) {
/* possible shell-builtin, invoke with shell */
int sh_items;
sh_items = w32_perlshell_items;
DllExport DIR *
win32_dirp_dup(DIR *const dirp, CLONE_PARAMS *const param)
{
- dVAR;
PerlInterpreter *const from = param->proto_perl;
PerlInterpreter *const to = (PerlInterpreter *)PERL_GET_THX;
* caching reasons, and the child thread was attached to a different CPU
* therefore there is no workload on that CPU and Sleep(0) returns control
* without yielding the time slot.
- * https://rt.perl.org/rt3/Ticket/Display.html?id=88840
+ * https://github.com/Perl/perl5/issues/11267
*/
Sleep(0);
win32_async_check(aTHX);
dTHX;
int res;
int nlink = 1;
+ unsigned __int64 ino = 0;
+ DWORD vol = 0;
BOOL expect_dir = FALSE;
+ struct _stati64 st;
if (l > 1) {
switch(path[l - 1]) {
/* We must open & close the file once; otherwise file attribute changes */
/* might not yet have propagated to "other" hard links of the same file. */
/* This also gives us an opportunity to determine the number of links. */
- HANDLE handle = CreateFileA(path, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
+ HANDLE handle = CreateFileA(path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (handle != INVALID_HANDLE_VALUE) {
BY_HANDLE_FILE_INFORMATION bhi;
- if (GetFileInformationByHandle(handle, &bhi))
+ if (GetFileInformationByHandle(handle, &bhi)) {
nlink = bhi.nNumberOfLinks;
+ ino = bhi.nFileIndexHigh;
+ ino <<= 32;
+ ino |= bhi.nFileIndexLow;
+ vol = bhi.dwVolumeSerialNumber;
+ }
CloseHandle(handle);
}
else {
}
/* path will be mapped correctly above */
-#if defined(WIN64) || defined(USE_LARGE_FILES)
res = _stati64(path, sbuf);
-#else
- res = stat(path, sbuf);
-#endif
+ sbuf->st_dev = vol;
+ sbuf->st_ino = ino;
+ sbuf->st_mode = st.st_mode;
sbuf->st_nlink = nlink;
+ sbuf->st_uid = st.st_uid;
+ sbuf->st_gid = st.st_gid;
+ sbuf->st_rdev = st.st_rdev;
+ sbuf->st_size = st.st_size;
+ sbuf->st_atime = st.st_atime;
+ sbuf->st_mtime = st.st_mtime;
+ sbuf->st_ctime = st.st_ctime;
if (res < 0) {
/* CRT is buggy on sharenames, so make sure it really isn't.
return res;
}
+static void
+translate_to_errno(void)
+{
+ /* This isn't perfect, eg. Win32 returns ERROR_ACCESS_DENIED for
+ both permissions errors and if the source is a directory, while
+ POSIX wants EACCES and EPERM respectively.
+
+ Determined by experimentation on Windows 7 x64 SP1, since MS
+ don't document what error codes are returned.
+ */
+ switch (GetLastError()) {
+ case ERROR_BAD_NET_NAME:
+ case ERROR_BAD_NETPATH:
+ case ERROR_BAD_PATHNAME:
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_FILENAME_EXCED_RANGE:
+ case ERROR_INVALID_DRIVE:
+ case ERROR_PATH_NOT_FOUND:
+ errno = ENOENT;
+ break;
+ case ERROR_ALREADY_EXISTS:
+ errno = EEXIST;
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_PRIVILEGE_NOT_HELD:
+ errno = EACCES;
+ break;
+ case ERROR_NOT_SAME_DEVICE:
+ errno = EXDEV;
+ break;
+ case ERROR_DISK_FULL:
+ errno = ENOSPC;
+ break;
+ case ERROR_NOT_ENOUGH_QUOTA:
+ errno = EDQUOT;
+ break;
+ default:
+ /* ERROR_INVALID_FUNCTION - eg. symlink on a FAT volume */
+ errno = EINVAL;
+ break;
+ }
+}
+
+/* Adapted from:
+
+https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_reparse_data_buffer
+
+Renamed to avoid conflicts, apparently some SDKs define this
+structure.
+
+Hoisted the symlink data into a new type to allow us to make a pointer
+to it, and to avoid C++ scoping issues.
+
+*/
+
+typedef struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[MAX_PATH*3];
+} MY_SYMLINK_REPARSE_BUFFER, *PMY_SYMLINK_REPARSE_BUFFER;
+
+typedef struct {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ MY_SYMLINK_REPARSE_BUFFER SymbolicLinkReparseBuffer;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ } Data;
+} MY_REPARSE_DATA_BUFFER, *PMY_REPARSE_DATA_BUFFER;
+
+static BOOL
+is_symlink(HANDLE h) {
+ MY_REPARSE_DATA_BUFFER linkdata;
+ const MY_SYMLINK_REPARSE_BUFFER * const sd =
+ &linkdata.Data.SymbolicLinkReparseBuffer;
+ DWORD linkdata_returned;
+
+ if (!DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, &linkdata, sizeof(linkdata), &linkdata_returned, NULL)) {
+ return FALSE;
+ }
+
+ if (linkdata_returned < offsetof(MY_REPARSE_DATA_BUFFER, Data.SymbolicLinkReparseBuffer.PathBuffer)
+ || linkdata.ReparseTag != IO_REPARSE_TAG_SYMLINK) {
+ /* some other type of reparse point */
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+DllExport int
+win32_lstat(const char *path, Stat_t *sbuf)
+{
+ HANDLE f;
+ int fd;
+ int result;
+ DWORD attr = GetFileAttributes(path); /* doesn't follow symlinks */
+
+ if (attr == INVALID_FILE_ATTRIBUTES) {
+ translate_to_errno();
+ return -1;
+ }
+
+ if (!(attr & FILE_ATTRIBUTE_REPARSE_POINT)) {
+ return win32_stat(path, sbuf);
+ }
+
+ f = CreateFileA(path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, 0);
+ if (f == INVALID_HANDLE_VALUE) {
+ translate_to_errno();
+ return -1;
+ }
+
+ if (!is_symlink(f)) {
+ CloseHandle(f);
+ return win32_stat(path, sbuf);
+ }
+
+ fd = win32_open_osfhandle((intptr_t)f, 0);
+ result = win32_fstat(fd, sbuf);
+ if (result != -1){
+ sbuf->st_mode = (sbuf->st_mode & ~_S_IFMT) | _S_IFLNK;
+ }
+ close(fd);
+ return result;
+}
+
#define isSLASH(c) ((c) == '/' || (c) == '\\')
#define SKIP_SLASHES(s) \
STMT_START { \
}
else {
/* failed a step, just return without side effects */
- /*PerlIO_printf(Perl_debug_log, "Failed to find %s\n", path);*/
errno = EINVAL;
return NULL;
}
static void
out_of_memory(void)
{
+
if (PL_curinterp)
croak_no_mem();
exit(1);
* then it will convert the short name instead.
*
* The buffer to the ansi pathname must be freed with win32_free() when it
- * it no longer needed.
+ * is no longer needed.
*
* The argument to win32_ansipath() must exist before this function is
* called; otherwise there is no way to determine the short path name.
else {
/* last ditch: allow any environment variables that begin with 'PERL'
to be obtained from the registry, if found there */
- if (strncmp(name, "PERL", 4) == 0)
+ if (strBEGINs(name, "PERL"))
(void)get_regstr(name, &curitem);
}
#endif
if (result == WAIT_OBJECT_0 + count) {
/* Message has arrived - check it */
(void)win32_async_check(aTHX);
+
+ /* retry */
+ if (ticks.ft_i64 > endtime)
+ endtime = ticks.ft_i64;
+
+ continue;
}
else {
/* Not timeout or message - one of handles is ready */
return win32_msgwait(aTHX_ 0, NULL, t * 1000, NULL) / 1000;
}
+DllExport int
+win32_pause(void)
+{
+ dTHX;
+ win32_msgwait(aTHX_ 0, NULL, INFINITE, NULL);
+ return -1;
+}
+
DllExport unsigned int
win32_alarm(unsigned int sec)
{
* additionally map them to corresponding Windows (sockets) error codes
* first to avoid getting the wrong system message.
*/
- else if (e >= EADDRINUSE && e <= EWOULDBLOCK) {
+ else if (inRANGE(e, EADDRINUSE, EWOULDBLOCK)) {
e = convert_errno_to_wsa_error(e);
}
#endif
DllExport Off_t
win32_ftell(FILE *pf)
{
-#if defined(WIN64) || defined(USE_LARGE_FILES)
fpos_t pos;
if (fgetpos(pf, &pos))
return -1;
return (Off_t)pos;
-#else
- return ftell(pf);
-#endif
}
DllExport int
win32_fseek(FILE *pf, Off_t offset,int origin)
{
-#if defined(WIN64) || defined(USE_LARGE_FILES)
fpos_t pos;
switch (origin) {
case SEEK_CUR:
return -1;
}
return fsetpos(pf, &offset);
-#else
- return fseek(pf, (long)offset, origin);
-#endif
}
DllExport int
DllExport int
win32_tmpfd(void)
{
+ return win32_tmpfd_mode(0);
+}
+
+DllExport int
+win32_tmpfd_mode(int mode)
+{
char prefix[MAX_PATH+1];
char filename[MAX_PATH+1];
DWORD len = GetTempPath(MAX_PATH, prefix);
+ mode &= ~( O_ACCMODE | O_CREAT | O_EXCL );
+ mode |= O_RDWR;
if (len && len < MAX_PATH) {
if (GetTempFileName(prefix, "plx", 0, filename)) {
HANDLE fh = CreateFile(filename,
| FILE_FLAG_DELETE_ON_CLOSE,
NULL);
if (fh != INVALID_HANDLE_VALUE) {
- int fd = win32_open_osfhandle((intptr_t)fh, 0);
+ int fd = win32_open_osfhandle((intptr_t)fh, mode);
if (fd >= 0) {
PERL_DEB(dTHX;)
DEBUG_p(PerlIO_printf(Perl_debug_log,
DllExport int
win32_fstat(int fd, Stat_t *sbufptr)
{
-#if defined(WIN64) || defined(USE_LARGE_FILES)
- return _fstati64(fd, sbufptr);
-#else
- return fstat(fd, sbufptr);
-#endif
+ int result;
+ struct _stati64 st;
+ dTHX;
+ result = _fstati64(fd, &st);
+ if (result == 0) {
+ sbufptr->st_mode = st.st_mode;
+ sbufptr->st_uid = st.st_uid;
+ sbufptr->st_gid = st.st_gid;
+ sbufptr->st_rdev = st.st_rdev;
+ sbufptr->st_size = st.st_size;
+ sbufptr->st_atime = st.st_atime;
+ sbufptr->st_mtime = st.st_mtime;
+ sbufptr->st_ctime = st.st_ctime;
+
+ if (w32_sloppystat) {
+ sbufptr->st_nlink = st.st_nlink;
+ sbufptr->st_dev = st.st_dev;
+ sbufptr->st_ino = st.st_ino;
+ }
+ else {
+ HANDLE handle = (HANDLE)win32_get_osfhandle(fd);
+ BY_HANDLE_FILE_INFORMATION bhi;
+ if (GetFileInformationByHandle(handle, &bhi)) {
+ sbufptr->st_nlink = bhi.nNumberOfLinks;
+ sbufptr->st_ino = bhi.nFileIndexHigh;
+ sbufptr->st_ino <<= 32;
+ sbufptr->st_ino |= bhi.nFileIndexLow;
+ sbufptr->st_dev = bhi.dwVolumeSerialNumber;
+ }
+ }
+ }
+
+ return result;
}
DllExport int
DllExport int
win32_chsize(int fd, Off_t size)
{
-#if defined(WIN64) || defined(USE_LARGE_FILES)
int retval = 0;
Off_t cur, end, extend;
}
win32_lseek(fd, cur, SEEK_SET);
return retval;
-#else
- return chsize(fd, (long)size);
-#endif
}
DllExport Off_t
win32_lseek(int fd, Off_t offset, int origin)
{
-#if defined(WIN64) || defined(USE_LARGE_FILES)
return _lseeki64(fd, offset, origin);
-#else
- return lseek(fd, (long)offset, origin);
-#endif
}
DllExport Off_t
win32_tell(int fd)
{
-#if defined(WIN64) || defined(USE_LARGE_FILES)
return _telli64(fd);
-#else
- return tell(fd);
-#endif
}
DllExport int
w32_child_pids[w32_num_children] = (DWORD)ret;
++w32_num_children;
}
- else {
+ else {
DWORD status;
win32_msgwait(aTHX_ 1, &ProcessInformation.hProcess, INFINITE, NULL);
/* FIXME: if msgwait returned due to message perhaps forward the
PERLIO_TERM;
MALLOC_TERM;
LOCALE_TERM;
+ ENV_TERM;
#ifndef WIN32_NO_REGISTRY
/* handles might be NULL, RegCloseKey then returns ERROR_INVALID_HANDLE
but no point of checking and we can't die() at this point */
void
Perl_sys_intern_clear(pTHX)
{
+
Safefree(w32_perlshell_tokens);
Safefree(w32_perlshell_vec);
/* NOTE: w32_fdpid is freed by sv_clean_all() */