readdir()->d_name giving weird values - c

I am trying to get the name of the parent directory by using this code:
dirp=opendir(cur_spot);
printf("parent name: %s\n", readdir(dirp)->d_name);
closedir(dirp);
cur_spot holds '..'.
i do this in a loop and it keeps climbing up the directories to the root, the sequence of my output it:
.
.bash_logout
.
.
srv
I know that it is traversing correctly because i am checking the inodes along the way.
Do i need to use something different than d_name?
Thanks

I came up with this based on the ideas in the comments under sjs' answer:
#include <dirent.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <sys/stat.h>
#include <limits.h>
#include <errno.h>
int LookupName(const char* parent, ino_t ino, char *name, size_t size)
{
DIR *dp = opendir(parent);
if (!dp) return -1;
int ret = -1;
struct dirent *de;
while (de = readdir(dp))
{
if (de->d_ino == ino)
{
strncpy(name, de->d_name, size);
ret = 0;
break;
}
}
closedir(dp);
if (ret == -1) errno = ENOENT;
return ret;
}
int GetWorkdir(char *workdir, size_t size)
{
struct stat st;
if (stat(".", &st)) return -1;
char path[PATH_MAX];
strncpy(path, "..", sizeof(path));
memset(workdir, '\0', sizeof(workdir));
char name[PATH_MAX];
while (1)
{
if (LookupName(path, st.st_ino, name, sizeof(name))) return -1;
if (!strcmp(name, "..") || !strcmp(name, "."))
{
strncpy(name, "/", sizeof(name));
strncat(name, workdir, sizeof(name));
strncpy(workdir, name, size);
break;
}
if (workdir[0] != '\0')
{
strncat(name, "/", sizeof(name));
}
strncat(name, workdir, sizeof(name));
strncpy(workdir, name, size);
if (stat(path, &st)) return -1;
strncat(path, "/..", sizeof(path));
}
return 0;
}
int main(int argc, char **argv)
{
char workDir[PATH_MAX];
assert(!GetWorkdir(workDir, sizeof(workDir)));
printf("%s\n", workDir);
}

readdir is reading the directory, so when you say
printf("parent name: %s\n", readdir(dirp)->d_name);
you are actually asking to have the name of the first entry inside .. printed for you, not the name of the .. directory.
Depending on what you are trying to do, perhaps parsing the output of getcwd might be a better approach?

Related

Iterate over directory and print some information

