c - Signaler SignFile signalet, hvis det udføres synkront

Indlæg af Hanne Mølgaard Plasc

Problem



Skifter WriteFile-funktionen begivenheden ind via parameteren lpOverlapped, hvis den udføres synkront og lykkes? Signaler det eventet, hvis det fejler synkront? Jeg har åbnet håndtaget til en fil med FILE\_FLAG\_OVERLAPPED flag. Jeg var ikke i stand til at finde ud af det fra dokumentationen og kunne ikke repro denne sag let i kode. [72]

Bedste reference


Først og fremmest vedrørte dette spørgsmål ikke kun WriteFile, men til enhver asynkron I/O-funktion - næsten alle funktioner, der får pointer til en OVERLAPPED struktur. fordi alle disse funktioner IRP ( I/O-anmodningspakke ) (se den definition i wdm.h ) er allokeret. hEvent håndtag fra OVERLAPPED konverteret til objektpeger og gemt i PKEVENT UserEvent; medlem af IRP. Begivenheden er indstillet (eller ikke indstillet) nøjagtigt, når IRP er afsluttet i IopCompleteRequest rutinen. IRP færdiggørelsesfunktionen er fælles for alle I/O-api, så og regler (når færdiggørelsesbrand) er relateret til alle. Det er desværre meget dårligt dokumenteret. win32-laget (sammenligne NT-lag) tilføjede yderligere, meget tynde problemer her. [73] [74]


baseret på wrk src kode, kan vi se, at I/O Manager brandafslutning (var 3 typer - hændelse, apc og iocp (gensidigt eksklusive)) til asynkron io når
!NT\_ERROR( irp->IoStatus.Status ) eller irp->PendingReturned. [75]


hvis vi bruger indfødte api, som direkte retur NTSTATUS - når (ULONG)status < 0xc0000000. men her var meget problematisk rækkevidde 0x80000000 <= status < 0xc0000000 eller NT\_WARNING(status) når det er uklart - er færdiggørelsen (selv sæt, apc eller pakke til iocp kø) indstillet. Dette skyldes, før du tildeler IRP I/O Manager, nogle grundlæggende checks og kan returnere fejl herfra. Normalt I/O Manager returnerer fejl fra NT\_ERROR(status), hvilket korrekt betyder, at der ikke vil være nogen færdiggørelse (begivenhed vil ikke blive indstillet)), men eksisterer og sjældent undtagelser. for eksempel til ReadDirectoryChangesW (eller ZwNotifyChangeDirectoryFile) skal markøren lpBuffer være DWORD-justeret (justeret nøjagtigt som FILE\_NOTIFY\_INFORMATION) ellers returnerer I/O-manager STATUS\_DATATYPE\_MISALIGNMENT (0x80000002) fra NT\_WARNING rækkevidde. men vil ikke være afsluttet (begivenhedssæt) i dette tilfælde, fordi funktionen mislykkes før tildele IRP. fra et andet tilfælde, hvis vi kalder FSCTL\_FILESYSTEM\_GET\_STATISTICS med ikke stor nok buffer-filsystemdriver (ikke I/O-manager) returnerer STATUS\_BUFFER\_OVERFLOW (0x80000005). men fordi på dette tidspunkt IRP allerede tildelt og kode ikke fra NT\_ERROR rækkevidde - vil være begivenhedssæt. [76] [77] [78]


så hvis fejl fra I/O Manager (før IRP tildelt) - vil ikke være færdig. ellers hvis fejl fra føreren (som bestået IRP), vil afslutningen ske hvis funktion returneres !NT\_ERROR(status). som resultat hvis vi får:



  • NT\_SUCCESS(status) (STATUS\_PENDING (0x103) er en del af dette) - vilje
    være
       færdiggørelse

  • NT\_ERROR(status) bliver ikke afsluttet

  • NT\_WARNING(status) - uklart, afhænge denne fejl fra I/O Manager
    (nej) eller driver (ja)



