Why is errno unexpectedly 0 upon failure? - c++

The following code tries to rename an existing file to a new location. In my environment, the rename fails because source and destination are on different mount points.
Given the failure, why is the value of errno reported as 0 in version2()? I can't imagine errno not being set synchronously with rename(), and I imagined that operands to std::ostream::operator<< were evaluated in left-to-right order. If that were true, shouldn't errno have acquired its nonzero value from rename's failure before it was passed to the output operator?
Obviously reality is not matching what I imagined; can someone explain the order of evaluation or other relevant reasons why version2 outputs an errno value of 0?
The code:
// main.cpp
// Prereq: "./" and "/tmp" are on different mount points.
// Prereq: "./foo.txt" exists.
#include <cstdio>
#include <iostream>
#include <cerrno>
#include <cstring>
void version1()
{
std::cout << __FUNCTION__ << std::endl;
std::cout << rename( "./foo.txt", "/tmp/bar.txt" ) << std::endl;
std::cout << errno << ": " << strerror( errno ) << std::endl;
}
void version2()
{
std::cout << __FUNCTION__ << std::endl;
std::cout << rename( "./foo.txt", "/tmp/bar.txt" ) << ": "
<< errno << ": " << strerror( errno ) << std::endl;
}
int main( int argc, char* argv[] )
{
errno = 0;
version1();
errno = 0;
version2();
return 0;
}
Compilation & output:
> g++ --version && g++ -g main.cpp && ./a.out
g++ (GCC) 4.8.3 20140911 (Red Hat 4.8.3-7)
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
version1
-1
18: Invalid cross-device link
version2
-1: 0: Success
(I searched SO for existing questions addressing this, but either none exist, or my search phrasings weren't adequate matches for anything existing)
Update:
I think this question resolves down to "what is the order of evaluation of the operands to the output operator?" in which case, according to Order of evaluation of arguments using std::cout the answer is "unspecified."
In the spirit of academic learning (and generating information useful to the community), I'm curious if anyone is aware of the history why the order of evaluation of output stream operands is undefined: It seems intuitive that they should be evaluated left-to-right...?

Related

reading large binary chunk > INT32_MAX with std::ifstream::read

I have a large file, about 4GB. I need to read a portion of that file that is bigger than INT32_MAX. std::ifstream::read() accepts a std::streamsize as second input (number of bytes to read). Since I'm on 64 bit, that is a typedef to ptrdiff_t which should be a int64_t.
So I would expect to be able to read 9223372036854775807 at once. My example below proves me wrong. The failbit is set when I read more than INT32_MAX.
What am I missing?
#include <fstream>
#include <iostream>
#include <limits>
int main() {
std::cout << "Maximum of std::streamsize: "
<< std::numeric_limits<std::streamsize>::max() << std::endl;
std::cout << "INT32_MAX: " << std::numeric_limits<int32_t>::max()
<< std::endl;
auto const filename = R"(C:\TEMP\test.dat)"; // a large file > INT32_MAX
auto dataStream = std::ifstream();
dataStream.open(filename, std::ios_base::binary);
dataStream.seekg(0, dataStream.end);
size_t filesize = dataStream.tellg();
std::cout << "Size of file: " << filesize << std::endl;
// buffer for the whole file
auto buffer = new uint8_t[filesize];
dataStream.seekg(0, dataStream.beg);
std::cout << "Reading INT32_MAX bytes..." << std::endl;
dataStream.read(reinterpret_cast<char*>(buffer),
std::numeric_limits<int32_t>::max());
std::cout << "Read failed: " << dataStream.fail() << std::endl;
dataStream.seekg(0, dataStream.beg);
std::cout << "Reading INT32_MAX + 1 bytes..." << std::endl;
dataStream.read(
reinterpret_cast<char*>(buffer),
static_cast<int64_t>(std::numeric_limits<int32_t>::max()) + 1);
std::cout << "Read failed: " << dataStream.fail() << std::endl;
delete[] buffer;
}
I compiled with:
$ g++ --version
g++.exe (Rev2, Built by MSYS2 project) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
On a Windows 7 Laptop gives:
Maximum of std::streamsize: 9223372036854775807
INT32_MAX: 2147483647
Size of file: 4001202702
Reading INT32_MAX bytes...
Read failed: 0
Reading INT32_MAX + 1 bytes...
Read failed: 1
I worked around this issue by reading multiple INT32_MAX sized chunks. I'm interested to know why this failed though.
EDIT: I did some more testing. compiled on Linux with GCC 8.3: works, compiled with MSVC15: works.
So I guess there's a problem with the libstdc++ that comes with MinGW-W64.
EDIT2: Problem still exists with
$ gcc --version
gcc.exe (Rev8, Built by MSYS2 project) 10.3.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
can you try this
auto buffer = new int32_t[filesize];

Boost process stream is empty

I just installed the Boost library and I am trying to get through the basic tutorial.
I am trying to open a process that runs g++ --version and pipes the output to std_std_out.
The code is copied from the tutorial with the following changes:
added cout statements to track progress
added call to bp::find_executable_in_path()
Here's the code:
//
// Boost.Process
// ~~~~~~~~~~~~~
//
// Copyright (c) 2006, 2007 Julio M. Merino Vidal
// Copyright (c) 2008 Boris Schaeling
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <boost/process.hpp>
#include <string>
#include <vector>
#include <iostream>
namespace bp = ::boost::process;
using namespace std;
bp::child start_child() {
string exec = bp::find_executable_in_path( "g++" );
cout << "full path is " << exec << endl;
cout << "starting child process" << endl;
vector<std::string> args;
args.push_back( "--version" );
bp::context ctx;
ctx.stdout_behavior = bp::capture_stream();
ctx.stderr_behavior = bp::capture_stream();
return bp::launch( exec, args, ctx );
}
int main() {
bp::child c = start_child();
bp::pistream &is = c.get_stdout();
cout << ( is ? "stream is valid" : "stream is NOT valid" ) << endl;
string line;
cout << "entering read/write loop" << endl;
while( getline( is, line ) ) {
cout << "copying a line" << endl;
cout << line << endl;
}
cout << "exiting read/write loop" << endl;
}
Here is what I see when I run g++ --version on the command line:
g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-11)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
This is the output of the compiled program:
full path is /usr/bin/g++
starting child process
stream is valid
entering read/write loop
exiting read/write loop
It never enters the read/write loop. What happened to the data from the stream?
You need to enter the full path to gcc, or use bp::find_executable_in_path("g++")
[edit] oops. Under windows:
bp::find_executable_in_path("g++.exe")

