How to check if a read-only file exists - c++

I'm looking for a way to check for a read-only file's existence without opening it. I've tried:
if (std::filesystem::exists("C:\\Windows\\System32\\hal.dll")) {
std::cout << "Exists" << std::endl;
}
else {
std::cout << "Does not exist" << std::endl;
}
But it returns "does not exist". I'm looking for a solution that can check the existence of files like these.

std::filesystem::exists() works fine. But, you might not be querying the file you are expecting.
First, not everyone installs Windows at C:\Windows, so you should ask the OS where it is actually installed, by using GetWindowsDirectory(), SHGetFolderPath(CSIDL_WINDOWS), or SHGetKnownFolderPath(FOLDERID_Windows), eg:
namespace fs = std::filesystem;
fs::path getWindowsPath() {
WCHAR szPath[MAX_PATH] = {};
GetWindowsDirectoryW(szPath, MAX_PATH);
return szPath;
// or equivalent...
}
Second, on 64-bit Windows systems, if a 32-bit app running inside the WOW64 emulator tries to access the 64-bit %WINDIR%\System32 folder, by default the access will get redirected to the 32-bit %WINDIR%\SysWOW64 folder instead, which could explain why your exists() check is not working.
To access the 64-bit System32 folder when running under WOW64, you have to either:
use the special SysNative alias, eg:
bool isWow64() {
#ifdef _WIN64
return false;
#else
BOOL bIsWow64 = FALSE;
return IsWow64Process(GetCurrentProcess(), &bIsWow64) && bIsWow64;
#endif
}
fs::path getSystemPath() {
if (isWow64()) {
return getWindowsPath() / "SysNative";
}
else {
return getWindowsPath() / "System32";
/* alternatively:
WCHAR szPath[MAX_PATH] = {};
GetSystemDirectoryW(szPath, MAX_PATH);
return szPath;
or equivalent...
*/
}
}
...
if (std::filesystem::exists(getSystemPath() / "hal.dll")) {
std::cout << "Exists" << std::endl;
}
else {
std::cout << "Does not exist" << std::endl;
}
disable the redirection temporarily by using the Wow64DisableWow64FsRedirection() function, eg:
fs::path getSystemPath() {
return getWindowsPath() / "System32";
// or equivalent ...
}
...
PVOID oldValue = NULL;
if (isWow64()) {
Wow64DisableWow64FsRedirection(&oldValue);
}
if (std::filesystem::exists(getSystemPath() / "hal.dll")) {
std::cout << "Exists" << std::endl;
}
else {
std::cout << "Does not exist" << std::endl;
}
if (isWow64()) {
Wow64RevertWow64FsRedirection(oldValue);
}
See File System Redirector on MSDN for more details.

If in Windows, function GetFileAttributes returns attributes if the file is there without opening. On error, it returns INVALID_FILE_ATTRIBUTES and then you can check with GetLastError().
If you are checking in order to open it later, this can lead to a race condition (TOCTOU bug as mentioned).
Besides that, there do exist reasons to check for the file existance without planning to open it, for example for an installer to see if the file to be replaced is there and warn the user.

Related

Loading a DLL with LoadLibraryA(path_to_dll) is changing the inherit handle flag (HANDLE_FLAG_INHERIT) from 1 to 0 for file descriptors 0, 1, and 2

