Advertisement
zergon321

fs

Nov 13th, 2020
1,178
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 17.31 KB | None | 0 0
  1. /**
  2.  * @file fs.c
  3.  * @author zergon321 (maximgradan@gmail.com)
  4.  * @brief Консольная утилита fs предназначена для создания
  5.  * виртуального диска и добавления на него файлов. Пример
  6.  * создания виртуального диска:
  7.  *
  8.  * fs create -o disk -s 1024
  9.  *
  10.  * Данная команда создаёт виртуальный диск disk размером
  11.  * 1024 блока. Для записи файла на диск необходимо написать
  12.  * следующее:
  13.  *
  14.  * fs write -d disk -f file.txt
  15.  *
  16.  * Данная команда записывает на виртуальный диск disk файл
  17.  * file.txt. Каждый файл на виртуальном диске будет занимать
  18.  * целое число блоков.
  19.  * @version 0.1
  20.  * @date 2020-11-08
  21.  *
  22.  * @copyright Copyright (c) 2020
  23.  *
  24.  */
  25.  
  26. #include <unistd.h>
  27. #include <fcntl.h>
  28. #include <stdio.h>
  29. #include <string.h>
  30. #include <stdlib.h>
  31. #include <limits.h>
  32. #include <sys/stat.h>
  33. #include <sys/types.h>
  34.  
  35. typedef int FILE_HANDLER;
  36. typedef unsigned short word;
  37. typedef unsigned char byte;
  38.  
  39. #define C_CREATE "create"
  40. #define P_NAME "-o"
  41. #define P_SIZE "-s"
  42.  
  43. #define C_WRITE "write"
  44. #define P_DISK "-d"
  45. #define P_FILE "-f"
  46.  
  47. // Размеры.
  48. #define BLOCK_SIZE 512
  49. #define CATALOG_SIZE 10
  50. #define METADATA_SIZE 11
  51. #define FILE_RECORD_SIZE 16
  52. #define FILE_NAME_SIZE 12
  53.  
  54. // Адреса.
  55. #define CATALOG_OFFSET 1
  56. #define BLOCK_SIZE_OFFSET 6
  57. #define FILES_COUNT_OFFSET 4
  58.  
  59. int main(int argc, char* argv[]) {
  60.     if (argc < 2) {
  61.         fprintf(stderr, "no command specified\n");
  62.         return 1;
  63.     }
  64.  
  65.     char* command = argv[1];
  66.  
  67.     if (strcmp(command, C_CREATE) == 0) {
  68.         char* name = NULL;
  69.         int size = -1;
  70.  
  71.         if (get_args_create(argc, argv, &name, &size) == 0) {
  72.             if (create_virtual_disk(name, size) >= 0) {
  73.                 printf("created a new virtual disk '%s' with size of '%d' blocks\n", name, size);
  74.             } else {
  75.                 fprintf(stderr, "couldn't create a new virtual disk\n");
  76.                 return -1;
  77.             }
  78.         } else {
  79.             fprintf(stderr, "invalid call format\n");
  80.             return -1;
  81.         }
  82.     } else if (strcmp(command, C_WRITE) == 0) {
  83.         char* disk = NULL;
  84.         char* file = NULL;
  85.  
  86.         if (get_args_write(argc, argv, &disk, &file) == 0) {
  87.             if (write_file_to_disk(disk, file) >= 0) {
  88.                 printf("wrote file '%s' to disk '%s'\n", file, disk);
  89.             } else {
  90.                 fprintf(stderr, "couldn't write file '%s' to disk '%s'\n", file, disk);
  91.                 return -1;
  92.             }
  93.         } else {
  94.             fprintf(stderr, "invalid call format\n");
  95.             return -1;
  96.         }
  97.     } else {
  98.         printf("unknown command\n");
  99.         return -1;
  100.     }
  101.  
  102.     return 0;
  103. }
  104.  
  105. /**
  106.  * @brief Возвращает размер файла в байтах.
  107.  *
  108.  * @param filename имя файла на диске.
  109.  * @return off_t - размер файла в байтах.
  110.  */
  111. off_t fsize(const char *filename) {
  112.     struct stat st;
  113.  
  114.     if (stat(filename, &st) == 0)
  115.         return st.st_size;
  116.  
  117.     return -1;
  118. }
  119.  
  120. /**
  121.  * @brief Записывает в бинарный файл значение типа 'word' (2 байта).
  122.  *
  123.  * @param file файл, в который записывается значение.
  124.  * @param value значение, которое записывается в файл.
  125.  * @return int - 0, если всё успешно, и -1, если возникла ошибка.
  126.  */
  127. int write_word_to_file(FILE_HANDLER file, word value) {
  128.     byte buffer[2];
  129.     clear_buffer(buffer, 2);
  130.  
  131.     buffer[0] = (value >> 8) & 0x00FF;
  132.     buffer[1] = value & 0x00FF;
  133.  
  134.     return write(file, buffer, 2);
  135. }
  136.  
  137. /**
  138.  * @brief Записать в файл значение типа word по адресу position.
  139.  *
  140.  * @param file файл, в который происходит запись.
  141.  * @param position смещение, с которого необходимо произвести запись.
  142.  * @param value значение, которое необходимо записать.
  143.  * @return int - 0, если запись прошла успешно, и -1, если нет.
  144.  */
  145. int write_word_to_file_at(FILE_HANDLER file, off_t position, word value) {
  146.     if (lseek(file, position, SEEK_SET) < 0) {
  147.         return -1;
  148.     }
  149.  
  150.     return write_word_to_file(file, value);
  151. }
  152.  
  153. /**
  154.  * @brief Считывает из файла значение типа word по адресу position.
  155.  *
  156.  * @param file файл, из которого происходит чтение.
  157.  * @param position адрес, по которому происходит чтение.
  158.  * @param value указатель на переменную для хранения считываемого значения.
  159.  * @return int - 0, если чтение прошло успешно, и -1, если нет.
  160.  */
  161. int read_word_from_file_at(FILE_HANDLER file, off_t position, word* value) {
  162.     byte buffer[2];
  163.     clear_buffer(buffer, 2);
  164.  
  165.     if (lseek(file, position, SEEK_SET) < 0) {
  166.         return -1;
  167.     }
  168.  
  169.     if (read(file, buffer, 2) <= 0) {
  170.         return -1;
  171.     }
  172.  
  173.     *value = (buffer[0] << 8) + buffer[1];
  174.  
  175.     return 0;
  176. }
  177.  
  178. /**
  179.  * @brief Создаёт новый виртуальный диск для хранения файлов.
  180.  *
  181.  * @param name имя файла, в котором будет срдержаться разметка виртуального диска.
  182.  * @param size размер виртуального диска в блоках.
  183.  * @return int - 0, если всё прошло успешно, и -1, если возникла ошибка.
  184.  */
  185. int create_virtual_disk(char* name, int size) {
  186.     if (size > USHRT_MAX) {
  187.         return -1;
  188.     }
  189.  
  190.     FILE_HANDLER file = -1;
  191.     word num_of_blocks = (word)size;
  192.     word magic = 0x7171;
  193.  
  194.     if ((file = open(name, O_CREAT | O_WRONLY | O_TRUNC, 0777)) >= 0) {
  195.         /* Записать суперблок. */
  196.         // Магическое число.
  197.         if (write_word_to_file(file, magic) < 0) {
  198.             return -1;
  199.         }
  200.  
  201.         // Число блоков.
  202.         if (write_word_to_file(file, num_of_blocks) < 0) {
  203.             return -1;
  204.         }
  205.  
  206.         // Число файлов на диске.
  207.         if (write_word_to_file(file, 0) < 0) {
  208.             return -1;
  209.         }
  210.  
  211.         // Размер блока.
  212.         if (write_word_to_file(file, BLOCK_SIZE) < 0) {
  213.             return -1;
  214.         }
  215.  
  216.         // Все оставшиеся байты суперблока установаить в 0.
  217.         byte filler = 0;
  218.  
  219.         for (int i = 0; i < BLOCK_SIZE - 8; i++) {
  220.             if (write(file, &filler, 1) < 0) {
  221.                 return -1;
  222.             }
  223.         }
  224.  
  225.         /* Записать область с именами и адресами файлов;
  226.            одна запись - 16 байтов, где 12 байтов - имя
  227.            файла, 2 байта - адрес первого блока файла на
  228.            диске, 2 байта - длина файла (в блоках).      */
  229.         for (int i = 0; i < CATALOG_SIZE * BLOCK_SIZE; i++) {
  230.             if (write(file, &filler, 1) < 0) {
  231.                 return -1;
  232.             }
  233.         }
  234.  
  235.         /* Записать область для хранения данных файлов. */
  236.         word file_data_size = size - METADATA_SIZE;
  237.  
  238.         for (int i = 0; i < file_data_size * BLOCK_SIZE; i++) {
  239.             if (write(file, &filler, 1) < 0) {
  240.                 return -1;
  241.             }
  242.         }
  243.     }
  244.  
  245.     return 0;
  246. }
  247.  
  248. /**
  249.  * @brief Считывает с виртуального диска запись о файле, расположенную по адресу position.
  250.  *
  251.  * @param disk дескриптор виртуального диска.
  252.  * @param position адрес записи о файле.
  253.  * @param name указатель на строку для хранения имени файла.
  254.  * @param address номер блока, с которого начинается файл.
  255.  * @param length длина файла в блоках.
  256.  * @return int - 0, если чтение прошло успешно, и -1,если нет.
  257.  */
  258. int read_file_record_at(FILE_HANDLER disk, off_t position, char* name, word* address, word* length) {
  259.     byte buffer[FILE_RECORD_SIZE];
  260.     clear_buffer(buffer, FILE_RECORD_SIZE);
  261.  
  262.     if (lseek(disk, position, SEEK_SET) < 0) {
  263.         return -1;
  264.     }
  265.  
  266.     if (read(disk, buffer, FILE_RECORD_SIZE) <= 0) {
  267.         return -1;
  268.     }
  269.  
  270.     if (name != NULL) {
  271.         memcpy(name, buffer, FILE_NAME_SIZE);
  272.     }
  273.    
  274.     if (address != NULL) {
  275.         *address = (buffer[12] << 8) + buffer[13];
  276.     }
  277.  
  278.     if (length != NULL) {
  279.         *length = (buffer[14] << 8) + buffer[15];
  280.     }
  281.  
  282.     return 0;
  283. }
  284.  
  285. /**
  286.  * @brief Очищает байтовый буфер, устанавливая все его элементы в 0.
  287.  *
  288.  * @param buffer буфер, который необходимо очистить.
  289.  * @param length длина буфера.
  290.  */
  291. void clear_buffer(byte buffer[], int length) {
  292.     for (int i = 0; i < length; i++) {
  293.         buffer[i] = 0;
  294.     }
  295. }
  296.  
  297. /**
  298.  * @brief Создаёт на виртуальном диске запись о файле по указанному адресу.
  299.  *
  300.  * @param disk дескриптор виртуального диска, на который производится запись.
  301.  * @param position адрес, по которому создаётся запись о новом файле.
  302.  * @param name имя файла.
  303.  * @param address адрес стартового блока файла.
  304.  * @param length длина файла в блоках.
  305.  * @return int - 0, если запись прошла успешно, -1, если нет.
  306.  */
  307. int write_file_record_at(FILE_HANDLER disk, off_t position, char* name, word address, word length) {
  308.     if (name == NULL) {
  309.         return -1;
  310.     }
  311.  
  312.     byte buffer[FILE_RECORD_SIZE];
  313.     clear_buffer(buffer, FILE_RECORD_SIZE);
  314.  
  315.     if (lseek(disk, position, SEEK_SET) < 0) {
  316.         return -1;
  317.     }
  318.  
  319.     memcpy(buffer, name, strlen(name) + 1);
  320.     buffer[12] = (address >> 8) & 0x00FF;
  321.     buffer[13] = address & 0x00FF;
  322.     buffer[14] = (length >> 8) & 0x00FF;
  323.     buffer[15] = length & 0x00FF;
  324.  
  325.     if (write(disk, buffer, FILE_RECORD_SIZE) < 0) {
  326.         return -1;
  327.     }
  328.  
  329.     return 0;
  330. }
  331.  
  332. /**
  333.  * @brief Добавляет новый файл на виртуальный диск.
  334.  *
  335.  * @param diskname имя виртуального диска.
  336.  * @param filename имя добавляемого файла.
  337.  * @return int - 0, если добавление нового файла на диск прошло успешно, и -1, если нет.
  338.  */
  339. int write_file_to_disk(char* diskname, char* filename) {
  340.     FILE_HANDLER disk = -1, file = -1;
  341.  
  342.     if ((disk = open(diskname, O_RDWR)) < 0) {
  343.         return -1;
  344.     }
  345.  
  346.     if ((file = open(filename, O_RDONLY, 0)) < 0) {
  347.         return -1;
  348.     }
  349.  
  350.     // Узнать размер файла.
  351.     off_t size = -1;
  352.  
  353.     if ((size = fsize(filename)) < 0) {
  354.         return -1;
  355.     }
  356.  
  357.     printf("file size is %d bytes\n", size);
  358.  
  359.     // Считать размер блока.
  360.     word block_size = 0;
  361.  
  362.     if (read_word_from_file_at(disk, BLOCK_SIZE_OFFSET, &block_size) < 0) {
  363.         return -1;
  364.     }
  365.  
  366.     printf("disk block size is %d bytes\n", block_size);
  367.  
  368.     // Вычислить размер файла в дисковых блоках.
  369.     word size_in_blocks = size / block_size;
  370.  
  371.     if (size % block_size > 0) {
  372.         size_in_blocks++;
  373.     }
  374.  
  375.     printf("file size is %d blocks\n", size_in_blocks);
  376.  
  377.     // Считать кол-во файлов на диске.
  378.     word files_count = 0;
  379.  
  380.     if (read_word_from_file_at(disk, FILES_COUNT_OFFSET, &files_count) < 0) {
  381.         return -1;
  382.     }
  383.  
  384.     printf("disk has %d files in total\n", files_count);
  385.  
  386.     // Вычислить блок, с которого должны начинаться
  387.     // данные нового файла. Для этого необходимо найти
  388.     // конец последнего записанного на диск файла.
  389.     word last_block = -1, length = -1;
  390.     off_t last_record_address = CATALOG_OFFSET * block_size + (files_count == 0 ? 0 : (files_count - 1)) * FILE_RECORD_SIZE;
  391.  
  392.     if (read_file_record_at(disk, last_record_address, NULL, &last_block, &length) < 0) {
  393.         return -1;
  394.     }
  395.  
  396.     if (last_block == 0) {
  397.         last_block = METADATA_SIZE;
  398.     }
  399.  
  400.     word start_block = last_block + length;
  401.     off_t record_address = files_count == 0 ? last_record_address : (last_record_address + FILE_RECORD_SIZE);
  402.  
  403.     printf("file record is at 0x%x\n", record_address);
  404.  
  405.     // Создать в каталоге новую запись с
  406.     // именем файла, его стартовым блоком
  407.     // и длиной (в блоках).
  408.     if (write_file_record_at(disk, record_address, filename, start_block, size_in_blocks) < 0) {
  409.         return -1;
  410.     }
  411.  
  412.     // Записать данные файла на диск, начиная
  413.     // с адреса стартового блока.
  414.     off_t data_address = start_block * block_size;
  415.     byte* buffer = (byte*)malloc(block_size);
  416.  
  417.     printf("file data is at 0x%x\n", data_address);
  418.  
  419.     if (lseek(disk, data_address, SEEK_SET) < 0) {
  420.         return -1;
  421.     }
  422.  
  423.     for (int i = start_block; i < start_block + size_in_blocks; i++) {
  424.         // Считать данные из файла.
  425.         if (read(file, buffer, block_size) < 0) {
  426.             free(buffer);
  427.             return -1;
  428.         }
  429.  
  430.         // Записать данные на виртуальный диск.
  431.         if (write(disk, buffer, block_size) < 0) {
  432.             free(buffer);
  433.             return -1;
  434.         }
  435.  
  436.         // Очистить буфер.
  437.         clear_buffer(buffer, block_size);
  438.     }
  439.  
  440.     free(buffer);
  441.  
  442.     // Повысить на 1 счётчик числа файлов на диске.
  443.     files_count++;
  444.  
  445.     if (write_word_to_file_at(disk, FILES_COUNT_OFFSET, files_count) < 0) {
  446.         return -1;
  447.     }
  448.  
  449.     return 0;
  450. }
  451.  
  452. /**
  453.  * @brief Считывает аргументы командной строки для команды 'create'.
  454.  *
  455.  * @param argc число аргументов командной строки.
  456.  * @param argv аргументы командной строки.
  457.  * @param name переменная для хранения имени выходного файла (диска).
  458.  * @param size переменная для хранения размера диска (в блоках).
  459.  * @return int - 0, если всё успешно, и -1, если формат ввода неверен.
  460.  */
  461. int get_args_create(int argc, char* argv[], char** name, int* size) {
  462.     if (argc < 6) {
  463.         return -1;
  464.     }
  465.  
  466.     char* param = argv[2];
  467.     int name_def = 0, size_def = 0;
  468.  
  469.     if (strcmp(param, P_NAME) == 0) {
  470.         *name = argv[3];
  471.         name_def = 1;
  472.     } else if (strcmp(param, P_SIZE) == 0) {
  473.         *size = atoi(argv[3]);
  474.  
  475.         if (*size == 0) {
  476.             return -1;
  477.         }
  478.  
  479.         size_def = 1;
  480.     } else {
  481.         return -1;
  482.     }
  483.    
  484.     param = argv[4];
  485.  
  486.     if (strcmp(param, P_NAME) == 0) {
  487.         if (!size_def) {
  488.             return -1;
  489.         }
  490.  
  491.         *name = argv[5];
  492.     } else if (strcmp(param, P_SIZE) == 0) {
  493.         if (!name_def) {
  494.             return -1;
  495.         }
  496.  
  497.         *size = atoi(argv[5]);
  498.  
  499.         if (*size == 0) {
  500.             return -1;
  501.         }
  502.     } else {
  503.         return -1;
  504.     }
  505.  
  506.     return 0;
  507. }
  508.  
  509. /**
  510.  * @brief Считывает аргументы командной строки для команды 'write'.
  511.  *
  512.  * @param argc число аргументов командной строки.
  513.  * @param argv аргументы командной строки.
  514.  * @param disk имя виртуального диска.
  515.  * @param file имя файла, который надо создать на виртуальном диске.
  516.  * @return int - 0, если всё успешно, и -1, если формат ввода неверен.
  517.  */
  518. int get_args_write(int argc, char* argv[], char** disk, char** file) {
  519.     if (argc < 6) {
  520.         return -1;
  521.     }
  522.  
  523.     char* param = argv[2];
  524.     int disk_def = 0, file_def = 0;
  525.  
  526.     if (strcmp(param, P_DISK) == 0) {
  527.         *disk = argv[3];
  528.         disk_def = 1;
  529.     } else if (strcmp(param, P_FILE) == 0) {
  530.         *file = argv[3];
  531.         file_def = 1;
  532.     } else {
  533.         return -1;
  534.     }
  535.    
  536.     param = argv[4];
  537.  
  538.     if (strcmp(param, P_DISK) == 0) {
  539.         if (!file_def) {
  540.             return -1;
  541.         }
  542.  
  543.         *disk = argv[5];
  544.     } else if (strcmp(param, P_FILE) == 0) {
  545.         if (!disk_def) {
  546.             return -1;
  547.         }
  548.  
  549.         *file = argv[5];
  550.     } else {
  551.         return -1;
  552.     }
  553.  
  554.     return 0;
  555. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement