Listing only regular files, problem with stat - c

I want to list regular files in a directory. However, stat fails for every file.
DIR* dp = NULL;
struct dirent* entry = NULL;
dp = opendir(directory);
if (!dp) { log_err("Could not open directory"); return -1; }
while (entry = readdir(dp))
{
struct stat s;
char path[1024]; path[0] = 0;
strcat(path, directory);
strcat(path, entry->d_name);
int status = 0;
if (status = stat(path, &s))
{
if (S_ISREG(s.st_mode))
{
printf("%s\n", entry->d_name);
}
}
else
{
fprintf(stderr, "Can't stat: %s\n", strerror(errno));
}
}
closedir(dp);
The output is
Can't stat: Resource temporarily
unavailable
Can't stat: Resource temporarily
unavailable
Can't stat: Resource temporarily
unavailable
(... many times)
errno is set to E_AGAIN (11).
Now, if I printf the resulting path, they are indeed valid file and directory names. The directory is readable, the user I run with does have the rights to do so (it's the directory where I write the program in).
What is causing this problem, and how can I do this correctly?

stat and many other system calls return 0 on success and -1 on failure. You are incorrectly testing the return value of stat.
Your code should be:
if (!stat(path, &s))
{
if (S_ISREG(s.st_mode))
{
printf("%s\n", entry->d_name);
}
}
else
{
fprintf(stderr, "Can't stat: %s\n", strerror(errno));
}

You are probably missing a delimitier.
strcat(path, directory);
strcat(path, "/"); //this is missing
strcat(path, entry->d_name);
Don't forget to account for the extra '/' when allocating your string.

Related

How to iterate in a directory and stop at a particular level using C?

I am trying to print the names of all the processes currently in the system, in the terminal. For that I have to get into all the directories named after the process ID in the "proc" directory. So I am looping till before the "acpi" directory and trying to read the status file in each process directory. But I don't exactly understand how to read a file in a directory which is inside a directory. On running my code below :
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <dirent.h>
int main(int argc, const char *argv[])
{
DIR* FD;
struct dirent* in_file;
FILE *process_file;
char ch, pname[1024];
int i=0;
FD = opendir ("/proc");
while ((in_file = readdir(FD)))
{
if (strcmp (in_file->d_name, "acpi") == 0)
break;
else
{
opendir(in_file->d_name);
process_file = fopen("status", "r");
while( ( ch = fgetc(process_file) ) != '\n' )
{
pname[i] = ch;
i++;
}
printf(" %s \n",pname);
fclose(process_file);
closedir(in_file->d_name);
}
}
closedir(FD);
return 0;
}
I get the error :
myps.c: In function ‘main’:
myps.c:38:13: warning: passing argument 1 of ‘closedir’ from incompatible pointer type
closedir(in_file->d_name);
^
In file included from myps.c:5:0:
/usr/include/dirent.h:149:12: note: expected ‘struct DIR *’ but argument is of type ‘char *’
extern int closedir (DIR *__dirp) __nonnull ((1));
^
This is a good example of when to use a recursive function.
The function would take a directory name, open that directory, and loop through the results. For each result that is not . or .., call the stat function to get status on each entry. Then use the S_ISREG and S_ISDIR macros against the file mode to see if it's a regular file or a directory. If it's a directory, build a new string from the parent directory and the one you just found and pass that to the recursive function call.
So the function would look something like this:
void processDirectory(char dirname[])
{
struct stat statbuf;
DIR *dir;
struct dirent *de;
char *subdirname;
int rval, ;
if ((dir = opendir(dirname)) == NULL) {
perror("Failed to open directory %s", dirname);
exit(1);
}
while ((errno = 0, de = readdir(dir)) != NULL) {
rval = stat(de->d_name, &statbuf);
if (rval == -1) {
perror("stat failed");
exit(1);
}
if (S_ISREG(statbuf.st_mode)) {
// process as a regular file
} else if (S_ISDIR(statbuf.st_mode)) {
if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
subdirname = malloc(strlen(dirname) + strlen(de->d_name) + 2);
if (subdirname == NULL) {
perror("malloc failed");
exit(1);
}
strcpy(subdirname, dirname);
strcat(subdirname, "/");
strcat(subdirname, de->d_name);
processDirectory(subdirname);
free(subdirname);
}
}
}
if (errno && (errno != ENOENT)) {
perror("Failed to read directory %s", dirname);
exit(1);
}
closedir(dir);
}
To solve the error, save the directory pointer you open. Then use that to close the directory.
DIR *process_dir = opendir(in_file->d_name);
closedir(process_dir);

