Using GetLastError when using Dos Commands in C++ - c++

So i have a bit of code that uses Dos Commands to try to rename a folder. So
system("rename C:\\Users\\me\\SecondDir NewDir);
So this tries to rename SecondDir to NewDir. There is already a folder at that location called NewDir so it should fail. And it does. Im then using GetLastError to get the error code returned to ensure the problem is what i expect it to be. But it only ever returns ERROR_NO_MORE_FILES. Which isnt the error i should be getting, which is ERROR_ALREADY_EXISTS. Im assuming this is something to do with using the system command?
EDIT: I just checked and i even get ERROR_NO_MORE_FILES returned when a command is successful.

GetLastError will not return a meaningful value except in the circumstances where it is documented to do so. This is not one of them - the values you are getting are irrelevant and intended for someone else.
To rename a file from C you should use the C runtime rename function not use system to invoke a rename utility.
GetLastError is only meaningful immediately after calling a Win32 function which is documented to set the thread Last Error using SetLastError. The C equivalent is errno, which applies to C functions.
The rename function returns -1 on failure and sets errno.
E.g.: http://msdn.microsoft.com/en-us/library/zw5t957f(v=VS.80).aspx

Related

Check for error in command passed to popen API in cpp

There is a cpp application where I want to read following type of compressed file-
file_name.gz
file_name.Z
file_name.tar.gz
For this purpose, I check the file extension and choose decompression technique accordingly. E.g. file_name.gz will be decompressed using "gunzip -C file_name.gz".
I want to get the FILE handle for decompressed file. I use popen() API for it. Now, there might be a case where gunzip/uncompress/tar fails while decompressing the file due to memory issues. How do I capture the failure in my CPP application. There is way to check if popen failed or not. What about command passed to popen().
Please help. I tried to find it at various places but could not get satisfactory solution.
When a process terminates normally, it is expected to return the exit code of 0 (legally, EXIT_SUCCESS) to the parent. Otherwise, in the case of a crash or any other abnormal termination, a non-zero value is expected to be returned. You can obtain the exit code by calling pclose(). If the code is 0, the child process most probably terminated successfully.

Are ALL system() calls a security risk in c++?

A post in this (Are system() calls evil?) thread says:
Your program's privileges are inherited by its spawned programs. If your application ever runs as a privileged user, all someone has to do is put their own program with the name of the thing you shell out too, and then can execute arbitrary code (this implies you should never run a program that uses system as root or setuid root).
But system("PAUSE") and system("CLS") shell to the OS, so how could a hacker possibly intervene if it ONLY shells to a specific secure location on the hard-drive?
Does explicitly flush—by using fflush or _flushall—or closing any stream before calling system eliminate all risk?
The system function passes command to the command interpreter, which executes the string as an operating-system command. system uses the COMSPEC and PATH environment variables to locate the command-interpreter file CMD.exe. If command is NULL, the function just checks whether the command interpreter exists.
You must explicitly flush—by using fflush or _flushall—or close any stream before you call system.
https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/system-wsystem
In case, there are any doubts here's the actual snippet from the MS' implementation (very simple and straightforward):
// omitted for brevity
argv[1] = _T("/c");
argv[2] = (_TSCHAR *) command;
argv[3] = NULL;
/* If there is a COMSPEC defined, try spawning the shell */
/* Do not try to spawn the null string */
if (argv[0])
{
// calls spawnve on value of COMSPEC vairable, if present
// omitted for brevity
}
/* No COMSPEC so set argv[0] to what COMSPEC should be. */
argv[0] = _T("cmd.exe");
/* Let the _spawnvpe routine do the path search and spawn. */
retval = (int)_tspawnvpe(_P_WAIT,argv[0],argv,NULL);
// clean-up part omitted
As to concerns of what _tspawnvpe may actually be doing, the answer is: nothing magical. The exact invocation sequence for spawnvpe and friends goes as following (as anybody with licensed version of MSVC can easily learn by inspecting the spanwnvpe.c source file):
Do some sanity checks on parameters
Try to invoke _tspawnve on the passed file name. spawnve will succeed if the file name represents an absolute path to an executable or a valid path relative to the current working directory. No further checks are done - so yes, if a file named cmd.exe exists in current directory it will be invoked first in the context of system() call discussed.
In a loop: obtain the next path element using `_getpath()
Append the file name to the path element
Pass the resulted path to spwanvpe, check if it was successful
That's it. No special tricks/checks involved.
The original question references POSIX not windows. Here there is no COMSPEC (there is SHELL but system() deliberately does not use it); however /bin/sh is completely, utterly vulnerable.
Suppose /opt/vuln/program does system("/bin/ls"); Looks completely harmless, right? Nope!
$ PATH=. IFS='/ ' /opt/vuln/program
This runs the program called bin in the current directory. Oops. Defending against this kind of thing is so difficult it should be left to the extreme experts, like the guys who wrote sudo. Sanitizing environment is extremely hard.
So you might be thinking what is that system() api for. I don't actually know why it was created, but if you wanted to do a feature like ftp has where !command is executed locally in the shell you could do ... else if (terminalline[0] == '!') system(terminalline+1); else ... Since it's going to be completely insecure anyway there's no point in making it secure. Of course a truly modern use case wouldn't do it that way because system() doesn't look at $SHELL but oh well.

errno failure with rename RTEMS

I am using the rename function with RTEMS to attempt to rename files. I am setting up a correct error handling and reporting system when i discovered that RTEMS does not seem to be conforming to its own errno reporting guide.
So the function in failure will always return -1. The current example is in a directory </D\> where i have the following:
</D\LALALA>
</D\LALALA_2>
</D\OTHER_DIRECTORY>
I call rename("LALALA_1", "LALALA_2") which works correctly and the folder is renamed. So i know the function works. If i call rename("LALALA", "LALALA_2"), it fails returning -1... which is great, but instead of EEXIST i get errno == -1.
So this looks to be a bug with the RTEMS implementation.
https://lists.rtems.org/pipermail/bugs/2014-January/004755.html
It is not likely that it will ever be fixed.

Diagnosing QDir::rmdir failure

I’m using the following code to delete an empty folder on Linux:
bool removeFolder (const QString& path)
{
QDir dir(path);
assert(dir.exists());
return dir.rmdir(".");
}
For some reason it sometimes returns false (for specific folders, but those folders don’t seem to be wrong in any way). If I subsequently use ::rmdir from <unistd.h> to remove the same folder, it succeeds.
How can I tell why QDir::rmdir is failing?
This never happened on Windows so far, QDir::rmdir just works.
Confirming: works on windown, fails on linux.
Reading the "rmdir" doc in <unistd>, here https://pubs.opengroup.org/onlinepubs/007904875/functions/rmdir.html, it says there that "If the path argument refers to a path whose final component is either dot or dot-dot, rmdir() shall fail." So what's probably happening is that QDir::rmdir() is calling the unistd rmdir() function in linux, and this one fails with ".".
I tried to just use the full absolute path ( QDir::rmdir(absolutePath) ) and it worked; however, i see basically no point in using QDir::rmdir() over unistd's rmdir(), so i''ll stick w/ the unistd rmdir() from now on.
note: QDir::removeRecursively() is a different story: it seems to work okay, and it's way more convenient than going through opendir() and then successive readdir()'s (or the nftw(...FTW_DEPTH...) thingie).
I had the same problem but on Windows, I could not delete an empty directory with QDir().rmdir(path);. This happened on some older hard drive so may be the ancient file system was to blame. But I found a hack:
QFile(path).setPermissions(QFile::WriteOther); // this works even for dirs
bool success = QDir().rmdir(path);
Of course, you should revert the permissions back to original values if the deletion was unsuccessful anyway, but that's a different story.
Try to use this one:
dir.rmdir(dir.absolutePath())

Mystery HRESULT, 0x889000D

Decimal: 143196173
Hex: 0x889000D
Results from a call to IAudioSessionControl2->GetProcessId().
GetLastError = 126*
Message = "The specified module could not be found"
I'm not really sure how to interpret this error. Additionally, I can't find a description of the HRESULT anywhere. The documented return codes are S_OK, E_POINTER, AUDCLNT_E_NO_SINGLE_PROCESS, and AUDCLNT_E_DEVICE_INVALIDATED.
Anyone know what this code indicates?
*This is an error marshalled across a managed/unmanaged boundary, obtained by Marshal.GetLastError with a Win32Exception providing the message. It could be bogus, but its what I've got. The HRESULT is pulled out of the unmanaged code directly.
Further investigation, FAILED() doesn't seem to think this is an error. However, the out parameter is cleared (set to 0) which doesn't really make sense. Also, GetErrorInfo returns S_FALSE; so there isn't any additional debug info to go on.
This is AUDCLNT_S_NO_CURRENT_PROCESS - I realized that it somehow missed the Windows 7 SDK headers too late.
The SDK documentation is going to be updated to reflect this.
The result means that the session is a cross process session. The process ID returned is the process ID for the first process which created the session, but if you get this result, you really can't depend on the process ID since the process ID isn't unique.
COM methods can set IErrorInfo on failure. Try to retrieve it - it can contain additional information. In unmanaged code you use GetErrorInfo() for that.