boost::filesystem::create_directories(".") fails

boost::filesystem::create_directories(".") always fails on my system. It seems like this might be a bug, but after reading the documentation I'm not entirely sure. Here's an example program:
#include <iostream>
#include <boost/filesystem.hpp>
int main(int argc, char* argv[])
try
{
namespace fs = boost::filesystem;
std::cerr << "is_directory: " << fs::is_directory(argv[1]) << '\n';
std::cerr << "create_directory: " << fs::create_directory(argv[1]) << '\n';
std::cerr << "create_directories: " << fs::create_directories(argv[1]) << '\n';
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << '\n';
}
If I run this with the argument . (meaning the current directory), it prints:
is_directory: 1
create_directory: 0
boost::filesystem::create_directories: Invalid argument
The first two lines are unsurprising: . is a directory, and create_directory() is documented saying:
Creation failure because p resolves to an existing directory shall not be treated as an error.
But the third line is a surprise to me: create_directories(".") failed even though . exists. There is no such failure if you use a different name, like foo - it will happily create that directory or just return false if it exists already.
The docs have this to say:
Effect: Establishes the postcondition by calling create_directory() for any element of p that does not exist.
Postcondition: is_directory(p)
We have shown that the postcondition is true beforehand, so why does it fail?
Edit: I'm using Boost 1.63. I see now that a commit three months ago to "Fix #12495" probably causes this problem: https://github.com/boostorg/filesystem/commit/216720de55359828c2dc915b50e6ead44e00cd15 - and even includes a unit test which demands that create_directories(".") must fail while create_directory(".") must succeed, which is bizarre .

readlink sets errno to ENOENT

