diff options
| -rw-r--r-- | include/copy.h | 19 | ||||
| -rw-r--r-- | src/copy.c | 191 | 
2 files changed, 210 insertions, 0 deletions
| diff --git a/include/copy.h b/include/copy.h new file mode 100644 index 0000000..9e8bc11 --- /dev/null +++ b/include/copy.h @@ -0,0 +1,19 @@ +#ifndef OMC_COPY_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <errno.h> +#include <sys/stat.h> +#include <unistd.h> +#include "omc.h" + +#define CT_OWNER 1 << 1 +#define CT_PERM 1 << 2 + +int copytree(const char *srcdir, const char *destdir, unsigned op); +int mkdirs(const char *_path, mode_t mode); +int copy2(const char *src, const char *dest, unsigned op); + +#endif // OMC_COPY_H
\ No newline at end of file diff --git a/src/copy.c b/src/copy.c new file mode 100644 index 0000000..fa623d2 --- /dev/null +++ b/src/copy.c @@ -0,0 +1,191 @@ +#include "copy.h" + +int copy2(const char *src, const char *dest, unsigned int op) { +    size_t bytes_read; +    size_t bytes_written; +    char buf[OMC_BUFSIZ]; +    struct stat src_stat, dnamest; +    FILE *fp1, *fp2; + +    if (lstat(src, &src_stat) < 0) { +        perror(src); +        return -1; +    } + +    if (access(dest, F_OK) == 0) { +        unlink(dest); +    } + +    char dname[1024] = {0}; +    strcpy(dname, dest); +    char *dname_endptr; + +    dname_endptr = strrchr(dname, '/'); +    if (dname_endptr != NULL) { +        *dname_endptr = '\0'; +    } + +    stat(dname, &dnamest); +    if (S_ISLNK(src_stat.st_mode)) { +        char lpath[1024] = {0}; +        if (readlink(src, lpath, sizeof(lpath)) < 0) { +            perror(src); +            return -1; +        } +        if (symlink(lpath, dest) < 0) { +            // silent +            return -1; +        } +    } else if (S_ISREG(src_stat.st_mode) && src_stat.st_nlink > 2 && src_stat.st_dev == dnamest.st_dev) { +        if (link(src, dest) < 0) { +            perror(src); +            return -1; +        } +    } else if (S_ISFIFO(src_stat.st_mode) || S_ISBLK(src_stat.st_mode) || S_ISCHR(src_stat.st_mode) || S_ISSOCK(src_stat.st_mode)) { +        if (mknod(dest, src_stat.st_mode, src_stat.st_rdev) < 0) { +            perror(src); +            return -1; +        } +    } else if (S_ISREG(src_stat.st_mode)) { +        fp1 = fopen(src, "rb"); +        if (!fp1) { +            perror(src); +            return -1; +        } + +        fp2 = fopen(dest, "w+b"); +        if (!fp2) { +            perror(dest); +            return -1; +        } + +        bytes_written = 0; +        while ((bytes_read = fread(buf, sizeof(char), sizeof(buf), fp1)) != 0) { +            bytes_written += fwrite(buf, sizeof(char), bytes_read, fp2); +        } +        fclose(fp1); +        fclose(fp2); + +        if (bytes_written != (size_t) src_stat.st_size) { +            fprintf(stderr, "%s: SHORT WRITE (expected %zu bytes, but wrote %zu bytes)\n", dest, src_stat.st_size, bytes_written); +            return -1; +        } + +        if (op & CT_OWNER && chown(dest, src_stat.st_uid, src_stat.st_gid) < 0) { +            perror(dest); +        } + +        if (op & CT_PERM && chmod(dest, src_stat.st_mode) < 0) { +            perror(dest); +        } +    } else { +        errno = EOPNOTSUPP; +        return -1; +    } +    return 0; +} + +int mkdirs(const char *_path, mode_t mode) { +    int status; +    char *token; +    char pathbuf[1024] = {0}; +    char *path; +    path = pathbuf; +    strcpy(path, _path); +    errno = 0; + +    char result[1024] = {0}; +    status = 0; +    while ((token = strsep(&path, "/")) != NULL && !status) { +        if (token[0] == '.') +            continue; +        strcat(result, token); +        strcat(result, "/"); +        status = mkdir(result, mode); +    } +    return status; +} + +int copytree(const char *srcdir, const char *destdir, unsigned int op) { +    DIR *dir; +    struct dirent *d; +    struct stat st; +    mode_t mode; +    mode_t umask_value; +    errno = 0; + +    dir = opendir(srcdir); +    if (!dir) { +        return -1; +    } + +    umask_value = umask(0); +    umask(umask_value); + +    if (lstat(srcdir, &st) < 0) { +        perror(srcdir); +        closedir(dir); +        return -1; +    } + +    if (op & CT_PERM) { +        mode = st.st_mode; +    } else { +        mode = 0777 & ~umask_value; +    } + +    // Source directory is not writable so modify the mode for the destination +    if (mode & ~S_IWUSR) { +        mode |= S_IWUSR; +    } + +    if (access(destdir, F_OK) < 0) { +        if (mkdirs(destdir, mode) < 0) { +            perror(destdir); +            closedir(dir); +            return -1; +        } +    } + +    memset(&st, 0, sizeof(st)); +    while ((d = readdir(dir)) != NULL) { +        char src[1024] = {0}; +        char dest[1024] = {0}; +        if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) { +            continue; +        } +        strcat(src, srcdir); +        strcat(src, "/"); +        strcat(src, d->d_name); + +        strcat(dest, destdir); +        strcat(dest, "/"); +        strcat(dest, d->d_name); + +        if (lstat(src, &st) < 0) { +            perror(srcdir); +            closedir(dir); +            return -1; +        } + +        if (d->d_type == DT_DIR) { +            if (strncmp(src, dest, strlen(src)) == 0) { +                closedir(dir); +                return -1; +            } + +            if (access(dest, F_OK) < 0) { +                if (mkdir(dest, mode) < 0) { +                    perror(dest); +                    closedir(dir); +                    return -1; +                } +            } + +            copytree(src, dest, op); +        } +        copy2(src, dest, op); +    } +    closedir(dir); +    return 0; +} | 