Excevp into array to be used later

I am using execvp to find certain files. This is the code I am using:
char *argv1[] = {"find", "-name", "*.jpg", NULL};
execvp("find",argv1);
I was wondering is there a way to store/hold the results from the execvp into an array for later user?
Also is there a way I can get only the desired file instead of the whole path?
This is my readdir() functoin.
static void list_dir (const char * dir_name)
{
char *ArrayFiles[100];
DIR * d;
/* Open the directory specified by "dir_name". */
int i = 0;
d = opendir (dir_name);
/* Check it was opened. */
if (!d) {
fprintf (stderr, "Cannot open directory '%s': %s\n",
dir_name, strerror (errno));
exit (EXIT_FAILURE);
}
while (1) {
struct dirent * entry;
const char * d_name;
/* "Readdir" gets subsequent entries from "d". */
entry = readdir (d);
if (! entry) {
/* There are no more entries in this directory, so break
out of the while loop. */
break;
}
d_name = entry->d_name;
if ((strstr(d_name, ".jpg")) || (strstr(d_name, ".JPG"))){
/* skip printing directories */
if (! (entry->d_type & DT_DIR)) {
char *filename = entry->d_name;
printf ("%s\n", d_name);
}
}
if (entry->d_type & DT_DIR) {
/* skip the root directories ("." and "..")*/
if (strcmp (d_name, "..") != 0 && strcmp (d_name, ".") != 0) {
int path_length;
char path[PATH_MAX];
path_length = snprintf (path, PATH_MAX,
"%s/%s", dir_name, d_name);
if (path_length >= PATH_MAX) {
fprintf (stderr, "Path length has got too long.\n");
exit (EXIT_FAILURE);
}
/* Recursively call "list_dir" with the new path. */
list_dir (path);
}
}
}
/* After going through all the entries, close the directory. */
if (closedir (d)) {
fprintf (stderr, "Could not close '%s': %s\n",
dir_name, strerror (errno));
exit (EXIT_FAILURE);
}
}

Copying files from one directory into another directory using C. Trouble with opening files in the sending directory. No such file or directory