I want to write a C program that takes as an argument the path to a folder and shows some info about the files it contains.
So far I have written this:
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv){
char* dir_path = argv[1];
char* dir_path_bar = strcat(dir_path, "/");
DIR* dir = opendir(dir_path);
for(struct dirent* entry = readdir(dir); entry != NULL; entry = readdir(dir)){
printf("Next entry is %s\n", entry->d_name);
char* entry_path = strcat(dir_path_bar, entry->d_name);
printf("%s\n", entry_path);
struct stat buf;
stat(entry_path, &buf);
printf("Its inode number is %s\n", entry->d_ino);
printf("Its inode number is %s\n", buf.st_ino);
printf("Its uid is %s\n", buf.st_uid);
printf("Its size is %s bytes\n", buf.st_size);
};
closedir(dir);
}
Which compiles, but the stat call is giving me a SEGFAULT. What is going on?
As others have mentioned, you can't append to argv[1]. You can't keep appending to it inside the loop. And, you can't use %s to output numbers.
Here is your code with the bugs annotated and fixed [using #if 0 to show the old code]:
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int
main(int argc, char **argv)
{
char *dir_path = argv[1];
// NOTE/BUG: argv[1] has a fixed size you can't append to it
#if 0
char *dir_path_bar = strcat(dir_path, "/");
#else
char dir_path_bar[PATH_MAX];
strcpy(dir_path_bar,dir_path);
strcat(dir_path_bar,"/");
#endif
DIR *dir = opendir(dir_path);
#if 1
if (dir == NULL) {
perror(dir_path);
exit(1);
}
#endif
for (struct dirent *entry = readdir(dir); entry != NULL;
entry = readdir(dir)) {
printf("Next entry is %s\n", entry->d_name);
// NOTE/BUG: because you don't reset dir_path_bar, this just keeps appending
// to it
#if 0
char *entry_path = strcat(dir_path_bar, entry->d_name);
#else
char entry_path[PATH_MAX];
strcpy(entry_path,dir_path_bar);
strcat(entry_path,entry->d_name);
#endif
printf("\n");
printf("%s\n", entry_path);
struct stat buf;
stat(entry_path, &buf);
// NOTE/BUG: these need one or more of: %d/%ld/%lld (vs %s)
#if 0
printf("Its inode number is %s\n", entry->d_ino);
printf("Its inode number is %s\n", buf.st_ino);
printf("Its uid is %s\n", buf.st_uid);
printf("Its size is %s bytes\n", buf.st_size);
#else
printf("Its inode number is %ld\n", entry->d_ino);
printf("Its inode number is %ld\n", buf.st_ino);
printf("Its uid is %d\n", buf.st_uid);
printf("Its size is %ld bytes\n", buf.st_size);
#endif
};
closedir(dir);
return 0;
}
Two problems:
You're appending continuously to the input (argv[1]) argument which is undefined behaviour. You can't append to the strings of argv.
Also printing integer values using %s which is undefined as well. %s expects a char * argument but you wanted to print integer values.
You can instead use a temporary buffer and pass it to stat(2):
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
if (argc != 2) {
printf("Usage: %s dir\n", argv[0]);
exit(1);
}
char* dir_path = argv[1];
DIR* dir = opendir(dir_path);
if (!dir) {
perror("opendir");
exit(1);
}
for(struct dirent* entry = readdir(dir); entry != NULL; entry = readdir(dir)) {
char entry_path[PATH_MAX] = {0};
int rc = snprintf(entry_path, sizeof entry_path, "%s/%s", dir_path, entry->d_name);
if ( rc < 0 || rc >= sizeof entry_path) {
fprintf(stderr, "Path truncated for '%s'\n", entry->d_name);
continue;
}
printf("Next entry is: %s\n", entry_path);
struct stat buf;
if (stat(entry_path, &buf) == 0) {
printf("Its inode number is %ju\n", (uintmax_t)entry->d_ino);
printf("Its inode number is %ju\n", (uintmax_t)buf.st_ino);
printf("Its uid is %jd\n", (intmax_t)buf.st_uid);
printf("Its size is %jd bytes\n", (intmax_t)buf.st_size);
} else {
perror("stat");
}
}
closedir(dir);
}
I have also added some error checking.
Not shown in the other 2 earlier answers is a nice avoidance of excessive copying.
When forming the entry_path, only the entry itself needs to be overwritten, not the entire string. This becomes valuable with a long pre-fixed directory string.
dir_path_len = strlen(dir_path);
if (dir_path_len >= PATH_MAX - 1) { return EXIT_FAILURE; } // too long
char entry_path[PATH_MAX];
strcpy(entry_path, dir_path);
strcpy(entry_path + dir_path_len++, "/"); // Can use strcpy() here
DIR *dir = opendir(dir_path);
...
for (struct dirent *entry = readdir(dir); entry != NULL; entry = readdir(dir)) {
printf("Next entry is %s\n", entry->d_name);
entry_len = strlen(entry->d_name);
if (dir_path_len + entry_len >= PATH_MAX) {
continue;
// or
return EXIT_FAILURE; // too long
}
strcpy(path + dir_path_len, entry->d_name); // strcpy(), not strcat()
printf("\n%s\n", entry_path);
struct stat buf;
if (stat(entry_path, &buf) ...
...

A C program that copies all the content a directory including files and folders

The last Code I just posted now works. That is, it is able to copy all files from one directory to another. But now, I wanted to update it in such a way that it copies also directories including it contents be it files or folders.
Here is what I did so far, but this has been unable to accomplish my dream.
I really don't know what is wrong with the code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#define Max 8192
int copy_files(char *src, char *dest);
int copy_dir(char *srcpath, char *destpath);
int copy_dir(char *srcpath, char *destpath)
{
DIR *sdp = NULL;
DIR *ddp = NULL;
struct dirent *entry;
struct stat sb;
char tempsrc[strlen(srcpath)+1];
char tempdest[strlen(destpath)+1];
strcat(srcpath, "/");
strcat(destpath, "/");
strcpy(tempdest, destpath);
strcpy(tempsrc, srcpath);
if( (sdp = opendir(srcpath)) == NULL )
{
printf ("%s is not an existing directory\n", srcpath);
return 0;
}
else
{
while( (entry = readdir(sdp)) )
{
stat(entry->d_name, &sb);
// printf("Cannot open directory\n");
// exit(EXIT_FAILURE);
switch (sb.st_mode & S_IFMT)
{
case S_IFREG:
{
strcat(tempdest, entry->d_name);
strcat(tempsrc, entry->d_name);
copy_files(tempsrc, tempdest);
strcpy(tempdest, destpath);
strcpy(tempsrc, srcpath);
break;
}
case S_IFDIR:
{
strcat(tempsrc, entry->d_name);
strcat(tempdest, entry->d_name);
mkdir(tempdest, 0777);
ddp = opendir(tempdest);
copy_dir(tempsrc, tempdest);
strcpy(tempdest, destpath);
strcpy(tempsrc, srcpath);
break;
}
}
}
closedir(sdp);
closedir(ddp);
return 1;
}
}
int copy_files(char *src, char *dest)
{
int sfd, dfd, ret_in, ret_out;
char buff[Max];
if ( (sfd = open(src, O_RDONLY)) == -1 )
{
printf("Error while reading %s\n", src);
perror(src);
exit(1);
}
if ( (dfd = creat(dest, 0644)) == -1 )
{
printf("Error while creating %s\n", dest);
perror(dest);
exit(1);
}
while( (ret_in = read(sfd, &buff, Max)) > 0 )
{
ret_out = write (dfd, &buff, ret_in);
if (ret_out != ret_in)
{
printf("write error to %s", dest);
perror(dest);
exit(1);
}
if (ret_in == -1)
{
printf("read error from %s", src);
perror(src);
exit(1);
}
}
close(sfd);
close(dfd);
return 1;
}
int main(int argc, char *argv[])
{
int i;
if (argc != 3)
{
printf ("Usage: Programme_name src dest\n e.g. ./cp src dest\n");
exit(1);
}
char *srcp = argv[1];
char *destp = argv[2];
if (srcp[0] == '/' && destp[0] == '/')
{
for (i = 1; i <= strlen(destp); i++)
destp[(i-1)] = destp[i];
for (i = 1; i <= strlen(srcp); i++)
srcp[(i-1)] = srcp[i];
copy_dir(srcp, destp);
}
else if (srcp[0] != '/' && destp[0] == '/') //./ass1 test /t2
{
for (i = 1; i <= strlen(destp); i++)
destp[i-1] = destp[i];
strcat(destp, "/");
strcat(destp, srcp);
copy_files(srcp, destp);
}
else
{
printf ("Usage: Programme_name src dest\n e.g. ./cp src dest\n");
exit(1);
}
}
You are indefinitely adding /. to the temporary source and destination paths when the directory entry . is read, which is present in all directories. Instead, you should skip the . and .. entries.
Another error is the dimensioning of the temporary paths:
char tempsrc[strlen(srcpath)+1];
char tempdest[strlen(destpath)+1];
The arrays are made just long enough to hold the original paths, though sub-directory names are then appended, overflowing the arrays. Better:
char tempsrc[PATH_MAX];
char tempdest[PATH_MAX];

Recursive listing of directories and files C

My C code for recursively listing directories and files get executed multiple times. I am not sure how to fix it and why it keeps happening... It is not infinite its just like 10 times shows the current directory.
void printdir(char *dir, int depth)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
int spaces = depth;
dp = opendir(dir);
while((entry = readdir(dp))) {
lstat(entry->d_name,&statbuf);
if(S_ISDIR(statbuf.st_mode)) {
/* Found a directory, but ignore . and .. */
if(strcmp(".",entry->d_name) == 0 ||
strcmp("..",entry->d_name) == 0)
continue;
printf("%*s%s/\n",spaces,"",entry->d_name);
/* Recurse at a new indent level */
printdir(entry->d_name,depth+1);
}
else printf("%*s%s\n",spaces,"",entry->d_name);
}
closedir(dp);
}
int print_file(char *file, char *dir, struct stat buf, int showinode, int showlong, int showRec)
{
if (showinode)
printf("%lld ", buf.st_ino);
if (showlong)
print_long(file, dir, buf);
if (showRec)
printdir(dir, 0);
else
printf("%s\n", file);
return 0;
}
Here's a recursive function that lists the directories it comes across, using openat(), fdopendir(), fstatat() to avoid string-operations on paths (and, possibly, race-conditions on the directory-tree):
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
int sanerecursivedirsearch(int dirfd)
{
DIR *curdir = fdopendir(dirfd);
if (!curdir)
{
perror("fdopendir()");
close(dirfd);
return -1;
}
struct dirent *direp;
while (!!(direp = readdir(curdir)))
{
if (!strcmp(direp->d_name, "..") || !strcmp(direp->d_name, "."))
continue;
struct stat statbuf;
fstatat(dirfd, direp->d_name, &statbuf, 0);
if (S_ISDIR(statbuf.st_mode))
{
int newfd = openat(dirfd, direp->d_name,
O_RDONLY | O_DIRECTORY);
if (newfd == -1)
{
perror("openat()");
continue;
}
printf("directory found:\t%s\n", direp->d_name);
sanerecursivedirsearch(newfd);
}
}
closedir(curdir);
return 0;
}
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr, "insufficient command-line arguments");
exit(EXIT_FAILURE);
}
int fd = openat(AT_FDCWD, argv[1],
O_RDONLY | O_DIRECTORY);
if (fd == -1)
{
perror("openat()");
exit(EXIT_FAILURE);
}
sanerecursivedirsearch(fd);
return 0;
}

