mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-10-22 13:31:34 +02:00

* Many more RTL units are enabled now, like SysUtils, Classes, Math, FGL, etc and Text-, File- and ConsoleIO features are enabled now as well (Threading and Processes are enabled, too, but their implementations are only stubs!). ConsoleIO isn't tested though, because the processes that are started by SMSS have their Standard Handles set to 0. git-svn-id: trunk@16553 -
403 lines
10 KiB
PHP
403 lines
10 KiB
PHP
{
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 2010 by Sven Barth
|
|
|
|
Low leve file functions
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
This program 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.
|
|
|
|
**********************************************************************}
|
|
|
|
|
|
{*****************************************************************************
|
|
Low Level File Routines
|
|
*****************************************************************************}
|
|
|
|
function do_isdevice(handle:thandle):boolean;
|
|
begin
|
|
do_isdevice := (handle = StdInputHandle) or
|
|
(handle = StdOutputHandle) or
|
|
(handle = StdErrorHandle);
|
|
end;
|
|
|
|
|
|
procedure do_close(h : thandle);
|
|
begin
|
|
if do_isdevice(h) then
|
|
Exit;
|
|
NtClose(h);
|
|
end;
|
|
|
|
|
|
procedure do_erase(p : pchar);
|
|
var
|
|
ntstr: TNtUnicodeString;
|
|
objattr: TObjectAttributes;
|
|
iostatus: TIOStatusBlock;
|
|
h: THandle;
|
|
disp: TFileDispositionInformation;
|
|
res: LongInt;
|
|
begin
|
|
InoutRes := 4;
|
|
DoDirSeparators(p);
|
|
|
|
SysPCharToNtStr(ntstr, p, 0);
|
|
SysInitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil);
|
|
|
|
res := NtCreateFile(@h, NT_DELETE or NT_SYNCHRONIZE, @objattr, @iostatus, Nil,
|
|
0, FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
|
|
FILE_OPEN, FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT,
|
|
Nil, 0);
|
|
|
|
if res >= 0 then begin
|
|
disp.DeleteFile := True;
|
|
|
|
res := NtSetInformationFile(h, @iostatus, @disp,
|
|
SizeOf(TFileDispositionInformation), FileDispositionInformation);
|
|
|
|
errno := res;
|
|
|
|
NtClose(h);
|
|
end else
|
|
if res = STATUS_FILE_IS_A_DIRECTORY then
|
|
errno := 2
|
|
else
|
|
errno := res;
|
|
|
|
SysFreeNtStr(ntstr);
|
|
Errno2InoutRes;
|
|
end;
|
|
|
|
|
|
procedure do_rename(p1,p2 : pchar);
|
|
var
|
|
h: THandle;
|
|
objattr: TObjectAttributes;
|
|
iostatus: TIOStatusBlock;
|
|
dest, src: TNtUnicodeString;
|
|
renameinfo: PFileRenameInformation;
|
|
res: LongInt;
|
|
begin
|
|
DoDirSeparators(p1);
|
|
DoDirSeparators(p2);
|
|
|
|
{ check whether the destination exists first }
|
|
SysPCharToNtStr(dest, p2, 0);
|
|
SysInitializeObjectAttributes(objattr, @dest, 0, 0, Nil);
|
|
|
|
res := NtCreateFile(@h, 0, @objattr, @iostatus, Nil, 0,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE, FILE_OPEN,
|
|
FILE_NON_DIRECTORY_FILE, Nil, 0);
|
|
if res >= 0 then begin
|
|
{ destination already exists => error }
|
|
NtClose(h);
|
|
errno := 5;
|
|
Errno2InoutRes;
|
|
end else begin
|
|
SysPCharToNtStr(src, p1, 0);
|
|
SysInitializeObjectAttributes(objattr, @src, 0, 0, Nil);
|
|
|
|
res := NtCreateFile(@h, GENERIC_ALL or NT_SYNCHRONIZE or FILE_READ_ATTRIBUTES,
|
|
@objattr, @iostatus, Nil, 0, FILE_SHARE_READ or FILE_SHARE_WRITE,
|
|
FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT or FILE_OPEN_REMOTE_INSTANCE
|
|
or FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, Nil,
|
|
0);
|
|
|
|
if res >= 0 then begin
|
|
renameinfo := GetMem(SizeOf(TFileRenameInformation) + dest.Length);
|
|
with renameinfo^ do begin
|
|
ReplaceIfExists := False;
|
|
RootDirectory := 0;
|
|
FileNameLength := dest.Length;
|
|
Move(dest.Buffer^, renameinfo^.FileName, dest.Length);
|
|
end;
|
|
|
|
res := NtSetInformationFile(h, @iostatus, renameinfo,
|
|
SizeOf(TFileRenameInformation) + dest.Length,
|
|
FileRenameInformation);
|
|
if res < 0 then begin
|
|
{ this could happen if src and destination reside on different drives,
|
|
so we need to copy the file manually }
|
|
{$message warning 'do_rename: Implement file copy!'}
|
|
errno := res;
|
|
Errno2InoutRes;
|
|
end;
|
|
|
|
NtClose(h);
|
|
end else begin
|
|
errno := res;
|
|
Errno2InoutRes;
|
|
end;
|
|
|
|
SysFreeNtStr(src);
|
|
end;
|
|
|
|
SysFreeNtStr(dest);
|
|
end;
|
|
|
|
|
|
function do_write(h:thandle;addr:pointer;len : longint) : longint;
|
|
var
|
|
res: LongInt;
|
|
iostatus: TIoStatusBlock;
|
|
begin
|
|
res := NtWriteFile(h, 0, Nil, Nil, @iostatus, addr, len, Nil, Nil);
|
|
|
|
if res = STATUS_PENDING then begin
|
|
res := NtWaitForSingleObject(h, False, Nil);
|
|
if res >= 0 then
|
|
res := iostatus.Status;
|
|
end;
|
|
|
|
if res < 0 then begin
|
|
errno := res;
|
|
Errno2InoutRes;
|
|
do_write := 0;
|
|
end else
|
|
do_write := LongInt(iostatus.Information);
|
|
end;
|
|
|
|
|
|
function do_read(h: thandle; addr: pointer; len: longint): longint;
|
|
var
|
|
iostatus: TIOStatusBlock;
|
|
res: LongInt;
|
|
begin
|
|
res := NtReadFile(h, 0, Nil, Nil, @iostatus, addr, len, Nil, Nil);
|
|
|
|
if res = STATUS_PENDING then begin
|
|
res := NtWaitForSingleObject(h, False, Nil);
|
|
if res >= 0 then
|
|
res := iostatus.Status;
|
|
end;
|
|
|
|
if (res < 0) and (res <> STATUS_PIPE_BROKEN) then begin
|
|
errno := res;
|
|
Errno2InoutRes;
|
|
do_read := 0;
|
|
end else
|
|
if res = STATUS_PIPE_BROKEN then
|
|
do_read := 0
|
|
else
|
|
do_read := LongInt(iostatus.Information);
|
|
end;
|
|
|
|
|
|
function do_filepos(handle : thandle) : Int64;
|
|
var
|
|
res: LongInt;
|
|
iostatus: TIoStatusBlock;
|
|
position: TFilePositionInformation;
|
|
begin
|
|
res := NtQueryInformationFile(handle, @iostatus, @position,
|
|
SizeOf(TFilePositionInformation), FilePositionInformation);
|
|
|
|
if res < 0 then begin
|
|
errno := res;
|
|
Errno2InoutRes;
|
|
do_filepos := 0;
|
|
end else
|
|
do_filepos := position.CurrentByteOffset.QuadPart;
|
|
end;
|
|
|
|
|
|
procedure do_seek(handle: thandle; pos: Int64);
|
|
var
|
|
position: TFilePositionInformation;
|
|
iostatus: TIoStatusBlock;
|
|
res: LongInt;
|
|
begin
|
|
position.CurrentByteOffset.QuadPart := pos;
|
|
res := NtSetInformationFile(handle, @iostatus, @position,
|
|
SizeOf(TFilePositionInformation), FilePositionInformation);
|
|
if res < 0 then begin
|
|
errno := res;
|
|
Errno2InoutRes;
|
|
end;
|
|
end;
|
|
|
|
|
|
function do_seekend(handle:thandle):Int64;
|
|
var
|
|
res: LongInt;
|
|
standard: TFileStandardInformation;
|
|
position: TFilePositionInformation;
|
|
iostatus: TIoStatusBlock;
|
|
begin
|
|
do_seekend := 0;
|
|
|
|
res := NtQueryInformationFile(handle, @iostatus, @standard,
|
|
SizeOf(TFileStandardInformation), FileStandardInformation);
|
|
if res >= 0 then begin
|
|
position.CurrentByteOffset.QuadPart := standard.EndOfFile.QuadPart;
|
|
res := NtSetInformationFile(handle, @iostatus, @position,
|
|
SizeOf(TFilePositionInformation), FilePositionInformation);
|
|
if res >= 0 then
|
|
do_seekend := position.CurrentByteOffset.QuadPart;
|
|
end;
|
|
|
|
if res < 0 then begin
|
|
errno := res;
|
|
Errno2InoutRes;
|
|
end;
|
|
end;
|
|
|
|
|
|
function do_filesize(handle : thandle) : Int64;
|
|
var
|
|
res: LongInt;
|
|
iostatus: TIoStatusBlock;
|
|
standard: TFileStandardInformation;
|
|
begin
|
|
res := NtQueryInformationFile(handle, @iostatus, @standard,
|
|
SizeOf(TFileStandardInformation), FileStandardInformation);
|
|
if res >= 0 then
|
|
do_filesize := standard.EndOfFile.QuadPart
|
|
else begin
|
|
errno := res;
|
|
Errno2InoutRes;
|
|
do_filesize := 0;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure do_truncate (handle:thandle;pos:Int64);
|
|
var
|
|
endoffileinfo: TFileEndOfFileInformation;
|
|
allocinfo: TFileAllocationInformation;
|
|
iostatus: TIoStatusBlock;
|
|
res: LongInt;
|
|
begin
|
|
// based on ReactOS' SetEndOfFile
|
|
endoffileinfo.EndOfFile.QuadPart := pos;
|
|
res := NtSetInformationFile(handle, @iostatus, @endoffileinfo,
|
|
SizeOf(TFileEndOfFileInformation), FileEndOfFileInformation);
|
|
if res < 0 then begin
|
|
errno := res;
|
|
Errno2InoutRes;
|
|
end else begin
|
|
allocinfo.AllocationSize.QuadPart := pos;
|
|
res := NtSetInformationFile(handle, @iostatus, @allocinfo,
|
|
SizeOf(TFileAllocationInformation), FileAllocationInformation);
|
|
if res < 0 then begin
|
|
errno := res;
|
|
Errno2InoutRes;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure do_open(var f;p:pchar;flags:longint);
|
|
{
|
|
filerec and textrec have both handle and mode as the first items so
|
|
they could use the same routine for opening/creating.
|
|
when (flags and $100) the file will be append
|
|
when (flags and $1000) the file will be truncate/rewritten
|
|
when (flags and $10000) there is no check for close (needed for textfiles)
|
|
}
|
|
var
|
|
shflags, cd, oflags: LongWord;
|
|
objattr: TObjectAttributes;
|
|
iostatus: TIoStatusBlock;
|
|
ntstr: TNtUnicodeString;
|
|
res: LongInt;
|
|
begin
|
|
DoDirSeparators(p);
|
|
{ close first if opened }
|
|
if ((flags and $10000)=0) then
|
|
begin
|
|
case filerec(f).mode of
|
|
fminput,fmoutput,fminout : Do_Close(filerec(f).handle);
|
|
fmclosed : ;
|
|
else
|
|
begin
|
|
{not assigned}
|
|
inoutres:=102;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
{ reset file handle }
|
|
filerec(f).handle:=UnusedHandle;
|
|
{ convert filesharing }
|
|
shflags := 0;
|
|
if ((filemode and fmshareExclusive) = fmshareExclusive) then
|
|
{ no sharing }
|
|
else
|
|
if (filemode = fmShareCompat) or ((filemode and fmshareDenyWrite) = fmshareDenyWrite) then
|
|
shflags := FILE_SHARE_READ
|
|
else
|
|
if ((filemode and fmshareDenyRead) = fmshareDenyRead) then
|
|
shflags := FILE_SHARE_WRITE
|
|
else
|
|
if ((filemode and fmshareDenyNone) = fmshareDenyNone) then
|
|
shflags := FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE;
|
|
{ convert filemode to filerec modes }
|
|
case (flags and 3) of
|
|
0 : begin
|
|
filerec(f).mode:=fminput;
|
|
oflags := GENERIC_READ;
|
|
end;
|
|
1 : begin
|
|
filerec(f).mode:=fmoutput;
|
|
oflags := GENERIC_WRITE;
|
|
end;
|
|
2 : begin
|
|
filerec(f).mode:=fminout;
|
|
oflags := GENERIC_WRITE or GENERIC_READ;
|
|
end;
|
|
end;
|
|
oflags := oflags or NT_SYNCHRONIZE or FILE_READ_ATTRIBUTES;
|
|
{ create it ? }
|
|
if (flags and $1000) <> 0 then
|
|
cd := FILE_OVERWRITE_IF
|
|
{ or Append/Open ? }
|
|
else
|
|
cd := FILE_OPEN;
|
|
{ empty name is special }
|
|
{ console i/o not supported yet }
|
|
if p[0]=#0 then
|
|
begin
|
|
case FileRec(f).mode of
|
|
fminput :
|
|
FileRec(f).Handle:=StdInputHandle;
|
|
fminout, { this is set by rewrite }
|
|
fmoutput :
|
|
FileRec(f).Handle:=StdOutputHandle;
|
|
fmappend :
|
|
begin
|
|
FileRec(f).Handle:=StdOutputHandle;
|
|
FileRec(f).mode:=fmoutput; {fool fmappend}
|
|
end;
|
|
end;
|
|
exit;
|
|
end;
|
|
|
|
SysPCharToNtStr(ntstr, p, 0);
|
|
|
|
SysInitializeObjectAttributes(objattr, @ntstr, OBJ_INHERIT, 0, Nil);
|
|
|
|
res := NtCreateFile(@filerec(f).handle, oflags, @objattr, @iostatus, Nil,
|
|
FILE_ATTRIBUTE_NORMAL, shflags, cd,
|
|
FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, Nil, 0);
|
|
|
|
SysFreeNtStr(ntstr);
|
|
|
|
{ append mode }
|
|
if (flags and $100 <> 0) and (res >= 0) then begin
|
|
do_seekend(filerec(f).handle);
|
|
filerec(f).mode := fmoutput; {fool fmappend}
|
|
end;
|
|
|
|
{ get errors }
|
|
if res < 0 then begin
|
|
errno := res;
|
|
Errno2InoutRes;
|
|
end;
|
|
end;
|