Загрузка данных
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