dcrearer dcrearer - 3 months ago 16
C Question

Win32 WriteFile return 0 bytes as the number of byte written. lpBuffer contained 4455 bytes

I have the following code which creates two files. When the write operation for the last 4455 byte is invoked it returns 0 as the number of bytes written. Could this be due to the dwFlagAttributes of sequential scan and no buffering?

hIn = CreateFile (fIn, GENERIC_READ, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | SSF | NBF, NULL);

Desired Access: Generic Read
Disposition: Open
Options: Sequential Access, No Buffering, Synchronous IO Non-Alert, Non-Directory File
Attributes: N
ShareMode: None
AllocationSize: n/a
OpenResult: Opened

hOut = CreateFile (fOut, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | SSF | NBF, NULL);

Desired Access: Generic Write, Read Attributes
Disposition: OverwriteIf
Options: Sequential Access, No Buffering, Synchronous IO Non-Alert, Non-Directory File
Attributes: N
ShareMode: None
AllocationSize: 0
OpenResult: Created


After the files are created there is a while loop that reads file1. Then writes the bytes to file2. file1 is approximately 13MB.

while (ReadFile (hIn, aBuffer, BUF_SIZE, &nIn, NULL) && nIn > 0 && WriteOK) {
for (iCopy = 0; iCopy < nIn; iCopy++)
ccBuffer [iCopy] = (BYTE)((aBuffer [iCopy] + shift) % 256);
WriteOK = WriteFile (hOut, ccBuffer, nIn, &nOut, NULL);
}


From the disassembly below its evident that the last 4455 bytes are not being written to file2. Could this be due to the CreateFile dwFlagAttributes?

0:000> kb
# ChildEBP RetAddr Args to Child
00 0018bd00 00411612 00000038 0018bdf8 00001167 kernel32!WriteFile
01 0018fe64 0041144c 00273a99 00273ac9 0000000a cci!cci_f+0x162
02 0018ff44 00411bbb 00000004 00273a78 002723e8 cci!main+0x6c
03 0018ff88 7698336a 7efde000 0018ffd4 77c29f72 cci!__tmainCRTStartup+0x122
04 0018ff94 77c29f72 7efde000 76d02536 00000000 kernel32!BaseThreadInitThunk+0xe
05 0018ffd4 77c29f45 00411122 7efde000 00000000 ntdll!__RtlUserThreadStart+0x70
06 0018ffec 00000000 00411122 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1b

0:000> dd 0018bd00 la
0018bd00 0018fe64 00411612 00000038 0018bdf8
0018bd10 00001167 0018fe34 00000000 0018ff44
0018bd20 0018fe78 00000000

0:000> $number of bytes to be written 0x1167
0:000> .formats 00001167
Evaluate expression:
Hex: 00001167
Decimal: 4455
Octal: 00000010547
Binary: 00000000 00000000 00010001 01100111
Chars: ...g
Time: Wed Dec 31 20:14:15 1969
Float: low 6.24278e-042 high 0
Double: 2.20106e-320

0:000> gu
eax=00000000 ebx=00000000 ecx=76b2df07 edx=00000057 esi=0018bd1c edi=0018fe64
eip=00411612 esp=0018bd1c ebp=0018fe64 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
cci!cci_f+0x162:
00411612 cmp esi,esp

0:000> $number of bytes written
0:000> dd 0018fe34 l1
0018fe34 00000000


enter image description here

Answer

Since you are writing non-buffered, you need to adhere to the alignment requirements that are documented here: https://msdn.microsoft.com/en-us/library/windows/desktop/cc644950.aspx

Specifically the file access must start at a multiple of the volume sector size, and have a size that is a multiple of the volume sector size. Your 4455 sized buffer does not meet the second requirement.

Of course, if you want to write those final 4455 bytes and no more then you are in a quandry. You need to round it up to a multiple of the volume sector size and write the actual 4455 bytes followed by padding. The set the file pointer back to the end of those 4455 bytes, and call SetEndOfFile.