C++ - stat(), access() not working under gnu gcc - c++

I've got a pretty basic console program here, to determine if a folder or file exists or not using stat:
#include <iostream>
#include <sys/stat.h>
using namespace std;
int main() {
char path[] = "myfolder/";
struct stat status;
if(stat(path,&status)==0) { cout << "Folder found." << endl; }
else { cout << "Can't find folder." << endl; } //Doesn't exist
cin.get();
return 0;
}
I have also tried the access version:
#include <iostream>
#include <io.h>
using namespace std;
int main() {
char path[] = "myfolder/";
if(access(path,0)==0) { cout << "Folder found." << endl; }
else { cout << "Can't find folder." << endl; } //Doesn't exist
cin.get();
return 0;
}
Neither of them find my folder (which is right there in the same directory as the program). These worked on my last compiler (the default one with DevCpp). I switched to CodeBlocks and am compiling with Gnu GCC now, if that helps. I'm sure it's a quick fix - can someone help out?
(Obviously I'm a noob at this so if you need any other information I've left out please let me know).
UPDATE
The problem was with the base directory. The updated, working program is as follows:
#include <iostream>
#include <sys/stat.h>
using namespace std;
int main() {
cout << "Current directory: " << system("cd") << endl;
char path[] = "./bin/Release/myfolder";
struct stat status;
if(stat(path,&status)==0) { cout << "Directory found." << endl; }
else { cout << "Can't find directory." << endl; } //Doesn't exist
cin.get();
return 0;
}
ANOTHER UPDATE
Turns out that a trailing backslash on the path is big trouble.

Right before your stat call, insert the code:
system("pwd"); // for UNIXy systems
system("cd"); // for Windowsy systems
(or equivalent) to check your current directory. I think you'll find it's not what you think.
Alternatively, run the executable from the command line where you know what directory you're in. IDEs will frequently run your executable from a directory you may not expect.
Or, use the full path name so that it doesn't matter which directory you're in.
For what it's worth, your first code segment works perfectly (gcc under Ubuntu 10):
pax$ ls my*
ls: cannot access my*: No such file or directory
pax$ ./qq
Cannot find folder.
pax$ mkdir myfolder
pax$ ll -d my*
drwxr-xr-x 2 pax pax 4096 2010-12-14 09:33 myfolder/
pax$ ./qq
Folder found.

Are you sure that the current directory of your running program is what you expect it to be? Try changing path to an absolute pathname to see if that helps.

Check your PWD when you running your program. This problem is not caused by compiler. You DevCpp may set a working directory for your program automatically.

You can find out why stat() failed (which is a C function, not C++, by the way), by checking errno:
#include <cerrno>
...
if (stat(path,&status) != 0)
{
std::cout << "stat() failed:" << std::strerror(errno) << endl;
}

Related

How to navigate through directories using c++ to create a file explorer

I'm trying to create a file explorer in C++ using Ncurses for my class. Currently I'm trying to find a way to navigate through the file system and find out if 'x' is file/directory and act accordingly.
The problem is I can't find a way to navigate through directories they way I'd like. For example, in the code below I start at "." and then read it while saving some info of said directory and its files. But I'd like to define the cwd to "/home" everytime the program runs and then go from there exploring whatever the user wants like:
display /home -> user selects /folder1 -> display /folder1 -> user selects /documents -> ...
I've read about scripts and tried to create a "cd /home" script but it doesn't work. Somewhere I read that the execve() function might work, but I don't understand it. I've got a feeling that I'm overthinking this, and frankly I'm stuck.
edit: In essence I'd like to find: How to make it so that my program starts at "path" so that when I call getcwd() it returns "path" and not the actual path of the program.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <linux/limits.h>
#include <iostream>
#include "contenido.cpp"
using namespace std;
//Inicia main
int main(int argc, char const *argv[]) {
DIR *dir; //dir is directory to open
struct dirent *sd;
struct stat buf; //buf will give us stat() atributes from 'x' file.
char currentpath[FILENAME_MAX]; //currentpath
contenido dcont;
//system (". /home/rodrigo/Documentos/Atom/Ncurses/Proyecto/beta/prueba.sh");
if((dir = opendir(".")) == NULL){ /*Opens directory*/
return errno;
}
if(getcwd(currentpath, FILENAME_MAX) == NULL){
return errno;
}
while ((sd= readdir(dir)) != NULL){ /*starts directory stream*/
if(strcmp(sd -> d_name,".")==0 || strcmp(sd -> d_name,"..") ==0){
continue;
}
//Gets cwd, then adds /filename to it and sends it to a linked list 'dcont'. Resets currentpath to cwd
//afterwards.
getcwd(currentpath, FILENAME_MAX);
strcat(currentpath, "/");
strcat(currentpath, sd->d_name);
string prueba(currentpath);
//std::cout << currentpath << '\n';
dcont.crearnodo(prueba);
if(stat(currentpath, &buf) == -1){
cout << currentpath << "\n";
perror("hey");
return errno;
}
getcwd(currentpath, FILENAME_MAX);
//Prints Files and Directories. If Directory prints "it's directory", else prints "file info".
if (S_ISDIR(buf.st_mode)) {
cout << sd->d_name << "\n";
cout << "ES DIRECTORIO\n";
}else
cout << sd->d_name << "\n";
cout <<"Su tamaƱo es: " << (int)buf.st_size << "\n";
//system("ls");
}
closedir(dir);
dcont.mostrardircont(); //prints contents of the linked list (position in list and path of file).
return 0;
}
To change your current working directory use chdir
If you want to change your cwd to "/home"
chdir("/home");
The chdir only persists within the program that did it (or subprocesses). It won't cause the shell to change. There's an application (wcd) which does something like what you're trying, which combines the navigation with a shell script.

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).

