{ Double Commander ------------------------------------------------------------------------- This unit contains Linux specific functions Copyright (C) 2023 Alexander Koblov (alexx2000@mail.ru) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see } unit DCLinux; {$mode objfpc}{$H+} {$packrecords c} interface uses Classes, SysUtils, BaseUnix, Unix; const CLOSE_RANGE_CLOEXEC = (1 << 2); const FS_IOC_GETFLAGS = $80086601; FS_IOC_SETFLAGS = $40086602; (* * Inode flags (FS_IOC_GETFLAGS / FS_IOC_SETFLAGS) *) FS_SECRM_FL = $00000001; //* Secure deletion */ FS_UNRM_FL = $00000002; //* Undelete */ FS_COMPR_FL = $00000004; //* Compress file */ FS_SYNC_FL = $00000008; //* Synchronous updates */ FS_IMMUTABLE_FL = $00000010; //* Immutable file */ FS_APPEND_FL = $00000020; //* Writes to file may only append */ FS_NODUMP_FL = $00000040; //* Do not dump file */ FS_NOATIME_FL = $00000080; //* Do not update atime */ FS_FL_USER_VISIBLE = $0003DFFF; //* User visible flags */ FS_FL_USER_MODIFIABLE = $000380FF; //* User modifiable flags */ type TFlagName = record Flag: UInt32; Name: AnsiChar; end; const FlagsName: array[1..8] of TFlagName = ( (Flag: FS_SECRM_FL; Name: 's'), (Flag: FS_UNRM_FL; Name: 'u'), (Flag: FS_SYNC_FL; Name: 'S'), (Flag: FS_IMMUTABLE_FL; Name: 'i'), (Flag: FS_APPEND_FL; Name: 'a'), (Flag: FS_NODUMP_FL; Name: 'd'), (Flag: FS_NOATIME_FL; Name: 'A'), (Flag: FS_COMPR_FL; Name: 'c') ); function FormatFileFlags(Flags: UInt32): String; function FileGetFlags(Handle: THandle; out Flags: UInt32): Boolean; function mbFileGetFlags(const FileName: String; out Flags: UInt32): Boolean; function mbFileGetXattr(const FileName: String): TStringArray; function mbFileCopyXattr(const Source, Target: String): Boolean; implementation uses InitC, DCConvertEncoding, DCOSUtils; function lremovexattr(const path, name: PAnsiChar): cint; cdecl; external clib; function llistxattr(const path: PAnsiChar; list: PAnsiChar; size: csize_t): ssize_t; cdecl; external clib; function lgetxattr(const path, name: PAnsiChar; value: Pointer; size: csize_t): ssize_t; cdecl; external clib; function lsetxattr(const path, name: PAnsiChar; const value: Pointer; size: csize_t; flags: cint): cint; cdecl; external clib; function FormatFileFlags(Flags: UInt32): String; var Index: Integer; begin Result:=StringOfChar('-', Length(FlagsName)); for Index:= 1 to High(FlagsName) do begin if Flags and FlagsName[Index].Flag <> 0 then begin Result[Index]:= FlagsName[Index].Name; end; end; end; function FileGetFlags(Handle: THandle; out Flags: UInt32): Boolean; begin Result:= (FpIOCtl(Handle, FS_IOC_GETFLAGS, @Flags) >= 0); end; function mbFileGetFlags(const FileName: String; out Flags: UInt32): Boolean; var Handle: THandle; begin Handle:= mbFileOpen(FileName, fmOpenRead or fmShareDenyNone); Result:= Handle <> feInvalidHandle; if Result then begin Result:= (FpIOCtl(Handle, FS_IOC_GETFLAGS, @Flags) >= 0); FileClose(Handle); end; end; function mbFileGetXattr(const FileName: String): TStringArray; var AList: String; ALength: ssize_t; AFileName: String; begin SetLength(AList, MaxSmallint); Result:= Default(TStringArray); AFileName:= CeUtf8ToSys(FileName); ALength:= llistxattr(PAnsiChar(AFileName), Pointer(AList), Length(AList)); if (ALength < 0) then begin if (fpgetCerrno <> ESysERANGE) then begin fpseterrno(fpgetCerrno); Exit; end else begin ALength:= llistxattr(PAnsiChar(AFileName), nil, 0); if ALength < 0 then begin fpseterrno(fpgetCerrno); Exit; end; SetLength(AList, ALength); ALength:= llistxattr(PAnsiChar(AFileName), Pointer(AList), ALength); if ALength < 0 then begin fpseterrno(fpgetCerrno); Exit; end; end; end; if (ALength > 0) then begin SetLength(AList, ALength - 1); Result:= AList.Split(#0); end; end; function mbFileCopyXattr(const Source, Target: String): Boolean; var Value: String; Index: Integer; ALength: ssize_t; Names: TStringArray; ASource, ATarget: String; begin Result:= True; ASource:= CeUtf8ToSys(Source); ATarget:= CeUtf8ToSys(Target); // Remove attributes from target Names:= mbFileGetXattr(Target); for Index:= 0 to High(Names) do begin lremovexattr(PAnsiChar(ATarget), PAnsiChar(Names[Index])); end; SetLength(Value, MaxSmallint); Names:= mbFileGetXattr(Source); for Index:= 0 to High(Names) do begin ALength:= lgetxattr(PAnsiChar(ASource), PAnsiChar(Names[Index]), Pointer(Value), Length(Value)); if (ALength < 0) then begin if (fpgetCerrno <> ESysERANGE) then begin fpseterrno(fpgetCerrno); Exit(False); end else begin ALength:= lgetxattr(PAnsiChar(ASource), PAnsiChar(Names[Index]), nil, 0); if ALength < 0 then begin fpseterrno(fpgetCerrno); Exit(False); end; SetLength(Value, ALength); ALength:= lgetxattr(PAnsiChar(ASource), PAnsiChar(Names[Index]), Pointer(Value), Length(Value)); if ALength < 0 then begin fpseterrno(fpgetCerrno); Exit(False); end; end; end; if (lsetxattr(PAnsiChar(ATarget), PAnsiChar(Names[Index]), Pointer(Value), ALength, 0) < 0) then begin fpseterrno(fpgetCerrno); Exit(fpgeterrno = ESysEOPNOTSUPP); end; end; end; end.