Issues multithreading in c - c

I'm trying to write a program in c that uses threads to print out the contents of the current working directory. Currently the right number of files are printed, however, some files repeat multiple times. Which files repeat and how many times seems random every time I run it. I tried using a mutex lock on what I thought was the critical section, but to no success.
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
struct arg_struct
{
struct dirent *current;
struct stat buf;
};
void * mybackup (void *arguments)
{
pthread_mutex_lock( &mutex );
struct arg_struct *args = arguments;
printf( "[THREAD %u] ", (unsigned int)pthread_self());
printf( "%s ", args->current->d_name );
printf( "(%lld bytes)\n", (long long) args->buf.st_size );
total_bytes += args->buf.st_size;
pthread_mutex_unlock( &mutex );
return NULL;
}
int main (int argc, char **argv)
{
if (argc == 1) //For part 1, when no cmd line argument is given
{
int children = 0;
int thread, i;
pthread_t tid[100];
char * current_path = ".";
DIR * dir = opendir((char*)current_path); //Opens the current directory
if (dir == NULL) //Detects failure to open directory
{
perror("opendir() Failed");
return EXIT_FAILURE;
}
struct dirent *current;
int rc = 0;
while ((current = readdir(dir)) != NULL)
{
struct stat buf;
char new[10000]; //Used to create the complete path name
//Next few lines create the complete path name required for Stat()
strcpy(new, current_path);
strcat(new, "/");
strcat(new, (char*)current->d_name);
rc = stat(new, &buf);
//rc = stat(current -> d_name, &buf);
if (rc < 0) //Detects stat() failure
{
perror("stat() Failed");
return 1;
}
else
{
if (S_ISREG(buf.st_mode)) //If directory entry is a regular file
{
struct arg_struct args;
args.current = current;
args.buf = buf;
thread = pthread_create(&tid[children], NULL, mybackup, (void *)&args);
if ( thread != 0 )
{
perror( "MAIN: Could not create child thread" );
}
children++;
}
}
}
for (i = 0; i < children; i++)
{
pthread_join(tid[i], NULL);
}
printf("Total bytes: %lld\n", total_bytes);
}
return 0;
}

