123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- {
- 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;
|