We have written some functions in golang and a c wrapper on top of that to invoke those functions. We first build golang code to create an archive file and then we build the wrapper code in c to be consumed as a DLL.
After loading this DLL using LoadLibraryA(path_to_dll) in my program I am seeing that the inherit flags for fd 0, 1, and 2 are getting changed from 1 to 0. This does not happen immediately after loading the DLL though. I had added sleep in my code after the load library call and seems like it takes a few milliseconds after loading the library to change the flag values.
I am using GetHandleInformation((HANDLE) sock, &flags) to get the inherit flag value.
Any idea/pointers on what could be causing this? Thanks!
Updates:
I was able to find out the exact line in the go code that is flipping the inherit flag values. The global variable reqHandler in below k8sService.go code is causing this. Any idea why the use of this global variable is flipping the inherit flag values?
my-lib/k8sService.go (go code)
package main
import "C"
import (
"my-lib/pkg/cmd"
)
func main() {
}
var reqHandler []*cmd.K8sRequest
my-lib/pkg/cmd/execute.go
import (
"my-lib/pkg/dto"
)
type K8sRequest struct {
K8sDetails dto.K8sDetails
}
my-lib/pkg/dto/structs.go
package dto
// K8sDetails contains all the necessary information about talking to the cluster. Below struct has few more variables.
type K8sDetails struct {
// HostName of the cluster's API server
HostName string `json:"hostname"`
// Port on which the API server listens on to
Port int `json:"port"`
}
We have a C wrapper on top of the above k8sService.go. We first build golang code to create an archive file and then with this archive file and wrapper code in C we build the target DLL. Below is the sample program which loads this DLL and also prints the inherit flag values before and after loading the DLL.
#include <windows.h>
#include <iostream>
#include <io.h>
#include "wrapper/cWrapper.h"
void printInheritVals() {
typedef SOCKET my_socket_t;
my_socket_t fd0 = _get_osfhandle(0);
my_socket_t fd1 = _get_osfhandle(1);
my_socket_t fd2 = _get_osfhandle(2);
std::cout << "fd0: " << fd0 << std::endl;
std::cout << "fd1: " << fd1 << std::endl;
std::cout << "fd2: " << fd2 << std::endl;
DWORD flags;
int inherit_flag_0 = -1;
int inherit_flag_1 = -1;
int inherit_flag_2 = -1;
if (!GetHandleInformation((HANDLE) fd0, &flags)) {
std::cout << "GetHandleInformation failed" << std::endl;
} else {
inherit_flag_0 = (flags & HANDLE_FLAG_INHERIT);
}
if (!GetHandleInformation((HANDLE) fd1, &flags)) {
std::cout << "GetHandleInformation failed" << std::endl;
} else {
inherit_flag_1 = (flags & HANDLE_FLAG_INHERIT);
}
if (!GetHandleInformation((HANDLE) fd2, &flags)) {
std::cout << "GetHandleInformation failed" << std::endl;
} else {
inherit_flag_2 = (flags & HANDLE_FLAG_INHERIT);
}
std::cout << "inherit_flag_0: " << inherit_flag_0 << std::endl;
std::cout << "inherit_flag_1: " << inherit_flag_1 << std::endl;
std::cout << "inherit_flag_2: " << inherit_flag_2 << std::endl;
}
int main()
{
printInheritVals(); // In output all flag values are 1
HINSTANCE hGetProcIDDLL = LoadLibraryA(PATH_TO_DLL);
if (!hGetProcIDDLL) {
std::cout << "could not load the dynamic library" << std::endl;
return EXIT_FAILURE;
}
std::cout << "Library loaded" << std::endl;
printInheritVals(); // In output all flag values are 1
Sleep(1000);
printInheritVals(); // In output all flag values are 0
return EXIT_SUCCESS;
}
This is a bug in the golang.org/x/sys/windows package. The same issue used to be in the built-in syscall package as well, but it was fixed in Go 1.17.
Something in your project must be importing the golang.org/x version of the package instead of the built-in one, and so the following code executes to initialize the Stdin, Stdout, and Stderr variables:
var (
Stdin = getStdHandle(STD_INPUT_HANDLE)
Stdout = getStdHandle(STD_OUTPUT_HANDLE)
Stderr = getStdHandle(STD_ERROR_HANDLE)
)
func getStdHandle(stdhandle uint32) (fd Handle) {
r, _ := GetStdHandle(stdhandle)
CloseOnExec(r)
return r
}
The fix for that code would be to remove the CloseOnExec call, which is what clears HANDLE_FLAG_INHERIT on the given file handle.
How to solve that in your project is less clear. I think you can vendor golang.org/x/sys module in your project, perhaps with a replace directive in your go.mod. Apply the fix in your local copy.
Meanwhile, I encourage you to also report the bug. The documentation instructs you to report the issue on the main Go project at GitHub, prefixing the title with x/sys.

boost::shared_ptr seems to disappear when using Linux

