Загрузка данных


1. Программа  remftrash

 a. В программу передается один параметр – имя файла в текущем каталоге вызова скрипта.

 b. Программа проверяет, создан ли скрытый каталог trash в домашнем каталоге пользователя. Если он не создан – создает его.

 c. После этого программа создает в этом каталоге жесткую ссылку на переданный файл с уникальным именем (например, присваивает каждой новой ссылке имя, соответствующее следующему натуральному числу) и удаляет файл в текущем каталоге.

 d. Затем в скрытый файл trash.log в домашнем каталоге

пользователя помещается запись, содержащая <имя ссылки> - полный исходный путь к удаленному файлу .

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

static void log_trash(const char *msg) {
    FILE *f = fopen(getenv("HOME") && getenv("HOME")[0] ? strcat(getenv("HOME"), "/.backup_manager.log") : "/tmp/.backup_manager.log", "a");
    if (f) { fprintf(f, "[TRASH] %s\n", msg); fclose(f); }
}

int main(int argc, char *argv[]) {
    if (argc != 2) { printf("Usage: %s <filename>\n", argv[0]); return 1; }

    char *home = getenv("HOME");
    if (!home) { printf("Error: HOME environment variable not set.\n"); return 1; }

    char trash_path[PATH_MAX];
    snprintf(trash_path, sizeof(trash_path), "%s/.trash", home);
    if (mkdir(trash_path, 0755) == -1 && errno != EEXIST) { printf("Error: cannot create trash directory.\n"); return 1; }

    char src_path[PATH_MAX];
    if (!realpath(argv[1], src_path)) { printf("Error: source file not found or invalid path.\n"); return 1; }
    if (access(src_path, F_OK) == -1) { printf("Error: file does not exist.\n"); return 1; }

    char counter_path[PATH_MAX], log_path[PATH_MAX], link_path[PATH_MAX];
    snprintf(counter_path, sizeof(counter_path), "%s/.counter", trash_path);
    snprintf(log_path, sizeof(log_path), "%s/trash.log", trash_path);

    FILE *cf = fopen(counter_path, "r+");
    if (!cf) cf = fopen(counter_path, "w+");
    if (!cf) { printf("Error: counter file access failed.\n"); return 1; }

    int next_id = 0;
    if (fscanf(cf, "%d", &next_id) == EOF) next_id = 1;

    snprintf(link_path, sizeof(link_path), "%s/%d", trash_path, next_id);
    if (link(src_path, link_path) == -1) { printf("Error: failed to create hard link.\n"); fclose(cf); return 1; }
    if (unlink(src_path) == -1) { printf("Error: failed to remove original file.\n"); unlink(link_path); fclose(cf); return 1; }

    fseek(cf, 0, SEEK_SET);
    fprintf(cf, "%d", next_id + 1);
    fclose(cf);

    FILE *lf = fopen(log_path, "a");
    if (!lf) { printf("Error: failed to open trash log.\n"); return 1; }
    fprintf(lf, "%d - %s\n", next_id, src_path);
    fclose(lf);

    char log_msg[PATH_MAX + 50];
    snprintf(log_msg, sizeof(log_msg), "Moved %s to trash as %d", argv[1], next_id);
    log_trash(log_msg);
    return 0;
}

2. Программа unftrash

 a. В программу передается один параметр – имя файла, который нужно восстановить (без полного пути – только имя).

 b. Программа по файлу trash.log должна найти все записи, содержащие в качестве имени файла переданный параметр, и выводить по одному на экран полные имена таких файлов с запросом подтверждения на восстановление.

 c. Если пользователь отвечает на подтверждение положительно, то предпринимается попытка восстановить файл по указанному полному пути (создать в соответствующем каталоге жесткую ссылку на файл из trash и удалить соответствующий файл из trash). Если каталога, указанного в полном пути к файлу, уже не существует, то файл восстанавливается в домашний каталог пользователя с выводом соответствующего сообщения.

unftrash.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <libgen.h>

static void log_trash(const char *msg) {
    FILE *f = fopen(getenv("HOME") && getenv("HOME")[0] ? strcat(getenv("HOME"), "/.backup_manager.log") : "/tmp/.backup_manager.log", "a");
    if (f) { fprintf(f, "[TRASH] %s\n", msg); fclose(f); }
}

