266 lines
5.8 KiB
C
266 lines
5.8 KiB
C
/* cpdir - copy directory Author: Erik Baalbergen */
|
|
|
|
/* Use "cpdir [-v] src dst" to make a copy dst of directory src.
|
|
Cpdir should behave like the UNIX shell command
|
|
(cd src; tar cf - .) | (mkdir dst; cd dst; tar xf -)
|
|
|
|
The -m "merge" flag enables you to copy into an existing directory.
|
|
The -s "similar" flag preserves the full mode, uid, gid and times.
|
|
The -v "verbose" flag enables you to see what's going on when running cpdir.
|
|
|
|
Work yet to be done:
|
|
|
|
- link checks, i.e. am I not overwriting a file/directory by itself?
|
|
|
|
Please report bugs and suggestions to erikb@cs.vu.nl
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
|
|
#define MKDIR1 "/bin/mkdir"
|
|
#define MKDIR2 "/usr/bin/mkdir"
|
|
|
|
#define BUFSIZE 1024
|
|
#define PLEN 256
|
|
#define DIRSIZ 16
|
|
|
|
#define MAXLINKS 512
|
|
|
|
struct {
|
|
unsigned short ino;
|
|
unsigned short dev;
|
|
char *path;
|
|
} links[MAXLINKS];
|
|
int nlinks = 0;
|
|
|
|
char *prog;
|
|
int vflag = 0; /* verbose */
|
|
int mflag = 0; /* force */
|
|
int sflag = 0; /* similar */
|
|
char *strcpy();
|
|
char *malloc();
|
|
|
|
main(argc, argv)
|
|
char *argv[];
|
|
{
|
|
int rc = 0;
|
|
char *p, *s;
|
|
struct stat st;
|
|
|
|
prog = *argv++;
|
|
if ((p = *argv) && *p == '-') {
|
|
argv++;
|
|
argc--;
|
|
while (*++p) {
|
|
switch (*p) {
|
|
case 'v': vflag = 1; break;
|
|
case 'm': mflag = 1; break;
|
|
case 's': sflag = 1; break;
|
|
default:
|
|
fatal("illegal flag %s", p);
|
|
}
|
|
}
|
|
}
|
|
if (argc != 3) fatal("Usage: cpdir [-msv] source destination");
|
|
s = *argv++;
|
|
if (stat(s, &st) < 0) fatal("can't get file status of %s", s);
|
|
if ((st.st_mode & S_IFMT) != S_IFDIR) fatal("%s is not a directory", s);
|
|
cpdir(&st, s, *argv);
|
|
exit(0);
|
|
}
|
|
|
|
cpdir(sp, s, d)
|
|
struct stat *sp;
|
|
char *s, *d;
|
|
{
|
|
char spath[PLEN], dpath[PLEN];
|
|
char ent[DIRSIZ + 1];
|
|
register char *send = spath, *dend = dpath;
|
|
int fd, n;
|
|
struct stat st;
|
|
static first = 1;
|
|
static dev_t dev;
|
|
static ino_t ino;
|
|
|
|
if ((fd = open(s, O_RDONLY)) < 0) {
|
|
nonfatal("can't read directory %s", s);
|
|
return;
|
|
}
|
|
if (
|
|
mflag == 0
|
|
||
|
|
stat(d, &st) != 0
|
|
||
|
|
(st.st_mode & S_IFMT) != S_IFDIR
|
|
) {
|
|
make_dir(d);
|
|
if (sflag) similar(sp, d);
|
|
}
|
|
if (first) {
|
|
stat(d, &st);
|
|
dev = st.st_dev;
|
|
ino = st.st_ino;
|
|
first = 0;
|
|
}
|
|
if (sp->st_dev == dev && sp->st_ino == ino) {
|
|
nonfatal("%s skipped to avoid an endless loop", s);
|
|
return;
|
|
}
|
|
while (*send++ = *s++) {
|
|
}
|
|
while (*dend++ = *d++) {
|
|
}
|
|
send[-1] = '/';
|
|
dend[-1] = '/';
|
|
ent[DIRSIZ] = '\0';
|
|
while ((n = read(fd, ent, DIRSIZ)) == DIRSIZ) {
|
|
if (!((ent[0] == '\0' && ent[1] == '\0') || (ent[2] == '.') &&
|
|
(ent[3] == '\0' || (ent[3] == '.' && ent[4] == '\0'))
|
|
)) {
|
|
strcpy(send, ent + 2);
|
|
strcpy(dend, ent + 2);
|
|
if (stat(spath, &st) < 0)
|
|
fatal("can't get file status of %s", spath);
|
|
if ((st.st_mode & S_IFMT) != S_IFDIR && st.st_nlink > 1)
|
|
if (cplink(st, spath, dpath) == 1) continue;
|
|
switch (st.st_mode & S_IFMT) {
|
|
case S_IFDIR:
|
|
cpdir(&st, spath, dpath);
|
|
break;
|
|
case S_IFREG:
|
|
cp(&st, spath, dpath);
|
|
break;
|
|
default:
|
|
cpspec(&st, spath, dpath);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
close(fd);
|
|
if (n) fatal("error in reading directory %s", spath);
|
|
}
|
|
|
|
make_dir(s)
|
|
char *s;
|
|
{
|
|
int pid, status;
|
|
|
|
if (vflag) printf("mkdir %s\n", s);
|
|
if ((pid = fork()) == 0) {
|
|
execl(MKDIR1, "mkdir", s, (char *) 0);
|
|
execl(MKDIR2, "mkdir", s, (char *) 0);
|
|
fatal("can't execute %s or %s", MKDIR1, MKDIR2);
|
|
}
|
|
if (pid == -1) fatal("can't fork", prog);
|
|
wait(&status);
|
|
if (status) fatal("can't create %s", s);
|
|
}
|
|
|
|
cp(sp, s, d)
|
|
struct stat *sp;
|
|
char *s, *d;
|
|
{
|
|
char buf[BUFSIZE];
|
|
int sfd, dfd, n;
|
|
|
|
if (vflag) printf("cp %s %s\n", s, d);
|
|
if ((sfd = open(s, O_RDONLY)) < 0)
|
|
nonfatal("can't read %s", s);
|
|
else {
|
|
if ((dfd = creat(d, sp->st_mode & 0777)) < 0)
|
|
fatal("can't create %s", d);
|
|
while ((n = read(sfd, buf, BUFSIZE)) > 0)
|
|
if (write(dfd, buf, n) != n)
|
|
fatal("error in writing file %s", d);
|
|
close(sfd);
|
|
close(dfd);
|
|
if (n) fatal("error in reading file %s", s);
|
|
if (sflag) similar(sp, d);
|
|
}
|
|
}
|
|
|
|
similar(sp, d)
|
|
struct stat *sp;
|
|
char *d;
|
|
{
|
|
time_t timep[2];
|
|
|
|
chmod(d, sp->st_mode);
|
|
chown(d, sp->st_uid, sp->st_gid);
|
|
timep[0] = sp->st_atime;
|
|
timep[1] = sp->st_mtime;
|
|
utime(d, timep);
|
|
}
|
|
|
|
nonfatal(s, a)
|
|
char *s, *a;
|
|
{
|
|
fprintf(stderr, "%s: ", prog);
|
|
fprintf(stderr, s, a);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
fatal(s, a)
|
|
char *s, *a;
|
|
{
|
|
nonfatal(s, a);
|
|
exit(1);
|
|
}
|
|
|
|
cpspec(sp, s, d)
|
|
struct stat *sp;
|
|
char *s, *d;
|
|
{
|
|
if (vflag) {
|
|
printf("copy special file %s to %s.", s, d);
|
|
printf(" Major/minor = %d/%d.",
|
|
sp->st_rdev >> 8, sp->st_rdev & 0177);
|
|
printf(" Mode = %o.\n", sp->st_mode);
|
|
}
|
|
if (mknod(d, sp->st_mode, sp->st_rdev, 0) < 0) {
|
|
perror("mknod");
|
|
nonfatal("Cannot create special file %s.\n", d);
|
|
}
|
|
if (sflag) similar(sp, d);
|
|
}
|
|
|
|
cplink(st, spath, dpath)
|
|
struct stat st;
|
|
char *spath, *dpath;
|
|
{
|
|
/* Handle files that are links. Returns 0 if file must be copied.
|
|
* Returns 1 if file has been successfully linked. */
|
|
int i;
|
|
int linkent;
|
|
|
|
linkent = -1;
|
|
for (i = 0; i < nlinks; i++) {
|
|
if (links[i].dev == st.st_dev
|
|
&& links[i].ino == st.st_ino)
|
|
linkent = i;
|
|
}
|
|
if (linkent >= 0) { /* It's already in the link table *//* we
|
|
* must have copied it earlier. So just link
|
|
* to the saved dest path. Don't copy it
|
|
* twice. */
|
|
if (vflag) printf("ln %s %s\n", links[linkent].path, dpath);
|
|
if (link(links[linkent].path, dpath) < 0)
|
|
fatal("Could not link to %s\n", dpath);
|
|
return(1); /* Don't try to copy it */
|
|
} else { /* Make an entry in the link table */
|
|
if (nlinks >= MAXLINKS) fatal("Too many links at %s\n", dpath);
|
|
links[nlinks].dev = st.st_dev;
|
|
links[nlinks].ino = st.st_ino;
|
|
links[nlinks].path = malloc(strlen(dpath) + 1);
|
|
if (links[nlinks].path == NULL)
|
|
fatal("No more memory at %s\n", dpath);
|
|
strcpy(links[nlinks].path, dpath);
|
|
nlinks++;
|
|
/* Go ahead and copy it the first time */
|
|
return(0);
|
|
}
|
|
}
|