#include "common.h" ssize_t readn(int fd, void *vptr, size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ((nread = read(fd, ptr, nleft)) < 0) { if (errno == EINTR) { nread = 0; /* call read() again */ } else { return -1; } } else if (nread == 0) { break; /* EOF */ } nleft -= nread; ptr += nread; } return n - nleft; } ssize_t writen(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ((nwritten = write(fd, ptr, nleft)) <= 0) { if (nwritten < 0 && errno == EINTR) nwritten = 0; /* and call write() again */ else return -1; /* error */ } nleft -= nwritten; ptr += nwritten; } return n; } // ─── Simple path creation ─────────────────────────────────────────────── int make_path(const char *path, mode_t mode) { char tmp[2048], *p = NULL; size_t len; snprintf(tmp, sizeof(tmp), "%s", path); len = strlen(tmp); if (tmp[len - 1] == '/') tmp[len - 1] = '\0'; for (p = tmp + 1; *p; p++) if (*p == '/') { *p = '\0'; if (mkdir(tmp, mode) == -1 && errno != EEXIST) return -1; *p = '/'; } if (mkdir(tmp, mode) == -1 && errno != EEXIST) return -1; return 0; } // ─── Work-Queue (client-side, thread-safe) ─────────────────────────────────── void wq_init(work_queue_t *q) { q->head = NULL; q->tail = NULL; q->done = 0; q->error = 0; pthread_mutex_init(&q->lock, NULL); pthread_cond_init(&q->cond, NULL); } void wq_push(work_queue_t *q, const char *full_path, const char *rel_path, uint64_t file_size, uint64_t checksum) { file_task_t *t = malloc(sizeof(file_task_t)); if (!t) { perror("wq_push malloc"); return; } snprintf(t->full_path, sizeof(t->full_path), "%s", full_path); snprintf(t->rel_path, sizeof(t->rel_path), "%s", rel_path); t->file_size = file_size; t->checksum = checksum; t->next = NULL; pthread_mutex_lock(&q->lock); if (q->tail) { q->tail->next = t; } else { q->head = t; } q->tail = t; pthread_cond_signal(&q->cond); pthread_mutex_unlock(&q->lock); } /* Returns NULL when queue is empty AND done=1 (no more work ever). */ file_task_t *wq_pop(work_queue_t *q) { pthread_mutex_lock(&q->lock); while (q->head == NULL && !q->done) { pthread_cond_wait(&q->cond, &q->lock); } file_task_t *t = q->head; if (t) { q->head = t->next; if (!q->head) q->tail = NULL; } pthread_mutex_unlock(&q->lock); return t; /* NULL means done */ } void wq_finish(work_queue_t *q) { pthread_mutex_lock(&q->lock); q->done = 1; pthread_cond_broadcast(&q->cond); /* wake ALL waiting workers */ pthread_mutex_unlock(&q->lock); } void wq_destroy(work_queue_t *q) { /* drain any leftover tasks */ file_task_t *t = q->head; while (t) { file_task_t *next = t->next; free(t); t = next; } pthread_mutex_destroy(&q->lock); pthread_cond_destroy(&q->cond); }