int main(int argc, char *argv[]) {
    if (argc != 2) { printf("Usage: %s <filename>\n", argv[0]); return 1; }

    char *home = getenv("HOME");
    if (!home) { printf("Error: HOME not set.\n"); return 1; }

    char log_path[PATH_MAX], trash_dir[PATH_MAX];
    snprintf(log_path, sizeof(log_path), "%s/.trash/trash.log", home);
    snprintf(trash_dir, sizeof(trash_dir), "%s/.trash", home);

    FILE *lf = fopen(log_path, "r");
    if (!lf) { printf("Error: trash log not found.\n"); return 1; }

    char line[PATH_MAX + 100];
    while (fgets(line, sizeof(line), lf)) {
        line[strcspn(line, "\n")] = 0;
        char *dash = strchr(line, '-');
        if (!dash) continue;
        *dash = '\0'; dash += 2;

        char target_basename[PATH_MAX];
        strncpy(target_basename, basename(dash), sizeof(target_basename) - 1);
        if (strcmp(target_basename, argv[1]) != 0 && strcmp(dash, argv[1]) != 0) continue;

        printf("Found: %s\nRestore to original location? (y/n): ", dash);
        char choice; scanf(" %c", &choice);
        if (choice != 'y' && choice != 'Y') continue;

        char link_path[PATH_MAX], restore_dir[PATH_MAX];
        snprintf(link_path, sizeof(link_path), "%s/%s", trash_dir, line);
        strncpy(restore_dir, dirname(dash), sizeof(restore_dir) - 1);

        if (access(restore_dir, F_OK | W_OK | X_OK) == -1) {
            snprintf(restore_dir, sizeof(restore_dir), "%s", home);
            printf("Original directory missing. Restoring to home directory.\n");
        }

        char dest[PATH_MAX];
        snprintf(dest, sizeof(dest), "%s/%s", restore_dir, basename(dash));
        if (link(link_path, dest) == -1) { printf("Error: restore failed.\n"); continue; }
        unlink(link_path);
        log_trash("Restored %s to %s", basename(dash), restore_dir);
        printf("Restored successfully.\n");
    }
    fclose(lf);
    return 0;
}

3. Программа makebackup

 a. Программа создаст в /home/<user>/ каталог с именем Backup-YYYYMM-DD, где YYYY-MM-DD – дата запуска программы, если в /home/<user>/ нет каталога с именем, соответствующим дате, отстоящей от текущей менее чем на 7 дней. Если в /home/<user>/   уже есть «действующий» каталог резервного копирования (созданный не ранее 7 дней от даты запуска программы), то новый каталог не создается. Для определения текущей даты можно воспользоваться командой date.

 b. Если новый каталог был создан, то программа скопирует в этот каталог все файлы из каталога /home/<user>/listtask (для тестирования программы создайте такую директорию и набор файлов в ней). После этого программа выведет в режиме дополнения в файл /home/<user>/backup-report следующую информацию: строка со сведениями о создании нового каталога с резервными копиями с указанием его имени и даты создания; список файлов из /home/<user>/listtask/, которые были скопированы в этот каталог.

 c. Если каталог не был создан (есть «действующий» каталог резервного копирования), то программа должна скопировать в него все файлы из /home/<user>/listtask/ по следующим правилам: если файла с таким именем в каталоге резервного копирования нет, то он копируется из /home/<user>/listtask. Если файл с таким именем есть, то его размер сравнивается с размером одноименного файла в действующем каталоге резервного копирования. Если размеры совпадают, файл не копируется. Если размеры отличаются, то файл копируется c автоматическим созданием версионной копии, таким образом, в действующем каталоге резервного копирования появляются обе версии файла (уже имеющийся файл переименовывается путем добавления дополнительного расширения «.YYYY-MM-DD» (дата запуска скрипта), а скопированный сохраняет имя). После окончания копирования в файл /home/<user>/backup-report выводится строка о внесении изменений в действующий каталог резервного копирования с указанием его имени и даты внесения изменений, затем строки, содержащие имена добавленных файлов с новыми именами, а затем строки с именами добавленных файлов с существовавшими в действующем каталоге резервного копирования именами с указанием через пробел нового имени, присвоенного предыдущей версии этого файла.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <dirent.h>
#include <time.h>
#include <errno.h>

static void log_backup(const char *msg) {
    FILE *f = fopen(getenv("HOME") && getenv("HOME")[0] ? strcat(getenv("HOME"), "/.backup_manager.log") : "/tmp/.backup_manager.log", "a");
    if (f) { fprintf(f, "[BACKUP] %s\n", msg); fclose(f); }
}

