check if something exists and is executable in C, using stat function - c

For example, I have directory a and b under my current working directory. I'm trying to locate file X, how can I modify the stat() command so that it checks both directory a and b instead of just current working directory? would stat(a/file, &buf) work? also, to check if it's executable, I know the code is buf.S_IXUSR, does if (buf.S_IXUSR) work?
thanks!

I suggest you consult the stat(2) man page.
Here's an example of how to use stat:
struct stat buf;
if (stat("a/file", &buf) != 0) {
// handle failure
}
// Check the `st_mode` field to see if the `S_IXUSR` bit is set
if (buf.st_mode & S_IXUSR) {
// Executable by user
}
However, for your use case, you might consider access(2) instead:
if (access("/path/to/file", X_OK) == 0) {
// File exists and is executable by the calling process's
// _real_ UID / GID.
}

Related

Check if a file exists on a given path in C

I'm writing a C program that functions like a shell. What it does is when user gives it a path, such as /bin, it needs to be able to check if a given executable program is on that path and if so, execute that program. From another question, I know I can check if a file exists using stat() function:
int file_exist (char *filename){
struct stat buffer;
return (stat (filename, &buffer) == 0);
}
and call it such as like this:
if (file_exist ("myfile.txt")) {
printf ("It exists\n");
}
But I have a path variable:
char* path = "/bin";
How do I search for an executable program is on this path, with the purpose of trying to execute it later on?

stat(), fstat(), lstat(), and fopen(); how to write TOCTOU protected system independent code

