diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/dktools/dk4symlink.ctr |
Initial commit
Diffstat (limited to 'support/dktools/dk4symlink.ctr')
-rw-r--r-- | support/dktools/dk4symlink.ctr | 357 |
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; +} + |