-
-
Save sophec/a5156ee84890a155e6af23a24245abca to your computer and use it in GitHub Desktop.
recursive file copying function in C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// note there is no check for excessive recursion so if something has already been copied it will be tried again | |
int cpfile(const char* src, const char* dst) { | |
char* b = basename(src); | |
if (b != NULL && (0 == strcmp(b, ".") || 0 == strcmp(b, ".."))) { | |
return 0; | |
} | |
struct stat srcstat, dststat; | |
bool dstexist = false; | |
int s = 0; | |
if (lstat(src, &srcstat) < 0) { | |
// couldn't stat source | |
return -1; | |
} | |
if (lstat(dst, &dststat) < 0) { | |
// couldn't stat target | |
if (errno != ENOENT) { | |
return -1; | |
} | |
} else { | |
if (srcstat.st_dev == dststat.st_dev && srcstat.st_ino == dststat.st_ino) { | |
// same file | |
return -1; | |
} | |
dstexist = true; | |
} | |
if (dstexist) { | |
return -1; | |
} | |
if (S_ISDIR(srcstat.st_mode)) { | |
DIR* d; | |
const char* tp; | |
struct dirent *de; | |
mode_t smask = umask(0); | |
mode_t mode = srcstat.st_mode & ~smask; | |
mode |= S_IRWXU; | |
if (mkdir(dst, mode) < 0) { | |
umask(smask); | |
return -1; | |
} | |
umask(smask); | |
d = opendir(src); | |
if (d == NULL) { | |
s = -1; | |
goto preserve; | |
} | |
while ((de = readdir(d)) != NULL) { | |
char *ns = malloc(PATH_MAX); | |
if (ns == NULL) { | |
s = -1; | |
} | |
char *nd = malloc(PATH_MAX); | |
if (nd == NULL) { | |
free(ns); | |
s = -1; | |
} | |
snprintf(ns, PATH_MAX, "%s/%s", src, de->d_name); | |
snprintf(nd, PATH_MAX, "%s/%s", dst, de->d_name); | |
if (cpfile(ns, nd) != 0) { | |
s = -1; | |
} | |
free(ns); | |
free(nd); | |
} | |
closedir(d); | |
chmod(dst, srcstat.st_mode & ~smask); | |
goto preserve; | |
} | |
if (S_ISREG(srcstat.st_mode)) { | |
int sfd, dfd; | |
mode_t nmode; | |
if (S_ISLNK(srcstat.st_mode)) { | |
goto notreg; | |
} | |
// TODO it may make more sense to use openat + an fd for the directory | |
sfd = open(src, O_RDONLY); | |
if (sfd == -1) { | |
return -1; | |
} | |
nmode = srcstat.st_mode; | |
if (!S_ISREG(srcstat.st_mode)) { | |
nmode = 0666; | |
} | |
dfd = open(dst, O_WRONLY|O_CREAT|O_EXCL, nmode); | |
if (dfd == -1) { | |
close(sfd); | |
return -1; | |
} | |
// copy the file | |
char cbuf[4096] = {0}; | |
while (1) { | |
ssize_t r, w; | |
r = read(sfd, cbuf, 4096); | |
if (!r) { | |
break; | |
} | |
if (r < 0) { | |
s = -1; | |
break; | |
} | |
w = write(dfd, cbuf, r); | |
if (w < r) { | |
s = -1; | |
break; | |
} | |
} | |
if (close(dfd) < 0) { | |
return -1; | |
} | |
close(sfd); | |
if (!S_ISREG(srcstat.st_mode)) { | |
return s; | |
} | |
goto preserve; | |
} | |
notreg: | |
// source is not a regular file (it's a symlink or special) | |
if (S_ISLNK(srcstat.st_mode)) { | |
char lbuf[PATH_MAX+1] = {0}; | |
ssize_t ls = readlink(src, lbuf, PATH_MAX); | |
if (ls == -1) { | |
return -1; | |
} | |
lbuf[ls] = '\0'; | |
int r = symlink(lbuf, dst); | |
if (r != 0) { | |
return -1; | |
} | |
// can't preserve stuff for symlinks | |
return 0; | |
} else if (S_ISBLK(srcstat.st_mode) || S_ISCHR(srcstat.st_mode) | |
|| S_ISSOCK(srcstat.st_mode) || S_ISFIFO(srcstat.st_mode)) { | |
if (mknod(dst, srcstat.st_mode, srcstat.st_rdev) < 0) { | |
return -1; | |
} | |
} else { | |
return -1; | |
} | |
preserve:; | |
// preserve mode, owner, attributes, etc. here | |
struct timeval t[2]; | |
t[1].tv_sec = t[0].tv_sec = srcstat.st_mtime; | |
t[1].tv_usec = t[0].tv_usec = 0; | |
// we will fail silently if any of these don't work | |
utimes(dst, t); | |
chown(dst, srcstat.st_uid, srcstat.st_gid); | |
chmod(dst, srcstat.st_mode); | |
return s; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment