Untitled

Go BackChange Paste Viewing Options
#define _GNU_SOURCE
#define _NO_PASARAN

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>

#ifndef PATH_MAX
    #define PATH_MAX 4096
#endif

void print_usage() {
    printf("Usage: backup source_dir destination_dir\n");
}

int f_copy(char *src_file, char *dest_file, mode_t mode) {
    char tail[9];
    char middle_file[PATH_MAX + 1];

    int i;
    for (i = 0; i < 8; ++i) {
        tail[i] = (char)(rand() % 10) + '0';
    }
    tail[8] = '\0';
    
    int res = snprintf(middle_file, PATH_MAX, "%s%s", dest_file, tail);
    if (res < 0 || res >= PATH_MAX) {
        fprintf(stderr, \
            "backup: path length limit exceeded while concatenating intermediate file '%s%s'; omitted\n", \
            dest_file, tail);
        return 666;
    }
    
    FILE *src_FILE = fopen(src_file, "r");
    FILE *dest_FILE = fopen(middle_file, "w");
    if (!src_FILE) {
        return 1;
    }
    if (!dest_FILE) {
        return 2;
    }
    
    int ch;
    while (1) {
        ch = getc(src_FILE);
        if (ch == EOF) {
            break;
        }
        putc(ch, dest_FILE);
    }

    fclose(src_FILE);
    fclose(dest_FILE);

    if (chmod(middle_file, mode)) {
        fprintf(stderr, "backup: unable to chmod '%s': ", dest_file);
        perror(NULL);
    }

    unlink(dest_file);
    rename(middle_file, dest_file);
    
    return 0;
}

int f_case_copy(char *src_file, char *dest_file) {
    struct stat src_stat, dest_stat;
        
    if (stat(src_file, &src_stat)) {
        fprintf(stderr, "backup: failed to get file status on '%s': ", src_file);
        perror(NULL);
        return -1;
    }

    int dest_stat_res;
    dest_stat_res = stat(dest_file, &dest_stat);

    if (dest_stat_res || (!S_ISDIR(dest_stat.st_mode) && dest_stat.st_mtime < src_stat.st_mtime)) {
        int res = f_copy(src_file, dest_file, src_stat.st_mode);
        switch (res) {
            case 1:  fprintf(stderr, "backup: failed to open file '%s' for reading: ", src_file);
                perror(NULL);
                return 1;
                break;
            case 2:  fprintf(stderr, "backup: failed to open file '%s' for writing: ", dest_file);
                perror(NULL);
                return 2;
                break;
        }
    } else {
        if (S_ISDIR(dest_stat.st_mode)) {
            fprintf(stderr, "backup: unable to replace directory '%s' by file '%s'", dest_file, src_file);
            perror(NULL);
            return 3;
        }
    }
    return 0;
}

void rec_copy(char *src_dir_s, char *dest_dir_s) {
    DIR *src_dir;
    struct stat src_dir_stat, dest_dir_stat;

    if (!(src_dir = opendir(src_dir_s))) {
        fprintf(stderr, "backup: skipping directory '%s': ", src_dir_s);
        perror(NULL);
        return;
    }

    if (stat(src_dir_s, &src_dir_stat)) {
        fprintf(stderr, "backup: failed to get directory status on '%s': ", src_dir_s);
        perror(NULL);
        return;
    }

    if (stat(dest_dir_s, &dest_dir_stat)) {
        if (mkdir(dest_dir_s, src_dir_stat.st_mode)) {
            fprintf(stderr, "backup: failed to get status on '%s' and create such a directory at the same time: ", dest_dir_s);
            perror(NULL);
            return;
        } else {
            if (chmod(dest_dir_s, src_dir_stat.st_mode)) {
                fprintf(stderr, "backup: unable to chmod '%s': ", dest_dir_s);
                perror(NULL);
            }            
        }
    } else {
        if (!S_ISDIR(dest_dir_stat.st_mode)) {
            fprintf(stderr, "backup: unable to copy to '%s', which exists and is not a directory: ", dest_dir_s);
            perror(NULL);
            return;
        }
    }

    struct dirent *src_dirent;
    while ((src_dirent  = readdir(src_dir)) != NULL) {
        struct stat f_stat;
        char name_s[PATH_MAX + 1];

        int res = snprintf(name_s, PATH_MAX, "%s/%s", src_dir_s, src_dirent->d_name);
        if (res < 0 || res >= PATH_MAX) {
            fprintf(stderr, \
                "backup: path length limit exceeded while concatenating '%s/%s'; omitted\n", \
                src_dir_s, src_dirent->d_name);
            continue;
        }

        stat(name_s, &f_stat);
        if (S_ISDIR(f_stat.st_mode)) {
            if (strcmp(src_dirent->d_name, ".") && strcmp(src_dirent->d_name, "..")){
                char name2_s[PATH_MAX + 1];

                int res = snprintf(name2_s, PATH_MAX, "%s/%s", dest_dir_s, src_dirent->d_name);
                if (res < 0 || res >= PATH_MAX) {
                    fprintf(stderr, \
                        "backup: path length limit exceeded while concatenating '%s/%s'; omitted\n", \
                        dest_dir_s, src_dirent->d_name);
                    continue;
                }

                rec_copy(name_s, name2_s);
            }
        } else {
            char name2_s[PATH_MAX + 1];

            int res = snprintf(name2_s, PATH_MAX, "%s/%s", dest_dir_s, src_dirent->d_name);
            if (res < 0 || res >= PATH_MAX) {
                fprintf(stderr, \
                    "backup: path length limit exceeded while concatenating '%s/%s'; omitted\n", \
                    dest_dir_s, src_dirent->d_name);
                continue;
            }
            
            f_case_copy(name_s, name2_s);            
        }
    }

    closedir(src_dir);
}

int main(int argc, char **argv) {
    if (argc != 3) {
        print_usage();
        exit(-1);
    }

    char src_dir[PATH_MAX + 1];
    char dest_dir[PATH_MAX + 1];

    if (!realpath(argv[1], src_dir)) {
        fprintf(stderr, "backup: '%s': ", src_dir);
        perror(NULL);
        exit(-1);
    }

    if (argv[2][0] != '/') {
        char *cwd = get_current_dir_name();
        int res = snprintf(dest_dir, PATH_MAX, "%s/%s", cwd, argv[2]);
        free(cwd);
        if (res < 0 || res >= PATH_MAX) {
            fprintf(stderr, \
                "backup: path length limit exceeded while concatenating '%s/%s'; omitted\n", \
                cwd, argv[2]);
            return -1;
        }        
    } else {
        int res = snprintf(dest_dir, PATH_MAX, "%s", argv[2]);
        if (res < 0 || res >= PATH_MAX) {
            fprintf(stderr, \
                "backup: path length limit exceeded while dealing with '%s'; omitted\n", \
                argv[2]);
            return -1;
        }           
    }

    rec_copy(src_dir, dest_dir);

    return 0;
}
			
Go Back