I am brand new to System Programming and am having some trouble learning how directories and files work. The program should take two arguments(directories), if both are existing directories then just copy the files from the first argument into the second. If the second is a file, return with an error, finally if the second argument doesn't exist, then create it and copy the files over.
The problem occurs when I am trying to open each file to copy over the contents to the new created copy. I can get the list of all files in the first directory. If I remove copy the data(aka. in_fd) the program copies all the files over, they just are empty files.
So far the program checks the input, makes the directory if needed. All that is left is to copy over the files.
Any help will be appreciated. I saw this on other questions but none of the answers seemed to help. Thank you in advance for all your help.
#define BUFFERSIZE 4096
#define COPYMODE 0644
void oops(char *, char *);
int main(int ac, char *av[])
{
int in_fd, out_fd, n_chars;
char buf[BUFFERSIZE];
/* check args */
if ( ac != 3 ){
fprintf( stderr, "usage: %s source destination\n", *av);
exit(1);
}
//Directory pointers
DIR *sender_dir_ptr;
DIR *receiver_dir_ptr;
struct dirent *direntp;
//Used to test second argument for new/existing directory
struct stat info;
if(lstat(av[2],&info) != 0) {
if(errno == ENOENT) {
//doesn't exist, make directory
mkdir(av[2], 0700);
if ((receiver_dir_ptr = opendir(av[2])) == NULL )
oops("cannot open %s\n", av[2]);
} else if(errno == EACCES) {
// we don't have permission to know if
// the path/file exists.. impossible to tell
oops("Permission Denied", av[2]);
}
}
//so, it exists.
if(S_ISDIR(info.st_mode)) {
//it's a directory. Assign the directory pointer
if ((receiver_dir_ptr = opendir(av[2])) == NULL )
oops("cannot open %s\n", av[2]);
} else if(S_ISREG(info.st_mode)) {
//it's a file, display error and exit
oops("File exists but looking for a directory", av[2]);
}
if ((sender_dir_ptr = opendir(av[1])) == NULL )
oops("cannot open %s\n", av[1]);
else
{
struct stat st_buf;
//Go through sender directory and copy over all files to new directory
while (( direntp = readdir(sender_dir_ptr)) != NULL )
{
lstat(direntp->d_name, &st_buf);
if (S_ISDIR (st_buf.st_mode))
{
continue;
}
else if (S_ISREG (st_buf.st_mode))
{
printf("direntp= %s\n",direntp->d_name);
char tmp_in[strlen(av[1])];
strcpy(tmp_in, av[1]);
strcat(tmp_in, "/");
strcat(tmp_in, direntp->d_name);
if ((in_fd=open(tmp_in, O_RDONLY)) == -1 )
oops("Cannot open,", direntp->d_name);
//Create pathname to the second directory
char* filename = av[2];
char tmp[strlen(av[2])];
strcpy(tmp, av[2]);
strcat(tmp, "/");
strcat(tmp, direntp->d_name);
printf("filename: %s \n", tmp);
//Create new file
if ((out_fd=creat(tmp, COPYMODE)) == -1 )
oops( "Cannot creat", tmp);
//Write old file data into the new files
while ( (n_chars = read(in_fd , buf, BUFFERSIZE)) > 0 )
if ( write(out_fd, buf, n_chars ) != n_chars )
oops("Write error to ", av[2]);
if ( n_chars == -1 )
oops("Read error from ", av[1]);
//close files
if ( close(in_fd) == -1 || close(out_fd) == -1 )
oops("Error closing files","");
}
else{
printf("File: %s \n",direntp->d_name);
}
}
//Close directories
closedir(sender_dir_ptr);
closedir(receiver_dir_ptr);
}
return 0;
}
void oops(char *s1, char *s2)
{
fprintf(stderr,"Error: %s ", s1);
perror(s2);
exit(1);
}
'direntp->d_name' is filename only, not a complete file specification as required by open() etc. You need to strcat the name to the folder path.

Is there fsync but with path parameter

