how to purposely fail "fopen" - c++

I need to purposely fail the "fopen"-ing of a file to test my code. How do I do that. My code so far:
void MeshTrian::init (std::string filename)
{
if (!sofa::helper::system::DataRepository.findFile(filename))
{
msg_error() << "File '" << filename << "' not found." ;
return;
}
FILE *f = fopen(filename.c_str(), "r");
bool status;
msg_error_when(!(status=f))<<sofa::helper::message::UnableToOpenFile(filename.c_str());
if (status)
{
readTrian (f);
fclose(f);
}
}

Related

open file by std::ifstream occasionaly fails on folder detected by FindFirstChangeNotification (Windows)

I have an application that must monitor some folders (in Windows) to detect if a file was created in that folder (real use is to detect incoming FTP files).
If a file is detected , it is read, then deleted .
Occasionally, I get a file reading error on a file that was detected.
Question is: Why?
To simulate the error, I created a simple program to reproduce it:
std::vector<std::filesystem::path> watch;
void main()
{
watch.push_back("D:\\test1"); //must exist
watch.push_back("D:\\test2");
watch_dir();
}
this example monitors 2 folders.
To simulate incoming files on the folder, another program copies files to that folder
continuously at configurable intervals (say 100 milliseconds).
To detect folder changes , WIN32 API functions FindFirstChangeNotification and WaitForMultipleObjects are used, based on this Microsoft example
https://learn.microsoft.com/en-us/windows/win32/fileio/obtaining-directory-change-notifications
detection function adapted from the example (Note: WaitForMultipleObjects blocks until a change is detected)
void watch_dir()
{
HANDLE handle[2];
memset(handle, 0, 2 * sizeof(HANDLE));
for (size_t idx = 0; idx < watch.size(); idx++)
{
std::string str = watch.at(idx).string();
LPTSTR path = (LPTSTR)str.c_str();
std::cout << "watch path " << path << std::endl;
handle[idx] = FindFirstChangeNotification(
path, // directory to watch
FALSE, // do not watch subtree
FILE_NOTIFY_CHANGE_FILE_NAME); // watch file name changes
if (handle[idx] == INVALID_HANDLE_VALUE)
{
assert(0);
ExitProcess(GetLastError());
}
}
while (TRUE)
{
std::cout << "Waiting for notification..." << std::endl;
DWORD wait_status = WaitForMultipleObjects(watch.size(), handle, FALSE, INFINITE);
std::cout << "Directory " << watch.at(wait_status) << " changed" << std::endl;
if (FindNextChangeNotification(handle[wait_status]) == FALSE)
{
assert(0);
ExitProcess(GetLastError());
}
std::filesystem::path path = watch.at(wait_status);
send_files_in_path(path);
}
}
Once a change is detected by the function above, then all files in the folder are listed
and read, by these functions
void send_files_in_path(const std::filesystem::path& ftp_path)
{
std::vector<std::filesystem::path> list = get_files(ftp_path);
for (size_t idx = 0; idx < list.size(); idx++)
{
std::string buf;
read_file(list.at(idx).string(), buf);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::filesystem::remove(list.at(idx));
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//get_files
//get all ".txt" files inside a FTP folder
/////////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<std::filesystem::path> get_files(const std::filesystem::path& base_archive_path)
{
std::vector<std::filesystem::path> list;
try
{
for (const auto& entry : std::filesystem::recursive_directory_iterator(base_archive_path))
{
std::filesystem::path path = entry.path();
if (!entry.is_regular_file())
{
continue;
}
std::string fname = entry.path().filename().string();
size_t len = fname.size();
size_t pos = len - 4;
//check if last 4 characters are ".txt"
if (fname.find(".txt", pos) == std::string::npos && fname.find(".TXT", pos) == std::string::npos)
{
continue;
}
SPDLOG_INFO("loading: " + entry.path().string());
list.push_back(path);
}//this path
} //try
catch (const std::exception& e)
{
SPDLOG_ERROR(e.what());
}
return list;
}
The function where the error happens is
int read_file(const std::string& fname, std::string& buf)
{
std::ifstream ifs;
std::ios_base::iostate mask = ifs.exceptions() | std::ios::failbit;
ifs.exceptions(mask);
std::this_thread::sleep_for(std::chrono::milliseconds(0));
std::cout << "opening : " << fname << std::endl;
try
{
ifs.open(fname);
if (!ifs.is_open())
{
std::cout << "open fail: " << fname << std::endl;
return -1;
}
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
return -1;
}
std::stringstream ss;
ss << ifs.rdbuf();
ifs.close();
buf = ss.str();
return 0;
}
the try/catch block, again, occasionally , is triggered with the error
ios_base::failbit set: iostream stream error
removing the try/catch block, and the open mask (just to try), then
ifs.is_open
fails.
A temporary solution was to detect the cases where the open() failed and repeat it.. which succeeds, because the file does exist.
Calling this with a small delay before the open call has the effect of reducing the open fails
std::this_thread::sleep_for(std::chrono::milliseconds(10));
ifs.open(fname);
But still would like to find out the reason for the occasional failure

Windows service cannot read file with owner other than LOCAL_SERVICE

I have a Windows service written in C++ with the Win32 API. Before entering in "service" mode, the C++ program tries to read a configuration file specified as an absolute path. But the program cannot read it and exits. Some debugging leaves me to suspect that this is because of file ownership.
Question is, how can I modify the file ownership (preferably with a power-shell script) , so that the file can be read?
Here are the relevant parts
main program (exits, file cannot be read)
int main()
{
std::string cfg_file_name = config::get_config(config::comm_config_file);
if (cfg.read(cfg_file_name) < 0)
{
events::start_log(cfg.log_path, cfg.log_spdlog_level);
SPDLOG_CRITICAL("Cannot read: " + cfg_file_name);
return 1;
}
function get_config() uses some Win32 API calls to get the executable path (where the file is located) and concatenates it with the file name, to get an absolute path
std::string config::get_config(const std::string& config_name)
{
#ifdef _MSC_VER
std::string s = config::get_executable_path();
//this is done before log starts; it will be written to C:\Windows\System32 as a first log/debugging tool
std::ofstream ofs("comm.txt");
ofs << "GetModuleFileName: " << s << std::endl;
TCHAR buf[MAX_PATH];
GetCurrentDirectory(MAX_PATH, buf);
ofs << "GetCurrentDirectory: " << buf << std::endl;
//change the current directory of the process to be the executable path
if (SetCurrentDirectory(s.c_str()) == 0)
{
ofs << "SetCurrentDirectory" << std::endl;
}
GetCurrentDirectory(MAX_PATH, buf);
ofs << "GetCurrentDirectory: " << buf << std::endl;
s += config_name;
ofs.close();
return s;
#else
return config_name;
#endif
}
service is created by a power-shell script
sc.exe create _comm_ftp_server binPath= "$install_dir\ftp_server.exe" start= auto obj= "NT AUTHORITY\LocalService" password= " "
to debug it, I wrote a simple test service that writes a file and reads that same file, with no problem (so, a file can be read)
int main(int argc, char* argv[])
{
std::string path = config::get_executable_path();
cfg.log_path = path;
events::start_log(cfg.log_path, "trace", true);
//A service process has a SERVICE_TABLE_ENTRY structure for each service that it can start.
//The structure specifies the service name and a pointer to the service main function for that service.
//The main function of a service program calls the StartServiceCtrlDispatcher
//function to connect to the service control manager (SCM)
SERVICE_TABLE_ENTRY service_table[] =
{
{ (LPSTR)service_name, (LPSERVICE_MAIN_FUNCTION)service_main },
{ NULL, NULL }
};
if (StartServiceCtrlDispatcher(service_table))
{
return 0;
}
else
{
return 1;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//service_main
/////////////////////////////////////////////////////////////////////////////////////////////////////
void WINAPI service_main(DWORD argc, LPTSTR* argv)
{
service_handle = RegisterServiceCtrlHandler(service_name, service_handler);
if (service_handle == NULL)
{
return;
}
service_stop_event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (service_stop_event == NULL)
{
return;
}
report_status(SERVICE_START_PENDING);
report_status(SERVICE_RUNNING);
SPDLOG_INFO("service running..." + std::to_string(current_state));
HANDLE thread_service = 0;
thread_service = CreateThread(NULL, 0, service_thread, NULL, 0, NULL);
WaitForSingleObject(thread_service, INFINITE);
/////////////////////////////////////////////////////////////////////////////////////////////////////
//service shutdown requested
/////////////////////////////////////////////////////////////////////////////////////////////////////
CloseHandle(thread_service);
report_status(SERVICE_STOP_PENDING);
SPDLOG_INFO("service stop pending..." + std::to_string(current_state));
CloseHandle(service_stop_event);
report_status(SERVICE_STOPPED);
SPDLOG_INFO("service stopped..." + std::to_string(current_state));
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//service_thread
/////////////////////////////////////////////////////////////////////////////////////////////////////
DWORD WINAPI service_thread(LPVOID lpParam)
{
std::string path = cfg.log_path;
SPDLOG_INFO("service started in..." + cfg.log_path);
path += "\\test.txt";
size_t i = 0;
while (WaitForSingleObject(service_stop_event, 0) != WAIT_OBJECT_0)
{
write_txt_file(path, "writing...#" + std::to_string(i));
i++;
Sleep(10000);
read_txt_file(path);
}
return ERROR_SUCCESS;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//write_txt_file
/////////////////////////////////////////////////////////////////////////////////////////////////////
void write_txt_file(const std::string& file_name, const std::string& input)
{
FILE* f = fopen(file_name.c_str(), "a+");
fprintf(f, "%s\n", input.c_str());
fclose(f);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//read_txt_file
/////////////////////////////////////////////////////////////////////////////////////////////////////
void read_txt_file(const std::string& file_name)
{
std::ifstream ifs;
ifs.open(file_name);
if (!ifs.is_open())
{
SPDLOG_ERROR("Cannot open: " + file_name);
return;
}
std::string line;
while (std::getline(ifs, line))
{
SPDLOG_INFO("Line: " + line);
}
ifs.close();
}
Examining the file written by the test service in Windows explorer (Properties->Details) reveals a file owner as LOCAL_SERVICE
The file that must be read has owner "Administrators"
This leaves me to suspect that this is the problem. How can the file ownership be changed, or is there a way to create the service with privileges that can read any file ?
reference for SC.EXE Create
https://learn.microsoft.com/en-US/windows-server/administration/windows-commands/sc-create
To read the file, std::ifstream is used (default read only)
int config::config_t::read(const std::string& fname)
{
try
{
std::ifstream ifs(fname);
ifs >> configuration_json;
ifs.close();
from_json(configuration_json, *this);
}
catch (const std::exception& e)
{
SPDLOG_ERROR(e.what());
return -1;
}
return 0;
}
The read error was because the library JSON for modern C++
https://github.com/nlohmann/json
detects an error reading the last entry of this file because of the comma ","
{
"archive_path":"D:\\archive",
"test_comm_input_path":"D:\\test_comm_input_path",
}
in the reading function
int config::config_t::read(const std::string& fname)
{
//this is done before log starts; it will be written to the executable path as a first log/debugging tool in service mode
std::ofstream ofs("comm.txt");
try
{
std::ifstream ifs;
std::ios_base::iostate mask = ifs.exceptions() | std::ios::failbit;
ifs.exceptions(mask);
ifs.open(fname);
if (!ifs.is_open())
{
ofs << "open fail: " << fname << std::endl;
ofs.close();
return -1;
}
else
{
ofs << "open: " << fname << std::endl;
}
ifs >> configuration_json;
ifs.close();
from_json(configuration_json, *this);
ofs << "read: " << fname << std::endl;
ofs.close();
}
catch (const std::exception& e)
{
ofs << "json read: " << e.what() << std::endl;
ofs.close();
return -1;
}
return 0;
}
where
void from_json(const nlohmann::json& j, config::config_t& c)
{
if (j.contains("archive_path"))
{
j.at("archive_path").get_to(c.archive_path);
}
}

c++ How to put path from command line argument to fprintf

I am having trouble figuring out how to take a path from the command line argument and having my program write to that folder using fprinf. For example: /here would write the file to the "here" folder with /here being the path from where the program is to where it needs to be written. Same for if the path were /there/here. This is just this section of my code:
void write_file(int sockfd, char* path)
{
int n;
FILE *fp;
char filename[] = "file1.txt";
char buffer[SIZE];
fp = fopen(filename, "w");
if (fp == NULL)
{
std::cerr << "ERROR: Could not write file.\n";
exit(EXIT_FAILURE);
}
while (1)
{
n = recv(sockfd, buffer, SIZE, 0);
if (n <= 0)
{
break;
return;
}
fprintf(fp, "%s", buffer);
bzero(buffer, SIZE);
}
fclose(fp);
return;
}
This code works but saves the file in my current folder. The variable "path" is the command line argument.
Edit: This is code that does not print lines to a file. Instead it copies lines from an existing file and puts those lines into the "file1.txt" file.
Here's an example using std::filesystem (C++17):
void write_file(int sockfd, const char* path)
{
auto filename = std::filesystem::path(path) / "file1.txt";
std::ofstream out{ filename, ios::trunc | ios::out };
if (!out) {
std::cerr << "ERROR: " << std::strerror(errno) << "\n";
exit(EXIT_FAILURE);
}
char buffer[SIZE];
while (1)
{
int n = recv(sockfd, buffer, sizeof(buffer), 0);
if (n == 0)
{
break;
}
if (n < 0) {
std::cerr << "ERROR: " << std::strerror(errno) << "\n";
break;
}
if (!out.write(buffer, n)) {
std::cerr << "ERROR: " << std::strerror(errno) << "\n";
break;
}
}
}
If you don't have std::filesystem support, you can replace the first 2 statements with some manual path concatenation like:
std::string filename = path;
if (filename != "" && filename.back() != '/')
filename += '/';
filename += "file1.txt";

ifstream: /dev/stdin is not working the same as std::cin

For my formation, an exercise ask us to create a program similar to the linux 'cat' command.
So to read the file, i use an ifstream, and everything work fine for regular file.
But not when i try to open /dev/ files like /dev/stdin: the 'enter' is not detected and so, getline really exit only when the fd is being closed (with a CTRL-D).
The problem seems to be around how ifstream or getline handle reading, because with the regular 'read' function from libc, this problem is not to be seen.
Here is my code:
#include <iostream>
#include <string>
#include <fstream>
#include <errno.h>
#ifndef PROGRAM_NAME
# define PROGRAM_NAME "cato9tails"
#endif
int g_exitCode = 0;
void
displayErrno(std::string &file)
{
if (errno)
{
g_exitCode = 1;
std::cerr << PROGRAM_NAME << ": " << file << ": " << strerror(errno) << std::endl;
}
}
void
handleStream(std::string file, std::istream &stream)
{
std::string read;
stream.peek(); /* try to read: will set fail bit if it is a folder. */
if (!stream.good())
displayErrno(file);
while (stream.good())
{
std::getline(stream, read);
std::cout << read;
if (stream.eof())
break;
std::cout << std::endl;
}
}
int
main(int argc, char **argv)
{
if (argc == 1)
handleStream("", std::cin);
else
{
for (int index = 1; index < argc; index++)
{
errno = 0;
std::string file = std::string(argv[index]);
std::ifstream stream(file, std::ifstream::in);
if (stream.is_open())
{
handleStream(file, stream);
stream.close();
}
else
displayErrno(file);
}
}
return (g_exitCode);
}
We can only use method from libcpp.
I have search this problem for a long time, and i only find this post where they seems to have a very similar problem to me:
https://github.com/bigartm/bigartm/pull/258#issuecomment-128131871
But found no really usable solution from them.
I tried to do a very ugly solution but... well...:
bool
isUnixStdFile(std::string file)
{
return (file == "/dev/stdin" || file == "/dev/stdout" || file == "/dev/stderr"
|| file == "/dev/fd/0" || file == "/dev/fd/1" || file == "/dev/fd/2");
}
...
if (isUnixStdFile(file))
handleStream(file, std::cin);
else
{
std::ifstream stream(file, std::ifstream::in);
...
As you can see, a lot of files are missing, this can only be called a temporary solution.
Any help would be appreciated!
The following code worked for me to deal with /dev/fd files or when using shell substitute syntax:
std::ifstream stream(file_name);
std::cout << "Opening file '" << file_name << "'" << std::endl;
if (stream.fail() || !stream.good())
{
std::cout << "Error: Failed to open file '" << file_name << "'" << std::endl;
return false;
}
while (!stream.eof() && stream.good() && stream.peek() != EOF)
{
std::getline(stream, buffer);
std::cout << buffer << std::endl;
}
stream.close();
Basically std::getline() fails when content from the special file is not ready yet.

how to use mount function from c?

I want to use mount function to implement NFS.
int mount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const void *data);
I can implement it by using mount command e.g mount 172.16.0.144:/tmp/test /tmp/test. But when I use the mount() function , it doesn't work. This is my code here .
#include<sys/mount.h>
#include<iostream>
#include<errno.h>
#include<fstream>
#include<string.h>
using namespace std;
int main(int argc, char**argv) {
const char* srcPath = "/tmp/watchman";
const char* targetPath = "172.16.0.144:/tmp/watchman";
if (argc == 3) {
srcPath = argv[1];
targetPath = argv[2];
cerr << "reset the src && target path\n";
} else {
if (argc != 1) {
cerr << "wrong input argument!\n";
return 0;
}
}
cerr << "srcPath = " << srcPath << endl;
cerr << "target = " << targetPath << endl;
int ret_val = mount(srcPath, targetPath, "", MS_SHARED, "");
if (ret_val == 0) {
cerr << "mount succeed\n";
string filename = string(srcPath) + "/" + "tmp.txt";
fstream fin(filename.c_str(), ios::out);
fin << "there is a write test from client\n";
fin.close();
ret_val = umount(srcPath);
if (ret_val == 0) {
cerr << "umount succeed \n";
} else {
cerr << "umount failed \n";
printf("%s/n", strerror(errno));
}
} else {
cout<<"ret_val = "<<ret_val<<endl;
cerr << "mount failed \n";
cerr << strerror(errno) << endl;
}
return 0;
}
It printf mount failed,No such file or directory. anyone can help me? please !!!
If you read the mount manual page you will see that
mount() attaches the filesystem specified by source (which is often a pathname referring to a device, but can also be the pathname of a directory or file, or a dummy string) to the location (a directory or file) specified by the pathname in target.
You have switched the source and target in your application.