blob: 8bd33bd70e22245f6c9be73fb1568d0e2830bded [file] [log] [blame] [raw]
/* A part of the Native C Library for Windows NT
Copyright 2007-2015 PC GO Ld.
Copyright 2015-2026 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 <fcntl.h>
#include <errno.h>
#include <pathname.h>
#include <ntstatus.h>
#include <assert.h>
int readlinkat(int dir_fd, const char *path, char *buffer, size_t size) {
if(dir_fd == -1) {
errno = EBADF;
return -1;
}
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 = { .Length = sizeof(OBJECT_ATTRIBUTES), .ObjectName = &wpath };
if(*path != '/') {
if(dir_fd == AT_FDCWD) {
RtlFreeUnicodeString(&wpath);
errno = EPERM;
return -1;
}
object_attribute.RootDirectory = (void *)dir_fd;
}
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;
}
int readlink(const char *path, char *buffer, size_t size) {
return readlinkat(AT_FDCWD, path, buffer, size);
}