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;
}
Related
I want to create file test in this path /tmp/a1/a2/a3/a4/test
But all the directories (a1..a4) doesn't exist, How can I create this file in C at Linux OS?
You can use the mkdir function from sys/stat.h to create the directories as you need them. E.g.,
mkdir("/tmp/a1",0766);
However, you should check, via stat, whether or not the directories exist already.
Once you've created the directory structure, your file can be created with
open("/tmp/a1/a2/a3/a4/test",O_WRONLY|O_CREAT);
Obviously, you need to check the return values of all of these function calls.
Below is a complete function in C that achieves what you want.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
int create_file_with_parent_dirs (char pathname[])
{
for (char *p = pathname; (p = strchr(p ,'/')) != NULL; ++p) {
char c = p[1];
p[1] = '\0';
errno = 0;
if (mkdir(pathname, 0700) != 0 && errno != EEXIST) {
perror("mkdir");
return -1;
}
p[1] = c;
}
int fd = creat(pathname, 0600);
if (fd < 0)
perror("creat");
return fd;
}
int main (void)
{
char pathname[] = "/tmp/a1/a2/a3/a4/test";
create_file_with_parent_dirs(pathname);
}
Note that the array pointed to by pathname must be modifiable. Do not call the function with a string literal. Also beware that the file will be truncated to zero length if it already exists.
You can use this code. This program split the path and check whether the path exist or not if not create the path and create the final path as file.
#include <dirent.h>
#include <errno.h>
#include <bits/stdc++.h>
#include <iostream>
#include <sys/stat.h>
#include <sys/types.h>
#include <vector>
#include <fstream>
using namespace std;
int checkDir(char * path)
{
DIR* dir = opendir(path);
if (dir) {
/* Directory exists. */
closedir(dir);
return 0;
} else if (ENOENT == errno) {
/* Directory does not exist. */
return -1;
} else {
/* opendir() failed for some other reason. */
return -2;
}
}
int make_dir(char *path)
{
int ret = checkDir(path);
if( ret == -1)
{
if (mkdir(path, 0777) == -1)
{
cerr << "Error : " << strerror(errno) << endl;
return -1;
}
else
{
cout << "Directory created";
return 0;
}
}
return ret;
}
int main(int args, char **argv)
{
std::string strpath(argv[1]);
std::string delimeter = "/";
std::string substr1 = "";
int cnt = 0;
std::vector<string> strPaths;
std::string strbck = strpath;
for( int i = strpath.find(delimeter); i != std::string::npos; i = strpath.find(delimeter))
{
if(cnt > 0)
{
substr1 = strbck.substr(0, substr1.length() + i + 1);
strPaths.push_back(substr1);
}
strpath = strpath.substr(i +1,strpath.length());
cnt++;
}
strPaths.push_back(strbck);
std::string str;
for_each( strPaths.begin() ,strPaths.end() -1 , [](std::string str) {make_dir((char*)str.c_str());});
ofstream outfile;
std::ofstream file {strPaths[strPaths.size() -1].c_str() };
file << "hello"<< std::endl;
file.close();
return 0;
}
#include <stdio.h>
#include <dirent.h>
int main() {
struct dirent *de;
DIR *dr;
int i = 1;
dr = opendir("."); // need to get directory through stdin insted of this
if (dr == NULL) printf("Could not open directory");
while (((de = readdir(dr)) != NULL))
{
printf("\t%d. %s\n", i, de -> d_name);
i++;
}
closedir(dr);
return 0;
}
You read it from stdin and use in place of ".". Here is the full example
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
int main(){
struct dirent *de;
char dirbuf[BUFSIZ] = {0};
DIR *dr;
int i = 1;
puts("Chose the directory: ");
if (fgets(dirbuf, BUFSIZ, stdin) == NULL) {
perror("fgets");
exit(-1);
}
dirbuf[strlen(dirbuf)-1] = '\0'; // remove \n
dr = opendir(dirbuf); // need to get directory through stdin insted of this
if (dr == NULL) {
perror("opendir");
exit(-1);
}
while(((de = readdir(dr)) != NULL))
{
printf("\t%d. %s\n", i, de -> d_name);
i++;
}
closedir(dr);
return 0;
}
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;
}
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.
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?