I've been dealing with a problem for a few weeks now updating 20 year code that needs to be system independent (work on both Linux and Windows). It involves Time-of-Check, Time-of-Use (TOCTOU) issues. I made a thread here, but it didn't go very far, and after ruminating on it for a while and searching deeper into the problem, I think I understand my question a bit better. Maybe I can ask it a bit better too...
From what I've read, the code needs to check if the file exists, if it is accessible, open the file, do some operations and finally close the file. It seems the best way to do this is a call to lstat(), a call to fopen(), a call to fstat() (to rule out the TOCTOU), and then the operations and closing the file.
However, I've been lead to believe that lstat() and fstat() are POSIX defined, not C Standard defined, ruling out their use for a system agnostic program, much in the same way open() shouldn't be used for cross-compatibility. How would you implement this?
If you look at my first post, you can see the developer from 20 years ago used the C preprocessor to cut the code into cross-compatible parts, but even if I did that, I wouldn't know what to replace lstat() or fstat() with (their windows counterparts).
Edit: Added abreviated code to this post; if something is unclear please go to the original post
#ifdef WIN32
struct _stat buf;
#else
struct stat buf;
#endif //WIN32
FILE *fp;
char data[2560];
// Make sure file exists and is readable
#ifdef WIN32
if (_access(file.c_str(), R_OK) == -1) {
#else
if (access(file.c_str(), R_OK) == -1) {
#endif //WIN32
char message[2560];
sprintf(message, "File '%s' Not Found or Not Readable", file.c_str());
throw message;
}
// Get the file status information
#ifdef WIN32
if (_stat(file.c_str(), &buf) != 0) {
#else
if (stat(file.c_str(), &buf) != 0) {
#endif //WIN32
char message[2560];
sprintf(message, "File '%s' No Status Available", file.c_str());
throw message;
}
// Open the file for reading
fp = fopen(file.c_str(), "r");
if (fp == NULL) {
char message[2560];
sprintf(message, "File '%s' Cound Not be Opened", file.c_str());
throw message;
}
// Read the file
MvString s, ss;
while (fgets(data, sizeof(data), fp) != (char *)0) {
s = data;
s.trimBoth();
if (s.compare( 0, 5, "GROUP" ) == 0) {
//size_t t = s.find_last_of( ":" );
size_t t = s.find( ":" );
if (t != string::npos) {
ss = s.substr( t+1 ).c_str();
ss.trimBoth();
ss = ss.substr( 1, ss.length() - 3 ).c_str();
group_list.push_back( ss );
}
}
}
// Close the file
fclose(fp);
}
The reliable way to check whether the file exists and can be opened is to try opening it. If it was opened, all was OK. If it was not opened, you can think about spending time to analyze what went wrong.
The access() function formally asks a different question from what you think; it asks 'can the real user ID or the real group ID access the file', but the program will use the effective user ID or the effective group ID to access the file. If your program is not running SUID or SGID, and was not launched from a program running SUID or SGID — and that's the normal case — then there's no difference. But the question is different.
The use of stat() or
lstat() doesn't seem helpful. In particular, lstat() only tells you whether you start at a symlink, but the code doesn't care about that.
Both the access() and the stat() calls provide you with TOCTOU windows of vulnerability; the file could be removed after they reported it was present, or created after they reported it was absent.
You should simply call fopen() and see whether it works; the code will be simpler and more resistant to TOCTOU problems. You might need to consider whether to use open() with all its extra controls (O_EXCL, etc), and then convert the file descriptor to a file pointer (fdopen()).
All of this applies to the Unix side.
The details will be different, but on the Windows side, you will still be best off trying to open the file and reacting appropriately to failure.
In both systems, make sure the options provided to the open function are appropriate.

C: creating named pipe using mknod() not working

Language: C
OS: Ubuntu
I'm simply trying to create a FIFO named pipe using the command:
state = mknod("pipe.txt", S_IFIFO | 0666, 0);
the problem is i always get the state's value to be -1 (meaning it has failed) instead of 0.
perror returns 'pipe.txt: File exists'
i have no idea how should i debug such issue or what could be the reason, hope anyone code guide me what's wrong.
(note: the file pipe.txt exist on same path as source file.)
Read: int mknod(const char *path, mode_t mode, rdev_t dev_identifier);
General Description:
Creates a new character special file or FIFO special file (named pipe), with the path name specified in the path argument.
If file already exists then it will fails with error: File exists
To avoid this error, remove(unlink()) the file, As I am doing in my below code(read comment):
int main() {
char* file="pipe.txt";
unlink(file); // Add before mknod()
int state = mknod(file, S_IFIFO | 0666, 0);
if(state < 0){
perror("mknod() error");
}
return 0;
}
You should examine errno to see what the error is but it's probably EEXIST since I believe that's what happens if the file already exists.
From the Linux documentation for mknod:
If pathname already exists, or is a symbolic link, this call fails with an EEXIST error.
However, if the file already exists and is the pipe you created in an earlier run, you can safely reopen it. All mknod (and its often preferred cousin, mkfifo) does is actually create the FIFO, you still have to open it at both ends to get the data transfer happening.

How can I check to see if user has execute permissions?

I wish to know how to check if the "user" (other than who is running this program)
has execute permissions on a file ? [C api]
I have looked at "access" it gives information with respect to the caller.
I am looking for something like :-
"<cmd> <user_name> <file_name>"
here I am trying to get if <user_name> has execute permissions for <file_name> ?
I am looking C api ?
Possible solution :- I am using the following algo to get this information
boolean_t
is_user_has_execute_permissions(char *run_as_user)
{
/* Check world execute permission */
if ((cmd_stat.st_mode & S_IXOTH) == S_IXOTH) {
return (TRUE);
}
/* group id for run_as_user */
getpwnam_r(run_as_user, &pw, buf, passwd_len);
/* Check group execute permission */
if ((cmd_stat.st_mode & S_IXGRP) == S_IXGRP) {
if (pw->pw_gid == cmd_stat.st_gid)
return (TRUE);
}
return (FALSE);
}
Did anyone see any error in this one ?
You need the call stat(2), which returns the permission bits, for the owner of the file, the owner group, and the others. Than you have to find out the id of the user you're interested in and ids its groups: see getpwent(3) and getgrouplist(3). The first which match, will give the resulting permissions.
From the command line, you can use an Awk program easily enough. Something like
ls -l filename | awk -F '{ if (substring($1,3,1) == "x" exit(0); exit(1)}'
will set the return code to 0 if it's found, 1 if it's not.
From a C program, you want to use fstat. Basically you open the file
int fd = fopen("filename", "r");
then get the file stat block with fstat
fstat(fd, &bufr)
and look at bufr.st_mode.
Here's a description of fstat.
Update
I'll note crankily that when the OP originally posted, it wasn't clear the C API was what was desired.

How to check if a dir exists?

How would I go about checking if a FILE is a directory? I have
if (file == NULL) {
fprintf(stderr, "%s: No such file\n", argv[1]);
return 1;
}
and that checks if the node exists at all, but I want to know if it's a dir or a file.
Filenames themselves don't carry any information about whether they exist or not or whether they are a directory with them - someone could change it out from under you. What you want to do is run a library call, namely stat(2), which reports back if the file exists or not and what it is. From the man page,
[ENOENT] The named file does not exist.
So there's an error code which reports (in errno) that the file does not exist. If it does exist, you may wish to check that it is actually a directory and not a regular file. You do this by checking st_mode in the struct returned:
The status information word st_mode has the following bits:
...
#define S_IFDIR 0040000 /* directory */
Check the manpage for further information.
struct stat st;
if(stat("/directory",&st) == 0)
printf(" /directory is present\n");
use opendir to try and open it as a directory. If that returns a null pointer it's clearly not a directory :)
Here's a snippet for your question:
#include <stdio.h>
#include <dirent.h>
...
DIR *dip;
if ((dip = opendir(argv[1])) == NULL)
{
printf("not a directory");
}
else closedir(dip);
If you're using *nix, stat().

Resources