Vishal
Vishal

Reputation:

how to get the size of a dir programatically in linux?

I want to get the exact size of a particular dir in linux through a C program. I tried using statfs(path,struct statfs &) but it doesn't give exact size. I also tried with stat() but it returns size as 4096 for any dir !

Please suggest me the way through which I can get the exact size of dir just like we get after "du -sh dirPath" command.

Also I dont wanna use du through system().

Thanks in advance.

Upvotes: 7

Views: 17752

Answers (4)

Dave Jarvis
Dave Jarvis

Reputation: 31211

Based on Jim Plank's example to get you started:

#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

int main( int argc, char **argv ) {
  DIR *d = opendir( "." );

  if( d == NULL ) {
    fprintf( stderr, "Cannot open current working directory\n" );
    return 1;
  }

  struct dirent *de;
  struct stat buf;
  int total_size = 0;

  for( de = readdir( d ); de != NULL; de = readdir( d ) ) {
    int exists = stat( de->d_name, &buf );

    if( exists < 0 ) {
      fprintf( stderr, "Cannot read file statistics for %s\n", de->d_name );
    } else {
      total_size += buf.st_size;
    }
  }

  closedir( d );
  printf( "%d\n", total_size );

  return 0;
}

Notes, considerations, and questions for the reader:

  • This is example is incomplete. See Plank's notes for more details.
  • What happens if there are locked files?
  • Do symbolic links need special handling (to avoid infinite loops)?
  • How would you output the full path name for erroneous files?

This answer is a starting point, not a complete and robust program to calculate directory sizes. If you need more help, read the source code for the du program.

Upvotes: 12

goodmove
goodmove

Reputation: 161

I guess the solution may be useful for those who still may encounter the problem.

Here's the function that was written to imitate linux du program. It recursively goes through all directories and adds up file sizes.

Note, that this function is still incomplete, since it behaves incorrectly on hard links. One should add a container to store file descriptors that point to the same inode entity and use that to get rid of multiple counts of the very same file. lstat() is used to handle symlinks (aka soft links), hard links is an issue here.

size_t countDiskUsage(const char* pathname)
{
  if (pathname == NULL) {
    printf("Erorr: pathname is NULL\n");
  }

  struct stat stats;

  if (lstat(pathname, &stats) == 0) {
    if (S_ISREG(stats.st_mode)){
      return stats.st_size;
    }
  } else {
    perror("lstat\n");
  }

  DIR* dir = opendir(pathname);

  if (dir == NULL) {
    perror("Error");
    return 0;
  }

  struct dirent *dirEntry;
  size_t totalSize = 4096;

  for (dirEntry = readdir(dir); dirEntry != NULL; dirEntry =   readdir(dir)) {
    long pathLength = sizeof(char) * (strlen(pathname) + strlen(dirEntry->d_name) + 2);
    char* name = (char*)malloc(pathLength);
    strcpy(name, pathname);
    strcpy(name + strlen(pathname), "/");
    strcpy(name + strlen(pathname) + 1, dirEntry->d_name);

    if (dirEntry->d_type == DT_DIR) {
      if (strcmp(dirEntry->d_name, ".") != 0 && strcmp(dirEntry->d_name, "..") != 0) {
        totalSize += countDiskUsage(name);
      }
    } else {
      int status = lstat(name, &stats);
      if (status == 0) {
        totalSize += stats.st_size;
      } else {
        perror("lstat\n");
      }
    }
    free(name);
  }

  closedir(dir);

  return totalSize;
}

Upvotes: 3

jfg956
jfg956

Reputation: 16748

If you do not want to use 'system', but are ok to use 'pipe', 'fork', 'execlp' and 'du', you could build a pipe, fork a new process, redirect 'STDOUT' of the child in the pipe, exec 'du' in the child, and read the result in the parent. A sample code would be:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {
  int pfd[2], n;
  char str[1000];

  if (pipe(pfd) < 0) {
    printf("Oups, pipe failed.  Exiting\n");
    exit(-1);
  }

  n = fork();

  if (n < 0) {
    printf("Oups, fork failed.  Exiting\n");
    exit(-2);
  } else if (n == 0) {
    close(pfd[0]);

    dup2(pfd[1], 1);
    close(pfd[1]);

    execlp("du", "du", "-sh", "/tmp", (char *) 0);
    printf("Oups, execlp failed.  Exiting\n"); /* This will be read by the  parent. */
    exit(-1); /* To avoid problem if execlp fails, especially if in a loop. */
  } else {
    close(pfd[1]);

    n = read(pfd[0], str, 1000); /* Should be done in a loop until read return 0, but I am lazy. */
    str[n] = '\0';

    close(pfd[0]);
    wait(&n); /* To avoid the zombie process. */

    if (n == 0) {
       printf("%s", str);
    } else {
       printf("Oups, du or execlp failed.\n");
    }
  }
}

Upvotes: 2

Byron Whitlock
Byron Whitlock

Reputation: 53921

You need to stat() all the files in the current directory and sub directories and add them up.

Consider using a recursive algorithm for this.

Upvotes: 5

Related Questions