none
Falsche Dateigröße RRS feed

  • Frage

  • Unter Windows 7 Ultimate 64Bit erhalte ich mit _stat in seltenen Fällen eine falsche Dateigröße. Dabei wird im Prinzip der letzte Schreibvorgang unterschlagen.
    Auch die Dateieigenschaften im Windowsexplorer enthalten die falsche Dateigröße. FindFirstFile auf die Datei ergibt ebenfalls die falsche Größe.
    Nachdem die Datei z.B. ins Notepad geladen wurde, wird die Dateigröße im Filesystem korrigiert. Ansonsten bleibt stundenlang die falsche Dateigröße im Dateisystem.

    Das Problem lässt sich einfach reproduzieren.

    #include <windows.h> 
    
    #include <stdio.h> 
    
    #include <sys/types.h> 
    
    #include <sys/stat.h>
    
    
    
    typedef union 
    
    
    
    {
    
    	struct myI64 {
    
    
    
    	unsigned long ul;
    
    
    
    	unsigned long uh;
    
    
    
    	} v1;
    
    
    
    	__int64 v2;
    
    
    
    } int64;
    
    
    
    int CheckLen(char *filename, __int64 *filelen)
    
    
    
    {
    
    
    
        int64 i64;
    
    
    
    	HANDLE hFind;
    
    
    
    	WIN32_FIND_DATA fd;
    
    
    
    	struct _stati64 sb;
    
    
    
    	hFind=FindFirstFile(filename, &fd);
    
    
    
    	if(hFind!=INVALID_HANDLE_VALUE)
    
    
    
    	{
    
    
    
    		i64.v1.ul=fd.nFileSizeLow;
    
    
    
    		i64.v1.uh=fd.nFileSizeHigh;
    
    
    
    		FindClose(hFind);
    
    
    
    		if(i64.v2 != *filelen)
    
    
    
    		{
    
    
    
    			printf("Filesize differs: %I64d (%I64d)\n", *filelen, i64.v2);
    
    
    
    			if(!_stati64(filename, &sb))
    
    
    
    				printf("Filesize with stat: %I64d\n", sb.st_size);
    
    
    
    			return 0;
    
    
    
    		}
    
    	}
    
    
    
    	return 1;
    
    
    
    }
    
    
    
    
    
    int appendBuf(char *filename, char *buf, int size, __int64 *filelen)
    
    
    
    {
    
    
    
    	HANDLE hFile=CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    
    
    
    	DWORD dwh=0, dwl=0, dw;
    
    
    
    	int result=1;
    
    
    
    	if(hFile!=INVALID_HANDLE_VALUE)
    
    
    
    	{
    
    
    
    		dwl=SetFilePointer(hFile, 0, &dwh, FILE_END);
    
    
    
    		if(WriteFile(hFile, buf, size, &dw, NULL))
    
    
    
    		{
    
    			*filelen+=dw;
    
    		}
    
    
    
    		else
    
    
    
    		{
    
    			result=0;
    
    		}
    
    
    
    		CloseHandle(hFile);
    
    	}
    
    
    
    	else
    
    
    
    		result=0
    
    
    
    	return result;
    
    }
    
    
    
    int main(int argc, char **argv)
    
    
    
    {
    
    
    
    	char *filename;
    
    
    
    	__int64 filelen=0;
    
    
    
    	char buf[256];
    
    
    
    	int i;
    
    
    
    	if(argc==2)
    
    
    
    		filename=argv[1];
    
    
    
    	else
    
    	
    
    		return 1;
    
    
    
    	for(i=0; i<256; i++)
    
    
    
    		buf[i]='A' + ((rand()*32)/RAND_MAX);
    
    
    
    	do 
    
    
    
    	{
    
    
    
    		if(!appendBuf(filename, buf, 51, &filelen))
    
    			return 1
    
    	}
    
    
    
    	while(CheckLen(filename, &filelen));
    
    
    
    	return 0;
    
    
    
    }
    
    
    
    


    Um schneller den Fehler zu erhalten, muss man einfach eine 2. Instanz des Programms mit einer 2. Datei starten.

    Unter Windows XP 32bit kann ich diesen Fehler nicht reproduzieren. Es liegt dabei auch nicht an zu großen Dateien.
    Mittwoch, 24. Februar 2010 12:01

