Hello @Andrey.Golubkov, @atir.tahir
FILE_FLAG_BACKUP_SEMANTICS will only work with administrator privileges.
We verified that the user had administrator privileges on the file server we want to create an index for, although this does not seem to be strictly necessary. The user only needs to have the privilege SeBackupPrivilege
on Windows.
What makes us think that the problem itself is not on our side / related to the user:
-
We have 2 Windows services with the same service user.
-
Service 1 is a Delphi process that uses the backup privilege to read a file that can only be read with it. It works perfectly fine.
-
Service 2 is a DotNet process with GroupDocs.Search with the same user, but we get “System.UnauthorizedAccessException: Access to the path … is denied” for that file.
2 Ideas:
-
We get the exception via the event extractor.ErrorOccurred
by the way. I think, we haven’t mentioned that yet. Maybe, this helps reproducing it.
-
Let me give you the code we used to get the backup privilege with, so you can compare it to yours:
This is how we activated the backup privilege in our code (with CsWin32):
private bool EnableBackupPrivilegeIfPossible()
{
// This along is not enough. When reading a file, the FILE_FLAG_BACKUP_SEMANTICS flag needs to be passed, too!!
// open token for current process
var currentProcessHandle = new SafeProcessHandle(Process.GetCurrentProcess().Handle, ownsHandle: false);
bool success = PInvoke.OpenProcessToken(
currentProcessHandle,
TOKEN_ACCESS_MASK.TOKEN_ADJUST_PRIVILEGES | TOKEN_ACCESS_MASK.TOKEN_QUERY,
out var tokenHandle);
if (!success)
{
var errorCode = Marshal.GetLastPInvokeError();
var errorMessage = Marshal.GetPInvokeErrorMessage(errorCode);
m_Logger.LogOpenProcessTokenFailed(errorCode, errorMessage);
return false;
}
using (tokenHandle)
{
// Lookup für das BackupPrivilege
success = PInvoke.LookupPrivilegeValue(
null, // local PC
"SeBackupPrivilege", // name of privilege
out LUID privilegeLuid);
if (!success)
{
var errorCode = Marshal.GetLastPInvokeError();
var errorMessage = Marshal.GetPInvokeErrorMessage(errorCode);
m_Logger.LogLookupPrivilegeValueFailed(errorCode, errorMessage);
return false;
}
// structure for privileges change
var tokenPrivileges = new TOKEN_PRIVILEGES
{
PrivilegeCount = 1,
Privileges = default
};
tokenPrivileges.Privileges[0].Luid = privilegeLuid;
tokenPrivileges.Privileges[0].Attributes = TOKEN_PRIVILEGES_ATTRIBUTES.SE_PRIVILEGE_ENABLED;
// activate privilege
unsafe
{
TOKEN_PRIVILEGES previousState;
uint returnLength = 0;
success = PInvoke.AdjustTokenPrivileges(tokenHandle,
new BOOL(false),
&tokenPrivileges,
(uint)Marshal.SizeOf<TOKEN_PRIVILEGES>(),
&previousState,
&returnLength);
}
if (!success || Marshal.GetLastPInvokeError() != 0)
{
var errorCode = Marshal.GetLastPInvokeError();
var errorMessage = Marshal.GetPInvokeErrorMessage(errorCode);
m_Logger.LogAdjustTokenPrivilegesFailed(errorCode, errorMessage);
return false;
}
return true;
}
}
This is the code we used to actually read the file (first 48 bytes in this case):
// Win32 constants
private const uint GENERIC_READ = 0x80000000;
private const uint FILE_SHARE_READ = 0x00000001;
private const uint FILE_SHARE_WRITE = 0x00000002;
private const uint OPEN_EXISTING = 3;
private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
// Win32 API imports
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern SafeFileHandle CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
if (pFilePath.Contains("secret"))
{
try
{
// Open the file with FILE_FLAG_BACKUP_SEMANTICS
using (SafeFileHandle fileHandle = CreateFile(
pFilePath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
IntPtr.Zero))
{
if (fileHandle.IsInvalid)
{
int error = Marshal.GetLastWin32Error();
throw new IOException($"Failed to open file. Error code: {error}");
}
// Read the file content using the file handle
using (FileStream fileStream = new FileStream(fileHandle, FileAccess.Read))
{
// Create a buffer to read the file
byte[] buffer = new byte[48];
StringBuilder fileContent = new StringBuilder();
int bytesRead;
// Read the file in chunks
if ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) > 0)
{
fileContent.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
}
// Display the file content
m_Logger.LogFileContent(fileContent.ToString());
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
m_Logger.LogFileReadingError(ex);
}
}
We hope, this helps.