Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   .NET-Framework (managed code) (https://www.delphipraxis.net/79-net-framework-managed-code/)
-   -   C# ACL eines Folders auf anderen übertragen (https://www.delphipraxis.net/165225-acl-eines-folders-auf-anderen-uebertragen.html)

xaromz 19. Dez 2011 14:37

ACL eines Folders auf anderen übertragen
 
Hallo,

ich möchte die ACL eines Verzeichnisses auf ein anderes Verzeichnis übertragen. Dabei habe ich aber nur die Handles der Verzeichnisse, weshalb die eingebauten Klassen Directory bzw. DirectoryInfo nicht nutzbar sind. Die zugrundeliegenden Klasse DirectorySecurity ist leider auch nicht nutzbar, da der ctor internal ist. Ich muss das also wahrscheinlich direkt über das WinAPI umsetzen.
Kann mir da jemand helfen?

Gruß
xaromz

ASM 20. Dez 2011 02:26

AW: ACL eines Folders auf anderen übertragen
 
Hier ist alles, um
(1) zum Handle eines Folders den zugehörigen Pfadnamen zu erhalten
(2) zum Pfadnamen eines geöffneten Folders dessen zugehöriges Handle zu ermitteln.

Im ersten Fall muss man einen Umweg nehmen, indem man mit Hilfe des Handles zuerst den DevicePath (z.B. "\device\HardDisk1\...") bekommt und diesen dann in den üblichen DOSPath (z.B. "c:\..."} konvertieren muss.

Code:
const
  ObjectNameInformation = 1;
  FileDirectoryInformation = 1;
  FileNameInformation = 9;

type
  NT_STATUS = Cardinal;

const
  STATUS_SUCCESS = NT_STATUS($00000000);

type
  PGetFileNameThreadParam = ^TGetFileNameThreadParam;
  TGetFileNameThreadParam = packed record
    hFile: THandle;
    Data: array[0..MAX_PATH - 1] of Char;
    Status: NT_STATUS;
  end;

  FILE_NAME_INFORMATION = packed record
    FileNameLength: ULONG;
    FileName: array[0..MAX_PATH - 1] of WideChar;
  end;

  TUNICODE_STRING = packed record
    Length: WORD;
    MaximumLength: WORD;
    Buffer: array[0..MAX_PATH - 1] of WideChar;
  end;

  TOBJECT_NAME_INFORMATION = packed record
    Name: TUNICODE_STRING;
  end;

  PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK;
  IO_STATUS_BLOCK = packed record
    Status: NT_STATUS;
    Information: DWORD;
  end;

function NtQueryInformationFile(FileHandle: THandle;
  IoStatusBlock: PIO_STATUS_BLOCK; FileInformation: Pointer;
  Length: DWORD; FileInformationClass: DWORD): NT_STATUS;
  stdcall; external 'ntdll.dll';

function NtQueryObject(ObjectHandle: THandle;
  ObjectInformationClass: DWORD; ObjectInformation: Pointer;
  ObjectInformationLength: ULONG;
  ReturnLength: PDWORD): NT_STATUS; stdcall; external 'ntdll.dll';

function GetFileNameThread(lpParameters: Pointer): DWORD; stdcall;
var
  FileNameInfo: FILE_NAME_INFORMATION;
  ObjectNameInfo: TOBJECT_NAME_INFORMATION;
  IoStatusBlock: IO_STATUS_BLOCK;
  pThreadParam: TGetFileNameThreadParam;
  dwReturn: DWORD;
begin
  ZeroMemory(@FileNameInfo, SizeOf(FILE_NAME_INFORMATION));
  pThreadParam := PGetFileNameThreadParam(lpParameters)^;
  Result := NtQueryInformationFile(pThreadParam.hFile, @IoStatusBlock,
    @FileNameInfo, MAX_PATH * 2, FileNameInformation);
  if Result = STATUS_SUCCESS then
  begin
    Result := NtQueryObject(pThreadParam.hFile, ObjectNameInformation,
      @ObjectNameInfo, MAX_PATH * 2, @dwReturn);
    if Result = STATUS_SUCCESS then
    begin
      pThreadParam.Status := Result;
      WideCharToMultiByte(CP_ACP, 0,
        @ObjectNameInfo.Name.Buffer[ObjectNameInfo.Name.MaximumLength -
        ObjectNameInfo.Name.Length],
          ObjectNameInfo.Name.Length, @pThreadParam.Data[0],
        MAX_PATH, nil, nil);
    end
    else
    begin
      pThreadParam.Status := STATUS_SUCCESS;
      Result := STATUS_SUCCESS;
      WideCharToMultiByte(CP_ACP, 0,
        @FileNameInfo.FileName[0], IoStatusBlock.Information,
        @pThreadParam.Data[0],
        MAX_PATH, nil, nil);
    end;
  end;
  PGetFileNameThreadParam(lpParameters)^ := pThreadParam;
  ExitThread(Result);
end;

function GetFileNameFromHandle(hFile: THandle): string;
var
  lpExitCode: DWORD;
  pThreadParam: TGetFileNameThreadParam;
  hThread: THandle;
begin
  Result := '';
  ZeroMemory(@pThreadParam, SizeOf(TGetFileNameThreadParam));
  pThreadParam.hFile := hFile;
  hThread := CreateThread(nil, 0, @GetFileNameThread, @pThreadParam, 0, 0);
  if hThread <> 0 then
  try
    case WaitForSingleObject(hThread, 100) of
      WAIT_OBJECT_0:
        begin
          GetExitCodeThread(hThread, lpExitCode);
          if lpExitCode = STATUS_SUCCESS then
            Result := pThreadParam.Data;
        end;
      WAIT_TIMEOUT:
        TerminateThread(hThread, 0);
    end;
  finally
    CloseHandle(hThread);
  end;
end;

function ConvertDevicePathToDOSPath(DevicePath: string): string;
var
  i: integer;
  root: string;
  device: string;
  buffer: string; // pChar;
begin
  result := DevicePath;
  setlength(buffer, 1000);
  for i := Ord('c') to Ord('z') do
  begin
    root := Char(i) + ':';
    if (QueryDosDevice(PChar(root), pchar(buffer), 1000) <> 0) then
    begin
      device := pchar(buffer);
      if pos(device, DevicePath) > 0 then
      begin
        result := StringReplace(DevicePath, device, root, []);
        break;
      end;
    end;
  end;
end;

function GetDirnameFromHandle(DirHandle: THandle): string;
var
  DevicePath: string;
begin
  result := '';
  if DirHandle <> INVALID_HANDLE_VALUE then
  begin
    DevicePath := GetFileNameFromHandle(DirHandle);
    result := ConvertDevicePathToDOSPath(DevicePath);
  end;
end;

function GetHandleFromDirName(Folder: string): THandle;
begin
  result := CreateFile(PChar(folder),
    FILE_LIST_DIRECTORY or GENERIC_READ,
    FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
    nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or
    FILE_FLAG_OVERLAPPED, 0);
end;
// Beispiel:
Code:
// Beispiel:
Var folderpath: String = 'c:\temp\';

procedure TForm1.Button1Click(Sender: TObject);
var
  hdl: THandle;
  FolderZuHandle, FolderNameFromHandle: string;
begin
  memo1.Clear;
  folderZuHandle := FolderPath;
  if not DirectoryExists(folderZuHandle) then exit;
  memo1.Lines.add('Get Handle of Startfolder: '+folderZuHandle);
  hdl := GetHandleFromDirName(FolderZuHandle);
  FolderNameFromHandle := GetDirnameFromHandle(hdl);
  memo1.Lines.add('Foldername from Handle: '+FolderNameFromHandle);
end;
Und um die Zugriffsrechte eines Folders zu ändern (wozu man aber ggf. Administratorrechte braucht):
Code:
function ChangeUserPermission(aFile, Name: string; aMode: TACCESSMODE): string;
var
  pDACL: PACL;
  R: DWORD;
  pEA: TExplicitAccess;
begin
  result := '';
  try
    try
      BuildExplicitAccessWithName(@pEA, PAnsiChar(Name), GENERIC_READ, aMode,
        NO_INHERITANCE);
      R := SetEntriesInAcl(1, @pEA, nil, pDACL);
      if R = ERROR_SUCCESS then
      begin
        if SetNamedSecurityInfo(PAnsiChar(aFile), SE_FILE_OBJECT,
          DACL_SECURITY_INFORMATION,
          nil, nil, pDACL, nil) <> ERROR_SUCCESS then
          result := '*:' + SysErrorMessage(GetLastError);
        LocalFree(Cardinal(pDACL));
      end
      else
      begin
        result := '#:' + SysErrorMessage(R);
      end;
    except
      result := 'Exception raised';
    end;
  finally
  end;
end;
// Beispiel:
Code:
procedure TForm1.Button2Click(Sender: TObject);
var
  report: string;
  AccountName: string;
  SecuritySetting: _ACCESS_MODE;
begin
  AccountName := 'USER';
  SecuritySetting := GRANT_ACCESS; // GRANT_ACCESS oder DENY_ACCESS
  if ChangeUserPermission(FolderPath, AccountName, SecuritySetting) = '' then
    showmessage('Permission has been changed successfully')
  else
    Showmessage('Changing permission failed: '#13#10 + report);
end;
Für alles braucht man:
Code:
Uses {...,} JwaAclApi, JwaWinNT, JwaAccCtrl, JwaWinBase;

xaromz 20. Dez 2011 10:35

AW: ACL eines Folders auf anderen übertragen
 
Hallo,

danke für Deine ausführliche Antwort. Mal schauen, ob mir das weiterhilft, hier geht es ja um C# bzw. .Net. Da muss ich sehen, ob ich das umsetzten kann.

Gruß
xaromz

generic 20. Dez 2011 15:27

AW: ACL eines Folders auf anderen übertragen
 
Im Zweifelsfall kann du das über CLI machen.

"icacls"
Zitat:

Speichert die DACLs für die Dateien und Ordner mit übereinstimmendem Namen
zur späteren Verwendung mit "/restore" in der ACL-Datei. SACLs,
Besitzer oder Integritätsbezeichnungen werden nicht gespeichert.

xaromz 20. Dez 2011 17:51

AW: ACL eines Folders auf anderen übertragen
 
Hallo,

ja, über CLI geht das grundsätzlich; ich schreibe allerdings an einem Backupprogramm, da geht es um tausende Ordner. Wenn ich da für jeden ein Programm aufrufe ist mit der Performance Essig.

Gruß
xaromz

Dezipaitor 20. Dez 2011 19:51

AW: ACL eines Folders auf anderen übertragen
 
@ASM:
Kannst du mir erklären, warum GetFileNameFromHandle einen eigenen Thread erzeugt?


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:05 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz