Skip to content

Commit 1d83a51

Browse files
committed
zip done
1 parent 965c593 commit 1d83a51

6 files changed

Lines changed: 328 additions & 0 deletions

File tree

src/hashing.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#include "hashing.h"
2+
3+
int calculate_file_hash(const char* filename, unsigned char hash[SHA256_DIGEST_LENGTH]) {
4+
FILE* file = fopen(filename, "rb");
5+
if (file == NULL) {
6+
perror("Error opening file");
7+
return -1;
8+
}
9+
10+
EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
11+
if (mdctx == NULL) {
12+
perror("Error creating context");
13+
fclose(file);
14+
return -1;
15+
}
16+
17+
if (EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL) != 1) {
18+
perror("Error initializing digest");
19+
EVP_MD_CTX_free(mdctx);
20+
fclose(file);
21+
return -1;
22+
}
23+
24+
unsigned char buffer[2 << 13];
25+
size_t bytesRead;
26+
while ((bytesRead = fread(buffer, 1, sizeof(buffer), file)) != 0) {
27+
if (EVP_DigestUpdate(mdctx, buffer, bytesRead) != 1) {
28+
perror("Error updating digest");
29+
EVP_MD_CTX_free(mdctx);
30+
fclose(file);
31+
return -1;
32+
}
33+
}
34+
35+
unsigned int digestLength;
36+
if (EVP_DigestFinal_ex(mdctx, hash, &digestLength) != 1) {
37+
perror("Error finalizing digest");
38+
EVP_MD_CTX_free(mdctx);
39+
fclose(file);
40+
return -1;
41+
}
42+
43+
EVP_MD_CTX_free(mdctx);
44+
fclose(file);
45+
46+
return 0;
47+
}

src/hashing.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef SRC_HASHING_H_
2+
#define SRC_HASHING_H_
3+
4+
#include <stdio.h>
5+
#include <string.h>
6+
#include <openssl/evp.h>
7+
#include <openssl/sha.h>
8+
9+
int calculate_file_hash(const char* filename, unsigned char hash[SHA256_DIGEST_LENGTH]);
10+
11+
12+
#endif // SRC_HASHING_H_

src/unzip.c

Whitespace-only changes.

src/unzip.h

Whitespace-only changes.

