#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#ifdef DIRBLKSIZ
#undef DIRBLKSIZ
#endif
#include "defs.h"
struct FSNode {
struct FSNode *fs_Next;
struct FSNode *fs_HNext;
struct FSNode *fs_Base;
struct FSNode *fs_Parent;
struct stat fs_St;
char *fs_Data;
int fs_Bytes;
int fs_Marker;
char fs_Name[4];
};
static
fsnode_t
fsmknode(const char *path)
{
int pathlen = strlen(path);
fsnode_t node = malloc(offsetof(struct FSNode, fs_Name[pathlen+1]));
if (node == NULL) {
fprintf(stderr, "ran out of memory copying filesystem\n");
return(NULL);
}
bzero(node, sizeof(*node));
bcopy(path, node->fs_Name, pathlen + 1);
if (lstat(path, &node->fs_St) < 0) {
fprintf(stderr, "Unable to lstat(\"%s\")\n", path);
free(node);
return(NULL);
}
return(node);
}
static fsnode_t
fsgethlink(fsnode_t hlinks, fsnode_t node)
{
fsnode_t scan;
for (scan = hlinks; scan; scan = scan->fs_HNext) {
if (scan->fs_St.st_dev == node->fs_St.st_dev &&
scan->fs_St.st_ino == node->fs_St.st_ino
) {
return(scan);
}
}
return(NULL);
}
static char *
fshardpath(fsnode_t hlink, fsnode_t node)
{
fsnode_t scan;
char *path;
char *tmp;
for (scan = hlink; scan; scan = scan->fs_Parent)
scan->fs_Marker = 1;
for (scan = node; scan; scan = scan->fs_Parent) {
if (scan->fs_Marker == 1) {
scan->fs_Marker = 2;
break;
}
}
if (scan == NULL)
return(NULL);
asprintf(&path, "%s", hlink->fs_Name);
for (scan = hlink->fs_Parent; scan->fs_Marker == 1; scan = scan->fs_Parent) {
tmp = path;
asprintf(&path, "%s/%s", scan->fs_Name, tmp);
free(tmp);
}
for (scan = node->fs_Parent; scan; scan = scan->fs_Parent) {
if (scan->fs_Marker == 2)
break;
tmp = path;
asprintf(&path, "../%s", tmp);
free(tmp);
}
for (scan = hlink; scan; scan = scan->fs_Parent)
scan->fs_Marker = 0;
for (scan = node; scan; scan = scan->fs_Parent)
scan->fs_Marker = 0;
return(path);
}
fsnode_t
FSCopy(fsnode_t *phlinks, const char *path)
{
int n;
DIR *dir;
fsnode_t node;
char buf[1024];
node = fsmknode(path);
if (node) {
switch(node->fs_St.st_mode & S_IFMT) {
case S_IFIFO:
break;
case S_IFCHR:
break;
case S_IFDIR:
if ((dir = opendir(path)) != NULL) {
struct dirent *den;
fsnode_t scan;
fsnode_t *pscan;
if (chdir(path) < 0) {
fprintf(stderr, "Unable to chdir into %s\n", path);
break;
}
pscan = &node->fs_Base;
while ((den = readdir(dir)) != NULL) {
if (strcmp(den->d_name, ".") == 0)
continue;
if (strcmp(den->d_name, "..") == 0)
continue;
scan = FSCopy(phlinks, den->d_name);
if (scan) {
*pscan = scan;
scan->fs_Parent = node;
pscan = &scan->fs_Next;
}
}
if (chdir("..") < 0) {
fprintf(stderr, "Unable to chdir .. after scanning %s\n", path);
exit(1);
}
closedir(dir);
}
break;
case S_IFBLK:
break;
case S_IFREG:
if (node->fs_St.st_nlink > 1 && fsgethlink(*phlinks, node)) {
node->fs_Bytes = -1;
} else if (node->fs_St.st_size >= 0x80000000LL) {
fprintf(stderr, "File %s too large to copy\n", path);
free(node);
node = NULL;
} else if ((node->fs_Data = malloc(node->fs_St.st_size)) == NULL) {
fprintf(stderr, "Ran out of memory copying %s\n", path);
free(node);
node = NULL;
} else if ((n = open(path, O_RDONLY)) < 0) {
fprintf(stderr, "Unable to open %s for reading\n", path);
free(node->fs_Data);
free(node);
node = NULL;
} else if (read(n, node->fs_Data, node->fs_St.st_size) != node->fs_St.st_size) {
fprintf(stderr, "Unable to read %s\n", path);
free(node->fs_Data);
free(node);
node = NULL;
} else {
node->fs_Bytes = node->fs_St.st_size;
if (node->fs_St.st_nlink > 1) {
node->fs_HNext = *phlinks;
*phlinks = node;
}
}
break;
case S_IFLNK:
if ((n = readlink(path, buf, sizeof(buf))) > 0) {
if ((node->fs_Data = malloc(n + 1)) == NULL) {
fprintf(stderr, "Ran out of memory\n");
free(node);
node = NULL;
} else {
node->fs_Bytes = n;
bcopy(buf, node->fs_Data, n);
node->fs_Data[n] = 0;
}
} else if (n == 0) {
node->fs_Data = NULL;
node->fs_Bytes = 0;
} else {
fprintf(stderr, "Unable to read link: %s\n", path);
free(node);
node = NULL;
}
break;
case S_IFSOCK:
break;
case S_IFWHT:
break;
default:
break;
}
}
return(node);
}
void
FSPaste(const char *path, fsnode_t node, fsnode_t hlinks)
{
struct timeval times[2];
fsnode_t scan;
int fd;
int ok = 0;
switch(node->fs_St.st_mode & S_IFMT) {
case S_IFIFO:
break;
case S_IFCHR:
case S_IFBLK:
if (mknod(path, node->fs_St.st_mode, node->fs_St.st_rdev) < 0) {
fprintf(stderr, "Paste: mknod failed on %s\n", path);
break;
}
ok = 1;
break;
case S_IFDIR:
fd = open(".", O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Paste: cannot open current directory\n");
exit(1);
}
if (mkdir(path, 0700) < 0 && errno != EEXIST) {
printf("Paste: unable to create directory %s\n", path);
close(fd);
break;
}
if (chdir(path) < 0) {
printf("Paste: unable to chdir into %s\n", path);
exit(1);
}
for (scan = node->fs_Base; scan; scan = scan->fs_Next) {
FSPaste(scan->fs_Name, scan, hlinks);
}
if (fchdir(fd) < 0) {
fprintf(stderr, "Paste: cannot fchdir current dir\n");
close(fd);
exit(1);
}
close(fd);
ok = 1;
break;
case S_IFREG:
if (node->fs_St.st_nlink > 1 && node->fs_Bytes < 0) {
if ((scan = fsgethlink(hlinks, node)) == NULL) {
fprintf(stderr, "Cannot find hardlink for %s\n", path);
} else {
char *hpath = fshardpath(scan, node);
if (hpath == NULL || link(hpath, path) < 0) {
fprintf(stderr, "Cannot create hardlink: %s->%s\n", path, hpath ? hpath : "?");
if (hpath)
free(hpath);
break;
}
ok = 1;
free(hpath);
}
break;
}
if ((fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0) {
fprintf(stderr, "Cannot create file: %s\n", path);
break;
}
if (node->fs_Bytes > 0) {
if (write(fd, node->fs_Data, node->fs_Bytes) != node->fs_Bytes) {
fprintf(stderr, "Cannot write file: %s\n", path);
remove(path);
close(fd);
break;
}
}
close(fd);
ok = 1;
break;
case S_IFLNK:
if (symlink(node->fs_Bytes > 0 ? node->fs_Data : "", path) < 0) {
fprintf(stderr, "Unable to create symbolic link: %s\n", path);
break;
}
ok = 1;
break;
case S_IFSOCK:
break;
case S_IFWHT:
break;
default:
break;
}
if (ok) {
struct stat *st = &node->fs_St;
times[0].tv_sec = st->st_atime;
times[0].tv_usec = 0;
times[1].tv_sec = st->st_mtime;
times[1].tv_usec = 0;
if (lchown(path, st->st_uid, st->st_gid) < 0)
fprintf(stderr, "lchown failed on %s\n", path);
if (lutimes(path, times) < 0)
fprintf(stderr, "lutimes failed on %s\n", path);
if (lchmod(path, st->st_mode & ALLPERMS) < 0)
fprintf(stderr, "lchmod failed on %s\n", path);
if (!S_ISLNK(st->st_mode)) {
if (chflags(path, st->st_flags) < 0)
fprintf(stderr, "chflags failed on %s\n", path);
}
}
}