Skip to content

Commit

Permalink
Test if the reparse point is a symlink
Browse files Browse the repository at this point in the history
Introduce the caml/winsupport.h header to hold common code between the
runtime, lib-sys, and win32unix. This is also mandated by the name
conflict between ATOM from <windef.h> and caml/instruct.h.
  • Loading branch information
MisterDA committed Oct 12, 2021
1 parent d2bda41 commit bf65520
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 54 deletions.
6 changes: 4 additions & 2 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ Working version
Allsopp, Nicolás Ojeda Bär, Vincent Laviron)

- #10642: On Windows, Sys.remove and Unix.unlink now remove symlinks
to directories instead of raising EACCES. (Antonin Décimo, review
by David Allsopp, Xavier Leroy)
to directories instead of raising EACCES. Introduce
caml/winsupport.h to hold more common code between the runtime,
lib-sys, and win32unix. (Antonin Décimo, review by David Allsopp,
Xavier Leroy)

### Other libraries:

Expand Down
1 change: 1 addition & 0 deletions otherlibs/win32unix/readlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "unixsupport.h"
#include <errno.h>
#include <winioctl.h>
#include <caml/winsupport.h>

CAMLprim value unix_readlink(value opath)
{
Expand Down
1 change: 1 addition & 0 deletions otherlibs/win32unix/stat.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <sys/stat.h>
#include <time.h>
#include <winioctl.h>
#include "caml/winsupport.h"

#ifndef S_IFLNK
/*
Expand Down
39 changes: 0 additions & 39 deletions otherlibs/win32unix/unixsupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,45 +92,6 @@ extern int win_set_inherit(HANDLE fd, BOOL inherit);
}
#endif

/*
* This structure is defined inconsistently. mingw64 has it in ntdef.h (which
* doesn't look like a primary header) and technically it's part of ntifs.h in
* the WDK. Requiring the WDK is a bit extreme, so the definition is taken from
* ntdef.h. Both ntdef.h and ntifs.h define REPARSE_DATA_BUFFER_HEADER_SIZE
*/
#ifndef REPARSE_DATA_BUFFER_HEADER_SIZE
typedef struct _REPARSE_DATA_BUFFER
{
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union
{
struct
{
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct
{
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct
{
UCHAR DataBuffer[1];
} GenericReparseBuffer;
};
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
#endif

#define EXECV_CAST (const char_os * const *)

#endif /* CAML_UNIXSUPPORT_H */
65 changes: 65 additions & 0 deletions runtime/caml/winsupport.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**************************************************************************/
/* */
/* OCaml */
/* */
/* David Allsopp, MetaStack Solutions Ltd. */
/* */
/* Copyright 2015 MetaStack Solutions Ltd. */
/* */
/* All rights reserved. This file is distributed under the terms of */
/* the GNU Lesser General Public License version 2.1, with the */
/* special exception on linking described in the file LICENSE. */
/* */
/**************************************************************************/

/* Operating system - Windows specific stuff */

#ifndef CAML_WINSUPPORT_H
#define CAML_WINSUPPORT_H

#if defined(_WIN32) && defined(CAML_INTERNALS)

#include <windef.h>

/*
* This structure is defined inconsistently. mingw64 has it in ntdef.h (which
* doesn't look like a primary header) and technically it's part of ntifs.h in
* the WDK. Requiring the WDK is a bit extreme, so the definition is taken from
* ntdef.h. Both ntdef.h and ntifs.h define REPARSE_DATA_BUFFER_HEADER_SIZE
*/
#ifndef REPARSE_DATA_BUFFER_HEADER_SIZE
typedef struct _REPARSE_DATA_BUFFER
{
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union
{
struct
{
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct
{
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct
{
UCHAR DataBuffer[1];
} GenericReparseBuffer;
};
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
#endif

#endif

#endif /* CAML_WINSUPPORT_H */
47 changes: 34 additions & 13 deletions runtime/win32.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <wtypes.h>
#include <winbase.h>
#include <winsock2.h>
#include <winioctl.h>
#include <direct.h>
#include <stdlib.h>
#include <stdio.h>
Expand All @@ -47,6 +48,7 @@
#include "caml/osdeps.h"
#include "caml/signals.h"
#include "caml/sys.h"
#include "caml/winsupport.h"

#include "caml/config.h"

Expand Down Expand Up @@ -799,24 +801,43 @@ int caml_win32_rename(const wchar_t * oldpath, const wchar_t * newpath)
}

int caml_win32_unlink(const wchar_t * path) {
int ret, attrs;
int ret;

ret = _wunlink(path);
/* On Windows, trying to unlink a symlink to a directory will return
* EACCES, but the symlink can be deleted with rmdir. */
if (ret == -1 && errno == EACCES) {
WIN32_FIND_DATAW data;
HANDLE h = FindFirstFileExW(path, FindExInfoStandard, &data,
FindExSearchNameMatch, NULL, 0);
if (h != INVALID_HANDLE_VALUE &&
(data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
data.dwReserved0 == IO_REPARSE_TAG_SYMLINK) {
FindClose(h);
ret = _wrmdir(path);
if (ret == -1)
errno = EACCES;
}
HANDLE h;
DWORD attrs, dummy;
union {
char buffer[16384];
REPARSE_DATA_BUFFER * point;
} buffer;

attrs = GetFileAttributes(path);
if (attrs == INVALID_FILE_ATTRIBUTES ||
!(attrs & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)))
return -1;

h = CreateFile(path,
FILE_READ_ATTRIBUTES,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
NULL);
if (h == INVALID_HANDLE_VALUE)
return -1;

ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, &buffer.point,
sizeof(buffer), &dummy, NULL);
CloseHandle(h);
if (!ret || buffer.point->ReparseTag != IO_REPARSE_TAG_SYMLINK)
return -1;

ret = _wrmdir(path);
if (ret == -1)
errno = EACCES;
}
return ret;
}
Expand Down

0 comments on commit bf65520

Please sign in to comment.