Iterative folder read using C and GSList

I'm trying to create an iterative program that reads all the folders from a specific starting folder using GSList and C. I haven't managed to find what the flaw in my code is for now.
The problem I'm having is that it reads each folder and all of it's subfolders until in reaches one with more subfolders. After that it just repeats to open only one directory.
The running result is below:
http://pastebin.com/jZMFBrxC
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <glib.h>
#include <glib/gprintf.h>
#include <limits.h>
#include <errno.h>
#include <sys/types.h>
int main(int argc, char *argv[]) {
GSList *list = NULL;
list = g_slist_prepend(list, "/home/ravior/Documente"); /* Folder for searching */
DIR *d;
int index = 0;
while((char *)g_slist_nth_data(list, 0) != NULL) {
gchar *element = g_strdup((char *)g_slist_nth_data(list, 0));
d = opendir(element);
if(!d) {
fprintf(stderr, "Couldn't open '%s' : %s\n", (char *)g_slist_nth_data(list, 0), strerror(errno));
exit(EXIT_FAILURE);
}
printf("\n\nThe opened folder is: %s\n\n", (char *)g_slist_nth_data(list, 0));
while(TRUE) {
struct dirent *entry;
const char *d_name;
entry = readdir(d);
if(!entry) {
break;
}
d_name = entry->d_name;
/* Some code here... */
if(entry->d_type & DT_DIR && strcmp(d_name, "..") != 0 && strcmp(d_name, ".") != 0) {
int path_length;
static char path[PATH_MAX];
path_length = snprintf(path, PATH_MAX, "%s/%s",element, d_name);
if(path_length >= PATH_MAX) {
fprintf(stderr, "Path length has got too long.\n");
exit(EXIT_FAILURE);
}
printf("%s\n", path);
list = g_slist_append(list, path);
index++;
printf("The appended element is: %s\n", (char *)g_slist_nth_data(list, index));
}
}
if(closedir(d)){
fprintf(stderr, "Couldn't close' '%s': %s\n",(char *)g_slist_nth_data(list, 0), strerror(errno));
}
list = g_slist_remove(list, (char *)g_slist_nth_data(list, 0));
free(element);
element = NULL;
index--;
}
g_slist_free(list);
return EXIT_SUCCESS;
}
Any help to solve this is more than appreciated. Also, if you have any other implementation for this problem using C, sharing it will be more than appreciated.
I've managed to figure it out eventually.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <glib.h>
#include <glib/gprintf.h>
#include <limits.h>
#include <errno.h>
#include <sys/types.h>
int main(int argc, char *argv[]) {
GSList *list = NULL;
list = g_slist_prepend(list, "/home/ravior/Documente"); /* Folder for searching */
DIR *d;
int index = 0;
while((char *)g_slist_nth_data(list, 0) != NULL) {
gchar *element = g_strdup((char *)g_slist_nth_data(list, 0));
d = opendir(element);
if(!d) {
fprintf(stderr, "Couldn't open '%s' : %s\n", element, strerror(errno));
exit(EXIT_FAILURE);
}
printf("\n\nThe opened folder is: %s\n\n", element);
while(TRUE) {
struct dirent *entry;
const char *d_name;
entry = readdir(d);
if(!entry) {
break;
}
d_name = entry->d_name;
/* Some code here... */
if(entry->d_type & DT_DIR && strcmp(d_name, "..") != 0 && strcmp(d_name, ".") != 0) {
int path_length;
static char path[PATH_MAX];
path_length = snprintf(path, PATH_MAX, "%s/%s",element, d_name);
if(path_length >= PATH_MAX) {
fprintf(stderr, "Path length has got too long.\n");
exit(EXIT_FAILURE);
}
printf("%s\n", path);
list = g_slist_append(list, strdup(path));
index++;
printf("The appended element is: %s\n", (char *)g_slist_nth_data(list, index));
}
}
if(closedir(d)){
fprintf(stderr, "Couldn't close' '%s': %s\n", element, strerror(errno));
}
list = g_slist_remove(list, (char *)g_slist_nth_data(list, 0));
free(element);
element = NULL;
index--;
}
g_slist_free(list);
return EXIT_SUCCESS;
}
I hope this piece of code will help someone in the future.

