flock-ing a C++ ifstream on Linux (GCC 4.6) - c++

context
I'm slowly writing a specialized web server application in C++ (using the C onion http server library and the JSONCPP library for JSON serialization, if that matters)., for a Linux system with GCC 4.6 compiler (I don't care about portability to non Linux systems, or to GCC before 4.5 or to Clang before 3.0).
I decided to keep the user "database" (there will be very few users, probably one or two, so performance is not a concern, and O(n) access time is acceptable) in JSON format, probably as a small array of JSON objects like
{ "_user" : "basile" ;
"_crypasswd" : "XYZABC123" ;
"_email" : "basile#starynkevitch.net" ;
"firstname" : "Basile" ;
"lastname" : "Starynkevitch" ;
"privileges" : "all" ;
}
with the convention (à la .htpasswd) that the _crypasswd field is the crypt(3) "encryption" of the user password, salted by the _user name;
The reason I want to describe users by Json objects is that my application might add (not replace) some JSON fields (like e.g. privileges above) in such Json objects describing users. I'm using JsonCpp as a Json parsing library for C++. This library wants an ifstream to be parsed.
So I am reading my password file with
extern char* iaca_passwd_path; // the path of the password file
std::ifstream jsinpass(iaca_passwd_path);
Json::Value jpassarr;
Json::Reader reader;
reader.parse(jsinpass,jpassarr,true);
jsinpass.close();
assert (jpassarr.isArray());
for (int ix=0; ix<nbu; ix++) {
const Json::Value&jcuruser= jpassarr[ix];
assert(jcuruser.isObject());
if (jcuruser["_user"].compare(user) == 0) {
std::string crypasswd = jcuruser["_crypasswd"].asString();
if (crypasswd.compare(crypted_password(user,password)) == 0) {
// good user
}
}
}
question
Obviously, I want to flock or lockf the password file, to ensure that only one process is reading or writing it. To call these functions, I need to get the file descriptor (in Unix parlance) of the ifstream jsinpass. But Google gives me mostly Kreckel's fileno (which I find complete, but a bit insane) to get the file descriptor of an std::ifstream and I am not sure that the constructor won't pre-read some of it. Hence my question:
how can I lock a C++ ifstream (Linux, GCC 4.6) ?
(Or do you find some other way to tackle that issue?)
Thanks

My solution to this problem is derived from this answer: https://stackoverflow.com/a/19749019/5899976
I've only tested it with GCC 4.8.5.
#include <cstring> // for strerror()
#include <iostream> // for std::cerr
#include <fstream>
#include <ext/stdio_filebuf.h>
extern "C" {
#include <errno.h>
#include <sys/file.h> // for flock()
}
// Atomically increments a persistent counter, stored in /tmp/counter.txt
int increment_counter()
{
std::fstream file( "/tmp/counter.txt" );
if (!file) file.open( "/tmp/counter.txt", std::fstream::out );
int fd = static_cast< __gnu_cxx::stdio_filebuf< char > * const >( file.rdbuf() )->fd();
if (flock( fd, LOCK_EX ))
{
std::cerr << "Failed to lock file: " << strerror( errno ) << "\n";
}
int value = 0;
file >> value;
file.clear(); // clear eof bit.
file.seekp( 0 );
file << ++value;
return value;
// When 'file' goes out of scope, it's closed. Moreover, since flock() is
// tied to the file descriptor, it gets released when the file is closed.
}

You might want to use a separate lockfile rather than trying to get the descriptor from the ifstream. It's much easier to implement, and you could probably wrap the ifstream in a class that automates this.
If you want to ensure atomic open/lock, You might want to construct a stream using the method suggested in this SO answer, following open and flock

A deficiency with the filestream API is that you cannot (at least not easily) access the file descriptor of an fstream (see here and here, for example). This is because there is no requirement that fstream is implemented in terms of FILE* or file descriptors (though in practice it always is).
This is also required for using pipes as C++ streams.
Therefore the 'canonical' answer (as implied in the comments to the question) is:
create a stream buffer (derived from std::basic_streambuf) that uses Posix and C stdio I/O functions (i.e open etc) and thus gives access to the file descriptor.
Create your own 'LockableFileStream' (derived from std::basic_iostream) using your stdio based stream buffer instead of std::streambuf.
You may now have a fstream like class from which you may gain access to the file descriptor and thus use fcntl (or lockf) as appropriate.
There are a few libraries which provide this out of the box.
I had thought this was addressed partly now that we've reached C++17 but I can't find the link so I must have dreamed it.

Is the traditional unix-y solution of relying on the atomicity of rename() unacceptable?
I mean, unless your JSON serialization format supports in-place update (with a transaction log or whatever), then updating your password database entails rewriting the entire file, doesn't it? So you might as well write it to a temporary file, then rename it over the real name, thus ensuring that readers read a consistent entry? (Of course, in order for this to work each reader must open() the file each time it wants to access a DB entry, leaving the file open doesn't cut it)

