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;
}
Related
WHAT I HAVE TO DO
I have to find, starting from a directory, a file located in one of all directories that have as root the directory passed in input.
Something as shell command find.
INPUT/OUTPUT
Having this in input:
./myfind /home/claudio/Scrivania file.txt
I need something as this in output, absolute path and last modify date ecc:
/home/claudio/Scrivania/SistemiOperativi/file.txt Tue Mar 30 19:51:54 2021
MY CODE
It doesn't print anything.
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <errno.h>
#if !defined(NAME_MAX)
#define NAME_MAX 256
#endif
void find(const char* passed_dir_name, const char* passed_file_name) {
if (chdir(passed_dir_name) == -1) {
perror("FATAL ERROR CHANGING DIRECTORY");
exit(EXIT_FAILURE);
}
DIR* current_directory;
if ((current_directory = opendir(".")) == NULL) {
perror("FATAL ERROR OPENING CURRENT WORKING DIRECTORY");
exit(EXIT_FAILURE);
}
char* buf;
if ((buf = calloc(NAME_MAX, sizeof(char))) == NULL) {
perror("FATAL ERROR ALLOCATING MEMORY");
exit(EXIT_FAILURE);
}
struct dirent* dir;
while ((dir = readdir(current_directory)) != NULL) {
struct stat statbuf;
stat(dir->d_name, &statbuf);
if (S_ISDIR(statbuf.st_mode)) {
if (strncmp(dir->d_name, ".", 1) != 0 && strncmp(dir->d_name, "..", 2) != 0) {
find(dir->d_name, passed_file_name);
}
} else {
if (strncmp(dir->d_name, passed_file_name, strlen(passed_file_name) == 0)) {
if (getcwd(buf, NAME_MAX) == NULL) {
perror("FATAL ERROR");
exit(EXIT_FAILURE);
}
fprintf(stdout, "%s/%s %s", buf, dir->d_name, ctime(&statbuf.st_mtime));
}
}
}
if (closedir(current_directory) == -1) {
perror("FATAL ERROR CLOSING CURRENT WORKING DIRECTORY");
exit(EXIT_FAILURE);
}
chdir("..");
free(buf);
}
int main(int argc, char** argv) {
if (argc != 3) {
fprintf(stderr, "ERROR: RUn as ./myfind directory\n");
exit(EXIT_FAILURE);
}
const char* dir = argv[1];
const char* file = argv[2];
struct stat statbuf;
stat(dir, &statbuf);
if(!S_ISDIR(statbuf.st_mode)) {
fprintf(stderr, "FATAL ERROR: %s IS NOT A DIRECTORY\n", dir);
exit(EXIT_FAILURE);
}
find(dir, file);
exit(EXIT_SUCCESS);
}
Your parentheses are wrong in:
if (strncmp(dir->d_name, passed_file_name, strlen(passed_file_name) == 0))
You need to write:
if (strncmp(dir->d_name, passed_file_name, strlen(passed_file_name)) == 0)
Since strncmp(x, y, 0) will always return 0, the condition is never being met.
But note that there's no point in using strncmp here at all. strncmp is only needed if you don't know that one of your entries is a null terminated string. You have a guarante that d_name is null-terminated, and if passed_file_name is not, then strlen is going to be problematic. You might as well just write strcmp(dir->d_name, passed_file_name).
I'm trying to get the size of a directory recursively but I only get segfaults. I really can't see where I'm wrong, could someone help me?
P.S. I don't need to verify if the file exist or not, this is only a try for another function I have to write.
Here's the code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <limits.h>
int main(int argc, char * argv[])
{
printf("%d\n", size(argv[1]));
return 0;
}
int is_folder(char * path)
{
struct stat path_stat;
stat(path, &path_stat);
return !(S_ISREG(path_stat.st_mode));
}
int size(char * name)
{
int dir_size = 0;
struct dirent * pDirent;
DIR * pDir = opendir(name);
while ((pDirent = readdir(pDir)) != NULL)
{
char buf[PATH_MAX + 1];
realpath(pDirent->d_name, buf);
if (is_folder(buf))
{
size(buf);
}
else
{
struct stat st;
stat(buf, &st);
int sz = st.st_size;
dir_size += sz;
}
}
return dir_size;
}
Building on your code, below is the closest I was able to reproduce what du -sk returns:
#include <stdio.h>
#include <sys/stat.h>
#include <dirent.h>
#include <limits.h>
#include <string.h>
off_t directorySize(char *directory_name)
{
off_t directory_size = 0;
DIR *pDir;
if ((pDir = opendir(directory_name)) != NULL)
{
struct dirent *pDirent;
while ((pDirent = readdir(pDir)) != NULL)
{
char buffer[PATH_MAX + 1];
strcat(strcat(strcpy(buffer, directory_name), "/"), pDirent->d_name);
struct stat file_stat;
if (stat(buffer, &file_stat) == 0)
{
directory_size += file_stat.st_blocks * S_BLKSIZE;
}
if (pDirent->d_type == DT_DIR)
{
if (strcmp(pDirent->d_name, ".") != 0 && strcmp(pDirent->d_name, "..") != 0)
{
directory_size += directorySize(buffer);
}
}
}
(void) closedir(pDir);
}
return directory_size;
}
int main(int argc, char *argv[])
{
printf("%lldKiB\n", directorySize(argv[1]) / 1024);
return 0;
}
I'm guessing the difference you're seeing is due to directories consuming a small amount of space which you need to include in your total and that file space usage is chunked so you need to count blocks, not bytes.
After one night spent on trying how to find a size that is quiet the same of the one reported by du -sh: I wrote this and it seems to work also on large directories (like the linux source that is 750MB or more).
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>
#include <limits.h>
long folder_size(char *);
int main(int argc, char * argv[])
{
if (argc < 2)
{
exit(1);
}
printf("%ld\n", mysize(argv[1]));
return 0;
}
int not_folder(char * path)
{
struct stat path_stat;
stat(path, &path_stat);
return (S_ISREG(path_stat.st_mode));
}
long folder_size(char * name)
{
long dir_size = 0L;
struct dirent * pDirent;
DIR * pDir = opendir(name);
while ((pDirent = readdir(pDir)))
{
if (strcmp (pDirent->d_name, "..") != 0 && strcmp (pDirent->d_name, ".") != 0)
{
char buf[PATH_MAX];
strcpy(buf, name);
strcat(buf, "/");
strcat(buf, pDirent->d_name);
if (not_folder(buf))
{
struct stat st;
stat(buf, &st);
dir_size += st.st_blocks * S_BLKSIZE;
printf("%s %ld\n", buf, (long)st.st_size);
}
else
{
dir_size += folder_size(buf);
}
}
}
(void) closedir(pDir);
return dir_size;
}
I'm stuck on unix system programming and am beginner about it. Assume that there is a directory which is X. There are a file(text1.txt) and another directory which is Y in X. Last, there are two files(text2.noExtension) and text3.noExtension) and another directory which is Z in Y. My goal is that read files and enter directories until there is no directory. Candidly, I really don't have any idea how to go on.
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
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 want to list all directories and sub-directories, try something recursive. E.g.:
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void listOfDir(char * dirname, int level)
{
struct dirent *direntp;
DIR *dirp;
char *subdirname;
if ((dirp = opendir(dirname)) == NULL)
{
return;
}
while ((direntp = readdir(dirp)) != NULL)
{
if(strcmp(direntp->d_name, ".")==0 || strcmp(direntp->d_name, "..")==0)
continue; // skip current and parent directories
printf("%*c%s\n", level, '>', direntp->d_name);
if( direntp->d_type == 4)
{
// build child dir name and call listOfDir
subdirname = (char*)malloc(strlen(direntp->d_name) + strlen(dirname) + 2);
strcpy(subdirname, dirname);
strcat(subdirname, "/");
strcat(subdirname, direntp->d_name);
listOfDir(subdirname, level+1);
free(subdirname);
}
}
closedir(dirp);
}
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;
}
listOfDir(argv[1], 1);
return 0;
}
Expression like printf("%*c", level, '>') just make indents for elach level of nesting
I am writing a program in c that takes a path name and traverses that path, and prints out all file paths it comes across and the size of that file in blocks and then if it is a dir it prints out the dir pathname and size in blocks.
The code is ending up in an infinate loop and keeps saying "Failed to get status:too many files open."
#include <dirent.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char **argv ){
struct stat statbuf;
struct dirent *direntp;
DIR *dirp;
if(stat(argv[1], &statbuf) == -1){
perror("Failed to get file status");
return 1;
}
else if(argc != 2){
perror("Invalid amount of arguments, showtreesize requires 1 pathname");
return 1;
}
else{
if(S_ISDIR(statbuf.st_mode) || S_ISREG(statbuf.st_mode)){
printf("%s %d", argv[1], depthfirstapply(argv[1], sizepathfun(argv[1])));
}
else{
if(S_ISCHR(statbuf.st_mode)){
printf("%s is a character special file.", argv[1]);
}
if(S_ISBLK(statbuf.st_mode)){
printf("%s is a block special file", argv[1]);
}
if(S_ISFIFO(statbuf.st_mode)){
printf("%s is a FIFO special file", argv[1]);
}
else{
printf("%s is not a valid filetype", argv[1]);
}
}
return 0;
}
}
int sum = 0;
int levelcount = 0;
int isDirectory(char *path){
struct stat statbuf;
if(stat(path, &statbuf) == -1)
return 0;
else
return S_ISDIR(statbuf.st_mode);
}
int depthfirstapply(char *path, int pathfun(char *path1)){
struct dirent *direntp;
DIR *dirp;
if(isDirectory(path)){
printf("%s\n", path);
if((dirp = opendir(path)) == NULL){
perror ("Failed to open directory");
return -1;
}
else{
while((direntp = readdir(dirp)) != NULL) {
if(isDirectory(direntp->d_name)){
int result = depthfirstapply(direntp->d_name, pathfun);
if (result > 0){
sum += result;
}
}
else{
if(pathfun(direntp->d_name) >= 0){
sum += pathfun(direntp->d_name);
}
}
}
while ((closedir(dirp) == -1) && (errno == EINTR)) ;
}
}
else{
sum += pathfun(path);
}
return sum;
}
int sizepathfun(char *path){
struct stat statbuf;
if(stat(path, &statbuf) == -1){
perror("Failed to get file status");
return -1;
}
if(S_ISREG(statbuf.st_mode) == 0){
return -1;
}
else{
printf("%s %d", path, statbuf.st_blocks);
return statbuf.st_blocks;
}
}
Several problems:
You need to skip the . and .. entries. Otherwise, you'll keep looping on the same directory.
Use lstat() rather than stat() in isDirectory. Otherwise, you'll recurse into symbolic links to directories, which could cause a loop.
As you go down each directory level, you need to concatenate the directory components to the names.
The 2nd argument to depthfirstapply is supposed to be a function. But in main() you call it with sizepathfun(argv[1)), which returns an integer. The argument should just be sizepathfun. You should be getting a compilation warning because of the type mismatch.
POSIX provides a standard set of functions for this, fts_XXX().
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?