src/zip.c

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
#include "zip.h"
2+
#include "hashing.h"
3+
4+
static int isNumber(char* s);
5+
static void printInfo();
6+
7+
int main(int argc, char** argv) {
8+
OptionsConfig config = {0};
9+
int retCode = (int)OK;
10+
11+
// parsing args with getopt
12+
if (argc > 1) {
13+
int ind;
14+
if ((ind = scanOptions(argc, argv, &config)) != -1) {
15+
argv += ind;
16+
argc = argc - ind;
17+
} else {
18+
retCode = (int)ERROR;
19+
}
20+
} else {
21+
retCode = (int)ERROR;
22+
}
23+
24+
25+
if (config.hFlag) {
26+
retCode = ERROR;
27+
printInfo();
28+
}
29+
30+
if (retCode == (int)OK) {
31+
// filename or filename + archive name
32+
if (argc >= 1 && argc <= 2) {
33+
const char* input_filename = argv[0];
34+
const char* archive_filename = (argv[1]) ? argv[1] : "archive.zip";
35+
if (!config.cFlag) {
36+
config.chunksNumber = DEFAULT_CHUNK_SIZE;
37+
}
38+
retCode = zip(input_filename, archive_filename, config);
39+
} else {
40+
fprintf(stderr, USAGE);
41+
}
42+
} else if (retCode == ERROR) {
43+
fprintf(stderr, USAGE);
44+
45+
}
46+
47+
return retCode;
48+
}
49+
50+
// parsing args with getopt
51+
int scanOptions(int argc, char** argv, OptionsConfig* config) {
52+
int flag;
53+
int result = (int)OK;
54+
while (((flag = getopt(argc, argv, "hfc:"))) != -1) {
55+
switch (flag) {
56+
case 'h':
57+
config->hFlag = 1;
58+
break;
59+
case 'f':
60+
config->fFlag = 1;
61+
break;
62+
case 'c':
63+
config->cFlag = 1;
64+
result = (int)readFromC(optarg, config);
65+
break;
66+
default:
67+
result = (int)ERROR;
68+
}
69+
70+
if (result < 0) break;
71+
}
72+
73+
if (result >= 0) {
74+
result = optind;
75+
}
76+
77+
return result;
78+
}
79+
80+
eErrorCode readFromC (char* optarg, OptionsConfig* config) {
81+
eErrorCode retCode = OK;
82+
int chunksNum = atoi(optarg);
83+
int isNum = isNumber(optarg);
84+
if (!chunksNum || !isNum || ((unsigned)chunksNum > MAX_CHUNK_SIZE) ||
85+
((unsigned)chunksNum < MIN_CHUNK_SIZE)) {
86+
fprintf(stderr, "-c arg is number from 1 to 1024 * 1024 * 1024\n");
87+
retCode = ERROR;
88+
} else {
89+
config->chunksNumber = chunksNum;
90+
config->cFlag = 1;
91+
}
92+
93+
return retCode;
94+
}
95+
96+
// compression func
97+
int zip(const char* filename, const char* archive_filename, OptionsConfig config) {
98+
// calc file hash at start
99+
unsigned char hash_start[SHA256_DIGEST_LENGTH];
100+
if (calculate_file_hash(filename, hash_start) == -1) {
101+
perror("Error hashing files");
102+
return EXIT_FAILURE;
103+
}
104+
105+
// open input file for reading
106+
int input_fd = open(filename, O_RDONLY);
107+
if (input_fd == -1) {
108+
perror("Error opening input file");
109+
return EXIT_FAILURE;
110+
}
111+
112+
// getting input file stats
113+
struct stat st;
114+
if (fstat(input_fd, &st) == -1) {
115+
perror("Error getting file size");
116+
close(input_fd);
117+
return EXIT_FAILURE;
118+
}
119+
120+
// open archive file for writing
121+
int archiveOverwrite = (config.fFlag) ? (O_CREAT | O_WRONLY | O_TRUNC) : (O_CREAT | O_WRONLY | O_TRUNC | O_EXCL);
122+
int archive_fd = open(archive_filename, archiveOverwrite, 0666);
123+
if (archive_fd == -1 || archive_fd == EEXIST) {
124+
if (archive_fd == EEXIST) {
125+
perror("Archive already exist, run with -f to overwrite");
126+
} else {
127+
perror("Error opening output file");
128+
}
129+
close(input_fd);
130+
return EXIT_FAILURE;
131+
}
132+
133+
// getting input file size
134+
off_t input_size = st.st_size;
135+
136+
// mem mapping of input file
137+
void* input_data = mmap(NULL, input_size, PROT_READ, MAP_PRIVATE, input_fd, 0);
138+
if (input_data == MAP_FAILED) {
139+
perror("Error mapping input file to memory");
140+
close(input_fd);
141+
close(archive_fd);
142+
return EXIT_FAILURE;
143+
}
144+
145+
// struct init for compression
146+
z_stream stream;
147+
memset(&stream, 0, sizeof(stream));
148+
149+
if (deflateInit(&stream, Z_DEFAULT_COMPRESSION) != Z_OK) {
150+
perror("Error initializing compression stream");
151+
munmap(input_data, input_size);
152+
close(input_fd);
153+
close(archive_fd);
154+
return EXIT_FAILURE;
155+
}
156+
157+
// passing input data ptr and its size to struct
158+
stream.avail_in = input_size;
159+
stream.next_in = (Bytef*)input_data;
160+
161+
unsigned char out[config.chunksNumber];
162+
do {
163+
stream.avail_out = config.chunksNumber;
164+
stream.next_out = out;
165+
// input file compression and writing to archive
166+
if (deflate(&stream, Z_FINISH) == Z_STREAM_ERROR) {
167+
perror("Error compressing data");
168+
deflateEnd(&stream);
169+
munmap(input_data, input_size);
170+
close(input_fd);
171+
close(archive_fd);
172+
return EXIT_FAILURE;
173+
}
174+
write(archive_fd, out, config.chunksNumber - stream.avail_out);
175+
} while (stream.avail_out == 0);
176+
177+
// calc file hash at end
178+
unsigned char hash_end[SHA256_DIGEST_LENGTH];
179+
if (calculate_file_hash(filename, hash_end) == -1) {
180+
perror("Error while hashing files");
181+
munmap(input_data, input_size);
182+
close(input_fd);
183+
close(archive_fd);
184+
return EXIT_FAILURE;
185+
}
186+
187+
// checking hashes (file at start <-> file at end)
188+
if ((memcmp(hash_end, hash_start, SHA256_DIGEST_LENGTH) != 0)) {
189+
fprintf(stderr, "Error: File content was modified during copying\n");
190+
munmap(input_data, input_size);
191+
close(input_fd);
192+
close(archive_fd);
193+
return EXIT_FAILURE;
194+
}
195+
196+
// free
197+
deflateEnd(&stream);
198+
munmap(input_data, input_size);
199+
close(input_fd);
200+
close(archive_fd);
201+
return 0;
202+
}
203+
204+
// -------------------------- static funcs --------------------------
205+
int isNumber(char* s) {
206+
for (int i = 0; s[i]!= '\0'; i++) {
207+
if (!isdigit(s[i])) {
208+
return 0;
209+
}
210+
}
211+
return 1;
212+
}
213+
214+
void printInfo() {
215+
printf("Usage: zip [options] filename [dest_archive.zip]\n"\
216+
"Options:\n"\
217+
" -c CHUNKS_NUMBER\n"\
218+
" Chunks size in compression algorithm in bytes from 1 to 1024 * 1024 * 2 (2MB)\n"\
219+
" -f\n"\
220+
" Force overwriting of destonation file, if it's existing.");
221+
}