Antworten

  • Hallo FredSt!

    > Danke für die Antworten.

    Also, hier die Antwort der MS Windows Produkt-Gruppe:

    NTFS directory information is typically not updated in a synchronous
    fashion and FindFirstFile keys off NtQueryDirectoryFile (i.e. reading
    directory dup info) and thus are prone to getting stale data, but there
    are other API's that should get you live data like GetFileSize(). There
    is some information like file size duplicated in the file record and in
    the directory entry that points to the file record (to facilitate speedy
    directory enumerations). The directory entry is updated lazily (for
    performance reasons). Hence the discrepancy.

    Documentation of FindFirstFile
    http://msdn.microsoft.com/en-us/library/aa364418(VS.85).aspx notes the
    following:

    Note In rare cases, file information on NTFS file systems may not be
    current at the time you call this function. To be assured of getting the
    current file information, call the GetFileInformationByHandle function.



    Das sollte Dir eigentlich weiterhelfen. Es ist also "by design".


    Jochen Kalmbach (MVP VC++)
    Dienstag, 9. März 2010 20:51
  • Noch als Info: Wenn Du vor dem CloseHandle die Funktion "FlushFileBuffers" aufrufst, tritt das Problem nicht mehr auf...
    Jochen Kalmbach (MVP VC++)
    Montag, 1. März 2010 06:27