men med win32 lag gør situationen mere værre. fordi det er uklart, hvordan det fortolker NT\_WARNING(status) - fortolker de fleste win32 api dette som fejl - returner false og sætter den sidste fejl (konverteret fra status). men nogle api - for eksempel ReadDirectoryChangesW fortolker dette som succes kode - returner sandt og ikke angivet sidste fejl. som resultat hvis vi kalder ReadDirectoryChangesW med dårlig justeret buffer (men gyldige andre parametre) - den vender tilbage .. sand og ikke indstillet nogen fejl. men api-opkald er virkelig fejl. ZwNotifyChangeDirectoryFile intern returnering STATUS\_DATATYPE\_MISALIGNMENT her. [79] [80]


fra en anden side, hvis DeviceIoControl for FSCTL\_FILESYSTEM\_GET\_STATISTICS fejler (returner falsk) med kode ERROR\_MORE\_DATA (konverteret fra STATUS\_BUFFER\_OVERFLOW) begivenhed (færdiggørelse) vil blive indstillet i denne sag.


også ved win32 fejlkode vi kan ikke forstå - oprindelig status var NT\_ERROR eller NT\_WARNING kode - konvertering (RtlNtStatusToDosError) status til win32 fejl mistede denne info [81]


problem med NT\_WARNING(status) rækkevidde, start fra vista, kan løses, hvis vi bruger IOCP-afslutning (i stedet for hændelse) og indstil FILE\_SKIP\_COMPLETION\_PORT\_ON\_SUCCESS på fil - i dette tilfælde er I/O-manager kø en færdiggørelse til porten, når og kun når STATUS\_PENDING vil blive returneret af indfødte api-opkald. for win32 lag betyder dette normalt, at api returnerer falsk og sidste fejl er ERROR\_IO\_PENDING. undtagelser - WriteFileEx, ReadFileEx som vender tilbage her. men det hjælper ikke i tilfælde ReadDirectoryChangesW alligevel (jeg antager, at dette er windows bug) [82]


læs også FILE\_SKIP\_SET\_EVENT\_ON\_HANDLE sektionen - dette implicit sige, når eksplicit begivenhed (fra overlappede) indstilles i tilfælde af asynkron funktion - når anmodningen returneres med en succeskode , eller den returnerede fejl er ERROR\_IO\_PENDING . men her spørgsmålet - hvad er succeskode ? sand returneret af win32 api? ikke altid, som synlig fra FSCTL\_FILESYSTEM\_GET\_STATISTICS - ERROR\_MORE\_DATA (STATUS\_BUFFER\_OVERFLOW) også succeskode. eller STATUS\_NO\_MORE\_FILES returneret af NtQueryDirectoryFile også succes kode - begivenhed (apc eller iocp fuldførelse) vil blive indstillet. men samme NtQueryDirectoryFile kan returnere STATUS\_DATATYPE\_MISALIGNMENT, når FileInformation dårligt justeret - dette er fejlkode, fordi det er returneret fra I/O Manager før tildeling af IRP [83] [84] [85]


NT\_WARNING status er i de fleste tilfælde succeskode (vil være færdiggørelse), men win32-lag definerer i de fleste tilfælde det som fejlkode (return false).


kodeeksempel til test:


ULONG BOOL\_TO\_ERROR(BOOL fOk)
{
    return fOk ? NOERROR : GetLastError();
}