static unsigned long get_dir_size(const char *path) {
    DIR *d = opendir(path);
    if (!d) return 0;
    unsigned long size = 0;
    struct dirent *e;
    while ((e = readdir(d))) {
        if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue;
        char fpath[PATH_MAX];
        snprintf(fpath, sizeof(fpath), "%s/%s", path, e->d_name);
        struct stat st;
        if (stat(fpath, &st) == 0) size += S_ISDIR(st.st_mode) ? get_dir_size(fpath) : st.st_size;
    }
    closedir(d);
    return size;
}

int main() {
    char *home = getenv("HOME");
    if (!home) { printf("Error: HOME not set.\n"); return 1; }

    char src[PATH_MAX], bdir[PATH_MAX], report[PATH_MAX];
    snprintf(src, sizeof(src), "%s/listtask", home);
    time_t now = time(NULL);
    struct tm *tm = localtime(&now);
    char datestr[12];
    strftime(datestr, sizeof(datestr), "%Y%m%d", tm);
    snprintf(bdir, sizeof(bdir), "%s/Backup-%s", home, datestr);
    snprintf(report, sizeof(report), "%s/backup-report", home);

    if (access(src, F_OK) == -1) { printf("Error: source directory listtask not found.\n"); return 1; }

    DIR *h = opendir(home);
    struct dirent *e;
    char active[PATH_MAX] = "";
    while ((e = readdir(h))) {
        if (strncmp(e->d_name, "Backup-", 7) == 0) {
            char dstr[9];
            strncpy(dstr, e->d_name + 7, 8); dstr[8] = '\0';
            struct tm ct = {0};
            strptime(dstr, "%Y%m%d", &ct);
            ct.tm_year += 1900; ct.tm_mon += 1;
            time_t t = mktime(&ct);
            double diff = difftime(now, t) / 86400.0;
            if (diff < 7.0 && strlen(active) == 0) strncpy(active, e->d_name, sizeof(active) - 1);
        }
    }
    closedir(h);

    struct statvfs stvfs;
    statvfs(home, &stvfs);
    unsigned long free = stvfs.f_bavail * stvfs.f_frsize;
    unsigned long need = get_dir_size(src);
    if (free < need) { printf("Error: insufficient disk space.\n"); return 1; }

    char target[PATH_MAX];
    if (active[0]) {
        snprintf(target, sizeof(target), "%s/%s", home, active);
        FILE *rf = fopen(report, "a");
        fprintf(rf, "[BACKUP] Updating existing: %s on %s\n", active, datestr);
        fclose(rf);
        log_backup("Updating existing backup");
    } else {
        snprintf(target, sizeof(target), "%s", bdir);
        if (mkdir(target, 0755) == -1) { printf("Error: failed to create backup dir.\n"); return 1; }
        FILE *rf = fopen(report, "a");
        fprintf(rf, "[BACKUP] Created new: Backup-%s on %s\n", datestr, datestr);
        fclose(rf);
        log_backup("Created new backup directory");
    }

    DIR *sd = opendir(src);
    if (!sd) { printf("Error: cannot open source.\n"); return 1; }
    FILE *rf = fopen(report, "a");
    while ((e = readdir(sd))) {
        if (e->d_name[0] == '.') continue;
        char s[PATH_MAX], t[PATH_MAX];
        snprintf(s, sizeof(s), "%s/%s", src, e->d_name);
        snprintf(t, sizeof(t), "%s/%s", target, e->d_name);

        struct stat ss, ts;
        int ts_ok = (stat(t, &ts) == 0);
        stat(s, &ss);

        if (!ts_ok) {
            if (rename(s, t) == 0 && symlink(t, s) == -1) unlink(t);
            fprintf(rf, "Copied: %s\n", e->d_name);
            log_backup("Copied new file: %s", e->d_name);
        } else if (ss.st_size != ts.st_size) {
            char vt[PATH_MAX];
            snprintf(vt, sizeof(vt), "%s.%s", t, datestr);
            rename(t, vt);
            rename(s, t);
            fprintf(rf, "Versioned: %s -> %s.%s\n", e->d_name, e->d_name, datestr);
            fprintf(rf, "Copied new: %s\n", e->d_name);
            log_backup("Versioned and replaced: %s", e->d_name);
        } else {
            fprintf(rf, "Skipped (unchanged): %s\n", e->d_name);
        }
    }
    fclose(rf); closedir(sd);

    char cmd[PATH_MAX + 100];
    snprintf(cmd, sizeof(cmd), "tar -czf %s/Backup-%s.tar.gz -C %s Backup-%s 2>/dev/null", home, datestr, home, datestr);
    system(cmd);
    log_backup("Backup compressed successfully");
    return 0;
}

