I'm playing around with abstracting Windows, Linux, and Mac File IO calls into macros (to avoid C runtime, so no fopen, fclose, etc...). I've actually got quite a bit working but I've run into a stumbling block.
I'm trying to boil down all of the possible errors that could be thrown by each of these platforms into a subset of common ones: Not Found, Exists, Invalid Access, etc.
Linux is obviously well document, Mac even has the most common ones, but Windows does not specify which errors are thrown for its native File I/O functions. We obviously need to use GetLastError(), but I could not find a reference to what the possible values would be.
How about this?
I'm writing a Windows application and using the CreatFile() API. I'd like to handle any errors as gracefully as I can, and perhaps recover from said error instead of telling the user "Crap! Can't do that." But the MSDN docs don't list the possible error codes that could be generated.
Does anyone have a reference to the possible error codes that Windows File functions may generate, specifically (for now) CreateFile()?
Windows supports installable file systems. Microsoft cannot possibly predict what kind of errors a 3rd party file system driver is going to generate so does not make any attempt to promise a strict sub-set of possible error codes.
So yes, you really do have to use GetLastError(). The FormatMessage() function is available to generate a readable string for the error code. In general, the user will get a decent error message that helps him diagnose the root cause. It isn't quite specific enough to tell that an earthquake in Chile severed an undersea communication cable but it will certainly help him to start looking at networking problems for example. You can use the CRT wrappers as well, but with the inevitable loss of specificity. That might be a service call that you have to handle instead of the user's IT staff.
to avoid C runtime
You're reinventing the wheel. C runtime was created so people could write platform-independent programs that (theorietcally) would compile anywhere, as long as you aren't using something platform-specific.
Right now, you're doing the same thing.
I'd advise to stop doing that and either use standard C file function, or cross-platform framework that support several platform. You could use either Boost or Qt 4.
Regarding your question
Does anyone have a reference to the possible error codes that Windows File functions may generate,
WinAPI documentation is available on MSDN and you should read it.
CreateFile
GetLastError
SystemErrorCodes
Does anyone have a reference to the possible error codes that Windows File functions may generate
Because CreateFile doesn't only deal with "files" (it also works with directories, pipes, physical devices, etc), you should assume that it can generate any operating system code that exists. According to msdn, there are 16000 codes total. Moral of the story: if you want to generate human-readable message, use FormatMessage. If documentation doesn't list possible error codes (for CreateFile), it automatically means, that CreateFile can produce any error code that exists.
As SigTerm stated, there's no definitive list of system error codes associated with CreateFile. Your best bet is searching for the keyword "ERROR" in the CreateFileA/CreateFileW documentation.
Otherwise, there's always web search engine. A quick DuckDuckGo search of "CreateFile" and "Error" reveals a slew of errors and error codes
You should also consult Microsoft's lengthy lists of System Error Codes and Win32 Error Codes.
Excerpts from CreateFileA/CreateFileW Documentation
param: dwShareMode
You cannot request a sharing mode that conflicts with the access mode that is specified in an existing request that has an open handle. CreateFile would fail and the GetLastError function would return ERROR_SHARING_VIOLATION (32).
param: dwCreationDisposition
CREATE_ALWAYS
If the specified file exists and is writable, the function overwrites the file, the function succeeds, and last-error code is set to ERROR_ALREADY_EXISTS (183).
CREATE_NEW
If the specified file exists, the function fails and the last-error code is set to ERROR_FILE_EXISTS (80).
OPEN_ALWAYS
If the specified file exists, the function succeeds and the last-error code is set to ERROR_ALREADY_EXISTS (183).
OPEN_EXISTING
If the specified file or device does not exist, the function fails and the last-error code is set to ERROR_FILE_NOT_FOUND (2).
TRUNCATE_EXISTING
If the specified file does not exist, the function fails and the last-error code is set to ERROR_FILE_NOT_FOUND (2).
Files
If you call CreateFile on a file that is pending deletion as a result of a previous call to DeleteFile, the function fails. The operating system delays file deletion until all handles to the file are closed. GetLastError returns ERROR_ACCESS_DENIED.
If CREATE_ALWAYS and FILE_ATTRIBUTE_NORMAL are specified, CreateFile fails and sets the last error to ERROR_ACCESS_DENIED if the file exists and has the FILE_ATTRIBUTE_HIDDEN or FILE_ATTRIBUTE_SYSTEM attribute. To avoid the error, specify the same attributes as the existing file.
When an application creates a file across a network, it is better to use GENERIC_READ | GENERIC_WRITE for dwDesiredAccess than to use GENERIC_WRITE alone. The resulting code is faster, because the redirector can use the cache manager and send fewer SMBs with more data. This combination also avoids an issue where writing to a file across a network can occasionally return ERROR_ACCESS_DENIED.
Consoles
If lpFileName is indeterminate "CON" and dwDesiredAccess is GENERIC_READ | GENERIC_WRITE, CreateFile fails; GetLastError returns ERROR_FILE_NOT_FOUND.
"CON" and GENERIC_READ implies a console handle for input.
"CON" and "GENERIC_WRITE` implies a console handle for output.
Pipes
If the CreateNamedPipe function was not successfully called on the server prior to this operation, a pipe will not exist and CreateFile will fail with ERROR_FILE_NOT_FOUND.
If there is at least one active pipe instance but there are no available listener pipes on the server, which means all pipe instances are currently connected, CreateFile fails with ERROR_PIPE_BUSY.
Various errors from across the internet
ERROR_INVALID_FUNCTION (1)
ERROR_FILE_NOT_FOUND (2)
ERROR_PATH_NOT_FOUND (3)
ERROR_ACCESS_DENIED (5)
ERROR_GEN_FAILURE (31) - A device attached to the system is not functioning. This can also occur when working with network devices e.g. virtual network adapters
ERROR_SHARING_VIOLATION (32) - The process cannot access the file because it is being used by another process.
ERROR_INVALID_PARAMETER (87) - "An application must meet certain requirements when working with files that are opened with FILE_FLAG_NO_BUFFERING: File access must begin at byte offsets within a file that are integer multiples of the volume sector size. File access must be for numbers of bytes that are integer multiples of the volume sector size. For example, if the sector size is 512 bytes, an application can request reads and writes of 512, 1024, 1536, or 2048 bytes, but not of 335, 981, or 7171 bytes. Buffer addresses for read and write operations should be sector aligned, which means aligned on addresses in memory that are integer multiples of the volume sector size. Depending on the disk, this requirement may not be enforced."
ERROR_CALL_NOT_IMPLEMENTED (120) - using P/Invoke and calling to coredll.dll for Windows Pocket PC
ERROR_INVALID_NAME (123)
ERROR_FILE_SYSTEM_VIRTUALIZATION_INVALID_OPERATION (181)
ERROR_FILE_TOO_LARGE (223)
ERROR_CONNECTION_COUNT_LIMIT (1238)
ERROR_MUTUAL_AUTH_FAILED (1397) - network auth e.g. serial COM port over Bluetooth
ERROR_NO_SYSTEM_RESOURCES (1450) - e.g. working with Serial Ports
Related
Execution wise, is any difference between those two commands, except the fact that the one is for C++ and the other is the CMD?
Because I'm having a strange problem, I have an .exe that it takes arguments. When I call this exe with it's arguments from the CMD it is working normally. But when I do the same thing from ShellExecute the program returns an error.
What could be wrong?
ShellExecute(
NULL,
_T("open"),
_T("C:\\connect\\connect.exe"),
_T("J11"),
NULL,
SW_SHOW);
Yes a very big difference.
CMD processes what you type then passes it to CreateProcessEx. CMD's Start command, run dialog, or double clicking a file, gets passed to one of the ShellExecuteEx functions, which in turn calls CreateProcessEx.
For CMD see Windows NT Shell Scripting, Chapter 2, The Windows NT Command Shell by Tim Hill (1998), available from MS's web site (only that chapter is available for free). CMD's preprocessing of what is passed to CreateProcessEx is detailed. CMD also does it own emulation of ShellExecute but it is under Windows 95 rules for ShellExecute, rather than the frequently updated shell32 implementation.
For ShellExecute see the ShellExecuteEx documentation.
To see CreateProcessEx rules see it's documentation.
I was going to paste the rules, but CMD's ran to pages.
The Return value tells you why your command isn't working.
Return Value
Returns a value greater than 32 if successful, or an error value that is less than or equal to 32 otherwise. The following table lists the error values. The return value is cast as an HINSTANCE for backward compatibility with 16-bit Windows applications. It is not a true HINSTANCE, however. The only thing that can be done with the returned HINSTANCE is to cast it to an int and compare it with the value 32 or one of the error codes below.
0 The operating system is out of memory or resources.
ERROR_FILE_NOT_FOUND The specified file was not found.
ERROR_PATH_NOT_FOUND The specified path was not found.
ERROR_BAD_FORMAT The .exe file is invalid (non-Microsoft Win32 .exe or error in .exe image).
SE_ERR_ACCESSDENIED The operating system denied access to the specified file.
SE_ERR_ASSOCINCOMPLETE The file name association is incomplete or invalid.
SE_ERR_DDEBUSY The Dynamic Data Exchange (DDE) transaction could not be completed because other DDE transactions were being processed.
SE_ERR_DDEFAIL The DDE transaction failed.
SE_ERR_DDETIMEOUT The DDE transaction could not be completed because the request timed out.
SE_ERR_DLLNOTFOUND The specified DLL was not found.
SE_ERR_FNF The specified file was not found.
SE_ERR_NOASSOC There is no application associated with the given file name extension. This error will also be returned if you attempt to print a file that is not printable.
SE_ERR_OOM There was not enough memory to complete the operation.
SE_ERR_PNF The specified path was not found.
SE_ERR_SHARE A sharing violation occurred.
We create a file for use as memorymappedfile.
we open with GENERIC_READ | GENERIC_WRITE
we use share with FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
we use file attributes FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE
we create the file successfully. We can reopen it as many times with the same flags as we want.
Once one handle has been closed, we can no longer open any more handles, it returns with ERROR_ACCESS_DENIED. We can cause this by closing any of the handles, either the first from CreateFile(ALWAYS_CREATE), or the others from CreateFile(OPEN_EXISTING).
Is there any way to avoid this ? We use the memoryMappedFile as communication between the different processes that must share resources. these processes sometimes start and stop. Right now as soon as we close one handle, we are stuck unable to open the memorymappedfile.
I have tried changing the open calls to use FILE_ATTRIBUTE_NORMAL, so only the create call uses CLOSE_ON_DELETE but that has no effect on this situation.
The problem you're running into is that once a file handle opened with FILE_FLAG_DELETE_ON_CLOSE is closed, the operating system will no longer allow new handles to be created.
The gory details: When processing an IRP_MJ_CLEANUP (which is what happens win a handle is closed) for a file opened for delete-on-close, Windows file systems will set an internal flag on the file object indicating that it's on it's way out. Subsequent open attempts on the file will be failed with STATUS_DELETE_PENDING, which the Win32 subsystem will map to the Win32 ERROR_ACCESS_DENIED code you're seeing.
For your use case, you might want to consider using the Named Shared Memory (MSDN) pattern. Basically, let the operating system manage the space for your shared memory. Just make sure you apply the appropriate security attributes, and you're good to go.
It turns out the venerable Raymond Chen replied to this on his Microsoft devblog, The Old New Thing. Bukes is correct but as an alternative, as Raymond says in his article:
It looks like they really want the file to remain valid (including allowing further CreateFile calls to succeed) for as long as any open handle continues to refer to the file. Fortunately, the customer needed the handle only to create a memory-mapped view. The file pointer was not important. Therefore, the customer could use DuplicateHandle instead of CreateFile to get additional handles to the file. Since all of the handles refer to the same file object, the file object will not delete the file until all of the handles are closed.
There is a static library I use in my program which can only take filenames as its input, not actual file contents. There is nothing I can do about the library's source code. So I want to: create a brand-new file, store data to being processed into it, flush it onto the disk(?), pass its name to the library, then delete it.
But I also want this process to be rather secure:
1) the file must be created anew, without any bogus data (maybe it's not critical, but whatever);
2) anyone but my process must not be able read or write from/to this file (I want the library to process my actual data, not bogus data some wiseguy managed to plug in);
3) after I'm done with this file, it must be deleted (okay, if someone TerminateProcess() me, I guess there is nothing much can be done, but still).
The library seems to use non-Unicode fopen() to open the given file though, so I am not quite sure how to handle all this, since the program is intended to run on Windows. Any suggestions?
You have a lot of suggestions already, but another option that I don't think has been mentioned is using named pipes. It will depend on the library in question as to whether it works or not, but it might be worth a try. You can create a named pipe in your application using the CreateNamedPipe function, and pass the name of the pipe to the library to operate on (the filename you would pass would be \\.\pipe\PipeName). Whether the library accepts a filename like that or not is something you would have to try, but if it works the advantage is your file never has to actually be written to disk.
This can be achieved using the CreateFile and GetTempFileName functions (if you don't know if you can write to the current working directory, you may also want to use , GetTempPath).
Determine a directory to store your temporary file in; the current directory (".") or the result of GetTempPath would be good candidates.
Use GetTempFileName to create a temporary file name.
Finally, call CreateFile to create the temporary file.
For the last step, there are a few things to consider:
The dwFlagsAndAttributes parameter of CreateFile should probably include FILE_ATTRIBUTE_TEMPORARY.
The dwFlagsAndAttributes parameter should probably also include FILE_FLAG_DELETE_ON_CLOSE to make sure that the file gets deleted no matter what (this probably also works if your process crashes, in which case the system closes all handles for you).
The dwShareMode parameter of CreateFile should probably be FILE_SHARE_READ so that other attempts to open the file will succeed, but only for reading. This means that your library code will be able to read the file, but nobody will be able to write to it.
This article should give you some good guidelines on the issue.
The gist of the matter is this:
The POSIX mkstemp() function is the secure and preferred solution where available. Unfortunately, it is not available in Windows, so you would need to find a wrapper that properly implements this functionality using Windows API calls.
On Windows, the tmpfile_s() function is the only one that actually opens the temporary file atomically (instead of simply generating a filename), protecting you from a race condition. Unfortunately, this function does not allow you to specify which directory the file will be created in, which is a potential security issue.
Primarily, you can create file in user's temporary folder (eg. C:\Users\\AppData\Local\Temp) - it is a perfect place for such files. Secondly, when creating a file, you can specify, what kind of access sharing do you provide.
Fragment of CreateFile help page on MSDN:
dwShareMode
0 Prevents other processes from opening a file or device
if they request delete, read, or write access.
FILE_SHARE_DELETE Enables subsequent open operations on a file or device to
request delete access. Otherwise, other processes cannot open the file or device if they
request delete access. If this flag is not specified, but the file or device has been opened for delete access, the function fails. Note: Delete access allows both delete and rename operations.
FILE_SHARE_READ Enables subsequent open operations on a
file or device to request read access. Otherwise, other processes cannot open the file or device if they request read access. If this flag is not specified, but the file or device has been opened for read access, the function fails.
FILE_SHARE_WRITE Enables subsequent open operations on a file or device to request
write access.
Otherwise, other processes cannot open the file or device if they
request write access.
If this flag is not specified, but the file or device has been opened
for write access or has a file mapping with write access, the function
fails.
Whilst suggestions given are good, such as using FILE_SHARE_READ, FILE_DELETE_ON_CLOSE, etc, I don't think there is a completely safe way to do thist.
I have used Process Explorer to close files that are meant to prevent a second process starting - I did this because the first process got stuck and was "not killable and not dead, but not responding", so I had a valid reason to do this - and I didn't want to reboot the machine at that particular point due to other processes running on the system.
If someone uses a debugger of some sort [including something non-commercial, written specifically for this purpose], attaches to your running process, sets a breakpoint and stops the code, then closes the file you have open, it can write to the file you just created.
You can make it harder, but you can't stop someone with sufficient privileges/skills/capabilities from intercepting your program and manipulating the data.
Note that file/folder protection only works if you reliably know that users don't have privileged accounts on the machine - typical Windows users are either admins right away, or have another account for admin purposes - and I have access to sudo/root on nearly all of the Linux boxes I use at work - there are some fileservers that I don't [and shouldn't] have root access. But all the boxes I use myself or can borrow of testing purposes, I can get to a root environment. This is not very unusual.
A solution I can think of is to find a different library that uses a different interface [or get the sources of the library and modify it so that it]. Not that this prevents a "stop, modify and go" attack using the debugger approach described above.
Create your file in your executable's folder using CreateFile API, You can give the file name some UUID, each time its created, so that no other process can guess the file name to open it. and set its attribute to hidden. After using it, just delete the file .Is it enough?
We currently face the problem of a call to WriteFile (or, rather CFile::Write - but that just calls WriteFile internally) causing the Win32 error 5 ERROR_ACCESS_DENIED.
(EDIT: Note that we can't repro the behavior. All we have at the moment is a logfile indicating the source line where the CFile::Write was and containing as error ERROR_ACCESS_DENIED!)
(EDIT: The file is on a local drive and it is in fact a file and not a directory.)
Now, WriteFiles's documentation doesn't really help, and experimenting with a simple test-app yields the following results:
WriteFile will cause ERROR_ACCESS_DENIED if it is called for a file handle that is not opened for writing (i.e. is opened for reading only).
It will not cause ERROR_ACCESS_DENIED if
The handle is not valid or the file isn't open at all
The access rights, or the write protected flag for the file are modified after the file has been opened by the process. (If these are modified before the file is opened, then we never get to WriteFile because opening the file will fail.)
The file is somehow locked by another process/handle (This will at best result in error 32 ERROR_SHARING_VIOLATION).
That leaves us with the situation, that apparently the only possibility for this call to fail if the file was actually opened with the read flag instead of the write flag. However, looking at our code, this seems extremely unlikely. (Due to our tracing, we can be sure that WriteFile failed and we can be sure that the error is ERROR_ACCESS_DENIED, we cannot be 100.1% sure of the opening flags, because these are not traced out.)
Are there any other known circumstances where WriteFile (CFile::Write) would cause an ERROR_ACCESS_DENIED?
Note: To additionally clarify the context of this question:
The file was open, therefore it can't be a directory or somesuch
All tests I performed indicate that while the file is open it cannot be deleted, so the file should still have been there on the call to WriteFile
The file is located on a local drive and not on a network drive.
I should add that we're running on WIndows XP sp3 and the app is compiled with Visual Studio 2005.
The question was
What causes WriteFile to return
ERROR_ACCESS_DENIED?
and I stated in the question
WriteFile will cause
ERROR_ACCESS_DENIED if it is called
for a file handle that is not opened
for writing (i.e. is opened for
reading only).
After adding further logging for the open flags and another incident, it turns out this was correct. The logging for the open flags shows that at the point of error, the file object was opened with CFile::modeRead and therefore we got ERROR_ACCESS_DENIED.
Haven't found out yet which weird code path leads to this, but this just goes to show: Never trust your own code. :-)
(Oh, and btw. It wasn't ::WriteFile that failed, but the ::FlushFileBuffers API, but apparently that returns the same error.)
There is about a dozen different situations that might result in ERROR_ACCESS_DENIED. Internally, all WriteFile does is call NtWriteFile and map its (somewhat meaningful) NTSTATUS error code into a less meaningful HRESULT.
Among other things, ERROR_ACCESS_DENIED could indicate that the file is on a network volume and something went wrong with write permissions, or that the file is really not a file but a directory.
if you can debug it, you should. it could be a million things:
msdn is wrong (it happens a lot)
some app (virus?) is hooking WriteFile and causing different behavior
filesystem problem?
something wrong in your logging, or observations
I'am planning to write a sample program which identifies a file (a dll file) locked by / used by some process.
How can we achieve this programmatically using WIN API (a C/C++ function)? Actually, when we are performing some software upgrade process some other process might be using the library which will interim fail the upgrade operation.
The best example I would like to bring here is the Unlocker tool which lists all the process/dll which are using a particular file.
You could try opening the file(s) yourself for exclusive access. If any other process has them open, this should fail.
I don't think it is possible to determine the processes without writing a driver. Fortunately, Russinovich's Handle tool includes such a driver; I suggest that you run this tool.
If you don't need to know which processes use the file in question, you can simply open the file for exclusive access using CreateFile.
::CreateFile(filename, 0, 0, 0, OPEN_EXISTING, 0, 0);
In Windows, a file is not 'locked' or 'unlocked'. If a file is open, the share mode specified when opening it determines if and under what circumstances other attempts to open the file succeed.
If the FILE_SHARE_NONE flag is specified, then the file is completely locked, and under no circumstances will any other attempt to open the file succeed. If FILE_SHARE_READ was specified, attempts to open the file with GENERIC_READ access will succeed, but GENERIC_WRITE will fail, for example. FILE_SHARE_WRITE allows other handles open for write access, and FILE_SHARE_DELETE the same for deletion.
Once you've decided which level of exclusion you consider to mean 'locked', you can just try to open each file with the relevant access and see if it fails with ERROR_SHARING_VIOLATION or not.
Seems to me the windows API provides EnumProcesses() to easily get a list of active processID and EnumProcessModules to get a list of module handles (that if EXE and DLL's associated with it) for each process; finally GetModuleFileNameEx() gives you the full path and filename of the loaded module.
Thus you could easily iterate through all the loaded DLL names and at least know which process was holding them if you detected a problem - and possibly automatically end that process.