void CheckEventState(HANDLE hEvent, ULONG err, NTSTATUS status = RtlGetLastNtStatus())
{
    DbgPrint("error = \%u(\%x)", err, err ? status : STATUS\_SUCCESS);

    switch (WaitForSingleObject(hEvent, 0))
    {
    case WAIT\_OBJECT\_0:
        DbgPrint("Signaled
");
        break;
    case WAIT\_TIMEOUT:
        DbgPrint("NON signaled
");
        break;
    default:
        DbgPrint("error=\%u
", GetLastError());
    }
#if 1
    EVENT\_BASIC\_INFORMATION ebi;
    if (0 <= ZwQueryEvent(hEvent, EventBasicInformation, &ebi, sizeof(ebi), 0))
    {
        DbgPrint("EventState = \%x
", ebi.EventState);
    }
#endif
}

void demoIoEvent()
{
    WCHAR sz[MAX\_PATH];
    GetSystemDirectoryW(sz, RTL\_NUMBER\_OF(sz));

    HANDLE hFile = CreateFileW(sz, 0, FILE\_SHARE\_VALID\_FLAGS, 0, 
        OPEN\_EXISTING, FILE\_FLAG\_OVERLAPPED|FILE\_FLAG\_BACKUP\_SEMANTICS, 0);

    if (hFile != INVALID\_HANDLE\_VALUE)
    {
        FILESYSTEM\_STATISTICS fs;

        OVERLAPPED ov = {};

        if (ov.hEvent = CreateEvent(0, TRUE, FALSE, 0))
        {
            FILE\_NOTIFY\_INFORMATION fni;
            IO\_STATUS\_BLOCK iosb;

            // STATUS\_DATATYPE\_MISALIGNMENT from I/O manager
            // event will be not set
            NTSTATUS status = ZwNotifyChangeDirectoryFile(hFile, ov.hEvent, 0, 0, &iosb, 
                (FILE\_NOTIFY\_INFORMATION*)(1 + (PBYTE)&fni), 1, FILE\_NOTIFY\_VALID\_MASK, FALSE);

            CheckEventState(ov.hEvent, ERROR\_NOACCESS, status);

            // windows bug ! ReadDirectoryChangesW return .. true and no last error
            // but really api fail. event will be not set and no notifications
            ULONG err = BOOL\_TO\_ERROR(ReadDirectoryChangesW(hFile,
                (FILE\_NOTIFY\_INFORMATION*)(1 + (PBYTE)&fni), 1, 0, FILE\_NOTIFY\_VALID\_MASK, 0, &ov, 0));

            CheckEventState(ov.hEvent, err);

            // fail with ERROR\_INSUFFICIENT\_BUFFER (STATUS\_BUFFER\_TOO\_SMALL)
            // NT\_ERROR(c0000023) - event will be not set
            err = BOOL\_TO\_ERROR(DeviceIoControl(hFile, 
                FSCTL\_FILESYSTEM\_GET\_STATISTICS, 0, 0, 0, 0, 0, &ov));

            CheckEventState(ov.hEvent, err);

            // ERROR\_MORE\_DATA (STATUS\_BUFFER\_OVERFLOW)
            // !NT\_ERROR(80000005) - event will be set
            // note - win 32 api return false and error != ERROR\_IO\_PENDING
            err = BOOL\_TO\_ERROR(DeviceIoControl(hFile, 
                FSCTL\_FILESYSTEM\_GET\_STATISTICS, 0, 0, &fs, sizeof(fs), 0, &ov));

            CheckEventState(ov.hEvent, err);

            if (err == ERROR\_MORE\_DATA)
            {
                SYSTEM\_INFO si;
                GetSystemInfo(&si);

                ULONG cb = si.dwNumberOfProcessors * fs.SizeOfCompleteStructure;

                union {
                    PVOID pv;
                    PBYTE pb;
                    PFILESYSTEM\_STATISTICS pfs;
                };

                pv = alloca(cb);

                // must be NOERROR(0) here
                // !NT\_ERROR(0) - event will be set
                err = BOOL\_TO\_ERROR(DeviceIoControl(hFile, FSCTL\_FILESYSTEM\_GET\_STATISTICS, 0, 0, 
                    pv, cb, 0, &ov));

                CheckEventState(ov.hEvent, err);

                if (!err && GetOverlappedResult(hFile, &ov, &cb, FALSE))
                {
                    do 
                    {
                        // use pfs here
                    } while (pb += fs.SizeOfCompleteStructure, --si.dwNumberOfProcessors);
                }
            }

            CloseHandle(ov.hEvent);
        }
        CloseHandle(hFile);
    }
}


og output:


error = 998(80000002)NON signaled
EventState = 0
error = 0(0)NON signaled
EventState = 0
error = 122(c0000023)NON signaled
EventState = 0
error = 234(80000005)Signaled
EventState = 1
error = 0(0)Signaled
EventState = 1