Related

Error in a C++(files and streams) program for creating a file

I have written this program in order to create a file using fstream and the output should show either the file has been created or not. I have run it on several online compilers like Codechef,C++ shell etc. The compilers has successfully compiled this program but the output is not coming accordingly, instead of saying file created compiler says error in creating file.
Can this be due to development tool?
Following is the code for this program:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
fstream file;
file.open("a.txt");
if(!file)
{
cout<<"Error in creating file!!!";
}
else
{
cout<<"File created successfully.";
file.close();
}
}
fstream.open() will fail if the file does not exist.
To create the file if it doesn't exist
file.open("a.txt", ios_base::out);
Or use ofstream
ofstream file;
file.open("a.txt");
The fstream constructor and open function open for read/write by default. The file must already exist to be opened in this mode. Instead, open for write:
file.open("a.txt", ios::out);
Your program's behavior is probably depending upon the operating system. BTW, if you want to learn more about them, read Operating Systems: Three Easy Pieces. Perhaps the current working directory already contains the file to be written or does not have the appropriate permissions (it should be writable to enable file creation). Details are operating system (and perhaps file system) specific. IIRC, some OSes (Windows probably) disallow opening a file which is already opened by some other process.
On Linux, you could use strace(1) to find out what system calls have failed (actually, it tells you all the system calls that have been executed by some given program or process).
This is [probably] not always guaranteed by C++ standards (but see sync_with_stdio), but many C++ standard libraries are above (and compatible with) the C standard library which sets errno(3) (see also strerror(3) and perror(3) ...) on failure; then you might try:
fstream file;
file.open("a.txt", ios::out);
if (!file) {
// perror("a.txt");
cout<<"Error in creating file!!!" << strerror(errno) << endl;
}
Of course, as other answers told you (this & that) you need the correct mode for open...
See also std::system_error

boost::iostreams::copy - sink - ENOSPC (No space left on device) error handling