Check whether file name already exists in a folder or not?

In C++ I need to check whether a entered file name exists in that folder or not. I'm writing code for Linux, using the g++ compiler.
please help guys :)
I saw this code somewhere on net for my problem but I strongly feel it wont serve my purpose:
ofstream fout(filename);
if(fout)
{
cout<<"File name already exists";
return 1;
}
You can do this by testing with an ifstream, but there is a subtle difference between using that and the C level stat() interface.
#include <cerrno>
#include <cstring>
#include <iostream>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
using namespace std;
int main (int argc, const char *argv[]) {
if (argc < 2) {
cerr << "Filepath required.\n";
return 1;
}
ifstream fs(argv[1]);
if (fs.is_open()) {
fs.close();
cout << "ifstream says file exists.\n";
} else cout << "ifstream says file does not exist.\n";
struct stat info;
if ((stat(argv[1], &info)) == -1) {
if (errno == ENOENT) cout << "stat() says file does not exist.\n";
else cout << "stat() says " << strerror(errno) << endl;
} else cout << "stat() says file exists.\n";
return 0;
}
If you run this on a file that exists and you have read permission on, you'll get the same answer both ways.
If you run this on a file that does not exist, you get the same answer both ways.
If you run this on a file that exists but you do not have read permissions on, you'll get two different answers. fstream will say the file does not exist, but stat() will say it does. Note that if you run ls in the same directory, it will show the file, even though you cannot read it; it does exist.
So if the last case is not significant -- i.e., a file you can't read might as well not exist -- then use the ifstream test. However, if it is important, then use the stat() test. See man 2 stat (the 2 is important) for more, and remember, to use it you need:
#include <cerrno>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
cerrno is required to check errno if stat() fails, which can happen. For example, if read permission on a directory in the path is denied, then stat() will fail and errno will be equal to EACCES; if you try it with the above program you'll get stat() says Permission denied. This does not mean the file exists. It means you can't check whether it exists or not.
Beware, if you have not used errno before: You must check immediately on a failed call, before you make any others which may set it differently. It is, however, thread safe.
If you want to be cross-platform and C++'y I recommend the Boost Filesystem library.
For your purposes I think something similar to this Boost sample code
#include <iostream>
#include <boost/filesystem.hpp>
using namespace boost::filesystem;
int main(int argc, char* argv[])
{
path p (argv[1]); // p reads clearer than argv[1] in the following code
if (exists(p)) // does p actually exist?
{
if (is_regular_file(p)) // is p a regular file?
cout << p << " size is " << file_size(p) << '\n';
else if (is_directory(p)) // is p a directory?
cout << p << "is a directory\n";
else
cout << p << "exists, but is neither a regular file nor a directory\n";
}
else
cout << p << "does not exist\n";
return 0;
}
would do the job.
Maybe what you want is fstat:
http://codewiki.wikidot.com/c:system-calls:fstat

Ifstream file does not open although everything seems in place (c++)

I'm trying to write a program to parse the first and sixteenth columns of a CSV file (converted into .txt). I have the CSV ("posts.txt") document in the folder with the executable. But, whenever I try to run the executable, my program delivers that it cannot open the file (or that "!infile.is_open()"). Mind giving me some assistance? I'm running in Xcode 3.2.3 on Mac OSX 10.8.3. The code is shows below.
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string.h>
using namespace std;
void answeredPostGrabber()
{
ifstream inFile("posts.txt");
string postNumber;
string answerNumber;
string throwAway;
if(inFile.is_open())
{
while(inFile.good())
{
getline(inFile,postNumber,',');
cout << postNumber << ",";
for(int y=1;y++;y<16)
{
getline(inFile,throwAway,',');
}
getline(inFile,answerNumber,',');
cout << answerNumber << endl;
ofstream edges;
edges.open("edges.txt",ios::app);
edges << postNumber << "," << answerNumber<< endl;
edges.close();
ofstream nodes;
nodes.open("nodes.txt",ios::app);
nodes << postNumber << "\n" << answerNumber << endl;
nodes.close();
getline(inFile,throwAway);
}
}else cout << "ERROR: Unable to open file." << endl;
}
int main ()
{
answeredPostGrabber();
return 0;
}
Thank you in advance!
I have the CSV ("posts.txt") document in the folder with the executable.
The file should be present in the current working directory of your process, which may or may not be the same directory where the executable lives. If in doubt, try specifying the full path in ifstream inFile(...); to see whether that changes things.
Additionally, the file needs to have the correct permissions to ensure that it's readable by the process.

