Наш продукт часто имеет дело с удаленными общими каталогами / каталогами NTFS, и мы стараемся, чтобы он корректно работал, если что-то не так в самом каталоге share /, например. В разрешении отказано, путь не найден, а что нет.
Но недавно мы столкнулись со случаем, когда следующая настройка была выполнена на стороне сервера (который имеет один диск, 3 раздела или 3 разных диска с C,D,E
буквы диска). Все соединения созданы с использованием mklink /J
команда.
1) Машина А: C:\sharefolder
поделился с именем пользователя $(USERNAME)
нажав на расширенный вариант обмена. все разрешенные
2) в папке общего доступа есть узел (названный A-M
) что говорит A-M
в D:\A-M
так выглядит
C:\sharefolder\A-M <==> D:\A-M
3) D:\A-M
есть еще один перекресток (названный кампус) E:\Campus
так становится D:\A-M\Campus <==> E:\Campus
5) Конечное имя акции становится \\MachineA\ShareFolder\A-M\Campus
Наш код клиента (миниатюрная версия), как показано ниже
filevol.h
#ifndef FILEVOL_H
#define FILEVOL_H
#include <Windows.h>
#include <memory>#if !defined (REPARSE_DATA_BUFFER_HEADER_SIZE)
// Note:
// See http://msdn.microsoft.com/en-us/library/ff552012.aspx for information about
// the following structure.
//
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;
#define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
#endif#if !defined (IO_REPARSE_TAG_DEDUP)
#define IO_REPARSE_TAG_DEDUP (0x80000013L)
#endif
#if !defined (SYMLINK_FLAG_RELATIVE)
#define SYMLINK_FLAG_RELATIVE (0x00000001L)
#endif
/**
* file handle context
*
* This structure close a file handle in the destructor.
*/
struct file_handle_close
{
void operator()(void *handle) const
{
if (static_cast<HANDLE>(handle) != INVALID_HANDLE_VALUE)
{
CloseHandle(static_cast<HANDLE>(handle));
}
}
}; // end of file_handle_close structure
typedef std::unique_ptr<void, file_handle_close> file_handle_uptr;
// Query the reparse data
union reparse_data_context
{
char buff[REPARSE_DATA_BUFFER_HEADER_SIZE + MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
REPARSE_DATA_BUFFER reparse_data_buff;
}; // end of reparse_data_context union
void printrtag(ULONG rtag)
{
switch (rtag)
{
case IO_REPARSE_TAG_RESERVED_ZERO:
std::cout << "Tag reserved zero \n\n";
break;
case IO_REPARSE_TAG_RESERVED_ONE:
std::cout << "Tag reserved one \n\n" ;
break;
case IO_REPARSE_TAG_MOUNT_POINT:
std::cout << "Tag mount point \n\n";
break;
case IO_REPARSE_TAG_HSM:
std::cout << "Tag legacy HSM \n\n" ;
break;
case IO_REPARSE_TAG_HSM2:
std::cout << "Tag legacy HSM2 \n\n" ;
break;
case IO_REPARSE_TAG_SIS:
std::cout << "Tag SIS \n\n";
break;
case IO_REPARSE_TAG_DFS:
std::cout << "Tag DFS \n\n";
break;
case IO_REPARSE_TAG_SYMLINK:
std::cout << "Tag symbolic link \n\n";
break;
case IO_REPARSE_TAG_DFSR:
std::cout << "Tag DFSR \n\n";
break;
case IO_REPARSE_TAG_NFS:
std::cout << "Tag NFS \n\n";
break;
case IO_REPARSE_TAG_DEDUP:
std::cout << "Tag dedup \n\n";
break;
default:
std::cout << "Unknown reparse tag \n\n";
}
}
std::wstring printtagvalue(reparse_data_context& reparse_data_ctx, ULONG reparse_tag)
{
std::wstring target_name;
if (reparse_tag == IO_REPARSE_TAG_SYMLINK)
{
if (reparse_data_ctx.reparse_data_buff.SymbolicLinkReparseBuffer.PrintNameLength != 0)
{
target_name = std::wstring(static_cast<const wchar_t*>(reparse_data_ctx.reparse_data_buff.SymbolicLinkReparseBuffer.PathBuffer) +
(reparse_data_ctx.reparse_data_buff.SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t)),
(static_cast<size_t>(reparse_data_ctx.reparse_data_buff.SymbolicLinkReparseBuffer.PrintNameLength) / sizeof(wchar_t)));
}
else
{
target_name = std::wstring(static_cast<const wchar_t*>(reparse_data_ctx.reparse_data_buff.SymbolicLinkReparseBuffer.PathBuffer) +
(reparse_data_ctx.reparse_data_buff.SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)),
(static_cast<size_t>(reparse_data_ctx.reparse_data_buff.SymbolicLinkReparseBuffer.SubstituteNameLength) / sizeof(wchar_t)));
}
}
else if (reparse_tag == IO_REPARSE_TAG_MOUNT_POINT)
{
if (reparse_data_ctx.reparse_data_buff.MountPointReparseBuffer.PrintNameLength != 0)
{
target_name = std::wstring(static_cast<const wchar_t*>(reparse_data_ctx.reparse_data_buff.MountPointReparseBuffer.PathBuffer) +
(reparse_data_ctx.reparse_data_buff.MountPointReparseBuffer.PrintNameOffset / sizeof(wchar_t)),
(static_cast<size_t>(reparse_data_ctx.reparse_data_buff.MountPointReparseBuffer.PrintNameLength) / sizeof(wchar_t)));
}
else
{
target_name = std::wstring(static_cast<const wchar_t*>(reparse_data_ctx.reparse_data_buff.MountPointReparseBuffer.PathBuffer) +
(reparse_data_ctx.reparse_data_buff.MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar_t)),
(static_cast<size_t>(reparse_data_ctx.reparse_data_buff.MountPointReparseBuffer.SubstituteNameLength) / sizeof(wchar_t)));
}
}
return target_name;
}
#endif
filevol.cpp
#include <Windows.h>
#include <errno.h>
#include <iostream>
#include <string>
#include "filevol.h"
DWORD read_reparse_data_context(reparse_data_context &reparse_data_ctx, const std::wstring &path_name)
{
file_handle_uptr handle_uptr(CreateFileW(path_name.c_str(),
FILE_READ_EA,
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
0,
OPEN_EXISTING,
(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT),
0));
HANDLE handle(static_cast<HANDLE>(handle_uptr.get()));
if (handle == INVALID_HANDLE_VALUE)
{
const DWORD lerror = GetLastError();
std::cout << "Error occurred in CreateFileW : " << lerror << std::endl;
return lerror;
}
// Query the reparse data
DWORD ret_len;
if (!DeviceIoControl(handle,
FSCTL_GET_REPARSE_POINT,
nullptr,
0,
reparse_data_ctx.buff,
sizeof(reparse_data_ctx),
&ret_len,
nullptr))
{
const DWORD lerror = GetLastError();
std::cout << "Error occurred in DeviceIoControl : " << lerror << std::endl;
return lerror;
}
return ERROR_SUCCESS;
} // end of read_reparse_data_context()bool is_link(DWORD file_attrib, const std::wstring& path, reparse_data_context& reparse_data_ctx)
{
if ((file_attrib & FILE_ATTRIBUTE_REPARSE_POINT))
{
// get reparse data context
const DWORD retval = read_reparse_data_context(reparse_data_ctx, path);
if (retval == ERROR_SUCCESS)
{
ULONG reparse_tag(reinterpret_cast<const REPARSE_DATA_BUFFER*>(reparse_data_ctx.buff)->ReparseTag);
std::cout << "Reparse tag is found to be : " << reparse_tag << std::endl;
printrtag(reparse_tag);
const auto str = printtagvalue(reparse_data_ctx, reparse_tag);
std::wcout << str << std::endl;
if(IsReparseTagMicrosoft(reparse_tag))
{
std::cout << "Microsoft reparse tag \n\n" ;
}
else
{
std::cout << "Not a Microsoft reparse tag \n\n";
}
if(IsReparseTagNameSurrogate(reparse_tag))
{
std::cout << "Surrogate reparse tag \n\n";
}
else
{
std::cout << "Not a surrogate reparse tag \n\n";
}
return true;
}
}
return false;
}
void function2(const std::wstring& path)
{
WIN32_FILE_ATTRIBUTE_DATA attr_ctx;
if (GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &attr_ctx) == 0)
{
DWORD error_code(GetLastError());
std::cout << "Error occurred in GetFileAttributeExW : " << error_code << '\n';
}
else
{
reparse_data_context reparse_data_ctx;
if(is_link(attr_ctx.dwFileAttributes,path,reparse_data_ctx))
{
std::cout << "This is a symbolic link !!!\n\n";
}
else
{
std::cout << "This is not a symbolic link !!!\n\n";
}
}
}
int main()
{
std::wstring path1 = L"\\MachineA\ShareFolder\A-M\Campus";
function2(path1);
}
Когда мы запускаем приложение этого автономного кода с машины B, мы получаем следующий вывод.
Reparse tag is found to be : 2684354563
Tag mount point
E:\Campus
Microsoft reparse tag
Surrogate reparse tag
This is a symbolic link !!!
Это заставило наш продукт не распознавать путь, так как он получал ответы от Windows API для локального пути на удаленной машине, что нежелательно.
Вопросы:
На удаленном клиентском компьютере B, с которого запускается приложение
1) Почему тег печатается как точка монтирования тега, а не как символьная ссылка?
2) почему значение разрешенной ссылки / цели получается как E:\Campus
вместо \\MachineA\ShareFolder\A-M\Campus
?
3) Разве Windows API не может понять, что если запрос поступает с удаленной машины, разрешенный путь должен быть возвращен как имя общего ресурса вместо пути к локальному диску?
Или есть какой-то другой волшебный Windows API, который может дать мне правильное разрешенное имя для удаленного общего ресурса, содержащего точки соединения / точки монтирования?
Любая помощь будет оценена.
Задача ещё не решена.
Других решений пока нет …