bacb61f6ec
The file_meta_t and file_task_t structs were missing the checksum field that was being used in both client and server code, causing a compilation error. Added uint64_t checksum to both structs and updated wq_push declaration to match implementation. Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
131 lines
3.5 KiB
C
131 lines
3.5 KiB
C
#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);
|
|
}
|