#include #include #include #include #include #include #include #include #include #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED #include #endif #include "uthash.h" char **dests; int nbdests; #define HASH_FIND_INO(head,ino,out) \ HASH_FIND(hh,head,ino,sizeof(ino_t),out) #define HASH_ADD_INO(head,ino,add) \ HASH_ADD(hh,head,ino,sizeof(ino_t),add) struct hardlinks { ino_t inode; char path[MAXPATHLEN]; UT_hash_handle hh; }; static void setattrs(struct stat *st, char *path) { static struct timeval tv[2]; TIMESPEC_TO_TIMEVAL(&tv[0], &st->st_atim); TIMESPEC_TO_TIMEVAL(&tv[1], &st->st_mtim); lutimes(path, tv); lchown(path, st->st_uid, st->st_gid); } void copy_file(FTSENT *f, char *p) { char buf[BUFSIZ]; char *bufp; int ffd, r, w, wr, i; int *tfds; char dpath[MAXPATHLEN]; #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED char *p; #endif ffd = open(f->fts_path, O_RDONLY, 0); if (ffd == -1) { warn("%s", f->fts_path); return; } tfds = malloc(nbdests * sizeof(int)); for (i = 0; i < nbdests; i++) { snprintf(dpath, sizeof(dpath), "%s/%s", dests[i], f->fts_path + strlen(p)); tfds[i] = open(dpath, O_WRONLY|O_TRUNC|O_CREAT, f->fts_statp->st_mode); if (tfds[i] == -1) { warn("%s", dpath); continue; } } #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED if (f->fts_statp->st_size > 0 && f->fts_statp->st_size <= 8 * 1024 * 1024 && (p = mmap(NULL, (size_t)f->fts_statp->st_size, PROT_READ, MAP_SHARED, ffd, (off_t)0)) != MAP_FAILED) { for (i = 0; i < nbdests; i++) { for (bufp = p, wr < f->fts_statp->st_size; ; bufp += w, wr -= w) { w = write(tfds[i], bufp, wr); if (w <= 0) break; if (w >= wr) break; } } } else #endif { while ((r = read(ffd, buf, BUFSIZ)) > 0) { for (i = 0; i < nbdests; i++) { if (tfds[i] == -1) continue; for (bufp = buf, w = 0, wr = r; ; bufp += w, wr -= r) { w = write(tfds[i], bufp, wr); if (w <= 0) break; if (w >= wr) break; } } } } for (i = 0; i < nbdests; i++) { if (tfds[i] == -1) continue; snprintf(dpath, sizeof(dpath), "%s/%s", dests[i], f->fts_path + strlen(p)); setattrs(f->fts_statp, dpath); close(tfds[i]); } free(tfds); } int main(int argc, char **argv) { FTS *ftsp; FTSENT *curr; char *path[2]; char dpath[MAXPATHLEN]; char slink[MAXPATHLEN]; int i, len; struct hardlinks *hl = NULL; struct hardlinks *h; if (argc < 3) err(EXIT_FAILURE, "usage()"); path[0] = argv[1]; path[1] = NULL; dests = argv + 2; nbdests = argc - 2; if ((ftsp = fts_open(path, FTS_PHYSICAL|FTS_NOCHDIR|FTS_XDEV, NULL)) == NULL) err(EXIT_FAILURE, "fts_open"); while ((curr = fts_read(ftsp)) != NULL) { if (curr->fts_level == FTS_ROOTLEVEL) continue; switch (curr->fts_info) { case FTS_NS: case FTS_DNR: case FTS_ERR: warnx("%s: %s", curr->fts_path, strerror(curr->fts_errno)); continue; case FTS_DC: warnx("%s: directory causes a cycle", curr->fts_path); continue; case FTS_D: for (i = 0; i < nbdests; i++) { snprintf(dpath, sizeof(dpath), "%s/%s", dests[i], curr->fts_path + strlen(path[0])); mkdir(dpath, curr->fts_statp->st_mode); setattrs(curr->fts_statp, dpath); } break; case FTS_SL: len = readlink(curr->fts_path, slink, sizeof(slink) -1); slink[len] = '\0'; for (i = 0; i < nbdests; i++) { snprintf(dpath, sizeof(dpath), "%s/%s", dests[i], curr->fts_path + strlen(path[0])); symlink(slink, dpath); setattrs(curr->fts_statp, dpath); } break; case FTS_F: HASH_FIND_INO(hl, &curr->fts_statp->st_ino, h); if (h != NULL) { for (i = 0; i < nbdests; i++) { snprintf(slink, sizeof(slink), "%s/%s", dests[i], h->path); snprintf(dpath, sizeof(dpath), "%s/%s", dests[i], curr->fts_path + strlen(path[0])); link(slink, dpath); setattrs(curr->fts_statp, dpath); } continue; } h = malloc(sizeof(struct hardlinks)); h->inode = curr->fts_statp->st_ino; strlcpy(h->path, curr->fts_path + strlen(path[0]), sizeof(h->path)); HASH_ADD_INO(hl, inode, h); /* copy file */ copy_file(curr, path[0]); break; default: break; } } }