Is there linux system call similar to fsync but uses file path instead of file descriptor?
I modify file modification time using utime, the file is in USB, I need to make sure the time is flushed to usb after setting utime.
As far as I know there isn't.
To do so you need to
open() the file
call fsync() on the fd returned by 1.
close() the fd returned by 1.
open() the containing directory
call fsync() on the fd returned by 4.
close() the fd returned by 4.
As you are after updating the file's meta-data the steps 4 to 6 are necessary to get the directory's entries (which carry the file's meta data) to disk.
As per Throwback1986's comment please note that this is not working recursivly in any way.
Here is a snippet of code I lifted from some of my utility routines in a POSIX-like system. The code includes the synching helper function, as well as the recursive caller. It may not compile "out of the box" since it was from a specialized system, but it's close enough to get you on the right track.
static int sync_path(const char *path)
{
int fd = 0;
int ret = 0;
if (path == NULL)
{
printf("Invalid path (NULL)");
return -1;
}
fd = open(path, O_RDONLY);
if (fd < 0)
{
printf("Failed to open dir [%d:%s] (%s)",
errno, strerror(errno), path);
return errno;
}
ret = fsync(fd);
if (ret < 0)
{
printf("Failed to sync dir [%d:%s] (%s)",
errno, strerror(errno), path);
close(fd);
return errno;
}
ret = close(fd);
if (ret < 0)
{
printf("Failed to close dir [%d:%s] (%s)",
errno, strerror(errno), path);
return errno;
}
return 0;
}
int sync_tree(const char *path)
{
static size_t depth = 0;
struct dirent *entry = NULL;
DIR *dir = NULL;
const char *p = NULL;
int ret = 0;
if (path == NULL)
{
printf("Invalid path (NULL)");
return -1;
}
depth++;
if (depth > MAX_RECURSION_DEPTH)
{
printf("Recursion limit reached [%d]",
MAX_RECURSION_DEPTH);
return -1;
}
ret = chdir(path);
if (ret)
{
printf("Unable to chdir [%d:%s] (%s)",
errno, strerror(errno), path);
return errno;
}
// Operate on the current dir (after the chdir above)
dir = opendir("./");
if (dir == NULL)
{
printf("Unable to open dir [%d:%s] (%s)",
errno, strerror(errno), path);
return errno;
}
entry = readdir(dir);
while (entry && (ret == Success))
{
p = entry -> d_name;
if (!p)
{
break;
}
if (entry -> d_type != DT_DIR)
{
ret = sync_path(p);
}
else
{
// If not dir is not . or .., then process it (depth-first)
if (strnicmp(p, ".", 1) && strnicmp(p, "..", 2))
{
// Recursion here...
ret = sync_tree(p);
if (ret)
{
// Do nothing - any error message should already be handled
}
else
{
// Restore current dir
ret = chdir("../");
if (ret)
{
printf("Unable to chdir [%d:%s] (%s)",
errno, strerror(errno), "../");
}
else
{
// All is well...
}
}
}
}
if (ret == 0)
{
entry = readdir(dir);
}
}
closedir(dir);
// Sync this level
ret = sync_path("./");
if (ret)
{
// Any error message should already be printed, so just return
return -1;
}
else
{
// All is well..
}
depth--;
return Success;
}

Seemingly random opendir() failure C

So I've written a short C program that explores the files on my computer to look for a certain file. I wrote a simple function that takes a directory, opens it an looks around:
int exploreDIR (char stringDIR[], char search[])
{
DIR* dir;
struct dirent* ent;
if ((dir = opendir(stringDIR)) == NULL)
{
printf("Error: could not open directory %s\n", stringDIR);
return 0;
}
while ((ent = readdir(dir)) != NULL)
{
if(strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
continue;
if (strlen(stringDIR) + 1 + strlen(ent->d_name) > 1024)
{
perror("\nError: File path is too long!\n");
continue;
}
char filePath[1024];
strcpy(filePath, stringDIR);
strcat(filePath, "/");
strcat(filePath, ent->d_name);
if (strcmp(ent->d_name, search) == 0)
{
printf(" Found it! It's at: %s\n", filePath);
return 1;
}
struct stat st;
if (lstat(filePath, &st) < 0)
{
perror("Error: lstat() failure");
continue;
}
if (st.st_mode & S_IFDIR)
{
DIR* tempdir;
if ((tempdir = opendir (filePath)))
{
exploreDIR(filePath, search);
}
}
}
closedir(dir);
return 0;
}
However, I keep getting the output:
Error: could not open directory /Users/Dan/Desktop/Box/Videos
Error: could not open directory /Users/Dan/Desktop/compilerHome
The problem is, I have no idea what it is about these files that could cause opendir() to fail. I don't have them open in any program. They're just simple folders I created on my desktop. Does anyone have any idea what the problem could be?
You are calling opendir() twice for each closedir(). Maybe you are running out of resources.

Resources