1{ 2 3 This file is part of the Free Pascal run time library. 4 Copyright (c) 2010 by Sven Barth 5 member of the Free Pascal development team 6 7 Sysutils unit for NativeNT 8 9 See the file COPYING.FPC, included in this distribution, 10 for details about the copyright. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 15 16 **********************************************************************} 17unit sysutils; 18interface 19 20{$MODE objfpc} 21{$MODESWITCH OUT} 22{ force ansistrings } 23{$H+} 24{$modeswitch typehelpers} 25{$modeswitch advancedrecords} 26 27uses 28 ndk; 29 30{$DEFINE HAS_SLEEP} 31{$DEFINE HAS_CREATEGUID} 32 33type 34 TNativeNTFindData = record 35 SearchSpec: UnicodeString; 36 NamePos: LongInt; 37 Handle: THandle; 38 IsDirObj: Boolean; 39 SearchAttr: LongInt; 40 Context: ULONG; 41 LastRes: NTSTATUS; 42 end; 43 44{ used OS file system APIs use ansistring } 45{$define SYSUTILS_HAS_UNICODESTR_FILEUTIL_IMPL} 46{ OS has an ansistring/single byte environment variable API (actually it's 47 unicodestring, but that's not yet implemented) } 48{$define SYSUTILS_HAS_ANSISTR_ENVVAR_IMPL} 49 50{ Include platform independent interface part } 51{$i sysutilh.inc} 52 53implementation 54 55 uses 56 sysconst, ndkutils; 57 58{$DEFINE FPC_NOGENERICANSIROUTINES} 59 60{ Include platform independent implementation part } 61{$i sysutils.inc} 62 63{**************************************************************************** 64 File Functions 65****************************************************************************} 66 67function FileOpen(const FileName : UnicodeString; Mode : Integer) : THandle; 68const 69 AccessMode: array[0..2] of ACCESS_MASK = ( 70 GENERIC_READ, 71 GENERIC_WRITE, 72 GENERIC_READ or GENERIC_WRITE); 73 ShareMode: array[0..4] of ULONG = ( 74 0, 75 0, 76 FILE_SHARE_READ, 77 FILE_SHARE_WRITE, 78 FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE); 79var 80 ntstr: UNICODE_STRING; 81 objattr: OBJECT_ATTRIBUTES; 82 iostatus: IO_STATUS_BLOCK; 83begin 84 UnicodeStrToNtStr(FileName, ntstr); 85 InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil); 86 NtCreateFile(@Result, AccessMode[Mode and 3] or NT_SYNCHRONIZE, @objattr, 87 @iostatus, Nil, FILE_ATTRIBUTE_NORMAL, ShareMode[(Mode and $F0) shr 4], 88 FILE_OPEN, FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, Nil, 0); 89 FreeNtStr(ntstr); 90end; 91 92 93function FileCreate(const FileName : UnicodeString) : THandle; 94begin 95 FileCreate := FileCreate(FileName, fmShareDenyNone, 0); 96end; 97 98 99function FileCreate(const FileName : UnicodeString; Rights: longint) : THandle; 100begin 101 FileCreate := FileCreate(FileName, fmShareDenyNone, Rights); 102end; 103 104 105function FileCreate(const FileName : UnicodeString; ShareMode : longint; Rights: longint) : THandle; 106const 107 ShareModeFlags: array[0..4] of ULONG = ( 108 0, 109 0, 110 FILE_SHARE_READ, 111 FILE_SHARE_WRITE, 112 FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE); 113var 114 ntstr: UNICODE_STRING; 115 objattr: OBJECT_ATTRIBUTES; 116 iostatus: IO_STATUS_BLOCK; 117 res: NTSTATUS; 118begin 119 UnicodeStrToNtStr(FileName, ntstr); 120 InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil); 121 NtCreateFile(@Result, GENERIC_READ or GENERIC_WRITE or NT_SYNCHRONIZE, 122 @objattr, @iostatus, Nil, FILE_ATTRIBUTE_NORMAL, 123 ShareModeFlags[(ShareMode and $F0) shr 4], FILE_OVERWRITE_IF, 124 FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, Nil, 0); 125 FreeNtStr(ntstr); 126end; 127 128 129function FileRead(Handle : THandle; out Buffer; Count : longint) : Longint; 130var 131 iostatus: IO_STATUS_BLOCK; 132 res: NTSTATUS; 133begin 134 res := NtReadFile(Handle, 0, Nil, Nil, @iostatus, @Buffer, Count, Nil, Nil); 135 136 if res = STATUS_PENDING then begin 137 res := NtWaitForSingleObject(Handle, False, Nil); 138 if NT_SUCCESS(res) then 139 res := iostatus.union1.Status; 140 end; 141 142 if NT_SUCCESS(res) then 143 Result := LongInt(iostatus.Information) 144 else 145 Result := -1; 146end; 147 148 149function FileWrite(Handle : THandle; const Buffer; Count : Longint) : Longint; 150var 151 iostatus: IO_STATUS_BLOCK; 152 res: NTSTATUS; 153begin 154 res := NtWriteFile(Handle, 0, Nil, Nil, @iostatus, @Buffer, Count, Nil, 155 Nil); 156 157 if res = STATUS_PENDING then begin 158 res := NtWaitForSingleObject(Handle, False, Nil); 159 if NT_SUCCESS(res) then 160 res := iostatus.union1.Status; 161 end; 162 163 if NT_SUCCESS(res) then 164 Result := LongInt(iostatus.Information) 165 else 166 Result := -1; 167end; 168 169 170function FileSeek(Handle : THandle;FOffset,Origin : Longint) : Longint; 171begin 172 Result := longint(FileSeek(Handle, Int64(FOffset), Origin)); 173end; 174 175 176function FileSeek(Handle : THandle; FOffset: Int64; Origin: Longint) : Int64; 177const 178 ErrorCode = $FFFFFFFFFFFFFFFF; 179var 180 position: FILE_POSITION_INFORMATION; 181 standard: FILE_STANDARD_INFORMATION; 182 iostatus: IO_STATUS_BLOCK; 183 res: NTSTATUS; 184begin 185 { determine the new position } 186 case Origin of 187 fsFromBeginning: 188 position.CurrentByteOffset.QuadPart := FOffset; 189 fsFromCurrent: begin 190 res := NtQueryInformationFile(Handle, @iostatus, @position, 191 SizeOf(FILE_POSITION_INFORMATION), FilePositionInformation); 192 if res < 0 then begin 193 Result := ErrorCode; 194 Exit; 195 end; 196 position.CurrentByteOffset.QuadPart := 197 position.CurrentByteOffset.QuadPart + FOffset; 198 end; 199 fsFromEnd: begin 200 res := NtQueryInformationFile(Handle, @iostatus, @standard, 201 SizeOf(FILE_STANDARD_INFORMATION), FileStandardInformation); 202 if res < 0 then begin 203 Result := ErrorCode; 204 Exit; 205 end; 206 position.CurrentByteOffset.QuadPart := standard.EndOfFile.QuadPart + 207 FOffset; 208 end; 209 else begin 210 Result := ErrorCode; 211 Exit; 212 end; 213 end; 214 215 { set the new position } 216 res := NtSetInformationFile(Handle, @iostatus, @position, 217 SizeOf(FILE_POSITION_INFORMATION), FilePositionInformation); 218 if res < 0 then 219 Result := ErrorCode 220 else 221 Result := position.CurrentByteOffset.QuadPart; 222end; 223 224 225procedure FileClose(Handle : THandle); 226begin 227 NtClose(Handle); 228end; 229 230 231function FileTruncate(Handle : THandle;Size: Int64) : boolean; 232var 233 endoffileinfo: FILE_END_OF_FILE_INFORMATION; 234 allocinfo: FILE_ALLOCATION_INFORMATION; 235 iostatus: IO_STATUS_BLOCK; 236 res: NTSTATUS; 237begin 238 // based on ReactOS' SetEndOfFile 239 endoffileinfo.EndOfFile.QuadPart := Size; 240 res := NtSetInformationFile(Handle, @iostatus, @endoffileinfo, 241 SizeOf(FILE_END_OF_FILE_INFORMATION), FileEndOfFileInformation); 242 if NT_SUCCESS(res) then begin 243 allocinfo.AllocationSize.QuadPart := Size; 244 res := NtSetInformationFile(handle, @iostatus, @allocinfo, 245 SizeOf(FILE_ALLOCATION_INFORMATION), FileAllocationInformation); 246 Result := NT_SUCCESS(res); 247 end else 248 Result := False; 249end; 250 251function NTToDosTime(const NtTime: LARGE_INTEGER): LongInt; 252var 253 userdata: PKUSER_SHARED_DATA; 254 local, bias: LARGE_INTEGER; 255 fields: TIME_FIELDS; 256 zs: LongInt; 257begin 258 userdata := SharedUserData; 259 repeat 260 bias.u.HighPart := userdata^.TimeZoneBias.High1Time; 261 bias.u.LowPart := userdata^.TimeZoneBias.LowPart; 262 until bias.u.HighPart = userdata^.TimeZoneBias.High2Time; 263 264 local.QuadPart := NtTime.QuadPart - bias.QuadPart; 265 266 RtlTimeToTimeFields(@local, @fields); 267 268 { from objpas\datutil.inc\DateTimeToDosDateTime } 269 Result := - 1980; 270 Result := Result + fields.Year and 127; 271 Result := Result shl 4; 272 Result := Result + fields.Month; 273 Result := Result shl 5; 274 Result := Result + fields.Day; 275 Result := Result shl 16; 276 zs := fields.Hour; 277 zs := zs shl 6; 278 zs := zs + fields.Minute; 279 zs := zs shl 5; 280 zs := zs + fields.Second div 2; 281 Result := Result + (zs and $ffff); 282end; 283 284function DosToNtTime(aDTime: LongInt; var aNtTime: LARGE_INTEGER): Boolean; 285var 286 fields: TIME_FIELDS; 287 local, bias: LARGE_INTEGER; 288 userdata: PKUSER_SHARED_DATA; 289begin 290 { from objpas\datutil.inc\DosDateTimeToDateTime } 291 fields.Second := (aDTime and 31) * 2; 292 aDTime := aDTime shr 5; 293 fields.Minute := aDTime and 63; 294 aDTime := aDTime shr 6; 295 fields.Hour := aDTime and 31; 296 aDTime := aDTime shr 5; 297 fields.Day := aDTime and 31; 298 aDTime := aDTime shr 5; 299 fields.Month := aDTime and 15; 300 aDTime := aDTime shr 4; 301 fields.Year := aDTime + 1980; 302 303 Result := RtlTimeFieldsToTime(@fields, @local); 304 if not Result then 305 Exit; 306 307 userdata := SharedUserData; 308 repeat 309 bias.u.HighPart := userdata^.TimeZoneBias.High1Time; 310 bias.u.LowPart := userdata^.TimeZoneBias.LowPart; 311 until bias.u.HighPart = userdata^.TimeZoneBias.High2Time; 312 313 aNtTime.QuadPart := local.QuadPart + bias.QuadPart; 314end; 315 316function FileAge(const FileName: UnicodeString): Longint; 317begin 318 { TODO } 319 Result := -1; 320end; 321 322 323function FileGetSymLinkTarget(const FileName: UnicodeString; out SymLinkRec: TUnicodeSymLinkRec): Boolean; 324begin 325 Result := False; 326end; 327 328 329function FileExists(const FileName: UnicodeString; FollowLink : Boolean): Boolean; 330var 331 ntstr: UNICODE_STRING; 332 objattr: OBJECT_ATTRIBUTES; 333 res: NTSTATUS; 334 iostatus: IO_STATUS_BLOCK; 335 h: THandle; 336begin 337 UnicodeStrToNtStr(FileName, ntstr); 338 InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil); 339 res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr, 340 @iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE, 341 FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT); 342 Result := NT_SUCCESS(res); 343 344 if Result then 345 NtClose(h); 346 FreeNtStr(ntstr); 347end; 348 349 350function DirectoryExists(const Directory : UnicodeString; FollowLink : Boolean) : Boolean; 351var 352 ntstr: UNICODE_STRING; 353 objattr: OBJECT_ATTRIBUTES; 354 res: NTSTATUS; 355 iostatus: IO_STATUS_BLOCK; 356 h: THandle; 357begin 358 UnicodeStrToNtStr(Directory, ntstr); 359 InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil); 360 361 { first test wether this is a object directory } 362 res := NtOpenDirectoryObject(@h, DIRECTORY_QUERY, @objattr); 363 if NT_SUCCESS(res) then 364 Result := True 365 else begin 366 if res = STATUS_OBJECT_TYPE_MISMATCH then begin 367 { this is a file object! } 368 res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr, 369 @iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE, 370 FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT); 371 Result := NT_SUCCESS(res); 372 end else 373 Result := False; 374 end; 375 376 if Result then 377 NtClose(h); 378 FreeNtStr(ntstr); 379end; 380 381{ copied from rtl/unix/sysutils.pp and adapted to UTF-16 } 382Function FNMatch(const Pattern,Name:UnicodeString):Boolean; 383Var 384 LenPat,LenName : longint; 385 386 function NameUtf16CodePointLen(index: longint): longint; 387 begin 388 { see https://en.wikipedia.org/wiki/UTF-16#Description for details } 389 Result:=1; 390 { valid surrogate pair? } 391 if (Name[index]>=#$D800) and 392 (Name[index]<=#$DBFF) then 393 begin 394 if (index+1<=LenName) and 395 (Name[index+1]>=#$DC00) and 396 (Name[index+1]<=#$DFFF) then 397 inc(Result) 398 else 399 exit; 400 end; 401 { combining diacritics? 402 1) U+0300 - U+036F 403 2) U+1DC0 - U+1DFF 404 3) U+20D0 - U+20FF 405 4) U+FE20 - U+FE2F 406 } 407 while (index+Result+1<=LenName) and 408 ((word(ord(Name[index+Result+1])-$0300) <= word($036F-$0300)) or 409 (word(ord(Name[index+Result+1])-$1DC0) <= word($1DFF-$1DC0)) or 410 (word(ord(Name[index+Result+1])-$20D0) <= word($20FF-$20D0)) or 411 (word(ord(Name[index+Result+1])-$FE20) <= word($FE2F-$FE20))) do 412 begin 413 inc(Result) 414 end; 415 end; 416 417 procedure GoToLastByteOfUtf16CodePoint(var j: longint); 418 begin 419 { Take one less, because we have to stop at the last word of the sequence. 420 } 421 inc(j,NameUtf16CodePointLen(j)-1); 422 end; 423 424 { input: 425 i: current position in pattern (start of utf-16 code point) 426 j: current position in name (start of utf-16 code point) 427 update_i_j: should i and j be changed by the routine or not 428 429 output: 430 i: if update_i_j, then position of last matching part of code point in 431 pattern, or first non-matching code point in pattern. Otherwise the 432 same value as on input. 433 j: if update_i_j, then position of last matching part of code point in 434 name, or first non-matching code point in name. Otherwise the 435 same value as on input. 436 result: true if match, false if no match 437 } 438 function CompareUtf16CodePoint(var i,j: longint; update_i_j: boolean): Boolean; 439 var 440 words, 441 new_i, 442 new_j: longint; 443 begin 444 words:=NameUtf16CodePointLen(j); 445 new_i:=i; 446 new_j:=j; 447 { ensure that a part of an UTF-8 codepoint isn't interpreted 448 as '*' or '?' } 449 repeat 450 dec(words); 451 Result:= 452 (new_j<=LenName) and 453 (new_i<=LenPat) and 454 (Pattern[new_i]=Name[new_j]); 455 inc(new_i); 456 inc(new_j); 457 until not(Result) or 458 (words=0); 459 if update_i_j then 460 begin 461 i:=new_i; 462 j:=new_j; 463 end; 464 end; 465 466 467 Function DoFNMatch(i,j:longint):Boolean; 468 Var 469 Found : boolean; 470 Begin 471 Found:=true; 472 While Found and (i<=LenPat) Do 473 Begin 474 Case Pattern[i] of 475 '?' : 476 begin 477 Found:=(j<=LenName); 478 GoToLastByteOfUtf16CodePoint(j); 479 end; 480 '*' : Begin 481 {find the next character in pattern, different of ? and *} 482 while Found do 483 begin 484 inc(i); 485 if i>LenPat then 486 Break; 487 case Pattern[i] of 488 '*' : ; 489 '?' : begin 490 if j>LenName then 491 begin 492 DoFNMatch:=false; 493 Exit; 494 end; 495 GoToLastByteOfUtf16CodePoint(j); 496 inc(j); 497 end; 498 else 499 Found:=false; 500 end; 501 end; 502 Assert((i>LenPat) or ( (Pattern[i]<>'*') and (Pattern[i]<>'?') )); 503 { Now, find in name the character which i points to, if the * or 504 ? wasn't the last character in the pattern, else, use up all 505 the chars in name } 506 Found:=false; 507 if (i<=LenPat) then 508 begin 509 repeat 510 {find a letter (not only first !) which maches pattern[i]} 511 while (j<=LenName) and 512 ((name[j]<>pattern[i]) or 513 not CompareUtf16CodePoint(i,j,false)) do 514 begin 515 GoToLastByteOfUtf16CodePoint(j); 516 inc(j); 517 end; 518 if (j<LenName) then 519 begin 520 { while positions i/j have already been checked, we have to 521 ensure that we don't split a code point } 522 if DoFnMatch(i,j) then 523 begin 524 i:=LenPat; 525 j:=LenName;{we can stop} 526 Found:=true; 527 Break; 528 end 529 { We didn't find one, need to look further } 530 else 531 begin 532 GoToLastByteOfUtf16CodePoint(j); 533 inc(j); 534 end; 535 end 536 else if j=LenName then 537 begin 538 Found:=true; 539 Break; 540 end; 541 { This 'until' condition must be j>LenName, not j>=LenName. 542 That's because when we 'need to look further' and 543 j = LenName then loop must not terminate. } 544 until (j>LenName); 545 end 546 else 547 begin 548 j:=LenName;{we can stop} 549 Found:=true; 550 end; 551 end; 552 #$D800..#$DBFF: 553 begin 554 { ensure that a part of an UTF-16 codepoint isn't matched with 555 '*' or '?' } 556 Found:=CompareUtf16CodePoint(i,j,true); 557 { at this point, either Found is false (and we'll stop), or 558 both pattern[i] and name[j] are the end of the current code 559 point and equal } 560 end 561 else {not a wildcard character in pattern} 562 Found:=(j<=LenName) and (pattern[i]=name[j]); 563 end; 564 inc(i); 565 inc(j); 566 end; 567 DoFnMatch:=Found and (j>LenName); 568 end; 569 570Begin {start FNMatch} 571 LenPat:=Length(Pattern); 572 LenName:=Length(Name); 573 FNMatch:=DoFNMatch(1,1); 574End; 575 576 577function FindGetFileInfo(const s: UnicodeString; var f: TAbstractSearchRec; var Name: UnicodeString): Boolean; 578var 579 ntstr: UNICODE_STRING; 580 objattr: OBJECT_ATTRIBUTES; 581 res: NTSTATUS; 582 h: THandle; 583 iostatus: IO_STATUS_BLOCK; 584 attr: LongInt; 585 filename: UnicodeString; 586 isfileobj: Boolean; 587 objinfo: OBJECT_BASIC_INFORMATION; 588 fileinfo: FILE_BASIC_INFORMATION; 589 time: LongInt; 590begin 591 UnicodeStrToNtStr(s, ntstr); 592 InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil); 593 594 filename := ExtractFileName(s); 595 596 { TODO : handle symlinks } 597{ If Assigned(F.FindHandle) and ((((PUnixFindData(f.FindHandle)^.searchattr)) and faSymlink) > 0) then 598 FindGetFileInfo:=(fplstat(pointer(s),st)=0) 599 else 600 FindGetFileInfo:=(fpstat(pointer(s),st)=0);} 601 602 attr := 0; 603 Result := False; 604 605 if (faDirectory and f.FindData.SearchAttr <> 0) and 606 ((filename = '.') or (filename = '..')) then begin 607 attr := faDirectory; 608 res := STATUS_SUCCESS; 609 end else 610 res := STATUS_INVALID_PARAMETER; 611 612 isfileobj := False; 613 614 if not NT_SUCCESS(res) then begin 615 { first check whether it's a directory } 616 res := NtOpenDirectoryObject(@h, DIRECTORY_QUERY, @objattr); 617 if not NT_SUCCESS(res) then 618 if res = STATUS_OBJECT_TYPE_MISMATCH then begin 619 res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr, 620 @iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE, 621 FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT); 622 isfileobj := NT_SUCCESS(res); 623 end; 624 625 if NT_SUCCESS(res) then 626 attr := faDirectory; 627 end; 628 629 if not NT_SUCCESS(res) then begin 630 { first try whether we have a file object } 631 res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr, 632 @iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE, 633 FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT); 634 isfileobj := NT_SUCCESS(res); 635 if res = STATUS_OBJECT_TYPE_MISMATCH then begin 636 { is this an object? } 637 res := NtOpenFile(@h, FILE_READ_ATTRIBUTES or NT_SYNCHRONIZE, @objattr, 638 @iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE, 639 FILE_SYNCHRONOUS_IO_NONALERT); 640 if (res = STATUS_OBJECT_TYPE_MISMATCH) 641 and (f.FindData.SearchAttr and faSysFile <> 0) then begin 642 { this is some other system file like an event or port, so we can only 643 provide it's name } 644 res := STATUS_SUCCESS; 645 attr := faSysFile; 646 end; 647 end; 648 end; 649 650 FreeNtStr(ntstr); 651 652 if not NT_SUCCESS(res) then 653 Exit; 654 655 time := 0; 656 657 if isfileobj then begin 658 res := NtQueryInformationFile(h, @iostatus, @fileinfo, SizeOf(fileinfo), 659 FileBasicInformation); 660 if NT_SUCCESS(res) then begin 661 time := NtToDosTime(fileinfo.LastWriteTime); 662 { copy file attributes? } 663 end; 664 end else begin 665 res := NtQueryObject(h, ObjectBasicInformation, @objinfo, SizeOf(objinfo), 666 Nil); 667 if NT_SUCCESS(res) then begin 668 time := NtToDosTime(objinfo.CreateTime); 669 { what about attributes? } 670 end; 671 end; 672 673 if (attr and not f.FindData.SearchAttr) = 0 then begin 674 Name := filename; 675 f.Attr := attr; 676 f.Size := 0; 677{$ifndef FPUNONE} 678 if time = 0 then 679 { for now we use "Now" as a fall back; ideally this should be the system 680 start time } 681 f.Time := DateTimeToFileDate(Now) 682 else 683 f.Time := time; 684{$endif} 685 Result := True; 686 end else 687 Result := False; 688 689 NtClose(h); 690end; 691 692 693Procedure InternalFindClose (var Handle: THandle; var FindData: TFindData); 694begin 695 if FindData.Handle <> 0 then 696 begin 697 NtClose(FindData.Handle); 698 FindData.Handle:=0; 699 end; 700end; 701 702 703Function InternalFindNext (Var Rslt : TAbstractSearchRec; var Name: UnicodeString) : Longint; 704{ 705 re-opens dir if not already in array and calls FindGetFileInfo 706} 707Var 708 DirName : UnicodeString; 709 FName, 710 SName : UnicodeString; 711 Found, 712 Finished : boolean; 713 ntstr: UNICODE_STRING; 714 objattr: OBJECT_ATTRIBUTES; 715 buf: array of WideChar; 716 len: LongWord; 717 res: NTSTATUS; 718 i: LongInt; 719 dirinfo: POBJECT_DIRECTORY_INFORMATION; 720 filedirinfo: PFILE_DIRECTORY_INFORMATION; 721 pc: PChar; 722 filename: UnicodeString; 723 iostatus: IO_STATUS_BLOCK; 724begin 725 { TODO : relative directories } 726 Result := -1; 727 { SearchSpec='' means that there were no wild cards, so only one file to 728 find. 729 } 730 if Rslt.FindData.SearchSpec = '' then 731 Exit; 732 { relative directories not supported for now } 733 if Rslt.FindData.NamePos = 0 then 734 Exit; 735 736 if Rslt.FindData.Handle = 0 then begin 737 if Rslt.FindData.NamePos > 1 then 738 filename := Copy(Rslt.FindData.SearchSpec, 1, Rslt.FindData.NamePos - 1) 739 else 740 if Rslt.FindData.NamePos = 1 then 741 filename := Copy(Rslt.FindData.SearchSpec, 1, 1) 742 else 743 filename := Rslt.FindData.SearchSpec; 744 UnicodeStrToNtStr(filename, ntstr); 745 InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil); 746 747 res := NtOpenDirectoryObject(@Rslt.FindData.Handle, 748 DIRECTORY_QUERY or DIRECTORY_TRAVERSE, @objattr); 749 if not NT_SUCCESS(res) then begin 750 if res = STATUS_OBJECT_TYPE_MISMATCH then 751 res := NtOpenFile(@Rslt.FindData.Handle, 752 FILE_LIST_DIRECTORY or NT_SYNCHRONIZE, @objattr, 753 @iostatus, FILE_SHARE_READ or FILE_SHARE_WRITE, 754 FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT); 755 end else 756 Rslt.FindData.IsDirObj := True; 757 758 FreeNTStr(ntstr); 759 760 if not NT_SUCCESS(res) then 761 Exit; 762 end; 763{ if (NTFindData^.SearchType = 0) and 764 (NTFindData^.Dirptr = Nil) then 765 begin 766 If NTFindData^.NamePos = 0 Then 767 DirName:='./' 768 Else 769 DirName:=Copy(NTFindData^.SearchSpec,1,NTFindData^.NamePos); 770 NTFindData^.DirPtr := fpopendir(Pchar(pointer(DirName))); 771 end;} 772 SName := Copy(Rslt.FindData.SearchSpec, Rslt.FindData.NamePos + 1, 773 Length(Rslt.FindData.SearchSpec)); 774 Found := False; 775 Finished := not NT_SUCCESS(Rslt.FindData.LastRes) 776 or (Rslt.FindData.LastRes = STATUS_NO_MORE_ENTRIES); 777 SetLength(buf, 200); 778 dirinfo := @buf[0]; 779 filedirinfo := @buf[0]; 780 while not Finished do begin 781 if Rslt.FindData.IsDirObj then 782 res := NtQueryDirectoryObject(Rslt.FindData.Handle, @buf[0], 783 Length(buf) * SizeOf(buf[0]), True, False, 784 @Rslt.FindData.Context, @len) 785 else 786 res := NtQueryDirectoryFile(Rslt.FindData.Handle, 0, Nil, Nil, @iostatus, 787 @buf[0], Length(buf) * SizeOf(buf[0]), FileDirectoryInformation, 788 True, Nil, False); 789 if Rslt.FindData.IsDirObj then begin 790 Finished := (res = STATUS_NO_MORE_ENTRIES) 791 or (res = STATUS_NO_MORE_FILES) 792 or not NT_SUCCESS(res); 793 Rslt.FindData.LastRes := res; 794 if dirinfo^.Name.Length > 0 then begin 795 SetLength(FName, dirinfo^.Name.Length div 2); 796 move(dirinfo^.Name.Buffer[0],FName[1],dirinfo^.Name.Length); 797{$ifdef debug_findnext} 798 Write(FName, ' ('); 799 for i := 0 to dirinfo^.TypeName.Length div 2 - 1 do 800 if dirinfo^.TypeName.Buffer[i] < #256 then 801 Write(AnsiChar(Byte(dirinfo^.TypeName.Buffer[i]))) 802 else 803 Write('?'); 804 Writeln(')'); 805{$endif debug_findnext} 806 end else 807 FName := ''; 808 end else begin 809 SetLength(FName, filedirinfo^.FileNameLength div 2); 810 move(filedirinfo^.FileName[0],FName[1],filedirinfo^.FileNameLength); 811 end; 812 if FName = '' then 813 Finished := True 814 else begin 815 if FNMatch(SName, FName) then begin 816 Found := FindGetFileInfo(Copy(Rslt.FindData.SearchSpec, 1, 817 Rslt.FindData.NamePos) + FName, Rslt, Name); 818 if Found then begin 819 Result := 0; 820 Exit; 821 end; 822 end; 823 end; 824 end; 825end; 826 827 828Function InternalFindFirst (Const Path : UnicodeString; Attr : Longint; out Rslt : TAbstractSearchRec; var Name : UnicodeString) : Longint; 829{ 830 opens dir and calls FindNext if needed. 831} 832Begin 833 Result := -1; 834 if Path = '' then 835 Exit; 836 Rslt.FindData.SearchAttr := Attr; 837 {Wildcards?} 838 if (Pos('?', Path) = 0) and (Pos('*', Path) = 0) then begin 839 if FindGetFileInfo(Path, Rslt, Name) then 840 Result := 0; 841 end else begin 842 {Create Info} 843 Rslt.FindData.SearchSpec := Path; 844 Rslt.FindData.NamePos := Length(Rslt.FindData.SearchSpec); 845 while (Rslt.FindData.NamePos > 0) 846 and (Rslt.FindData.SearchSpec[Rslt.FindData.NamePos] <> DirectorySeparator) 847 do 848 Dec(Rslt.FindData.NamePos); 849 Result := InternalFindNext(Rslt,Name); 850 end; 851 if Result <> 0 then 852 InternalFindClose(Rslt.FindHandle,Rslt.FindData); 853end; 854 855 856function FileGetDate(Handle: THandle): Longint; 857var 858 res: NTSTATUS; 859 basic: FILE_BASIC_INFORMATION; 860 iostatus: IO_STATUS_BLOCK; 861begin 862 res := NtQueryInformationFile(Handle, @iostatus, @basic, 863 SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation); 864 if NT_SUCCESS(res) then 865 Result := NtToDosTime(basic.LastWriteTime) 866 else 867 Result := -1; 868end; 869 870 871function FileSetDate(Handle: THandle;Age: Longint): Longint; 872var 873 res: NTSTATUS; 874 basic: FILE_BASIC_INFORMATION; 875 iostatus: IO_STATUS_BLOCK; 876begin 877 res := NtQueryInformationFile(Handle, @iostatus, @basic, 878 SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation); 879 if NT_SUCCESS(res) then begin 880 if not DosToNtTime(Age, basic.LastWriteTime) then begin 881 Result := -1; 882 Exit; 883 end; 884 885 res := NtSetInformationFile(Handle, @iostatus, @basic, 886 SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation); 887 if NT_SUCCESS(res) then 888 Result := 0 889 else 890 Result := res; 891 end else 892 Result := res; 893end; 894 895 896function FileGetAttr(const FileName: UnicodeString): Longint; 897var 898 objattr: OBJECT_ATTRIBUTES; 899 info: FILE_NETWORK_OPEN_INFORMATION; 900 res: NTSTATUS; 901 ntstr: UNICODE_STRING; 902begin 903 UnicodeStrToNtStr(FileName, ntstr); 904 InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil); 905 906 res := NtQueryFullAttributesFile(@objattr, @info); 907 if NT_SUCCESS(res) then 908 Result := info.FileAttributes 909 else 910 Result := 0; 911 912 FreeNtStr(ntstr); 913end; 914 915 916function FileSetAttr(const Filename: UnicodeString; Attr: LongInt): Longint; 917var 918 h: THandle; 919 objattr: OBJECT_ATTRIBUTES; 920 ntstr: UNICODE_STRING; 921 basic: FILE_BASIC_INFORMATION; 922 res: NTSTATUS; 923 iostatus: IO_STATUS_BLOCK; 924begin 925 UnicodeStrToNtStr(Filename, ntstr); 926 InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil); 927 res := NtOpenFile(@h, 928 NT_SYNCHRONIZE or FILE_READ_ATTRIBUTES or FILE_WRITE_ATTRIBUTES, 929 @objattr, @iostatus, 930 FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE, 931 FILE_SYNCHRONOUS_IO_NONALERT); 932 933 FreeNtStr(ntstr); 934 935 if NT_SUCCESS(res) then begin 936 res := NtQueryInformationFile(h, @iostatus, @basic, 937 SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation); 938 939 if NT_SUCCESS(res) then begin 940 basic.FileAttributes := Attr; 941 Result := NtSetInformationFile(h, @iostatus, @basic, 942 SizeOf(FILE_BASIC_INFORMATION), FileBasicInformation); 943 end; 944 945 NtClose(h); 946 end else 947 Result := res; 948end; 949 950 951function DeleteFile(const FileName: UnicodeString): Boolean; 952var 953 h: THandle; 954 objattr: OBJECT_ATTRIBUTES; 955 ntstr: UNICODE_STRING; 956 dispinfo: FILE_DISPOSITION_INFORMATION; 957 res: NTSTATUS; 958 iostatus: IO_STATUS_BLOCK; 959begin 960 UnicodeStrToNtStr(Filename, ntstr); 961 InitializeObjectAttributes(objattr, @ntstr, 0, 0, Nil); 962 res := NtOpenFile(@h, NT_DELETE, @objattr, @iostatus, 963 FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE, 964 FILE_NON_DIRECTORY_FILE); 965 966 FreeNtStr(ntstr); 967 968 if NT_SUCCESS(res) then begin 969 dispinfo.DeleteFile := True; 970 971 res := NtSetInformationFile(h, @iostatus, @dispinfo, 972 SizeOf(FILE_DISPOSITION_INFORMATION), FileDispositionInformation); 973 974 Result := NT_SUCCESS(res); 975 976 NtClose(h); 977 end else 978 Result := False; 979end; 980 981 982function RenameFile(const OldName, NewName: UnicodeString): Boolean; 983var 984 h: THandle; 985 objattr: OBJECT_ATTRIBUTES; 986 iostatus: IO_STATUS_BLOCK; 987 dest, src: UNICODE_STRING; 988 renameinfo: PFILE_RENAME_INFORMATION; 989 res: LongInt; 990begin 991 { check whether the destination exists first } 992 UnicodeStrToNtStr(NewName, dest); 993 InitializeObjectAttributes(objattr, @dest, 0, 0, Nil); 994 995 res := NtCreateFile(@h, 0, @objattr, @iostatus, Nil, 0, 996 FILE_SHARE_READ or FILE_SHARE_WRITE, FILE_OPEN, 997 FILE_NON_DIRECTORY_FILE, Nil, 0); 998 if NT_SUCCESS(res) then begin 999 { destination already exists => error } 1000 NtClose(h); 1001 Result := False; 1002 end else begin 1003 UnicodeStrToNtStr(OldName, src); 1004 InitializeObjectAttributes(objattr, @src, 0, 0, Nil); 1005 1006 res := NtCreateFile(@h, 1007 GENERIC_ALL or NT_SYNCHRONIZE or FILE_READ_ATTRIBUTES, 1008 @objattr, @iostatus, Nil, 0, FILE_SHARE_READ or FILE_SHARE_WRITE, 1009 FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT or FILE_OPEN_REMOTE_INSTANCE 1010 or FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, Nil, 1011 0); 1012 1013 if NT_SUCCESS(res) then begin 1014 renameinfo := GetMem(SizeOf(FILE_RENAME_INFORMATION) + dest.Length); 1015 with renameinfo^ do begin 1016 ReplaceIfExists := False; 1017 RootDirectory := 0; 1018 FileNameLength := dest.Length; 1019 Move(dest.Buffer^, renameinfo^.FileName, dest.Length); 1020 end; 1021 1022 res := NtSetInformationFile(h, @iostatus, renameinfo, 1023 SizeOf(FILE_RENAME_INFORMATION) + dest.Length, 1024 FileRenameInformation); 1025 if not NT_SUCCESS(res) then begin 1026 { this could happen if src and destination reside on different drives, 1027 so we need to copy the file manually } 1028 {$message warning 'RenameFile: Implement file copy!'} 1029 Result := False; 1030 end else 1031 Result := True; 1032 1033 NtClose(h); 1034 end else 1035 Result := False; 1036 1037 FreeNtStr(src); 1038 end; 1039 1040 FreeNtStr(dest); 1041end; 1042 1043 1044{**************************************************************************** 1045 Disk Functions 1046****************************************************************************} 1047 1048function diskfree(drive: byte): int64; 1049begin 1050 { here the mount manager needs to be queried } 1051 Result := -1; 1052end; 1053 1054 1055function disksize(drive: byte): int64; 1056begin 1057 { here the mount manager needs to be queried } 1058 Result := -1; 1059end; 1060 1061 1062{**************************************************************************** 1063 Time Functions 1064****************************************************************************} 1065 1066 1067procedure GetLocalTime(var SystemTime: TSystemTime); 1068var 1069 bias, syst: LARGE_INTEGER; 1070 fields: TIME_FIELDS; 1071 userdata: PKUSER_SHARED_DATA; 1072begin 1073 // get UTC time 1074 userdata := SharedUserData; 1075 repeat 1076 syst.u.HighPart := userdata^.SystemTime.High1Time; 1077 syst.u.LowPart := userdata^.SystemTime.LowPart; 1078 until syst.u.HighPart = userdata^.SystemTime.High2Time; 1079 1080 // adjust to local time 1081 repeat 1082 bias.u.HighPart := userdata^.TimeZoneBias.High1Time; 1083 bias.u.LowPart := userdata^.TimeZoneBias.LowPart; 1084 until bias.u.HighPart = userdata^.TimeZoneBias.High2Time; 1085 syst.QuadPart := syst.QuadPart - bias.QuadPart; 1086 1087 RtlTimeToTimeFields(@syst, @fields); 1088 1089 SystemTime.Year := fields.Year; 1090 SystemTime.Month := fields.Month; 1091 SystemTime.Day := fields.Day; 1092 SystemTime.Hour := fields.Hour; 1093 SystemTime.Minute := fields.Minute; 1094 SystemTime.Second := fields.Second; 1095 SystemTime.Millisecond := fields.MilliSeconds; 1096end; 1097 1098 1099{**************************************************************************** 1100 Misc Functions 1101****************************************************************************} 1102 1103procedure sysbeep; 1104begin 1105 { empty } 1106end; 1107 1108procedure InitInternational; 1109begin 1110 InitInternationalGeneric; 1111end; 1112 1113 1114{**************************************************************************** 1115 Target Dependent 1116****************************************************************************} 1117 1118function SysErrorMessage(ErrorCode: Integer): String; 1119begin 1120 Result := 'NT error code: 0x' + IntToHex(ErrorCode, 8); 1121end; 1122 1123{**************************************************************************** 1124 Initialization code 1125****************************************************************************} 1126 1127function wstrlen(p: PWideChar): SizeInt; external name 'FPC_PWIDECHAR_LENGTH'; 1128 1129function GetEnvironmentVariable(const EnvVar: String): String; 1130var 1131 s, upperenvvar : UTF8String; 1132 i : longint; 1133 hp: pwidechar; 1134 len: sizeint; 1135begin 1136 { TODO : test once I know how to execute processes } 1137 Result:=''; 1138 hp:=PPEB(CurrentPEB)^.ProcessParameters^.Environment; 1139 { first convert to UTF-8, then uppercase in order to avoid potential data 1140 loss } 1141 upperenvvar:=EnvVar; 1142 upperenvvar:=UpperCase(upperenvvar); 1143 while hp^<>#0 do 1144 begin 1145 len:=UnicodeToUTF8(Nil, hp, 0); 1146 SetLength(s,len); 1147 UnicodeToUTF8(PChar(s), hp, len); 1148 i:=pos('=',s); 1149 if uppercase(copy(s,1,i-1))=upperenvvar then 1150 begin 1151 { copy() returns a rawbytestring -> will keep UTF-8 encoding } 1152 Result:=copy(s,i+1,length(s)-i); 1153 break; 1154 end; 1155 { next string entry} 1156 hp:=hp+wstrlen(hp)+1; 1157 end; 1158end; 1159 1160function GetEnvironmentVariableCount: Integer; 1161var 1162 hp : pwidechar; 1163begin 1164 Result:=0; 1165 hp:=PPEB(CurrentPEB)^.ProcessParameters^.Environment; 1166 If (Hp<>Nil) then 1167 while hp^<>#0 do 1168 begin 1169 Inc(Result); 1170 hp:=hp+wstrlen(hp)+1; 1171 end; 1172end; 1173 1174function GetEnvironmentString(Index: Integer): {$ifdef FPC_RTL_UNICODE}UnicodeString{$else}AnsiString{$endif}; 1175var 1176 hp : pwidechar; 1177 len: sizeint; 1178begin 1179 Result:=''; 1180 hp:=PPEB(CurrentPEB)^.ProcessParameters^.Environment; 1181 If (Hp<>Nil) then 1182 begin 1183 while (hp^<>#0) and (Index>1) do 1184 begin 1185 Dec(Index); 1186 hp:=hp+wstrlen(hp)+1; 1187 end; 1188 If (hp^<>#0) then 1189 begin 1190{$ifdef FPC_RTL_UNICODE} 1191 Result:=hp; 1192{$else} 1193 len:=UnicodeToUTF8(Nil, hp, 0); 1194 SetLength(Result, len); 1195 UnicodeToUTF8(PChar(Result), hp, len); 1196 SetCodePage(RawByteString(Result),CP_UTF8,false); 1197{$endif} 1198 end; 1199 end; 1200end; 1201 1202 1203function ExecuteProcess(const Path: RawByteString; const ComLine: RawByteString; 1204 Flags: TExecuteFlags = []): Integer; 1205begin 1206 { TODO : implement } 1207 Result := 0; 1208end; 1209 1210function ExecuteProcess(const Path: RawByteString; 1211 const ComLine: Array of RawByteString; Flags:TExecuteFlags = []): Integer; 1212var 1213 CommandLine: RawByteString; 1214 I: integer; 1215begin 1216 Commandline := ''; 1217 for I := 0 to High (ComLine) do 1218 if Pos (' ', ComLine [I]) <> 0 then 1219 CommandLine := CommandLine + ' ' + '"' + ComLine [I] + '"' 1220 else 1221 CommandLine := CommandLine + ' ' + Comline [I]; 1222 ExecuteProcess := ExecuteProcess (Path, CommandLine,Flags); 1223end; 1224 1225function ExecuteProcess(const Path: UnicodeString; const ComLine: UnicodeString; 1226 Flags: TExecuteFlags = []): Integer; 1227begin 1228 { TODO : implement } 1229 Result := 0; 1230end; 1231 1232function ExecuteProcess(const Path: UnicodeString; 1233 const ComLine: Array of UnicodeString; Flags:TExecuteFlags = []): Integer; 1234var 1235 CommandLine: UnicodeString; 1236 I: integer; 1237begin 1238 Commandline := ''; 1239 for I := 0 to High (ComLine) do 1240 if Pos (' ', ComLine [I]) <> 0 then 1241 CommandLine := CommandLine + ' ' + '"' + ComLine [I] + '"' 1242 else 1243 CommandLine := CommandLine + ' ' + Comline [I]; 1244 ExecuteProcess := ExecuteProcess (Path, CommandLine,Flags); 1245end; 1246 1247procedure Sleep(Milliseconds: Cardinal); 1248const 1249 DelayFactor = 10000; 1250var 1251 interval: LARGE_INTEGER; 1252begin 1253 interval.QuadPart := - Milliseconds * DelayFactor; 1254 NtDelayExecution(False, @interval); 1255end; 1256 1257{**************************************************************************** 1258 Initialization code 1259****************************************************************************} 1260 1261initialization 1262 InitExceptions; { Initialize exceptions. OS independent } 1263 InitInternational; { Initialize internationalization settings } 1264 OnBeep := @SysBeep; 1265finalization 1266 FreeTerminateProcs; 1267 DoneExceptions; 1268end. 1269