Alle Antworten

  • Das ist eine gute Frage ;)
    Es scheint so, also ob da tatsächlich irgendwas "optimiert" wird...

    Hab das Programm mal Unicode-Tauglich gemacht und noch die Testdatei vorher gelöscht...
    Werde es mal der MS Produkt-Gruppe melden... ich hab aber irgendwo gelesen, dass es im NTFS Dateisystem irgendwelche "optimierungen" gab, was vielleicht zu diesem Phänomen führen könnte...

    Hier mal der "korrigierte" Code:
    #include <windows.h> 
    #include <stdio.h> 
    #include <tchar.h>
    #include <sys/types.h> 
    #include <sys/stat.h>
    
    typedef union 
    {
      struct myI64 {
        unsigned long ul;
        unsigned long uh;
      } v1;
      __int64 v2;
    } int64;
    
    int CheckLen(LPCTSTR filename, __int64 *filelen)
    {
      int64 i64;
      HANDLE hFind;
      WIN32_FIND_DATA fd;
      struct _stati64 sb;
      hFind=FindFirstFile(filename, &fd);
      if(hFind!=INVALID_HANDLE_VALUE)
      {
        i64.v1.ul=fd.nFileSizeLow;
        i64.v1.uh=fd.nFileSizeHigh;
        FindClose(hFind);
        if(i64.v2 != *filelen)
        {
          printf("Filesize differs: %I64d (%I64d)\n", *filelen, i64.v2);
          if(!_tstat64(filename, &sb))
            printf("Filesize with stat: %I64d\n", sb.st_size);
          return 0;
        }
      }
      return 1;
    }
    
    int appendBuf(LPCTSTR filename, char *buf, int size, __int64 *filelen)
    {
      HANDLE hFile=CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
      LONG dwh=0;
      DWORD dwl=0, dw;
      int result=1;
      if(hFile!=INVALID_HANDLE_VALUE)
      {
        dwl=SetFilePointer(hFile, 0, &dwh, FILE_END);
        if(WriteFile(hFile, buf, size, &dw, NULL))
        {
          *filelen+=dw;
        }
        else
        {
          result=0;
        }
        CloseHandle(hFile);
      }
      else
        result=0;
      return result;
    }
    
    int _tmain(int argc, TCHAR **argv)
    {
      LPTSTR filename;
      if(argc==2)
        filename=argv[1];
      else
      {
        printf("please provide a test-filename!\n");
        return 1;
      }
    
      char buf[256];
      for(int i=0; i<256; i++)
        buf[i]='A' + ((rand()*32)/RAND_MAX);
    
      // be sure the file is deleted before we start testing......
      _tremove(filename);
    
      __int64 filelen=0;
      do 
      {
        if(!appendBuf(filename, buf, 51, &filelen))
          return 1;
      } while(CheckLen(filename, &filelen));
    
      return 0;
    }
    

    Jochen Kalmbach (MVP VC++)
    Donnerstag, 25. Februar 2010 13:16
  • Nur als Hinweis noch:
    Falls Du oder Deine Firma eine MSDN-Subscription hast, rate ich Dir den MS-Produkt-Support zu kontaktieren. Das scheint mir ein Fall für den zu sein...

    Meine Anfrage an die Produktgruppe hat oft nicht den nötigen "Druck" ;)
    Jochen Kalmbach (MVP VC++)
    Donnerstag, 25. Februar 2010 13:25
  • Noch als Info: Wenn Du vor dem CloseHandle die Funktion "FlushFileBuffers" aufrufst, tritt das Problem nicht mehr auf...
    Jochen Kalmbach (MVP VC++)
    Montag, 1. März 2010 06:27
  • Danke für die Antworten.

    FlushFileBuffers hilft tatsächlich. Leider leidet darunter die Performance.  Ohne FlushFileBuffers wird die Datei ca 5 bis 6 mal so schnell gefüllt.
    Dienstag, 2. März 2010 08:40
  • Hallo FredSt!

    > FlushFileBuffers hilft tatsächlich. Leider leidet darunter die
    > Performance. Ohne FlushFileBuffers wird die Datei ca 5 bis 6 mal so
    > schnell gefüllt.

    Ja... ich würde Dir deshalb empfehlen Dich an den MS Product Support zu
    wenden!
    Es sieht mir nach einem Bug aus...

    Siehe:
    https://msdn.microsoft.com/en-us/subscriptions/bb266240.aspx

    Tel.: 0180 5 67 22 55

    Jochen Kalmbach (MVP VC++)
    Dienstag, 2. März 2010 09:34

  • > Tel.: 0180 5 67 22 55

    Als Anmerkung noch: Wenn Du eine MSDN-Subscription hast, dann hast Du 4
    Anfragen pro jahr frei!

    Jochen Kalmbach (MVP VC++)
    Dienstag, 2. März 2010 09:37
  • Naja... eine Lösung ist das nicht... das ist eher die Bekämpfung der Symtome!!!!
    Jochen Kalmbach (MVP VC++)
    Mittwoch, 3. März 2010 12:02
  • Oder zwei in der ganzen Subscriptionlaufzeit.

    Mittwoch, 3. März 2010 14:49
  • Hallo christian!

    > Oder zwei in der ganzen Subscriptionlaufzeit.

    Auf jeden Fall > 0 ;)

    Jochen Kalmbach (MVP VC++)
    Mittwoch, 3. März 2010 14:52
  • Hallo FredSt!

    > Danke für die Antworten.

    Also, hier die Antwort der MS Windows Produkt-Gruppe:

    NTFS directory information is typically not updated in a synchronous
    fashion and FindFirstFile keys off NtQueryDirectoryFile (i.e. reading
    directory dup info) and thus are prone to getting stale data, but there
    are other API's that should get you live data like GetFileSize(). There
    is some information like file size duplicated in the file record and in
    the directory entry that points to the file record (to facilitate speedy
    directory enumerations). The directory entry is updated lazily (for
    performance reasons). Hence the discrepancy.

    Documentation of FindFirstFile
    http://msdn.microsoft.com/en-us/library/aa364418(VS.85).aspx notes the
    following:

    Note In rare cases, file information on NTFS file systems may not be
    current at the time you call this function. To be assured of getting the
    current file information, call the GetFileInformationByHandle function.



    Das sollte Dir eigentlich weiterhelfen. Es ist also "by design".


    Jochen Kalmbach (MVP VC++)
    Dienstag, 9. März 2010 20:51