| /* A part of the Native C Library for Windows NT |
| Copyright 2007-2015 PC GO Ld. |
| Copyright 2015-2024 Rivoreo |
| |
| This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
| */ |
| |
| #include <windows.h> |
| #include <nt.h> |
| #include <errno.h> |
| #include <pathname.h> |
| #include <ntstatus.h> |
| #include <assert.h> |
| |
| //#include <stdio.h> |
| |
| int readlink(const char *path, char *buffer, size_t size) { |
| //printf("function: readlink(%p<%s>, %p, %lu)\n", path, path, buffer, (long int)size); |
| if(!path || !buffer) { |
| errno = EFAULT; |
| return -1; |
| } |
| if(!size) { |
| errno = EINVAL; |
| return -1; |
| } |
| if(!*path) { |
| errno = ENOENT; |
| return -1; |
| } |
| |
| UNICODE_STRING wpath; |
| if(!RtlCreateUnicodeStringFromAsciiz(&wpath, path)) { |
| errno = ENOMEM; |
| return -1; |
| } |
| PATHNAME_UNIX2NT_UTF16_STRUCT(wpath); |
| OBJECT_ATTRIBUTES object_attribute = { sizeof(OBJECT_ATTRIBUTES), NULL, &wpath, 0, NULL, NULL }; |
| void *handle; |
| long int status = NtOpenSymbolicLinkObject(&handle, GENERIC_READ, &object_attribute); |
| RtlFreeUnicodeString(&wpath); |
| if(status < 0) { |
| __set_errno_from_ntstatus(status); |
| return -1; |
| } |
| UNICODE_STRING rpath = { size * sizeof(wchar_t), (size + 1) * sizeof(wchar_t), malloc((size + 1) * sizeof(wchar_t)) }; |
| if(!rpath.Buffer) { |
| NtClose(handle); |
| errno = ENOMEM; |
| return -1; |
| } |
| char tmp_buffer[size * 2]; |
| unsigned long int rsize; // Size of the wide char string including L'\0' (MaximumLength) |
| status = NtQuerySymbolicLinkObject(handle, &rpath, &rsize); |
| if(status == STATUS_BUFFER_TOO_SMALL) { |
| UNICODE_STRING tpath = { rsize - sizeof(wchar_t), rsize, malloc(rsize) }; |
| if(!tpath.Buffer) { |
| free(rpath.Buffer); |
| NtClose(handle); |
| errno = ENOMEM; |
| return -1; |
| } |
| status = NtQuerySymbolicLinkObject(handle, &tpath, NULL); |
| if(status < 0) goto failed; |
| memcpy(rpath.Buffer, tpath.Buffer, rpath.Length); |
| rpath.Buffer[rpath.Length / sizeof(wchar_t)] = 0; |
| rsize = rpath.MaximumLength; |
| free(tpath.Buffer); |
| } else if(status < 0) { |
| failed: |
| free(rpath.Buffer); |
| NtClose(handle); |
| __set_errno_from_ntstatus(status); |
| return -1; |
| } |
| NtClose(handle); |
| assert(rsize / 2 - 1 <= size); |
| PATHNAME_NT2UNIX_UTF16_STRUCT(rpath); |
| int r = wcstombs(tmp_buffer, rpath.Buffer, rsize * 2); |
| //printf("nativelibc debug: r = %d\n", r); |
| free(rpath.Buffer); |
| if(r < 0) r = 0; |
| memcpy(buffer, tmp_buffer, r); |
| assert(r <= size); |
| return r; |
| } |