Listing directories in Linux from C

I am trying to simulate linux command ls using linux api from c. Looking at the code it does make sense, but when I run it I get "stat error: No such file or directory". I have checked that opendir is working ok. I think the problem is in stat, which is returning -1 even though I think it should return 0.
What am I missing?
Thanks for your help.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
int main(int argc, char *argv[])
{
DIR *dirp;
struct dirent *direntp;
struct stat stat_buf;
char *str;
if (argc != 2)
{
fprintf( stderr, "Usage: %s dir_name\n", argv[0]);
exit(1);
}
if ((dirp = opendir( argv[1])) == NULL)
{
perror(argv[1]);
exit(2);
}
while ((direntp = readdir( dirp)) != NULL)
{
if (stat(direntp->d_name, &stat_buf)==-1)
{
perror("stat ERROR");
exit(3);
}
if (S_ISREG(stat_buf.st_mode)) str = "regular";
else if (S_ISDIR(stat_buf.st_mode)) str = "directory";
else str = "other";
printf("%-25s - %s\n", direntp->d_name, str);
}
closedir(dirp);
exit(0);
}
It's because you aren't stating the actual file. It's in a different directory. If you want the real filename, combine argv[1] and direntp->d_name with a '/' between them.
Also, hungarian naming is icky, even the minor bit like 'p' on the end. If you have so many variables you need to keep track of their types in their names you're doing something wrong.
Here is a revised version of your program:
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#include <sys/param.h>
int main(int argc, char *argv[])
{
DIR *dirp;
struct dirent *direntp;
struct stat stat_buf;
char *str;
char fullpath[MAXPATHLEN + 1];
size_t dirnamelen;
if (argc != 2)
{
fprintf( stderr, "Usage: %s dir_name\n", argv[0]);
exit(1);
}
strncpy(fullpath, argv[1], MAXPATHLEN - 1); /* account for trailing '/' */
fullpath[MAXPATHLEN - 1] = '\0';
dirnamelen = strlen(fullpath);
if (strlen(argv[1]) > dirnamelen) {
fprintf( stderr, "Directory name is too long: %s", argv[1] );
exit(2);
}
fullpath[dirnamelen++] = '/';
fullpath[dirnamelen] = '\0';
if ((dirp = opendir( argv[1])) == NULL)
{
perror(argv[1]);
exit(2);
}
while ((direntp = readdir( dirp)) != NULL)
{
fullpath[dirnamelen] = '\0';
if ((dirnamelen + strlen(direntp->d_name)) > MAXPATHLEN) {
fprintf(stderr, "File %s + directory %s is too long.", direntp->d_name, fullpath);
continue;
} else {
/* strncpy is mild overkill because the if statement has verified that
there's enough space. */
strncpy(fullpath + dirnamelen, direntp->d_name, MAXPATHLEN - dirnamelen);
fullpath[MAXPATHLEN] = '\0';
}
if (stat(fullpath, &stat_buf)==-1)
{
perror("stat ERROR");
exit(3);
}
if (S_ISREG(stat_buf.st_mode)) str = "regular";
else if (S_ISDIR(stat_buf.st_mode)) str = "directory";
else str = "other";
printf("%-25s - %s\n", direntp->d_name, str);
}
closedir(dirp);
exit(0);
}
Note that I use MAXPATHLEN (from <limits.h>) and carefully check to make sure there aren't any buffer overflows. You should do the same in your code.
Edit: Changed code to use strn family functions for added safety.
Add
#include <unistd.h>
...
chdir(argv[1]);
or call stat with the full pathname like this
...
char fullpath[MAXPATHLEN];
snprintf(fullpath, sizeof(fullpath), "%s/%s", argv[1], direntp->d_name);
if (stat(fullpath, &stat_buf) == -1)
...
Others have suggested building a full path for stat(), or using chdir(). Both those will work (although they are subject to a race condition, if the directory is renamed while you are in the middle of reading it).
An alternative, which is not subject to the race condition, and is therefore arguably more "correct", is to use fstatat(). Just replace your existing stat() call with:
fstatat(dirfd(dirp), direntp->d_name, &stat_buf, 0)
(The chdir() method can be made race-condition-free too: either by using fchdir(dirfd(dirp)) instead of chdir(), or by changing directory to argv[1] and then opening "." with opendir(). The pathname construction method can't be made race-condition-free).
Why dont you try this? Just give the path to argv[1] like this /home/sabri/Desktop/Test
int main(int argc, char *argv[])
{
struct dirent *direntp;
DIR *dirp;
if (argc != 2)
{
fprintf(stderr, "Usage: %s directory_name\n", argv[0]);
return 1;
}
if ((dirp = opendir(argv[1])) == NULL)
{
perror ("Failed to open directory");
return 1;
}
while ((direntp = readdir(dirp)) != NULL)
printf("%s\n", direntp->d_name);
while ((closedir(dirp) == -1) && (errno == EINTR)) ;
return 0;
}
If you are using on unix, then you may use the system command.
system("ls -ltr | grep -d");

Resources