root/software/fss-parallel-tools/atdiff.c @ 6267e2f6
| 7e8045d8 | David Sorber | /**
|
|
* Copyright © 2019, Oracle and/or its affiliates. All rights reserved.
|
|||
*
|
|||
* Licensed under the Universal Permissive License v 1.0 as shown at
|
|||
* http://oss.oracle.com/licenses/upl
|
|||
*
|
|||
*/
|
|||
#include <assert.h>
|
|||
#include <sys/stat.h>
|
|||
#include <pthread.h>
|
|||
#include <dirent.h>
|
|||
#include <stdio.h>
|
|||
#include <stdlib.h>
|
|||
#include <string.h>
|
|||
#include <unistd.h>
|
|||
#include "putils.h"
|
|||
#include "malloc.h"
|
|||
/* parallel diff of a directory heirarchy that also checks ownership and attributes */
|
|||
struct diff_task {
|
|||
char *relative_path;
|
|||
struct diff_task *next;
|
|||
};
|
|||
// Worker state
|
|||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; ;
|
|||
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
|||
struct diff_task *tasks = NULL;
|
|||
enum { n_workers = 32};
|
|||
size_t n_working = 0; // When tasks==NULL and n_working==0, we are done
|
|||
pthread_t workers[n_workers];
|
|||
#define PMAX 4096
|
|||
const char *src_dir;
|
|||
const char *dst_dir;
|
|||
int check_ownership = 1;
|
|||
// From the qsort man page:
|
|||
static int cmpstringp(const void *p1, const void *p2) {
|
|||
// The actual arguments to this function are "pointers to pointers
|
|||
// to char", but strcmp(3) arguments are "pointers to char", hence
|
|||
// the following cast plus dereference.
|
|||
return strcmp(* (char * const *) p1, * (char * const *) p2);
|
|||
}
|
|||
static void collect_and_sort_pathnames(const char *abs_path,
|
|||
// output:
|
|||
size_t *np,
|
|||
char ***paths_p)
|
|||
// Collect all the pathnames in a directory, except for . and .., and
|
|||
// return them in sorted order.
|
|||
{
|
|||
size_t n = 0;
|
|||
size_t limit = 2;
|
|||
char **MALLOC_N(paths, limit);
|
|||
DIR *dir = opendir(abs_path);
|
|||
assert(dir);
|
|||
struct dirent *dentry;
|
|||
while ((dentry = readdir(dir))) {
|
|||
if (strcmp(dentry->d_name, ".") == 0) continue;
|
|||
if (strcmp(dentry->d_name, "..") == 0) continue;
|
|||
if (n >= limit) {
|
|||
limit *= 2;
|
|||
REALLOC(paths, limit);
|
|||
}
|
|||
assert(n < limit);
|
|||
paths[n++] = strdup(dentry->d_name);
|
|||
}
|
|||
closedir(dir);
|
|||
qsort(paths, n, sizeof(char*), cmpstringp);
|
|||
*np = n;
|
|||
*paths_p = paths;
|
|||
}
|
|||
static void diff_r(const char *relative_path) {
|
|||
char src_path[PMAX];
|
|||
{
|
|||
int n = snprintf(src_path, PMAX, "%s/%s", src_dir, relative_path);
|
|||
assert(n < PMAX);
|
|||
}
|
|||
char dst_path[PMAX];
|
|||
{
|
|||
int n = snprintf(dst_path, PMAX, "%s/%s", dst_dir, relative_path);
|
|||
assert(n < PMAX);
|
|||
}
|
|||
struct stat src_stat, dst_stat;
|
|||
{
|
|||
int src_r = lstat(src_path, &src_stat);
|
|||
assert (src_r == 0);
|
|||
}
|
|||
{
|
|||
int dst_r = lstat(dst_path, &dst_stat);
|
|||
assert(dst_r == 0);
|
|||
}
|
|||
assert((src_stat.st_mode & S_IFMT) == (dst_stat.st_mode & S_IFMT));
|
|||
{
|
|||
mode_t mask = 0777;
|
|||
if ((src_stat.st_mode & mask) != (dst_stat.st_mode & mask)) {
|
|||
fprintf(stderr, "masks differ %s and %s\n", src_path, dst_path);
|
|||
assert(0);
|
|||
}
|
|||
}
|
|||
if (check_ownership) {
|
|||
if (src_stat.st_uid != dst_stat.st_uid) {
|
|||
fprintf(stderr, "uid doesn't match for %s and %s\n", src_path, dst_path);
|
|||
}
|
|||
assert(src_stat.st_uid == dst_stat.st_uid);
|
|||
assert(src_stat.st_gid == dst_stat.st_gid);
|
|||
}
|
|||
if (S_ISREG(dst_stat.st_mode)) {
|
|||
assert(src_stat.st_size == dst_stat.st_size);
|
|||
FILE *src_f = fopen(src_path, "r");
|
|||
FILE *dst_f = fopen(dst_path, "r");
|
|||
if (src_f == NULL || dst_f == NULL) {
|
|||
fprintf(stderr, "Failed to open one or more files\n");
|
|||
return;
|
|||
}
|
|||
for (size_t i = 0 ; i < (size_t)(src_stat.st_size); i++) {
|
|||
int src_c = getc(src_f); assert(src_c != EOF);
|
|||
int dst_c = getc(dst_f); assert(dst_c != EOF);
|
|||
assert(src_c == dst_c);
|
|||
}
|
|||
fclose(src_f);
|
|||
fclose(dst_f);
|
|||
} else if (S_ISDIR(src_stat.st_mode)) {
|
|||
size_t sn, dn;
|
|||
char **sps, **dps;
|
|||
collect_and_sort_pathnames(src_path, &sn, &sps);
|
|||
collect_and_sort_pathnames(dst_path, &dn, &dps);
|
|||
assert(sn == dn);
|
|||
for (size_t i = 0; i < sn; i++) {
|
|||
assert(strcmp(sps[i], dps[i]) == 0);
|
|||
struct diff_task *MALLOC(task);
|
|||
char sub_rel_path[PMAX];
|
|||
{
|
|||
int n = snprintf(sub_rel_path, PMAX, "%s/%s", relative_path, sps[i]);
|
|||
assert(n < PMAX);
|
|||
}
|
|||
char *srp = strdup(sub_rel_path);
|
|||
pthread_mutex_lock(&mutex);
|
|||
*task = (struct diff_task){.relative_path = srp,
|
|||
.next = tasks};
|
|||
tasks = task;
|
|||
pthread_cond_signal(&cond);
|
|||
pthread_mutex_unlock(&mutex);
|
|||
}
|
|||
} else if (S_ISLNK(src_stat.st_mode)) {
|
|||
char slname[PMAX];
|
|||
ssize_t slen = readlink(src_path, slname, PMAX);
|
|||
assert(slen >= 0 && slen < PMAX);
|
|||
char dlname[PMAX];
|
|||
ssize_t dlen = readlink(dst_path, dlname, PMAX);
|
|||
assert(dlen >= 0 && dlen < PMAX);
|
|||
assert(slen == dlen);
|
|||
slname[slen] = 0;
|
|||
dlname[dlen] = 0;
|
|||
if (0 != strcmp(slname, dlname)) {
|
|||
fprintf(stderr, "%s -> %s is not the same as %s -> %s\n", src_path, slname, dst_path, dlname);
|
|||
assert(0);
|
|||
}
|
|||
}
|
|||
}
|
|||
static void* run_worker(void *d) {
|
|||
pthread_mutex_lock(&mutex);
|
|||
while (1) {
|
|||
// Lock is held here.
|
|||
while (!tasks && n_working) {
|
|||
pthread_cond_wait(&cond, &mutex);
|
|||
}
|
|||
// Either there is a task or n_working==0
|
|||
if (tasks) {
|
|||
struct diff_task *task = tasks;
|
|||
tasks = task->next;
|
|||
n_working++;
|
|||
pthread_mutex_unlock(&mutex);
|
|||
diff_r(task->relative_path);
|
|||
free(task->relative_path);
|
|||
free(task);
|
|||
pthread_mutex_lock(&mutex);
|
|||
n_working--;
|
|||
if (tasks == NULL && n_working == 0) {
|
|||
pthread_cond_broadcast(&cond);
|
|||
}
|
|||
} else if (n_working == 0) {
|
|||
// No tasks and nothing to do.
|
|||
pthread_mutex_unlock(&mutex);
|
|||
return d;
|
|||
}
|
|||
}
|
|||
}
|
|||
static void run_workers(void) {
|
|||
MALLOC(tasks);
|
|||
*tasks = (struct diff_task){.relative_path = strdup(""),
|
|||
.next = NULL};
|
|||
n_working = 0;
|
|||
for (size_t i = 0; i < n_workers; i++) {
|
|||
pthread_create(&workers[i], NULL, run_worker, &workers[i]);
|
|||
}
|
|||
for (size_t i = 0; i < n_workers; i++) {
|
|||
void *result;
|
|||
pthread_join(workers[i], &result);
|
|||
}
|
|||
}
|
|||
int main (int argc, const char *argv[]) {
|
|||
assert(argc == 3);
|
|||
src_dir = argv[1];
|
|||
dst_dir = argv[2];
|
|||
run_workers();
|
|||
return 0;
|
|||
}
|