I'm an inexperienced Linux programmer and am trying to learn to use readlink() based on this question and answer.
My call to readlink() returns -1 and sets errno to 2 (ENOENT).
The code:
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <iostream>
#include <algorithm>
#include <cstdio>
int main(int argc, char* argv[])
{
char szTmp[100];
snprintf(szTmp, 100, "proc/%d/exe", getpid());
std::cout << "szTmp is " << szTmp << std::endl;
char executingFolder[500];
errno = 0;
int bytes = std::min(readlink(szTmp, executingFolder, 500), (ssize_t)499);
if (bytes > 0)
{
executingFolder[bytes] = '\0';
}
std::cout << "bytes is " << bytes << std::endl;
std::cout << "errno is " << errno;
if (ENOENT == errno)
{
std::cout << " ENOENT";
}
std::cout << std::endl;
std::cout << "Executing folder is \"" << executingFolder << "\"" << std::endl;
return 0;
}
The output:
(An example from one iteration since pid changes)
szTmp is proc/22272/exe
bytes is -1
errno is 2 ENOENT
Executing folder is ""
Things I have tried:
After compilation: sudo ./a.out (thinking that directory access was restricted because of lack of permission). Result: unchanged behavior from ./a.out
SIGINT the program during execution, and verified that /proc/<pid>/exe exists. Result: it consistently exists for each run of the program.
Verified that the value of the target link is well within 499 chars.
Can someone please help identify the problem? Having read the readlink man page and online descriptions, and the noted StackOverflow article, I am still unclear what is wrong.
Thank you.
proc/1234/exe is a relative path.
I think you want /proc/%d/exe, which is an absolute path, and correctly refers to the /proc directory.
Secondly, because readlink() will truncate the result in case the buffer is too small, you should consider the case where the return value is == bufsiz to be an error, as truncation may have happened. You can't know.
Also, "Executing folder" is not what /proc/<pid>/exe gives you. /proc/<pid>/exe is a symlink to the currently running executable (file), not a directory.
proc/22272/exe is a relative path name. It resolves to the file exe, in the directory 22272, in the directory proc, in your current directory. Unless your current directory is /, that's unlikely to exist.
You want an absolute path name, starting with /, in this case /proc/22272/exe.
Change this:
snprintf(szTmp, 100, "proc/%d/exe", getpid());
to this:
snprintf(szTmp, 100, "/proc/%d/exe", getpid());
But before you fix your program, you might try this:
( cd / ; ~/a.out )
(assuming a.out is in your home directory).

errno value is not updated (c++)

I'm new to coding (currently learning c++ & I know a little of C)...
was reading about functions in math.h and read about errno...
According to the site I referred :-
Domain error (input argument is outside the range in which the operation is mathematically defined, e.g. std::sqrt(-1), std::log(-1), or std::acos(2)). If MATH_ERRNO bit is set, EDOM is assigned to errno. If MATH_ERREXCEPT bit is set, FE_INVALID is raised.
So I tried writing a small program with that knowledge...
#include <iostream>
#include <cerrno>
#include <cmath>
using namespace std;
int main (void)
{
errno = 0;
cout<<"\nWhat I get :-\n";
cout << "log(-3) = " << log(-3) << "\n";
//shouldn't it do (errno = EDOM) in the above step?
cout << "errno = " << errno << "\n";
cout << strerror(errno) << "\n";
errno = EDOM;
cout<<"\nWhat I want :-\n";
cout << "log(-3) = " << log(-3) << "\n";
cout << "errno = " << errno << "\n";
cout << strerror(errno) << "\n\n";
return(0);
}
And in the output I see that the errno is not getting updated to EDOM in my first block even though -3 is not in the domain of log()...
Output:-
What I get :-
log(-3) = nan
errno = 0
Undefined error: 0
What I want :-
log(-3) = nan
errno = 33
Numerical argument out of domain
I don't understand what I'm missing here...
Plz help....
I'm compiling my code on Apple LLVM version 7.3.0 (clang-703.0.31) in Mac.
#define MATH_ERRNO 1 is illegal. You should not redefine standard library symbols. MATH_ERRNO is already defined as 1 by standard.
You cannot set how implementation handles error (aside from compiler-specific switches. Read documentation for your compiler). You can only check it:
if (math_errhandling & MATH_ERRNO)
std::cout << "Error reporting uses errno\n";
else
std::cout << "Error reporting does not use errno\n";
if (math_errhandling & MATH_ERREXCEPT)
std::cout << "Error reporting uses floating-point exceptions\n";
else
std::cout << "Error reporting does not use floating-point exceptions\n";
For clang, relevant flags are -fmath-errno/-fmath-no-errno to use/not use errno.
From discussion on reported bug it seems, that Mac implementation of standard library doesn't use errno at all. So you are out of luck if you want use it for error reporting.
You can find complete example (in C) of math err handling at: http://www.cplusplus.com/reference/cmath/math_errhandling/
For completness example from that site:
#include <stdio.h> /* printf */
#include <math.h> /* math_errhandling */
#include <errno.h> /* errno, EDOM */
#include <fenv.h> /* feclearexcept, fetestexcept, FE_ALL_EXCEPT, FE_INVALID */
#pragma STDC FENV_ACCESS on
int main () {
errno = 0;
if (math_errhandling & MATH_ERREXCEPT) feclearexcept(FE_ALL_EXCEPT);
printf ("Error handling: %d",math_errhandling);
sqrt (-1);
if (math_errhandling & MATH_ERRNO) {
if (errno==EDOM) printf("errno set to EDOM\n");
}
if (math_errhandling &MATH_ERREXCEPT) {
if (fetestexcept(FE_INVALID)) printf("FE_INVALID raised\n");
}
return 0;
}