In the code fragment below, is there a way to handle ENOSPC?
#include <fstream>
#include <iostream>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/bzip2.hpp>
// open input file stream of the bzip2 file
std::ifstream ifs("file.bz2");
// open output stream to the "full" device
// full device is a "utility-device" to check how applications handle ENOSPC
// more details in "man full"
std::ofstream ofs("/dev/full");
// Setup the iostreams filter
boost::iostreams::filtering_streambuf<boost::iostreams::output> filters;
filters.push(boost::iostreams::bzip2_decompressor());
filters.push(ofs);
// "run" the filter
boost::iostreams::copy(ifs, filters);
If I do strace of the compiled binary, the code seem to infinitely call writev() with the same data and returns ENOSPC error.
writev(4, [{NULL, 0}, {"DATA DATA "..., 4096}], 2) = -1 ENOSPC (No space left on device)
How can this error be handled or made thrown as an error from boost::iostreams::copy().
Is it possible to set appropriate exceptions() on the ofstream object? I tried ofs.exceptions(std::ios::badbit | std::ios::failbit) but it didn't make any difference.
The code above was compiled with GCC and run on Linux. Boost version 1.55.
It's stuck in an infinite loop in non_blocking_adaptor<Device>::write(...):
std::streamsize result = 0;
while (result < n) {
std::streamsize amt =
iostreams::write(device_, s + result, n - result);
result += amt;
}
return result;
iostream::write(device_, ... keeps returning 0 (so n stays n, amt and result stay 0).
It would appear to be a bug in Boost IOstreams. Perhaps it was introduced when preliminary support for non-blocking (synchronous) IO was added. According to the documentation, this should be a work in progress.
Particularly enlightening was: http://www.boost.org/doc/libs/1_55_0/libs/iostreams/doc/guide/asynchronous.html
Filters
Filters are allowed to propagate temporary failure notifications: if a downstream Device consumes or produces fewer characters than requested by a Filter, and if as a result the Filter is not able to satisfy a read or write request, the Filter may return a value indicating that input or output is temporarily unavailable. It is hoped that this ability will suffice to allow the current Filter concepts to be used with both aynchronous and non-blocking i/o. However, in order to be useful with blocking i/o as well, a Filter must never return a temporary failure notification unless it has received a such notification from a downstream Device. This requirement is summarized by stating that Filters must be blocking-preserving. See Blocking.
(bold mine) It does appear as if IOStreams violated this principle by transforming the E_NOSPC condition into a temporary failure notification.
boost::iostreams::copy special-cases the case where source and destination Indirect devices. In this case, both are indirect. Now the special case wraps the sink in a non_blocking_adaptor. I don't know why, and this seems to contradict the following general advice taken from the same documentation page:
Streams and Stream Buffers
Although the Boost.Iostreams Filter and Device concepts can accommodate non-blocking i/o, the C++ standard library stream and stream buffer interfaces cannot, since they lack a means to distinguish between temporary and permanent failures to satisfy a read or write request. As a result, non-blocking Devices do not work properly with the templates stream, stream_buffer, filtering_stream and filtering_streambuf.
I tried to replace the files with file_sink and file_source instead, but there was no change. :(
Here's my reduced test case which still reproduces the problem:
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
int main()
{
using namespace boost::iostreams;
file_source ifs("/dev/zero");
file_sink ofs("/dev/full");
filtering_streambuf<output> filters(ofs);
copy(ifs, filters);
}
Perhaps you should report this as a bug with the developers/mailing list.
I think I have found a solution to this problem. What I have done is to make my own sink according to the documentation here.
Basically a sink that would handle write and then check the stream's good state.
struct safe_ofstream_sink
{
typedef char char_type;
typedef boost::iostreams::sink_tag category;
std::ofstream& ofs;
safe_ofstream_sink(std::ofstream& ofs) :
ofs(ofs)
{
}
std::streamsize write(const char* s, std::streamsize n)
{
ofs.write(s, n);
if (!ofs)
throw std::runtime_error("Failed writing to fstream");
return n;
}
};
boost::iostreams::filtering_streambuf<boost::iostreams::output> filters;
filters.push(boost::iostreams::bzip2_decompressor());
filters.push(safe_ofstream_sink(ofs));
boost::iostreams::copy(ifs, filters);

Force read on disk with std::ifstream instead of the file cache

I have a program that load data from a file using std::ifstream and store the data in a structure. After that, I verify if the data I want was in the file. If it is not, I ask the user to modify the file and press a key. I then reload the file. The problem is that even if the user modified the file, I always get the same data in the file because the file seems to be cache in the application. I've seen that in win32 API, it's possible to use the flag FILE_FLAG_NO_BUFFERING to avoid using a buffered copy when reading a file, but I would like to use that feature with std::ifstream. Is there any way to use the handle created through win32 api with ifstream or anyway to force it directly in std::ifstream ?
Here's a "simplified" code sample:
SomeStructure s = LoadData(fileName);
while(!DataValid(s))
s = LoadData(fileName);
SomeStructure LoadData(const std::string& fileName)
{
std::ifstream fileStream;
while(!OpenFileRead(fileName, fileStream))
{
std::cout<<"File not found, please update it";
fileStream.close();
//Wait for use input
std::string dummy;
std::getline(std::cin, dummy);
}
//... Read file, fill structure, and return
std::string line;
while(std::getline(fileStream, line) && line!="")
{
//At this point, I can see that line is wrong
StringArray namedatearray=Utils::String::Split(line, "|");
assert(namedatearray.size()==2);
//Add data to my structure ( a map)
}
fileStream.close();
//return structure
}
bool OpenFileRead(const std::string& name, std::fstream& file)
{
file.open(name.c_str(), std::ios::in);
return !file.fail();
}
Thanks.
Edit: Of course, it was a mistake because I had two time the same file in two very similar path. Looking at the handle of the file open with process explorer (and not the relative file path made me found it).
Instead of thinking that this is due to some kind of "buffering", I would look for the obvious things first.
Are you sure the user is changing the same file that you're reading?
Are you certain reloading the data is properly updating your data structure in memory?
Are you confident that DataValid() is doing what you want?
The fact that the OS uses file buffers to increase disk performance is generally not visible from the application level. As long as you're looking at the same file, the OS knows that the user updated the file, and if you reopen it, then you'll see the changed data. If the data never even had a chance to get flushed to disk, that won't affect your application.

Flushing a boost::iostreams::zlib_compressor. How to obtain a "sync flush"?

Is there some magic required to obtain a "zlib sync flush" when using boost::iostreams::zlib_compressor ? Just invoking flush on the filter, or strict_sync on a filtering_ostream containing it doesn't see to do the job (ie I want the compressor to flush enough that the decompressor can recover all the bytes consumed by the compressor so far, without closing the stream).
Looking at the header, there seem to be some "flush codes" defined (notably a sync_flush) but it's unclear to me how they should be used (bearing in mind my compressor is just added into a filtering_ostream).
It turns out there is a fundamental problem that the symmetric_filter that
zlib_compressor inherits from isn't itself flushable (which seems rather
an oversight).
Possibly adding such support to symmetric_filter would be as simple as adding the flushable_tag and exposing the existing private flush methods, but for now I can live with it.
This C++ zlib wrapper library, of which I'm the author, supports flush functionality and is arguably simpler to use:
https://github.com/rudi-cilibrasi/zlibcomplete
It is as easy as this:
#include <iostream>
#include <zlc/zlibcomplete.hpp>
using namespace zlibcomplete;
using namespace std;
int main(int argc, char **argv)
{
const int CHUNK = 16384;
char inbuf[CHUNK];
int readBytes;
ZLibCompressor compressor(9, auto_flush);
for (;;) {
cin.read(inbuf, CHUNK);
readBytes = cin.gcount();
if (readBytes == 0) {
break;
}
string input(inbuf, readBytes);
cout << compressor.compress(input);
}
cout << compressor.finish();
return 0;
}
The main difference from boost is that instead of using a template class filter you simply pass in a string and write out the compressed string that results as many times as you want. Each string will be flushed (in auto_flush mode) so it can be used in interactive network protocols. At the end just call finish to get the last bit of compressed data and a termination block. While the boost example is shorter, it requires using two other template classes that are not as well-known as std::string, namely filtering_streambuf and the less-standard boost::iostreams:copy. The boost interface to zlib is incomplete in that it does not support Z_SYNC_FLUSH. This means it is not appropriate for online streaming applications such as in TCP interactive protocols. I love boost and use it as my main C++ support library in all of my C++ projects but in this particular case it was not usable in my application due to the missing flush functionality.

How to create a temporary text file in C++?

I'm trying to create a temporary text file in C++ and then delete it at the end
of the program. I haven't had much luck with Google.
Could you tell me which functions to use?
The answers below tell me how to create a temp file. What if I just want to
create a file (tmp.txt) and then delete it? How would I do that?
Here's a complete example:
#include <unistd.h>
int main(void) {
char filename[] = "/tmp/mytemp.XXXXXX"; // template for our file.
int fd = mkstemp(filename); // Creates and opens a new temp file r/w.
// Xs are replaced with a unique number.
if (fd == -1) return 1; // Check we managed to open the file.
write(fd, "abc", 4); // note 4 bytes total: abc terminating '\0'
/* ...
do whatever else you want.
... */
close(fd);
unlink(filename); // Delete the temporary file.
}
If you know the name of the file you want to create (and are sure it won't already exist) then you can obviously just use open to open the file.
tmpnam and tmpfile should probably be avoided as they can suffer from race conditions - see man tmpfile(3) for the details.
Maybe this will help
FILE * tmpfile ( void );
http://www.cplusplus.com/reference/clibrary/cstdio/tmpfile/
Open a temporary file
Creates a temporary binary file, open
for update (wb+ mode -- see fopen for
details). The filename is guaranteed
to be different from any other
existing file. The temporary file
created is automatically deleted when
the stream is closed (fclose) or when
the program terminates normally.
See also
char * tmpnam ( char * str );
Generate temporary filename
A string containing a filename
different from any existing file is
generated. This string can be used to
create a temporary file without
overwriting any other existing file.
http://www.cplusplus.com/reference/clibrary/cstdio/tmpnam/
This may be a little off-topic because the author wanted to create a tmp.txt and delete it after using it, but that is trivial - you can simple open() it and delete it (using boost::filesystem of course).
mkstemp() is UNIX-based. With Windows you use GetTempFileName() and GetTempPath() to generate a path to a temp file. Sample code from MSDN:
http://msdn.microsoft.com/en-us/library/aa363875%28VS.85%29.aspx
On Linux (starting with kernel 3.11), there's flag to open(2) O_TMPFILE that creates a temporary file that doesn't have a name (i.e. it doesn't show up in the filesystem). This has a few interesting features:
No worries about unique names, it's just an inode, there is no name.
No race conditions during creation (e.g. symlink attacks).
No stray files if your app crashes, it's always automatically deleted.
I wonder why most of you guys showed him the C way of doing it instead of the C++ way.
Here's fstream.
Try that, deleting a file is OS depended but you can use boost.filesystem to make things easy for you.
If you need a named file (for example, so you can pass the name to another process, perhaps a compiler or editor), then register a cleanup function that removes the file with atexit(). You can use either C++ <iostream> or C FILE * (<cstdio>) to create the file. The not completely standard but widely available mkstemp() function creates a file and tells you its name as well as returning a file descriptor (a third I/O mechanism); you could use the fdopen() function to convert the file descriptor into a FILE *.
If you don't need a named file a C-style FILE * is OK, then look at tmpfile() as suggested by #Tom.
A clean, portable and non-deprecated way of creating a temporary file is provided by Boost:
auto temporary_file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
Well, assuming you have been successful in creating the temporary file, you can use the remove function to delete it.
The function is declared in stdio.h -
#include <stdio.h>
int remove(const char *pathname);
For example, if you want to delete a file named myfile.txt the code will be
#include<stdio.h>
int main()
{
if(remove("myfile.txt") == -1)
{
fprintf(stderr,"Remove failed");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
I hope by now, you already know how to create the temp file, so this should resolve your query.
Hope it helps.