BOOL IsReparseDataSame(void *d1, void *d2) { REPARSE_DATA_BUFFER *r1 = (REPARSE_DATA_BUFFER *)d1; REPARSE_DATA_BUFFER *r2 = (REPARSE_DATA_BUFFER *)d2; return r1->ReparseTag == r2->ReparseTag && r1->ReparseDataLength == r2->ReparseDataLength && !memcmp(&r1->GenericReparseBuffer, &r2->GenericReparseBuffer, r1->ReparseDataLength + (IsReparseTagMicrosoft(r1->ReparseTag) ? 0 : sizeof(GUID))); }
BOOL DeleteReparsePoint(HANDLE hFile, void *buf) { REPARSE_DATA_BUFFER rdbuf; DWORD size = IsReparseTagMicrosoft(((REPARSE_DATA_BUFFER *)buf)->ReparseTag) ? REPARSE_DATA_BUFFER_HEADER_SIZE : REPARSE_GUID_DATA_BUFFER_HEADER_SIZE; memcpy(&rdbuf, buf, size); rdbuf.ReparseDataLength = 0; return ::DeviceIoControl(hFile, FSCTL_DELETE_REPARSE_POINT, &rdbuf, size, 0, 0, &size, NULL); }
arx_error arx_sys_follow(arx_utf8 const *path, arx_utf8 *buffer, arx_count count, arx_count *result) { arx_error error = 0; arx_count length = 0; #ifndef __WINDOWS__ arx_mbcs *string = NULL; arx_mbcs *target = NULL; arx_mbcs stack[ARX_STRING_STACK]; #else BOOL ext = FALSE; DWORD total = 0; arx_wcs *string = NULL; arx_wcs *target = NULL; arx_wcs stack[ARX_STRING_STACK]; DWORD const access = FILE_READ_EA; DWORD const disposition = OPEN_EXISTING; DWORD const share = (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE); DWORD const flags = (FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT); HANDLE handle = INVALID_HANDLE_VALUE; LPREPARSE reparse = NULL; #endif if ((path == NULL) || (*path == '\0') || ((buffer == NULL) && (count != 0)) || ((buffer != NULL) && (count == 0))) return arx_argerror(); if ((error = arx_path_export('?', path, stack, sizeof(stack), &length))) { goto cleanup; } if (length > sizeof(stack)) { if ((string = malloc(length * sizeof(stack[0]))) == NULL) { error = arx_memerror(); goto cleanup; } if ((error = arx_path_export('?', path, string, length, &length))) { goto cleanup; } } else { string = stack; } #ifndef __WINDOWS__ if ((target = calloc((length = 1024), sizeof(arx_mbcs))) == NULL) { error = arx_memerror(); goto cleanup; } while (1) { arx_count const xlength = length; arx_mbcs *const xtarget = target; length = (arx_count)readlink(string, target, length); if (length != (arx_count)-1) { if (length < xlength) { break; } length = (xlength * 2); target = realloc(target, (length * sizeof(arx_mbcs))); if (target == NULL) { error = arx_memerror(); target = xtarget; goto cleanup; } } else { error = arx_unixerror(errno); goto cleanup; } } error = arx_path_mbimport('u', target, buffer, count, result); #else handle = CreateFileW(string, access, share, NULL, disposition, flags, NULL); if (handle == INVALID_HANDLE_VALUE) { error = arx_winerror(GetLastError()); goto cleanup; } if ((reparse = calloc(1, sizeof(REPARSE))) == NULL) { error = arx_winerror(GetLastError()); goto cleanup; } if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, reparse, sizeof(REPARSE), &total, NULL)) { error = arx_winerror(GetLastError()); goto cleanup; } if (!IsReparseTagMicrosoft(reparse->tag)) { error = arx_winerror(ERROR_INVALID_HANDLE); goto cleanup; } if ((reparse->tag != IO_REPARSE_TAG_SYMLINK) && (reparse->tag != IO_REPARSE_TAG_MOUNT_POINT)) { error = arx_winerror(ERROR_INVALID_HANDLE); goto cleanup; } if (reparse->tag == IO_REPARSE_TAG_SYMLINK) { arx_count const lengthgth = (reparse->symlink.target.lengthgth / sizeof(WCHAR)); arx_count const offset = (reparse->symlink.target.offset / sizeof(WCHAR)); arx_wcs const *const string = reparse->symlink.string; if ((target = calloc((lengthgth + 1), sizeof(arx_wcs))) == NULL) { error = arx_memerror(); goto cleanup; } wcsncpy(target, (string + offset), lengthgth); } else if (reparse->tag == IO_REPARSE_TAG_MOUNT_POINT) { arx_count lengthgth = (reparse->junction.target.lengthgth / sizeof(WCHAR)); arx_count offset = (reparse->junction.target.offset / sizeof(WCHAR)); arx_wcs *string = reparse->junction.string; if ((target = calloc((lengthgth + 1), sizeof(arx_wcs))) == NULL) { error = arx_memerror(); goto cleanup; } wcsncpy(target, (string + offset), lengthgth); } else { error = arx_winerror(ERROR_INVALID_HANDLE); goto cleanup; } ext = ((wcsncmp(target, L"\\??\\", 4) == 0) || (wcsncmp(target, L"\\\\?\\", 4) == 0)); error = arx_path_wcimport('w', (target + (ext ? 4 : 0)), buffer, count, result); #endif cleanup: #ifdef __WINDOWS__ if (handle != INVALID_HANDLE_VALUE) { CloseHandle(handle); } free(reparse); #endif free(target); if (string != stack) { free(string); } return error; }
NTKERNELAPI NTSTATUS NTAPI FsRtlValidateReparsePointBuffer(IN ULONG BufferLength, IN PREPARSE_DATA_BUFFER ReparseBuffer) { USHORT DataLength; ULONG ReparseTag; PREPARSE_GUID_DATA_BUFFER GuidBuffer; /* Validate data size range */ if (BufferLength < REPARSE_DATA_BUFFER_HEADER_SIZE || BufferLength > MAXIMUM_REPARSE_DATA_BUFFER_SIZE) { return STATUS_IO_REPARSE_DATA_INVALID; } GuidBuffer = (PREPARSE_GUID_DATA_BUFFER)ReparseBuffer; DataLength = ReparseBuffer->ReparseDataLength; ReparseTag = ReparseBuffer->ReparseTag; /* Validate size consistency */ if (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE != BufferLength && DataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE != BufferLength) { return STATUS_IO_REPARSE_DATA_INVALID; } /* REPARSE_DATA_BUFFER is reserved for MS tags */ if (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE == BufferLength && !IsReparseTagMicrosoft(ReparseTag)) { return STATUS_IO_REPARSE_DATA_INVALID; } /* If that a GUID data buffer, its GUID cannot be null, and it cannot contain a MS tag */ if (DataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE == BufferLength && ((!IsReparseTagMicrosoft(ReparseTag) && IsNullGuid(&GuidBuffer->ReparseGuid)) || (ReparseTag == IO_REPARSE_TAG_MOUNT_POINT || ReparseTag == IO_REPARSE_TAG_SYMLINK))) { return STATUS_IO_REPARSE_DATA_INVALID; } /* Check the data for MS non reserved tags */ if (!(ReparseTag & 0xFFF0000) && ReparseTag != IO_REPARSE_TAG_RESERVED_ZERO && ReparseTag != IO_REPARSE_TAG_RESERVED_ONE) { /* If that's a mount point, validate the MountPointReparseBuffer branch */ if (ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { /* We need information */ if (DataLength >= REPARSE_DATA_BUFFER_HEADER_SIZE) { /* Substitue must be the first in row */ if (!ReparseBuffer->MountPointReparseBuffer.SubstituteNameOffset) { /* Substitude must be null-terminated */ if (ReparseBuffer->MountPointReparseBuffer.PrintNameOffset == ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength + sizeof(UNICODE_NULL)) { /* There must just be the Offset/Length fields + buffer + 2 null chars */ if (DataLength == ReparseBuffer->MountPointReparseBuffer.PrintNameLength + ReparseBuffer->MountPointReparseBuffer.SubstituteNameLength + (FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.SubstituteNameOffset)) + 2 * sizeof(UNICODE_NULL)) { return STATUS_SUCCESS; } } } } } else { #define FIELDS_SIZE (FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) - FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.SubstituteNameOffset)) /* If that's not a symlink, accept the MS tag as it */ if (ReparseTag != IO_REPARSE_TAG_SYMLINK) { return STATUS_SUCCESS; } /* We need information */ if (DataLength >= FIELDS_SIZE) { /* Validate lengths */ if (ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength && ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength) { /* Validate unicode strings */ if (IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength) && IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength) && IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameOffset) && IsEven(ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameOffset)) { if ((DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE >= ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameOffset + ReparseBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength + FIELDS_SIZE + REPARSE_DATA_BUFFER_HEADER_SIZE) && (DataLength + REPARSE_DATA_BUFFER_HEADER_SIZE >= ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameLength + ReparseBuffer->SymbolicLinkReparseBuffer.PrintNameOffset + FIELDS_SIZE + REPARSE_DATA_BUFFER_HEADER_SIZE)) { return STATUS_SUCCESS; } } } } #undef FIELDS_SIZE } return STATUS_IO_REPARSE_DATA_INVALID; } return STATUS_IO_REPARSE_TAG_INVALID; }
bool os_get_symlink_target(const std::string &lname, std::string &target) { HANDLE hJunc=CreateFileW(ConvertToWchar(lname).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_ATTRIBUTE_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL); if(hJunc==INVALID_HANDLE_VALUE) return false; DWORD needed_buffer_size = 0; DWORD bytes_returned; std::string buffer; buffer.resize((std::max)((size_t)REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, (size_t)512)); BOOL b = DeviceIoControl(hJunc, FSCTL_GET_REPARSE_POINT, NULL, 0, &buffer[0], static_cast<DWORD>(buffer.size()), &bytes_returned, NULL); if(!b) { if (GetLastError() != ERROR_INSUFFICIENT_BUFFER && GetLastError() != ERROR_MORE_DATA) { CloseHandle(hJunc); return false; } } const REPARSE_DATA_BUFFER* reparse_buffer = reinterpret_cast<const REPARSE_DATA_BUFFER*>(buffer.data()); if(!IsReparseTagMicrosoft(reparse_buffer->ReparseTag)) { CloseHandle(hJunc); return false; } if(!b) { buffer.resize(reparse_buffer->ReparseDataLength+ REPARSE_GUID_DATA_BUFFER_HEADER_SIZE+10); reparse_buffer = reinterpret_cast<const REPARSE_DATA_BUFFER*>(buffer.data()); b = DeviceIoControl(hJunc, FSCTL_GET_REPARSE_POINT, NULL, 0, &buffer[0], static_cast<DWORD>(buffer.size()), &bytes_returned, NULL); if(!b) { CloseHandle(hJunc); return false; } } CloseHandle(hJunc); bool ret=true; std::wstring wtarget; if(reparse_buffer->ReparseTag==IO_REPARSE_TAG_SYMLINK) { wtarget.resize(reparse_buffer->SymbolicLinkReparseBuffer.SubstituteNameLength/sizeof(wchar_t)); memcpy(&wtarget[0], &reparse_buffer->SymbolicLinkReparseBuffer.PathBuffer[reparse_buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(wchar_t)], reparse_buffer->SymbolicLinkReparseBuffer.SubstituteNameLength); } else if(reparse_buffer->ReparseTag==IO_REPARSE_TAG_MOUNT_POINT) { wtarget.resize(reparse_buffer->MountPointReparseBuffer.SubstituteNameLength/sizeof(wchar_t)); memcpy(&wtarget[0], &reparse_buffer->MountPointReparseBuffer.PathBuffer[reparse_buffer->MountPointReparseBuffer.SubstituteNameOffset/sizeof(wchar_t)], reparse_buffer->MountPointReparseBuffer.SubstituteNameLength); } else { ret=false; } target = ConvertFromWchar(wtarget); if(next(target, 0, "\\??\\")) target.erase(0,4); return ret; }