Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with FileExists and Modified Date

On my server there are a few files with Modified Date 31/DEC/1979 (Don't ask me why). So FileExists returns false.

Sysutils.FileExists looks like this:

function FileAge(const FileName: string): Integer;
var
  Handle: THandle;
  FindData: TWin32FindData;
  LocalFileTime: TFileTime;
begin
  Handle := FindFirstFile(PChar(FileName), FindData);
  if Handle <> INVALID_HANDLE_VALUE then
  begin
    Windows.FindClose(Handle);
    if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
    begin
      FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
      if FileTimeToDosDateTime(LocalFileTime, LongRec(Result).Hi,
        LongRec(Result).Lo) then Exit;
    end;
  end;
  Result := -1;
end;

function FileExists(const FileName: string): Boolean;
begin
  Result := FileAge(FileName) <> -1;
end;

My question is, Why does the function depends on FileAge in the first place? Isn't the following line sufficient?:

if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
  // Yes the file exists!

Or even based on file attributes:

function MyFileExists(const Name: string): Boolean;
var
  R: DWORD;
begin
  R := GetFileAttributes(PChar(Name));
  Result := (R <> DWORD(-1)) and ((R and FILE_ATTRIBUTE_DIRECTORY) = 0);
end;
like image 699
ZigiZ Avatar asked Dec 19 '12 18:12

ZigiZ


1 Answers

The modern versions of Delphi implement FileExists in broadly the same way as your code does. The implementation has extra handling for symlinks but is otherwise essentially identical to your version.

There's one interesting nuance in the modern Delphi implementation. If the call to GetFileAttributes returns INVALID_FILE_ATTRIBUTES, then the code doesn't immediately bail out. Instead it does this:

LastError := GetLastError;
Result := (LastError <> ERROR_FILE_NOT_FOUND) and
  (LastError <> ERROR_PATH_NOT_FOUND) and
  (LastError <> ERROR_INVALID_NAME) and ExistsLockedOrShared(Filename);

And the implementation of ExistsLockedOrShared uses FindFirstFile and a check of the FILE_ATTRIBUTE_DIRECTORY on dwFileAttributes. This indicates that GetFileAttributes can fail when the file exists, but is locked. But that FindFirstFile can succeed in such a scenario. That's reasonable because FindFirstFile uses the file metadata rather than the data stored in the file itself.

It's hard to say why the code is is the way it is in the older versions. I think it's weak. Personally I would replace FileExists with a better version, using a code hook. For example: Patch routine call in delphi

As always, there's a Raymond Chen article on the subject: Superstition: Why is GetFileAttributes the way old-timers test file existence?

like image 144
David Heffernan Avatar answered Sep 20 '22 08:09

David Heffernan