summaryrefslogtreecommitdiff
path: root/support/dktools/dk4symlink.ctr
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/dktools/dk4symlink.ctr
Initial commit
Diffstat (limited to 'support/dktools/dk4symlink.ctr')
-rw-r--r--support/dktools/dk4symlink.ctr357
1 files changed, 357 insertions, 0 deletions
diff --git a/support/dktools/dk4symlink.ctr b/support/dktools/dk4symlink.ctr
new file mode 100644
index 0000000000..1b3138b0e6
--- /dev/null
+++ b/support/dktools/dk4symlink.ctr
@@ -0,0 +1,357 @@
+%% options
+
+copyright owner = Dirk Krause
+copyright year = 2015-xxxx
+license = bsd
+
+
+
+%% header
+
+/** @file dk4symlink.h Find symbolic link target.
+*/
+
+#ifndef DK4CONF_H_INCLUDED
+#include "dk4conf.h"
+#endif
+
+#ifndef DK4TYPES_H_INCLUDED
+#include "dk4types.h"
+#endif
+
+#ifndef DK4ERROR_H_INCLUDED
+#include "dk4error.h"
+#endif
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Find target for symbolic link.
+ @param bptr Destination buffer pointer.
+ @param szb Size of destination buffer (number of dkChar).
+ @param fn File name of link.
+ @param erp Error report, may be NULL.
+ @return 1 on success, 0 on error.
+
+ Error codes:
+ - DK4_E_INVALID_ARGUMENTS<br>
+ if bptr or fn is NULL or szb is 0,
+ - DK4_E_SYSTEM<br>
+ with errno value in iDetails1 if the readlink() function failed
+ on Linux/Unix,
+ - DK4_E_BUFFER_TOO_SMALL<br>
+ if the output buffer is too small,
+ - DK4_E_NOT_SUPPORTED<br>
+ if no function was found to read a symbolic link target,
+ - DK4_E_NOT_FOUND<br>
+ if the CreateFile() or DeviceIoControl() function failed
+ on Windows.
+*/
+int
+dk4symlink_target(dkChar *bptr, size_t szb, const dkChar *fn, dk4_er_t *erp);
+
+#ifdef __cplusplus
+}
+#endif
+
+%% module
+
+#include "dk4symlink.h"
+
+#if DK4_ON_WINDOWS
+#ifndef WINDOWS_H_INCLUDED
+#include <windows.h>
+#define WINDOWS_H_INCLUDED 1
+#endif
+#if DK4_HAVE_NTIFS_H
+#ifndef NTIFS_H_INCLUDED
+#include <ntifs.h>
+#define NTIFS_H_INCLUDED 1
+#endif
+#endif
+#endif
+
+#if DK4_HAVE_UNISTD_H
+#ifndef UNISTD_H_INCLUDED
+#include <unistd.h>
+#define UNISTD_H_INCLUDED 1
+#endif
+#endif
+
+#if DK4_HAVE_ERRNO_H
+#ifndef ERRNO_H_INCLUDED
+#include <errno.h>
+#define ERRNO_H_INCLUDED 1
+#endif
+#endif
+
+#include "dk4mpl.h"
+#include "dk4mem.h"
+#include "dk4strd.h"
+
+
+
+$!trace-include
+
+
+
+#if DK4_ON_WINDOWS
+
+#if !DK4_HAVE_NTIFS_H
+
+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
+
+
+#if defined(FSCTL_GET_REPARSE_POINT)
+/** Size of reparse point information for a symlink.
+*/
+#define SYMLINK_INFO_SIZE ((8 * (DK4_MAX_PATH))+sizeof(REPARSE_DATA_BUFFER))
+/** The above size aligned to 16 bytes.
+*/
+#define SYMLINK_INFO_ALIGNED (16 * (((SYMLINK_INFO_SIZE) / 16) + 1))
+
+#ifdef MAXIMUM_REPARSE_DATA_BUFFER_SIZE
+#define SYMLINK_BUFFER_SIZE \
+(MAXIMUM_REPARSE_DATA_BUFFER_SIZE + SYMLINK_INFO_ALIGNED)
+#endif
+#endif
+
+/** Copy path name from reparse point buffer to destination buffer.
+ @param bptr Destination buffer.
+ @param szb Destination buffer size (number of wchar_t).
+ @param sptr Source pointer.
+ @param offs Offset of path name in source (in bytes).
+ @param length Length of path name (in bytes).
+ @param erp Error report, may be NULL.
+ @return 1 on success, 0 on error.
+*/
+static
+int
+dk4symlink_copy_string(
+ wchar_t *bptr,
+ size_t szb,
+ const wchar_t *sptr,
+ USHORT offs,
+ USHORT length,
+ dk4_er_t *erp
+)
+{
+ int back = 0;
+ if ((length / sizeof(wchar_t)) < szb) {
+ DK4_MEMCPY(bptr,&(sptr[offs / sizeof(wchar_t)]),length);
+ bptr[length / sizeof(wchar_t)] = L'\0';
+ back = 1;
+ } else {
+ dk4error_set_simple_error_code(erp, DK4_E_BUFFER_TOO_SMALL);
+ }
+ return back;
+}
+
+#endif
+
+int
+dk4symlink_target(dkChar *bptr, size_t szb, const dkChar *fn, dk4_er_t *erp)
+{
+#if DK4_ON_WINDOWS
+#if defined(FSCTL_GET_REPARSE_POINT)
+ char repdabuf[SYMLINK_BUFFER_SIZE];
+ REPARSE_DATA_BUFFER *rdb;
+#endif
+ HANDLE fh;
+ DWORD dwres;
+ BOOL result;
+#else
+ ssize_t res;
+#endif
+ int back = 0;
+
+ $? "+ dk4symlink_target"
+ if ((NULL != bptr) && (NULL != fn) && (0 < szb)) {
+ *bptr = dkT('\0');
+#if DK4_ON_WINDOWS
+#if DK4_CHAR_SIZE > 1
+#if DK4_HAVE_GETFINALPATHNAMEBYHANDLE
+ fh = CreateFileW(
+ fn,
+ FILE_READ_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL
+ );
+ if (INVALID_HANDLE_VALUE != fh) {
+ dwres = GetFinalPathNameByHandleW(fh, bptr, (DWORD)szb, 0);
+ if (0 < dwres) {
+ if (dwres < (DWORD)szb) {
+ back = 1;
+ }
+ }
+ CloseHandle(fh);
+ }
+#else
+ $? ". no GetFinalPathNameByHandle"
+#endif
+ if (0 == back) {
+#if defined(FSCTL_GET_REPARSE_POINT)
+ /* !!!!! Second attempt with legacy procedure */
+ $? ". legacy procedure"
+ fh = CreateFileW(
+ fn,
+ FILE_READ_ATTRIBUTES,
+ (FILE_SHARE_READ | FILE_SHARE_WRITE),
+ NULL,
+ OPEN_EXISTING,
+ (
+ FILE_FLAG_BACKUP_SEMANTICS
+ | FILE_FLAG_OPEN_REPARSE_POINT
+ | FILE_ATTRIBUTE_REPARSE_POINT
+ ),
+ NULL
+ );
+ if (INVALID_HANDLE_VALUE != fh) {
+ dwres = 0; $? ". CreateFileW"
+ result = DeviceIoControl(
+ fh,
+ FSCTL_GET_REPARSE_POINT,
+ NULL,
+ (DWORD)0U,
+ (void *)repdabuf,
+ (DWORD)sizeof(repdabuf),
+ &dwres,
+ NULL
+ );
+ if (result) { $? ". DeviceIoControl"
+ rdb = (REPARSE_DATA_BUFFER *)repdabuf;
+ if (IO_REPARSE_TAG_MOUNT_POINT == rdb->ReparseTag) {
+ $? ". mount point"
+ back = dk4symlink_copy_string(
+ bptr, szb,
+ &(rdb->MountPointReparseBuffer.PathBuffer[0]),
+ rdb->MountPointReparseBuffer.SubstituteNameOffset,
+ rdb->MountPointReparseBuffer.SubstituteNameLength,
+ erp
+ );
+ } else {
+ if (IO_REPARSE_TAG_SYMLINK == rdb->ReparseTag) {
+ $? ". symbolic link"
+ back = dk4symlink_copy_string(
+ bptr, szb,
+ &(rdb->SymbolicLinkReparseBuffer.PathBuffer[0]),
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset,
+ rdb->SymbolicLinkReparseBuffer.SubstituteNameLength,
+ erp
+ );
+ } else {
+ /* !!!!! Not mount point, not symlink */
+ $? "! neither mount point nor symbolic link"
+ }
+ }
+ } else { $? "! DeviceIoControl"
+ dk4error_set_simple_error_code(erp, DK4_E_NOT_FOUND);
+ }
+ CloseHandle(fh);
+ } else { $? "! CreateFile"
+ dk4error_set_simple_error_code(erp, DK4_E_NOT_FOUND);
+ }
+#else
+ dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED);
+#endif
+ }
+#else
+#if DK4_HAVE_GETFINALPATHNAMEBYHANDLE
+ fh = CreateFileA(
+ fn,
+ FILE_READ_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL
+ );
+ if (INVALID_HANDLE_VALUE != fh) {
+ dwres = GetFinalPathNameByHandleA(fh, bptr, (DWORD)szb, 0);
+ if (0 < dwres) {
+ if (dwres < (DWORD)szb) {
+ back = 1;
+ }
+ }
+ CloseHandle(fh);
+ }
+#else
+ /* Legacy procedure only available for wchar_t.
+ */
+ dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED);
+#endif
+#endif
+ if (0 < back) {
+ if (dkT('\\') == bptr[0]) {
+ if ((dkT('\\') == bptr[1]) || (dkT('?') == bptr[1])) {
+ if (dkT('?') == bptr[2]) {
+ if (dkT('\\') == bptr[3]) {
+ if (260 > dk4str_len(&(bptr[4]))) {
+ dk4str_cpy_to_left(bptr, &(bptr[4]));
+ }
+ }
+ }
+ }
+ }
+ }
+#else
+#if DK4_CHAR_SIZE > 1
+ dk4error_set_simple_error_code(erp, DK4_E_NOT_SUPPORTED);
+#else
+ errno = 0;
+ res = readlink(fn, bptr, szb);
+ if (0 < res) {
+ if (res < szb) {
+ bptr[res] = dkT('\0');
+ back = 1;
+ } else { $? "! readlink requires larger buffer"
+ bptr[0] = dkT('\0');
+ dk4error_set_simple_error_code(erp, DK4_E_BUFFER_TOO_SMALL);
+ }
+ } else { $? "! readlink <= 0"
+ bptr[0] = dkT('\0');
+ if (0 > res) {
+ dk4error_set_idetails(erp, DK4_E_SYSTEM, errno);
+ }
+ }
+#endif
+#endif
+ if (0 == back) { *bptr = dkT('\0'); }
+ } else {
+ dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS);
+ } $? "- dk4symlink_target %d", back
+ return back;
+}
+