{
struct arg_struct args;
...
thread = pthread_create(&tid[children], NULL, mybackup, (void *)&args);
This can't be right. The (stack-based) object you pass to the to the thread function it is very likely to be overwritten before the created thread has had a chance to do anything with it.
You'll need to malloc that structure (and deal with freeing), or use an array (on stack or static) of such structs that is large enough so that you can assign one to each of your threads.
In fact, just allocating that structure's not enough, you can't pass the dirent* current as a pointer - you need to copy the data you need from it to your per-thread structure, or possibly use readdir_r instead (read the example in the man page carefully).

Related

Accessing struct pointer as pthread arg causes seg fault?

So here is a small portion of my code. Essentially I am starting another thread from a method, but when I pass the integer to the pthread, I can't access the struct members like I could only two lines before I called the thread. What has occurred since I passed this argument and run a new thread?
Note that without fail, my program always crashes immediately after printf("1\n");, I discovered this error when I realised FD_ZERO didn't work.
Struct definition (in global area):
typedef struct {
fd_set read_set, write_set;
unsigned int room_id;
char room_name[16];
} room;
Caller method:
void create_new_room(int cli_index, char buffer[]) {
pthread_mutex_lock(&mutex);
char room_name[16], password[16];
int capacity, r_index;
room *new_room = malloc(sizeof(room));
pthread_t tid;
FILE *room_file = NULL;
if((room_file = fopen("room-info.txt", "a")) == NULL) {
perror("Failed to open file.");
exit(-1);
}
// Split command data into separate strings ready to assign as room struct members
strtok(buffer, " ");
strcpy(room_name, strtok(NULL, " "));
capacity = atoi(strtok(NULL, " "));
strcpy(password, strtok(NULL, " "));
// Initialise room struct
// --Zero write set
FD_ZERO(&(new_room->write_set));
// --Set room name
strcpy(new_room->room_name, room_name);
// --Set room id
new_room->room_id = ++natural_id;
// --Add room to room_list[] and get its index in the array
for(r_index = 0; r_index < 10000; ++r_index) {
if(!room_list[r_index]) {
room_list[r_index] = new_room;
break;
}
}
// Write data to file
fprintf(room_file, "%s\n", room_name);
fprintf(room_file, "id=%u\n", natural_id);
fprintf(room_file, "owner=%s\n", client_list[cli_index]->name);
fprintf(room_file, "capacity=%d\n", capacity);
fclose(room_file);
printf("about to test\n");
printf("Testing room struct:\n");
printf("--room name = %s\n", room_list[r_index]->room_name);
printf("--room id = %u\n", room_list[r_index]->room_id);
printf("post-test.....................\n");
// Run new thread as active room
printf("Starting new room: %s\n", room_name);
pthread_create(&tid, NULL, (void *) &active_room, &r_index);
pthread_mutex_unlock(&mutex);
}
Start of new pthread method:
void active_room(void *index) {
char storage_buffer[BSIZE], output_buffer[BSIZE+32];
struct timeval timeout;
int r_index = *(int *) index;
while(1) {
// debugging lines.
printf("1\n");
printf("room name: %s\n", room_list[r_index]->room_name);
printf("room id: %u\n", room_list[r_index]->room_id);
FD_ZERO(&(room_list[r_index]->read_set));
read_set = write_set;
timeout.tv_sec = 5;
printf("2\n");
The r_index variable in create_new_room is local to the function. You then pass its address to pthread_create so the thread can use it, but then create_new_room exits, causing the lifetime of r_index to end. Then when the thread tried to dereference its argument, it is pointing to memory that is no longer valid, triggering undefined behavior.
You'll need to dynamically allocate memory for the thread's argument so it survives after create_new_room exits:
int *r_index_arg = malloc(sizeof *r_index_arg);
*r_index_arg = r_index;
pthread_create(&tid, NULL, (void *) &active_room, r_index_arg);
Then you need to make sure you free the memory in the thread:
int r_index = *(int *) index;
free(index);

Condition variable blocks on main thread

I want to use condition variables to launch at most N thread to process all files one one huge directory (1M files).
The code seems to work but after some times, it blocks in main thread. Below the frustrating code:
void* run(void* ctx)
{
clientCtx* client = (clientCtx*)ctx;
printf("New file from thread %d: %s\n", client->num, client->filename);
free(client->filename);
pthread_mutex_lock(&clientFreeMutex);
client->state = IDLE_STATE;
pthread_cond_signal(&clientFreeCond);
printf("Thread %d is free\n", client->num);
pthread_mutex_unlock(&clientFreeMutex);
return NULL;
}
int main(int argc, char** argv)
{
pthread_t client[MAX_CLIENT] = {0};
clientCtx ctx[MAX_CLIENT] = {0};
DIR* directory = NULL;
struct dirent* element = NULL;
/* Initialize condition variable for max clients */
pthread_mutex_init(&clientFreeMutex, NULL);
pthread_cond_init(&clientFreeCond, NULL);
/* Initialize contexts for clients */
for (int cnt = 0; cnt < MAX_CLIENT; cnt ++)
{
ctx[cnt].state = IDLE_STATE;
ctx[cnt].num = cnt;
}
directory = opendir(argv[1]);
while((element = readdir(directory)) != NULL)
{
pthread_mutex_lock(&clientFreeMutex);
int cnt;
for (cnt = 0; cnt < MAX_CLIENT; cnt++)
{
if(ctx[cnt].state == IDLE_STATE)
{
ctx[cnt].filename = strdup(element->d_name);
ctx[cnt].state = BUSY_STATE;
pthread_create(&client[cnt], NULL, run, &(ctx[cnt]));
break;
}
}
/* No free client */
if (cnt == MAX_CLIENT)
{
printf("No free thread. Waiting.\n");
pthread_cond_wait(&clientFreeCond, &clientFreeMutex);
}
pthread_mutex_unlock(&clientFreeMutex);
}
closedir(directory);
exit(EXIT_SUCCESS);
}
What is the problem? thanks for your help :)
Warning you use the value of readdir in separate threads without any protection against the multi-threading, so when you (try to) printf client->file->d_name may be you are doing at the same time readdir in the main thread modifying the saved result, this has an undefined behavior.
You need for example to save a strdup of element->file->d_name in main and save that string in the clientCtx rather than the struct dirent *, and of course to free it in run
Note also a closedir is missing at the end of main even in this case it is not a real problem (just do to remember for your other programs).
I finally found the problem: launched threads were not joined and pthread_create finally returned an error code with errno message set to "Could not allocate memory". The signal was never sent and the main thread was then blocking.
I fixed this creating a new state for already launched threads and adding a join in main loop.

Threads are not able to acquire locks even though no one else is using them

I am trying to write a code that is using threads in order to find files that contain a specific string in their name.
My code works most of the times. in some specific cases, threads are not able to acquire locks for some reason.
I tried debugging really hard (using prints, as you can see in the code) but I couldn't find the problem.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
void* threadFunction(void* searchTerm);
bool scanDirName(char * path, char * searchTerm);
int numOfThreads;
pthread_mutex_t qlock;
pthread_cond_t cond;
int count;
int matchingFiles;
struct Node {
char* data;
struct Node* next;
};
// Two global variables to store an address of front and rear nodes.
struct Node* front = NULL;
struct Node* rear = NULL;
// To Enqueue an integer
void Enqueue(char* x) {
/* printf("\nhere\n");*/
struct Node* temp = (struct Node*)malloc(sizeof(struct Node));
temp->data =x;
temp->next = NULL;
if(front == NULL && rear == NULL){
front = rear = temp;
pthread_cond_signal(&cond);
return;
}
rear->next = temp;
rear = temp;
}
// To Dequeue an integer.
char* Dequeue() {
struct Node* temp = front;
if(front == NULL) {
return NULL;
}
char* data;
data = front->data;
if(front == rear) {
front = rear = NULL;
}
else {
front = front->next;
}
// printf("\nfreeing %p, %s\n", temp, temp->data);
free(temp);
return data;
}
void* threadFunction(void* st) {
bool isFinished;
isFinished = false;
pthread_mutex_lock(&qlock);
while (true) {
char *filepath;
char *searchTerm;
searchTerm = (char *) st;
filepath = Dequeue();
pthread_mutex_unlock(&qlock);
if (filepath == NULL) {
printf("%ld waiting for lock \n",(long) pthread_self());
pthread_mutex_lock(&qlock);
count++;
if (isFinished) {
printf("%ld waking u up, we found %d items!\n",(long) pthread_self(), matchingFiles);
pthread_cond_broadcast(&cond);
if (count == numOfThreads) {
printf("Thread exited: %ld\n", (long) pthread_self());
pthread_mutex_unlock(&qlock);
pthread_exit((void*)0);
}
}
isFinished = false;
printf("%ld going to sleep\n",(long) pthread_self());
pthread_cond_wait(&cond, &qlock);
printf("%ld Woke up, try to compare %d == %d\n",(long) pthread_self(), count, numOfThreads);
if (count == numOfThreads) {
printf("Thread exited: %ld\n", (long) pthread_self());
pthread_mutex_unlock(&qlock);
pthread_exit((void*)1);
}
printf("%ld compare failed \n",(long) pthread_self());
count--;
}
else {
printf("%ld deq 1 item \n",(long) pthread_self());
isFinished = scanDirName(filepath, searchTerm);
}
}
}
bool scanDirName(char * path, char * searchTerm){
DIR * d = opendir(path); // open the path
char* str3;
if(d==NULL) return false; // if was not able return
;
struct dirent * dir; // for the directory entries
while ((dir = readdir(d)) != NULL) // if we were able to read somehting from the directory
{
printf("%ld STARTED A ROUND!\n",(long) pthread_self());
if(dir-> d_type == DT_DIR){ //
if (dir->d_type == DT_DIR && strcmp(dir->d_name, ".") != 0 & strcmp(dir->d_name, "..") != 0) // if it is a directory
{
str3 = malloc((1+strlen("/")+ strlen(path)+ strlen(dir->d_name))*sizeof(char));
if (!str3){
return false;
}
strcpy(str3, path);
strcat(str3, "/");
strcat(str3, dir->d_name);
// printf("\n---\n%s\n---\n",str3);
printf("%ld waiting for lock in func \n",(long) pthread_self());
pthread_mutex_lock(&qlock);
printf("%ld wake threads \n",(long) pthread_self());
Enqueue(str3);
pthread_cond_signal(&cond);
printf("%ld enq \n",(long) pthread_self());
pthread_mutex_unlock(&qlock);
printf("%ld locks gone \n",(long) pthread_self());
}
}
else if(dir-> d_type == DT_REG){ //
if(strstr(dir->d_name, searchTerm)){
matchingFiles++;
/*printf("%s/%s\n", path, dir->d_name);*/
}
}
printf("%ld finished A ROUND!\n",(long) pthread_self());
}
printf("%ld finished scanning!\n",(long) pthread_self());
fflush(stdout);
closedir(d); // finally close the directory
if (count == numOfThreads-1) {
return true;
}
return false;
}
int main(int argc, char* argv[]){
count = 0;
pthread_mutex_init(&qlock, NULL);
pthread_cond_init(&cond, NULL);
matchingFiles = 0;
if (argc != 4){
printf("ERROR\n");
exit(1);
}
char* rootSearchDir = argv[1];
char* searchTerm = argv[2];
int threadsNumber = atoi(argv[3]);
pthread_t threadsCollection[threadsNumber];
Enqueue(rootSearchDir);
numOfThreads = threadsNumber;
int i;
for (i=0; i<threadsNumber; i++){
if(pthread_create(&threadsCollection[i], NULL, threadFunction, (void*)searchTerm)) {
fprintf(stderr, "Error creating thread\n");
pthread_mutex_destroy(&qlock);
return 1;
}
}
int rc;
for (i=0; i<threadsNumber; i++){
rc = pthread_join((threadsCollection[i]), NULL);
if(rc) {
fprintf(stderr, "Error joining thread, %d\n", rc);
pthread_mutex_destroy(&qlock);
return 1;
}
}
}
I have attached all of the code (maybe it is important) but I suspect the problem is in scanDirName or threadFunction.
--- EDIT ---
I found the problem. I had a logical problem with the locks, fixed it.
Thanks all!
if filepath = Dequeue() returns a non-null; then your next iteration of this loop will call Dequeue() without holding &qlock. If you checked the return values, you would notice this.
scanDirName relies on count to set its return value; however it isn’t protected by qlock { see #1 }
If I read your code correctly, you are attempting to make a sort of dispatch mechanism, where names are emitted from scanDir() into a queue, and pulled from the queue, where a fresh scanDir() is spun up for each item. The handling of count is an attempt to detect whether the Dequeue failed because there is nothing left to do, or everyone is busy doing scanDirs.
There is a bit easier of a mechanism: use two queues. Scandir dumps entries into say “NameQ”, and thread function pulls names out of NameQ, dumping them into WorkQ. When a thread runs out of NameQ items, it looks into WorkQ for fresh ones to dispatch. Then you just have to keep track of how many threads are in scanDir(). If both queues are empty, and number of threads in scanDir is zero, there is nothing more to do. Your Basic logic ends up looking like this:
void* threadFunction(void* st) {
static int nreader = 0;
int ncount = 0, wcount = 0;
char *searchTerm;
searchTerm = (char *) st;
Menter(&qlock);
while (nreader || !(empty(&Names) && empty(&Work))) {
char *filepath;
filepath = Dequeue(&Names);
if (filepath) {
ncount++;
Enqueue(&Work, filepath);
} else if ((filepath = Dequeue(&Work)) != NULL) {
wcount++;
nreader++;
Mexit(&qlock);
scanDirName(filepath, searchTerm);
Menter(&qlock);
nreader--;
cv_wake(&cond);
} else {
cv_wait(&cond, &qlock);
}
}
Mexit(&qlock);
printf("%p: %d, %d items\n", pthread_self(), ncount, wcount);
return NULL;
}
Mexit, Menter are error checking covers for the posix funs, and a lot easier on the eyes than all that redundant typing...
static int menter(pthread_mutex_t *m, int line) {
if (pthread_mutex_lock(m) != 0) {
fprintf(stderr, "%p:%d Mutex lock failed!\n", pthread_self(),line);
abort();
}
DEBUG_LOCK("%d locked\n", line);
return 0;
}
#define Menter(x) menter((x), __LINE__)
static int mexit(pthread_mutex_t *m, int line) {
DEBUG_LOCK("%d unlocked\n", line);
if (pthread_mutex_unlock(m) != 0) {
fprintf(stderr, "%p:%d Mutex unlock failed!\n", pthread_self(),line);
abort();
}
return 0;
}
#define Mexit(x) mexit((x), __LINE__)

How can I share a struct with pointers between processes in C?

I'm trying to put a file in a struct but I'm having problems sharing the memory as I can access the fields in the created process where the mapping happens but cannot access the arrays(can only access the int) in the other processes. I tried a lot of different things but the way I present next is the one that makes more sense to me as I'm allocating memory the right way with shmget.
For clarity: The only thing that is being shared is the integer lim_thread. The other fields are in a area of memory that I can not access. Why?
As I see the pointers are pointing to a region of memory that is shared.
configs.txt:
Threads = 5
Domains = uc.pt; edu
LocalDomain = so.local
NamedPipeEstatisticas = statistics
struct:
typedef struct configs
{
int lim_thread;
char (*valid_domains)[MAX_DOMAIN_SIZE]; //max size for valid domains
char *domain;
char *stat_pipe;
sem_t sem;
} CONFIGS;
main.c:
/*Shared memory for configs*/
CONFIGS *_configs;
int _shmid_configs;
int main(int argc, char *argv[]) {
pid_t config_pid; //will hold the configuration process id
_shmid_configs = shmget(IPC_PRIVATE, sizeof(CONFIGS), IPC_CREAT|0666);
_configs = shmat(_shmid_configs, NULL, 0);
/*Semaphores*/
sem_init( &( _configs->sem), 1, 0);
//initializes processes
if( ( config_pid = fork() ) < 0) {
perror("Failed creating configuration manager process");
num_forks++;
}
else if( config_pid == 0 ) {
init_config();
exit(0);
}
sem_wait(&(_configs->sem));
/////////////////////////DEBUG////////////////////////////////
printf("%d\n", _configs->lim_thread);
printf("%s\n", *(_configs->valid_domains+1));
printf("%s\n", _configs->domain);
printf("%s\n", _configs->stat_pipe);
//////////////////////////////////////////////////////////////
return 0;
}
configs.c
#define MAX_LINE_SIZE 1000
int init_config() {
FILE *fp;
char domains[MAX_LINE_SIZE], line[MAX_LINE_SIZE], *saveptr, *aux_char;
int count = 0, aux;
int temp_shmid;
if( ( fp = fopen( "./configs.txt", "r")) == NULL) {
perror("Failed to open configs.txt");
return -1;
}
fscanf( fp,"Threads = %d\n", &(_configs->lim_thread));
//To start reading "Domains = "
fscanf(fp, "Domains = ");
fgets(domains, MAX_LINE_SIZE, fp);
domains[strlen(domains) -1] = '\0';
//counts the number of domains
for(aux = 0; aux < strlen(domains); aux++) {
if( domains[aux] == ';' ) {
count++;
}
}
//creates shared memory for the valid domains
temp_shmid = shmget(IPC_PRIVATE, (count+1) * sizeof( char[MAX_DOMAIN_SIZE]), IPC_CREAT|0666);
_configs->valid_domains = shmat( temp_shmid, NULL, 0);
//copies all the data to the struct
strcpy( *(_configs->valid_domains), strtok_r(domains, "; ", &saveptr) );
aux = 1;
while( ( aux_char = strtok_r( NULL, "; ", &saveptr) ) != NULL) {
strcpy( *(_configs->valid_domains + aux), aux_char);
aux++;
}
fscanf(fp, "LocalDomain = %s\n", line);
temp_shmid = shmget(IPC_PRIVATE, (strlen(line) + 1) * sizeof(char), IPC_CREAT|0660);
_configs->domain = (char*)shmat(temp_shmid, NULL, 0);
strcpy(_configs->domain, line);
fscanf(fp, "NamedPipeEstatisticas = %s\n", line);
temp_shmid = shmget( IPC_PRIVATE, (strlen(line) +1) * sizeof(char), IPC_CREAT|0660);
_configs->stat_pipe = (char*)shmat(temp_shmid, NULL, 0);
strcpy(_configs->stat_pipe, line);
fclose(fp);
sem_post( &(_configs->sem));
/////////////////////////DEBUG////////////////////////////////
printf("%d\n", _configs->lim_thread);
printf("%s\n", *(_configs->valid_domains+1));
printf("%s\n", _configs->domain);
printf("%s\n", _configs->stat_pipe);
//////////////////////////////////////////////////////////////
return 0;
}
As kaylum pointed out, each process might map a shared memory block to a different virtual address. Hence pointers cannot be shared, you need to work with offsets.
Allocate a single block of shared memory that you divide in two parts: a table of content and a data area. The table of content consists of variables that contain either a value or (instead of a pointer), the offset between the start of the shared memory block and the start of a data element inside the data area.
Then to obtain the address of a data element a process simply adds its offset to the address of the shared memory block in its address space.
You can do what you want. Just do all your shmget/shmat in your main process/thread before you create any threads.
The children will inherit the pointers and the values will be the same for all threads (e.g. the pointers are global. That is, they are not using _thread and are not in thread local storage).
This works fine. I've used it in systems with 50+ threads and it works great

pointer problems when trying to build a directory tree in memory

Problem 1: what's the best data structure to save the directory structure?
Problem 2: I have tried to use a general tree to solve it, but there are a lot of problems:
The number of files under a directory is not certain. So the number of child nodes under a tree node is also not certain. and I try to add a keyword nchild to each node, showing nchild child nodes. so there are nchild pointers (saved with **child) to the child nodes. And once that, **child and *child should be dynamically allocated space with no certain child nodes. So you know, this is really difficult to release these spaces(and the program below is not called free()). Is there a better way to solve it?
And sometimes the program below would get the garbage characters when I output the directory tree, which make me really confused. while debugging it, found that is the function ent=readdir(pDir); has read garbage characters. But when I write another simple program to read the same directory, that goes well. I think the problem is the recursive function, but I didn't get any idea. I will be appreciated if some one can give me a idea. Thanks!
```
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
typedef struct tree_file_s
{
char path[512];
time_t date;
char type;
long size;
int nchild;
struct tree_file_s **child;
} tree_file_t;
int dir_child_len(const char *dir)
{
int nchild = 0;
DIR *pDir;
struct dirent *ent;
pDir = opendir(dir);
while((ent=readdir(pDir)) != NULL)
{
if (strcmp(ent->d_name, ".")==0 || strcmp(ent->d_name, "..")==0)
{
continue;
}
nchild++;
}
return nchild;
}
void tree_create(tree_file_t *tft, const char *dir)
{
int nchild; // the tft has n child
DIR *pDir;
struct dirent *ent; // the directory dir dirent info
struct stat file_stat; // the new file's stat info
stat(dir, &file_stat);
nchild = dir_child_len(dir);
pDir = opendir(dir);
// Initialize the parent
//tft->path = calloc(1, strlen(dir)+1);
strcpy(tft->path, dir);
tft->date = file_stat.st_mtime;
tft->type = 'D';
tft->size = file_stat.st_size;
tft->nchild = nchild;
tft->child = calloc(1, nchild);
nchild = 0;
while ((ent=readdir(pDir)) != NULL)
{
if (ent->d_type & DT_DIR)
{
if (strcmp(ent->d_name, ".")==0 || strcmp(ent->d_name, "..")==0)
{
continue;
}
tree_file_t *new_dir = calloc(1, sizeof(tree_file_t));
tft->child[nchild] = new_dir;
char *new_path = calloc(1, strlen(dir)+strlen(ent->d_name)+1);
sprintf(new_path, "%s/%s", dir, ent->d_name);
tree_create(new_dir, new_path);
free(new_path);
} else {
tree_file_t *new_file = calloc(1, sizeof(tree_file_t));
char *new_path = calloc(1, strlen(dir)+strlen(ent->d_name)+1);
// new_file->path = calloc(1, strlen(dir)+strlen(ent->d_name)+1);
sprintf(new_path, "%s/%s", dir, ent->d_name);
stat(new_path, &file_stat);
strcpy(new_file->path, new_path);
free(new_path);
new_file->date = file_stat.st_mtime;
new_file->type = 'F';
new_file->size = file_stat.st_size;
new_file->nchild = 0;
new_file->child = 0;
tft->child[nchild] = new_file;
}
//free(new_path);
//new_path = 0;
nchild++;
}
}
void display_tree(tree_file_t *tft)
{
int nchild, i;
nchild = tft->nchild;
printf("%c: %s\n", tft->type, tft->path);
for(i = 0; i < nchild; i++)
{
if(tft->child[i]->type == 'F')
{
printf("%c: %s\n", tft->child[i]->type, tft->child[i]->path);
} else {
display_tree(tft->child[i]);
}
}
}
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usage: a.out dir\n");
exit(0);
}
char dir[512];
strcpy(dir, argv[1]);
tree_file_t *tft = calloc(1, sizeof(tree_file_t));
tree_create(tft, dir);
display_tree(tft);
return 0;
}
```
When you allocate space for new_path you need to add 2 (one for the slash, one for the null terminator). And you never close the directories you open (use closedir()).
An even more serious error is this line:
tft->child = calloc(1, nchild);
which only allocates nchild bytes, not enough to hold nchild pointers! Try:
tft->child = calloc(nchild, sizeof(*tft->child));

Resources