I am using a boost::shared_ptr to point to a plugin class. Plugin is a map <string, shared_ptr>. The first time I find a certain plugin in the map, it works fine. However, any subsequent time I try to find a particular plugin, I get a SIGSEGV error. When stepping through my code, I get to foundPlugin = a->second->onCommand(command);and find that a->second is not accessible anymore. This error only happens when I am running in Linux, however. I have no issues while running in Windows. Is there some sort of issue with boost::shared_ptr and linux? I have tried using std::shared_ptr, but I have to use a boost::dll::import function that returns a boost::shared_ptr, and I haven't found an alternative for that yet. Any insight is greatly appreciated!
I load plugins like this:
bool PluginManager::loadPlugin(std::string pluginPath, std::string
pluginName, std::string pluginType)
{
bool couldLoad = false;
boost::filesystem::path libPath = boost::filesystem::current_path();
boost::shared_ptr<my_plugin_api> plugin;
std::cout << "Loading the plugin " << pluginName << std::endl;
if (pluginName == "")
{
pluginName = "plugName";
}
try
{
plugin = boost::dll::import<my_plugin_api>(
libPath / pluginName,
pluginType,
dll::load_mode::append_decorations
);
Plugin.insert(std::pair<std::string,boost::shared_ptr<my_plugin_api>>
(pluginName, plugin));
std::cout << "Loading the plugin " << pluginName << " (SUCCESS)" <<
std::endl;
couldLoad = true;
}
catch (const std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return couldLoad;
}
After much more testing, I feel like my problems are in the above section of code. the boost::dll::import function acts as if it finds a .so, but does not return anything in the boost::shared_ptr, which in turn causes the second snippet of code to fail. Any ideas of why this boost::dll::import function might be acting weirdly in Linux?
bool PluginManager::onCommand(const char* command, const char* pluginName)
{
bool foundPlugin = false;
auto a = Plugin.find(pluginName);
if (a == Plugin.end())
{
std::cerr << "plugin " << pluginName << " not found" << std::endl;
}
else
{
foundPlugin = a->second->onCommand(command);
}
return foundPlugin;
}

Windows path problems using libssh C++ wrapper

I am having problems with Windows file path separators using the libssh c++ wrapper libsshpp.
Suppose I have following code:
#define SSH_NO_CPP_EXCEPTIONS
#include "libssh/libsshpp.hpp"
#include <iostream>
#pragma comment(lib, "ssh")
int main()
{
ssh::Session session;
int sessionMsg = -1;
std::string host = "myhost.com";
std::string user = "username";
std::string idfile = "%s\\.ssh\\id_ed25519";
std::string hostkeys = "ssh-ed25519";
std::string keyExchange = "curve25519-sha256";
session.setOption(SSH_OPTIONS_HOST, host.c_str());
session.setOption(SSH_OPTIONS_USER, user.c_str());
session.setOption(SSH_OPTIONS_STRICTHOSTKEYCHECK, (long)0);
session.setOption(SSH_OPTIONS_HOSTKEYS, hostkeys.c_str());
session.setOption(SSH_OPTIONS_KEY_EXCHANGE, keyExchange.c_str());
session.setOption(SSH_OPTIONS_ADD_IDENTITY, idfile.c_str());
std::cout << "Trying to connect to " << host << " with user " << user << "...\n";
session.connect();
if (session.isServerKnown() != SSH_SERVER_KNOWN_OK) {
std::cout << "Server unknown.\n";
if (session.writeKnownhost() != SSH_OK) {
std::cout << "Unable to write to known_hosts file.\n";
}
else {
session.connect();
}
}
sessionMsg = session.userauthPublickeyAuto();
std::string err = session.getError();
if (sessionMsg != SSH_AUTH_SUCCESS) {
if (!err.empty()) {
std::cout << err;
}
std::cout << "Auth failed.";
}
else {
std::cout << err.empty() ? session.getIssueBanner() : err;
}
}
In the beginning I had set the idfile value to just id_ed25519 but then libssh complained: Failed to read private key: C:\Users\MyUser/.ssh/id_ed25519 (notice the switching slashes). After changing it to %s\\.ssh\\id_ed25519 it seemed to have had a positive impact on the connection routine, however now I keep falling into the (session.writeKnownhost() != SSH_OK) code part.
Now, I am wondering if this might be due to the same "switching slashes" problem which came up for the private key file path because apparently libssh wants to access C:\Users\MyUser\.ssh\known_hosts but quite possibly the path is set as something like C:\Users\MyUser/.ssh/known_hosts.
My question is: is there a possibility to change the path seperators to windows-style somehow in the session or is there something else I am overseeing or doing wrong here?
I was able to solve the problem adding the SSH_OPTIONS_SSH_DIR option and changing the private key and known_hosts paths (now relative to the ssh directory path):
// note here: %s will be replaced by libssh with the home directory path
std::string sshDir = "%s//.ssh";
std::string idFile = "id_ed25519";
std::string knownHosts = "known_hosts";
// ...
session.setOption(SSH_OPTIONS_USER, user.c_str());
session.setOption(SSH_OPTIONS_SSH_DIR, sshDir.c_str()); // <-- added
// ...

c++ - _mkdir giving false errors windows

Hi I am trying to make a directory in windows with this code
header
#include <direct.h>
script
int main() {
string local = "C:/Program Files (x86)/Mail";
try
{
_mkdir (local.c_str ());
cout << "It is made?";
}
catch(invalid_argument& e)
{
cout << e.what () << " " << (char*) EEXIST;
if (e.what () == (char*) EEXIST) {
cout << e.what () << " " << (char*) EEXIST;
}
return;
}
}
The file is clearly not made, but it is also not making the error it should.
_mkdir won't throw an exception. (This is not python or boost, or any smart middleware)
Read the documentation you were referring to: it returns a value. 0 is OK, -1: error, ask why to errno
Don't ignore the return value. You probably have insufficient rights without UAC elevation to create the directory.
So I finally figured errno out, which for errno you need the <errno.h> header. The complete list of errno codes.
If you want to see what errno code something is throwing lets say
if (
_mkdir(((string)"C:/Program Files (x86)/Mail").c_str()) == 0 ||
errno == 17 /* this is the code for - File exists - */
){
// Do stuff
} else {
int errorCode = errno; // You need to save the code before anything else,
// because something else might change its value
cout << errorCode;
}

How to check if a file exists before creating a new file

I want to input some contents to a file, but I'd like to check first if a file with the name I wish to create exists. If so, I don't want to create any file, even if the file is empty.
My attempt
bool CreateFile(char name[], char content[]){
std::ofstream file(name);
if(file){
std::cout << "This account already exists" << std::endl;
return false;
}
file << content;
file.close();
return true;
}
Is there any way to do what I want?
Assuming it is OK that the operation is not atomic, you can do:
if (std::ifstream(name))
{
std::cout << "File already exists" << std::endl;
return false;
}
std::ofstream file(name);
if (!file)
{
std::cout << "File could not be created" << std::endl;
return false;
}
...
Note that this doesn't work if you run multiple threads trying to create the same file, and certainly will not prevent a second process from "interfering" with the file creation because you have TOCTUI problems. [We first check if the file exists, and then create it - but someone else could have created it in between the check and the creation - if that's critical, you will need to do something else, which isn't portable].
A further problem is if you have permissions such as the file is not readable (so we can't open it for read) but is writeable, it will overwrite the file.
In MOST cases, neither of these things matter, because all you care about is telling someone that "you already have a file like that" (or something like that) in a "best effort" approach.
you can also use Boost.
boost::filesystem::exists( filename );
it works for files and folders.
And you will have an implementation close to something ready for C++14 in which filesystem should be part of the STL (see here).
Try
ifstream my_file("test.txt");
if (my_file)
{
// do stuff
}
From: How to check if a file exists and is readable in C++?
or you could use boost functions.
Try this (copied-ish from Erik Garrison: https://stackoverflow.com/a/3071528/575530)
#include <sys/stat.h>
bool FileExists(char* filename)
{
struct stat fileInfo;
return stat(filename, &fileInfo) == 0;
}
stat returns 0 if the file exists and -1 if not.
As of C++17 there is:
if (std::filesystem::exists(pathname)) {
...
Looked around a bit, and the only thing I find is using the open system call. It is the only function I found that allows you to create a file in a way that will fail if it already exists
#include <fcntl.h>
#include <errno.h>
int fd=open(filename, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fd < 0) {
/* file exists or otherwise uncreatable
you might want to check errno*/
}else {
/* File is open to writing */
}
Note that you have to give permissions since you are creating a file.
This also removes any race conditions there might be
I just saw this test:
bool getFileExists(const TCHAR *file)
{
return (GetFileAttributes(file) != 0xFFFFFFFF);
}
C++17, cross-platform: Using std::filesystem::exists and std::filesystem::is_regular_file.
#include <filesystem> // C++17
#include <fstream>
#include <iostream>
namespace fs = std::filesystem;
bool CreateFile(const fs::path& filePath, const std::string& content)
{
try
{
if (fs::exists(filePath))
{
std::cout << filePath << " already exists.";
return false;
}
if (!fs::is_regular_file(filePath))
{
std::cout << filePath << " is not a regular file.";
return false;
}
}
catch (std::exception& e)
{
std::cerr << __func__ << ": An error occurred: " << e.what();
return false;
}
std::ofstream file(filePath);
file << content;
return true;
}
int main()
{
if (CreateFile("path/to/the/file.ext", "Content of the file"))
{
// Your business logic.
}
}
The easiest way to do this is using ios :: noreplace.