home

about

license

support

K/Base

Indy
HomeContactsSite Map


FindFirst, FindNext, and FindClose are evil

In the process of writing some FTP client server stuff, I have come accross many limitations in Borland's directory routines (FindFirst, FindNext, and FindClose). This is particularly the case with the FTP server we have. There are several problems:

  • The TSearchRec still uses a standard signed Integer to hold the size. This means that a filesize will not be correct if something is larger than 2GB's.
  • The Last Modified Time is given in Local Time ZONE instead of standard GMT (or Universal Time). While this is helpful for a user, GMT is the standard for many Internet protocols such as FSP, SSH File Transfer (SFTP), and FTP. In FTP, the only place where a local time-zone is used is in the MDTM set file date command (for compatibility) and for the standard LIST output (the Unix and DOS formats).
  • In DotNET, Borland for the sake of consistency made the routines use P/Invoke Win32 API calls meaning that anything use those routines couldn't be used on Mono .NET Framework in Linux and P/Invoke is problematic in some secured environments.
  • In Win32, you can not use those routines with multibyte character filenames even though those are valid in NTFS. You have to use the WideString Win32 API calls.   This is important because some languages such as Japanese, Chinies, Korean, and Arabic do not use a Latin alphabet.
  • There are quite a number of attributes that Borland’s routines do not support.  Those are compressed, encrypted, offline, reparse point, sparse file, temporary, device, and not content indexed.
  • In Kylix, you only need permission bits but user and group ownership.  
  • Some server protocols not only support a last modified time, a but a creation time, and a last access time.  The Indy FTP server can support any of these as an option by setting the MLSDFacts property and our FTP client also supports these in the TidMLSTFTPListItem object located in the IdFTPListTypes unit.

I do have some code a from a FTP server example that I haven't finished that may help.  It doesn't support Multi-byte characters but it is a nice attempt to work what I described in Win32:

===

function TForm1.FileTimeToTDateTime(const AFileTime: TFileTime): TDateTime;
var   LDosTime : LongInt;
begin
  Result := 0;
  if Windows.FileTimeToDosDateTime(AFileTime, LongRec(LDosTime).Hi,
      LongRec(LDosTime).Lo) then
  begin
    Result := SysUtils.FileDateToDateTime(LDosTime);
  end
  else
  begin
     SysUtils.RaiseLastOSError;
  end;
end;

procedure TForm1.IdFTPServer1ListDirectory(ASender: TIdFTPServerContext;
  const APath: string; ADirectoryListing: TIdFTPListOutput; const ACmd,
  ASwitches: string);
var
 LFTPItem :TIdFTPListOutputItem;
 SR : TSearchRec;
 SRI : Integer;
 LTmpPath : String;

begin
  {..}
  SRI := FindFirst(LTmpPath, faAnyFile , SR);// - faHidden - faSysFile, SR);

  While SRI = 0 do
  begin
    LFTPItem := ADirectoryListing.Add;
    LFTPItem.FileName := SR.Name;

    //This is necessary because the Borland RTL FindData Size is an Integer and can't handle
    //anything greater than 2GB.
    LFTPItem.Size := Int64(SR.FindData.nFileSizeHigh shl 32) + SR.FindData.nFileSizeLow;
      
	{..}
    //We don't use the DosDate from Borland's FindFirst, FindNext for two reasons:
    //1: We should be dealing with GMT time in all cases.  For Unix and WinNT style lists,
    //   it will be converted to LocalTime.
    //2: The Win32_FIND_DATA record has more information than simply the last modified date
    //   and the MLSD/MLST command permits us to return all of this in a standardized way.
    //   Indy can support a "Create", "Modified" and "windows.lastaccess" fact.
    //   For Linux, the POSIX filesystem, you would want to support only the Modified fact
    //   for file dates.
    LFTPItem.ModifiedDateGMT := FileTimeToTDateTime( SR.FindData.ftLastWriteTime);
    LFTPItem.CreationDateGMT := FileTimeToTDateTime( SR.FindData.ftCreationTime);
    LFTPItem.LastAccessDateGMT := FileTimeToTDateTime( SR.FindData.ftLastAccessTime);
    LFTPItem.WinAttribs := SR.FindData.dwFileAttributes;
    {..}
    SRI := FindNext(SR);
  end;
  FindClose(SR);
end;

=== 

Thank goodness that Delphi can access the API functions directly. 


Corporate Sponsors

Atozed







home

about

license

support

K/Base

site map

links

Copyright © 1993 - 2008 Chad Z. Hower (Kudzu) and the Indy Pit Crew.          Website design by RuInternet.ru