src/zip.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#ifndef SRC_ZIP_H_
2+
#define SRC_ZIP_H_
3+
4+
#define _GNU_SOURCE
5+
6+
#include <ctype.h>
7+
#include <getopt.h>
8+
#include <stdio.h>
9+
#include <stdlib.h>
10+
#include <unistd.h>
11+
12+
#include <sys/types.h>
13+
#include <sys/stat.h>
14+
#include <fcntl.h>
15+
#include <sys/mman.h>
16+
#include <zlib.h>
17+
18+
#include <err.h>
19+
#include <errno.h>
20+
21+
#define USAGE "Usage: zip [options] filename [archive.zip]\n"\
22+
" Run with -h for more info\n"
23+
24+
#define MB (1024U * 1024U)
25+
26+
#define MAX_CHUNK_SIZE (MB * 2U)
27+
#define MIN_CHUNK_SIZE (1U)
28+
#define DEFAULT_CHUNK_SIZE (2U << 12)
29+
30+
// config for read parameters
31+
typedef struct {
32+
int hFlag;
33+
int fFlag; // force writing to output (error if not set)
34+
int cFlag;
35+
int chunksNumber;
36+
} OptionsConfig;
37+
38+
// error types
39+
typedef enum {
40+
ERROR = -1,
41+
OK = 0
42+
} eErrorCode;
43+
44+
int zip (const char* filename, const char* archive_filename, OptionsConfig config);
45+
int scanOptions (int argc, char** argv , OptionsConfig* config);
46+
eErrorCode readFromC (char* optarg, OptionsConfig* config);
47+
48+
#endif // SRC_ZIP_H_

0 commit comments

Comments
 (0)