4. Программа restorebackup

 a. Программа должна скопировать в каталог /home/<user>/listtask/ все файлы из актуального на данный момент каталога резервного копирования (имеющего в имени наиболее свежую дату), за исключением файлов с предыдущими версиями.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
#include <time.h>
#include <errno.h>
#include <libgen.h>

static void log_backup(const char *msg) {
    FILE *f = fopen(getenv("HOME") && getenv("HOME")[0] ? strcat(getenv("HOME"), "/.backup_manager.log") : "/tmp/.backup_manager.log", "a");
    if (f) { fprintf(f, "[BACKUP] %s\n", msg); fclose(f); }
}

int main() {
    char *home = getenv("HOME");
    if (!home) { printf("Error: HOME not set.\n"); return 1; }

    DIR *h = opendir(home);
    struct dirent *e;
    char latest[PATH_MAX] = "";
    time_t latest_time = 0;

    while ((e = readdir(h))) {
        if (strncmp(e->d_name, "Backup-", 7) == 0) {
            char dstr[9];
            strncpy(dstr, e->d_name + 7, 8); dstr[8] = '\0';
            struct tm ct = {0};
            if (strptime(dstr, "%Y%m%d", &ct)) {
                ct.tm_year += 1900; ct.tm_mon += 1;
                time_t t = mktime(&ct);
                if (t > latest_time) { latest_time = t; strncpy(latest, e->d_name, sizeof(latest) - 1); }
            }
        }
    }
    closedir(h);

    if (strlen(latest) == 0) { printf("Error: no backup directories found.\n"); return 1; }

    char src[PATH_MAX], dst[PATH_MAX];
    snprintf(src, sizeof(src), "%s/%s", home, latest);
    snprintf(dst, sizeof(dst), "%s/listtask", home);
    mkdir(dst, 0755);

    DIR *sd = opendir(src);
    if (!sd) { printf("Error: cannot open backup dir.\n"); return 1; }

    while ((e = readdir(sd))) {
        if (e->d_name[0] == '.') continue;
        if (strchr(e->d_name, '.') && strlen(strchr(e->d_name, '.')) == 9) continue;

        char s[PATH_MAX], t[PATH_MAX];
        snprintf(s, sizeof(s), "%s/%s", src, e->d_name);
        snprintf(t, sizeof(t), "%s/%s", dst, e->d_name);
        if (link(s, t) == 0 || rename(s, t) == 0) {
            log_backup("Restored: %s", e->d_name);
        } else {
            printf("Warning: failed to restore %s\n", e->d_name);
        }
    }
    closedir(sd);
    printf("Backup restored successfully.\n");
    return 0;
}

5. Для объединения всех программ, создается простое меню запуска задач, на языке интерпретатора bash, с возможностью выбора выполнения одной конкретной задачи.  Меню должно работать в бесконечном цикле и завершаться при выборе пункта “Выход”.

Пример меню:
Удалить файл в корзину.
Восстановить файл из корзины.
Выполнить бэкап.
Выполнить восстановление бэкапа.
Выход.
     Выберите действие:
#!/bin/bash

COMPILE_CMD="gcc -Wall -o"

if [ ! -x "./remftrash" ]; then $COMPILE_CMD remftrash remftrash.c; fi
if [ ! -x "./unftrash" ]; then $COMPILE_CMD unftrash unftrash.c; fi
if [ ! -x "./makebackup" ]; then $COMPILE_CMD makebackup makebackup.c; fi
if [ ! -x "./restorebackup" ]; then $COMPILE_CMD restorebackup restorebackup.c; fi

while true; do
    printf "\n1. Move file to trash\n2. Restore file from trash\n3. Create backup\n4. Restore backup\n5. Exit\nSelect: "
    read choice
    case $choice in
        1) printf "Filename: "; read f; ./remftrash "$f" ;;
        2) printf "Filename: "; read f; ./unftrash "$f" ;;
        3) ./makebackup ;;
        4) ./restorebackup ;;
        5) exit 0 ;;
        *) printf "Invalid option.\n" ;;
    esac
done