How to (legitimately) access files after putting self into chrooted sandbox?

Changing a Linux C++ program which gives the user limited file access. Thus the program chroots itself to a sandbox with the files the user can get at. All worked well.
Now, however, the program needs to access some files for its own needs (not the user's) but they are outside the sandbox. I know chroot allows access to files opened before the chroot but in this case the needed files could a few among many hundreds so it is obviously impractical to open them all just for the couple that might be required.
Is there any way to get at the files?
Copy them into the sandbox or open them all before chrooting. Seriously. If there was a way to do this, there would be a way to suborn it to allow other access and make your protection useless.
The whole point of the sandbox is to prevent exactly what you're trying to achieve.
If the files are all in 1 directory, you could use mount to bind them to a directory inside the sandbox.
mount --bind /path/to/files /sandbox/files
The you can access the files through /sandbox/files/. If you don't want the user to see them, do mount --bind /path/to/files /sandbox/.files so the .files directory is hidden
I guess that you ought to be able to split your program into two parts, one which is chroot'ed and one which isn't, and have the chroot'ed portion request files' contents from the non-chroot'ed portion via the IPC mechanism of your choice.
This is a hack, and it may be easy to get wrong, negating any benefit of a chroot. Like paxdiablo says, you're trying to get around the whole purpose of a chroot sandbox, so your options are very, very limited.
Maybe if you explained a bit more what you're trying to accomplish, we might be able to offer some other ideas. For example, SELinux and AppArmor are more flexible than chroot and may be able to give you the security you seek.
If the files you need to access are within a few directories you could open those directories before you chroot and save the file descriptors. You can then use the so-called *at functions (e.g. openat(), renameat(), etc.) to get at the individual files. Basically you are opening the files relative to the already open directory file descriptors rather than the chrooted directory.
Whether this is a safe thing to do is open to question but it should work in Linux.
EDIT: This is on the ugly side but it seems to work. You should poke around a lot more for vulnerabilities than I have. I haven't tested how dropping privileges and so forth will effect things.
#include <iostream>
#include <string>
using namespace std;
#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
if (argc < 4)
{
cerr << "USAGE: " << argv[0] << " <jail directory> <freeworld directory> <filename>\n";
exit(EXIT_FAILURE);
}
const string JAILDIR(argv[1]);
const string FREEDIR(argv[2]);
string freefilename(argv[3]);
while (freefilename[0] == '/')
freefilename.erase(0, 1);
DIR *pDir;
if ((pDir = opendir(FREEDIR.c_str())) == NULL)
{
perror("Could not open outside dir");
exit(EXIT_FAILURE);
}
int freeFD = dirfd(pDir);
//cd to jail dir
if (chdir(JAILDIR.c_str()) == -1)
{
perror("cd before chroot");
exit(EXIT_FAILURE);
}
//lock in jail
if (chroot(JAILDIR.c_str()) < 0)
{
cerr << "Failed to chroot to " << JAILDIR << " - " << strerror(errno) << endl;
exit(EXIT_FAILURE);
}
//
//in jail, won't work
//
string JailFile(FREEDIR);
JailFile += "/";
JailFile += freefilename;
int jailFD;
if ((jailFD = open(JailFile.c_str(), O_RDONLY)) == -1)
{
cout << "as expected, could not open " << JailFile << endl;
perror("exected open fail");
}
else
{
cout << "defying all logic, opened " << JailFile << endl;
exit(EXIT_FAILURE);
}
//
//using this works
//
if ((jailFD = openat(freeFD, freefilename.c_str(), O_RDONLY)) == -1)
{
cout << "example did not work. Could not open " << freefilename << " Sorry!" << endl;
exit(EXIT_FAILURE);
}
else
cout << "opened " << freefilename << " from inside jail" << endl;
char buff[255];
ssize_t numread;
while (1)
{
if ((numread = read(jailFD, buff, sizeof(buff) - 1)) == -1)
{
perror("read");
exit(EXIT_FAILURE);
}
if (numread == 0)
break;
buff[numread] = '\0';
cout << buff << endl;
}
return 0;
}
To test:
echo "Hello World" >/tmp/mystuff.dat
mkdir /tmp/jail
sudo ./myprog /tmp/jail /tmp mystuff.dat