pcq a un moment faut bien commit
This commit is contained in:
parent
d5a774a533
commit
6456f05c2c
15 changed files with 391 additions and 27 deletions
|
|
@ -1,13 +1,13 @@
|
||||||
#include "archive.h"
|
#include "tar/archive.h"
|
||||||
#include "config.h"
|
#include "tar/extract.h"
|
||||||
#include "extract.h"
|
#include "utils/config.h"
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
// Handle args
|
// Handle args
|
||||||
struct config config;
|
struct config config;
|
||||||
enum arg_error_code err = args_handler(argc, argv, &config);
|
enum arg_error_code arg_err = args_handler(argc, argv, &config);
|
||||||
switch (err)
|
switch (arg_err)
|
||||||
{
|
{
|
||||||
case ARG_VALID:
|
case ARG_VALID:
|
||||||
break;
|
break;
|
||||||
|
|
@ -21,14 +21,32 @@ int main(int argc, char **argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum error_code err;
|
||||||
if (config.mode == EXTRACT)
|
if (config.mode == EXTRACT)
|
||||||
{
|
{
|
||||||
extract(config.archive_file, config.verbose);
|
err = extract(config.archive_file, config.verbose);
|
||||||
}
|
}
|
||||||
else if (config.mode == ARCHIVE)
|
else if (config.mode == ARCHIVE)
|
||||||
{
|
{
|
||||||
archive(config.archive_file, config.input_files, config.verbose);
|
err = archive(config.archive_file, config.input_files, config.verbose);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (err)
|
||||||
|
{
|
||||||
|
case SUCCESS:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
case FILE_NOT_FOUND:
|
||||||
|
case UNKNOWN_ERROR:
|
||||||
|
puts("epitar: error extracting tarball <tarball>");
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
case BAD_CHECKSUM:
|
||||||
|
puts("epitar: bad checksum");
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
default:
|
||||||
|
puts("epitar: error extracting tarball <tarball>");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6
epitar/src/tar/archive.c
Normal file
6
epitar/src/tar/archive.c
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include "archive.h"
|
||||||
|
#include "tar.h"
|
||||||
|
|
||||||
|
enum error_code archive(char *archive_name, char **files, bool verbose) {
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,6 @@
|
||||||
/* @brief Archives the listed `files` into `archive_name`
|
/* @brief Archives the listed `files` into `archive_name`
|
||||||
* @return 0 on success, the corresponding error code otherwise
|
* @return 0 on success, the corresponding error code otherwise
|
||||||
*/
|
*/
|
||||||
int archive(char *archive_name, char **files, bool verbose);
|
enum error_code archive(char *archive_name, char **files, bool verbose);
|
||||||
|
|
||||||
#endif // ARCHIVE_H
|
#endif // ARCHIVE_H
|
||||||
147
epitar/src/tar/extract.c
Normal file
147
epitar/src/tar/extract.c
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
#include "extract.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../utils/filesystem.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @arg stream Pointer to the file's content
|
||||||
|
* @arg header Pointer to the file header
|
||||||
|
*/
|
||||||
|
static enum error_code extract_ustar(FILE *stream, struct ustar_header *header)
|
||||||
|
{
|
||||||
|
// Length
|
||||||
|
size_t file_length = atol(header->size);
|
||||||
|
if (file_length == 0)
|
||||||
|
return INVALID_FORMAT;
|
||||||
|
|
||||||
|
struct file_stats stats = { .name = header->name,
|
||||||
|
.length = atol(header->size),
|
||||||
|
.content_stream = stream,
|
||||||
|
.mode = header->mode,
|
||||||
|
.uid = header->uid,
|
||||||
|
.gid = header->gid,
|
||||||
|
.mtime = header->mtime };
|
||||||
|
// Type
|
||||||
|
switch (header->typeflag)
|
||||||
|
{
|
||||||
|
// Regular file
|
||||||
|
case REGTYPE:
|
||||||
|
case AREGTYPE:
|
||||||
|
return create_file(&stats);
|
||||||
|
|
||||||
|
// Link
|
||||||
|
case LNKTYPE:
|
||||||
|
return create_link(&stats, header->linkname);
|
||||||
|
|
||||||
|
// Directory
|
||||||
|
case DIRTYPE:
|
||||||
|
return create_directory(&stats);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @arg stream Pointer to the file's content
|
||||||
|
* @arg header Pointer to the file header
|
||||||
|
*/
|
||||||
|
static enum error_code extract_unixtar(FILE *stream,
|
||||||
|
struct unixtar_header *header)
|
||||||
|
{
|
||||||
|
(void)stream;
|
||||||
|
(void)header;
|
||||||
|
return NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extracts a single file from the stream
|
||||||
|
* @arg content Pointer to the file's content
|
||||||
|
* @arg header Pointer to the file header
|
||||||
|
*/
|
||||||
|
static enum error_code extract_file(FILE *content, union tar_header *header,
|
||||||
|
bool verbose)
|
||||||
|
{
|
||||||
|
struct ustar_header *ustar_header;
|
||||||
|
struct unixtar_header *unixtar_header;
|
||||||
|
|
||||||
|
// USTAR
|
||||||
|
if ((ustar_header = get_ustar_header(header)) != NULL)
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
printf("./%s", ustar_header->name);
|
||||||
|
return extract_ustar(content, ustar_header);
|
||||||
|
}
|
||||||
|
// UNIXTAR
|
||||||
|
else if ((unixtar_header = get_unixtar_header(header)) != NULL)
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
printf("./%s", unixtar_header->name);
|
||||||
|
return extract_unixtar(content, unixtar_header);
|
||||||
|
}
|
||||||
|
// Unsupported format
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return INVALID_FORMAT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum error_code extract(char *archive_name, bool verbose)
|
||||||
|
{
|
||||||
|
FILE *stream = fopen(archive_name, "r");
|
||||||
|
if (stream == NULL)
|
||||||
|
return FILE_NOT_FOUND;
|
||||||
|
|
||||||
|
// // Check size
|
||||||
|
// fseek(file, 0L, SEEK_END);
|
||||||
|
// long size = ftell(file);
|
||||||
|
// if (size < BLOCK_SIZE) {
|
||||||
|
// fclose(file);
|
||||||
|
// return INVALID_FORMAT;
|
||||||
|
// }
|
||||||
|
// // Go back
|
||||||
|
// fseek(file, 0L, SEEK_SET);
|
||||||
|
|
||||||
|
enum error_code read_state = PENDING;
|
||||||
|
bool got_empty_block = false;
|
||||||
|
|
||||||
|
while (read_state == PENDING)
|
||||||
|
{
|
||||||
|
union tar_header header;
|
||||||
|
size_t bytes_read = fread(&header, 1, sizeof(union tar_header), stream);
|
||||||
|
if (bytes_read < sizeof(union tar_header))
|
||||||
|
{
|
||||||
|
read_state = INVALID_FORMAT; // Unexpected EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if empty
|
||||||
|
char empty_block[BLOCK_SIZE] = { 0 };
|
||||||
|
if (memcmp(&header.raw_block, empty_block, BLOCK_SIZE) != 0)
|
||||||
|
{
|
||||||
|
if (got_empty_block) // Previous block empty
|
||||||
|
read_state = SUCCESS;
|
||||||
|
else
|
||||||
|
got_empty_block = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
got_empty_block = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract file
|
||||||
|
enum error_code err = extract_file(stream, &header, verbose);
|
||||||
|
if (err != SUCCESS)
|
||||||
|
{
|
||||||
|
fclose(stream);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
#ifndef EXTRACT_H
|
#ifndef EXTRACT_H
|
||||||
#define EXTRACT_H
|
#define EXTRACT_H
|
||||||
|
|
||||||
#include "tar.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "../utils/errors.h"
|
||||||
|
#include "tar.h"
|
||||||
|
|
||||||
/* @brief Extract and write files from `archive_name`
|
/* @brief Extract and write files from `archive_name`
|
||||||
* @return 0 on success, the corresponding error code otherwise
|
* @return 0 on success, the corresponding error code otherwise
|
||||||
*/
|
*/
|
||||||
int extract(char *archive_name, bool verbose);
|
enum error_code extract(char *archive_name, bool verbose);
|
||||||
|
|
||||||
#endif // EXTRACT_H
|
#endif // EXTRACT_H
|
||||||
|
|
@ -42,14 +42,6 @@
|
||||||
|
|
||||||
// === Structures
|
// === Structures
|
||||||
|
|
||||||
enum error_codes
|
|
||||||
{
|
|
||||||
SUCCESS = 0,
|
|
||||||
UNKNOWN_ERROR,
|
|
||||||
FILE_NOT_FOUND,
|
|
||||||
BAD_CHECKSUM,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ustar_header
|
struct ustar_header
|
||||||
{
|
{
|
||||||
char name[100];
|
char name[100];
|
||||||
|
|
@ -88,7 +80,7 @@ union tar_header
|
||||||
{
|
{
|
||||||
struct unixtar_header unixtar;
|
struct unixtar_header unixtar;
|
||||||
struct ustar_header ustar;
|
struct ustar_header ustar;
|
||||||
char raw_block[512];
|
char raw_block[BLOCK_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
// === Functions
|
// === Functions
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
#ifndef UTILS_H
|
|
||||||
#define UTILS_H
|
|
||||||
|
|
||||||
void print_str_array(const char **input_files);
|
|
||||||
|
|
||||||
#endif /* UTILS_H */
|
|
||||||
17
epitar/src/utils/errors.h
Normal file
17
epitar/src/utils/errors.h
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef ERRORS_H
|
||||||
|
#define ERRORS_H
|
||||||
|
|
||||||
|
enum error_code
|
||||||
|
{
|
||||||
|
SUCCESS = 0,
|
||||||
|
PENDING,
|
||||||
|
UNKNOWN_ERROR,
|
||||||
|
FILE_NOT_FOUND,
|
||||||
|
BAD_CHECKSUM,
|
||||||
|
INVALID_FORMAT,
|
||||||
|
CANNOT_CREATE_TARGET,
|
||||||
|
TARGET_ALREADY_EXISTS,
|
||||||
|
NOT_IMPLEMENTED
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ERRORS_H
|
||||||
155
epitar/src/utils/filesystem.c
Normal file
155
epitar/src/utils/filesystem.c
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <utime.h>
|
||||||
|
|
||||||
|
enum error_code create_directory(struct file_stats *stats)
|
||||||
|
{
|
||||||
|
if (stats == NULL || stats->name == NULL || stats->mode == NULL || stats->uid == NULL || stats->gid == NULL || stats->mtime == NULL)
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
// Create dir
|
||||||
|
struct stat st = { 0 };
|
||||||
|
if (stat(stats->name, &st) == -1)
|
||||||
|
{
|
||||||
|
mode_t mode_val = strtol(stats->mode, NULL, 8);
|
||||||
|
int err = mkdir(stats->name, mode_val);
|
||||||
|
if (err != 0)
|
||||||
|
return CANNOT_CREATE_TARGET;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return TARGET_ALREADY_EXISTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set stats
|
||||||
|
enum error_code errc;
|
||||||
|
errc = setowner(stats->name, stats->uid, stats->gid);
|
||||||
|
if (errc != SUCCESS)
|
||||||
|
return errc;
|
||||||
|
errc = setmtime(stats->name, stats->mtime);
|
||||||
|
if (errc != SUCCESS)
|
||||||
|
return errc;
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum error_code create_link(struct file_stats *stats, char *target)
|
||||||
|
{
|
||||||
|
if (stats == NULL)
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
|
||||||
|
// Create link
|
||||||
|
int err = symlink(target, stats->name);
|
||||||
|
if (err != 0)
|
||||||
|
return CANNOT_CREATE_TARGET;
|
||||||
|
|
||||||
|
// Set stats
|
||||||
|
enum error_code errc;
|
||||||
|
errc = setowner(stats->name, stats->uid, stats->gid);
|
||||||
|
if (errc != SUCCESS)
|
||||||
|
return errc;
|
||||||
|
errc = setmtime(stats->name, stats->mtime);
|
||||||
|
if (errc != SUCCESS)
|
||||||
|
return errc;
|
||||||
|
// May change the target mode instead of the limk itsel
|
||||||
|
// also lchmod is not available on all systems so just ignoring this property here
|
||||||
|
// errc = setmode(stats->name, stats->mode);
|
||||||
|
// if (errc != SUCCESS)
|
||||||
|
// return errc;
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum error_code create_file(struct file_stats *stats)
|
||||||
|
{
|
||||||
|
// Turbo moche mais allez vous plaindre au clang format
|
||||||
|
// Check fields
|
||||||
|
if (stats == NULL || stats->name == NULL || stats->length == 0
|
||||||
|
|| stats->content_stream == NULL || stats->mode == NULL
|
||||||
|
|| stats->uid == NULL || stats->gid == NULL || stats->mtime == NULL)
|
||||||
|
{
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and open file
|
||||||
|
FILE *file_stream = fopen(stats->name, "w+");
|
||||||
|
if (file_stream == NULL)
|
||||||
|
return CANNOT_CREATE_TARGET;
|
||||||
|
// Copy content
|
||||||
|
char buffer[4096];
|
||||||
|
size_t remaining = stats->length;
|
||||||
|
while (remaining > 0) {
|
||||||
|
size_t to_read = (remaining < sizeof(buffer)) ? remaining : sizeof(buffer);
|
||||||
|
size_t bytes_read = fread(buffer, 1, to_read, stats->content_stream);
|
||||||
|
if (bytes_read == 0)
|
||||||
|
break; // Reached EOF
|
||||||
|
fwrite(buffer, 1, bytes_read, file_stream);
|
||||||
|
remaining -= bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close file
|
||||||
|
fclose(file_stream);
|
||||||
|
|
||||||
|
// Set stats
|
||||||
|
enum error_code errc;
|
||||||
|
errc = setowner(stats->name, stats->uid, stats->gid);
|
||||||
|
if (errc != SUCCESS)
|
||||||
|
return errc;
|
||||||
|
errc = setmtime(stats->name, stats->mtime);
|
||||||
|
if (errc != SUCCESS)
|
||||||
|
return errc;
|
||||||
|
errc = setmode(stats->name, stats->mode);
|
||||||
|
if (errc != SUCCESS)
|
||||||
|
return errc;
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum error_code setowner(char *path, char *uid, char *gid)
|
||||||
|
{
|
||||||
|
if (path == NULL || uid == NULL || gid == NULL)
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
|
||||||
|
uid_t uid_val = (uid_t)strtol(uid, NULL, 10);
|
||||||
|
gid_t gid_val = (gid_t)strtol(gid, NULL, 10);
|
||||||
|
|
||||||
|
int err = chown(path, uid_val, gid_val);
|
||||||
|
return err == 0 ? SUCCESS : CANNOT_CREATE_TARGET;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum error_code setmtime(char *path, char *mtime)
|
||||||
|
{
|
||||||
|
if (path == NULL || mtime == NULL)
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
|
||||||
|
// Parse octal mtime string from tar header
|
||||||
|
time_t time_val = strtol(mtime, NULL, 8);
|
||||||
|
if (time_val == 0 && mtime[0] != '0')
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
|
||||||
|
struct utimbuf utb;
|
||||||
|
utb.actime = time_val;
|
||||||
|
utb.modtime = time_val;
|
||||||
|
|
||||||
|
int err = utime(path, &utb);
|
||||||
|
return err == 0 ? SUCCESS : CANNOT_CREATE_TARGET;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum error_code setmode(char *path, char *mode)
|
||||||
|
{
|
||||||
|
if (path == NULL || mode == NULL)
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
|
||||||
|
// Parse octal mode string from tar header
|
||||||
|
mode_t mode_val = strtol(mode, NULL, 8);
|
||||||
|
if (mode_val == 0 && mode[0] != '0')
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
|
||||||
|
int err = chmod(path, mode_val);
|
||||||
|
return err == 0 ? SUCCESS : CANNOT_CREATE_TARGET;
|
||||||
|
}
|
||||||
28
epitar/src/utils/filesystem.h
Normal file
28
epitar/src/utils/filesystem.h
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef FILESYSTEM_H
|
||||||
|
#define FILESYSTEM_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
|
struct file_stats
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
size_t length;
|
||||||
|
FILE *content_stream;
|
||||||
|
char *mode;
|
||||||
|
char *uid;
|
||||||
|
char *gid;
|
||||||
|
char *mtime;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum error_code create_directory(struct file_stats *stats);
|
||||||
|
enum error_code create_link(struct file_stats *stats, char *target);
|
||||||
|
enum error_code create_file(struct file_stats *stats);
|
||||||
|
|
||||||
|
enum error_code setowner(char *path, char *uid, char *gid);
|
||||||
|
enum error_code setmtime(char *path, char *mtime);
|
||||||
|
enum error_code setmode(char *path, char *mode);
|
||||||
|
|
||||||
|
#endif // FILESYSTEM_H
|
||||||
6
epitar/src/utils/misc.h
Normal file
6
epitar/src/utils/misc.h
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef MISC_H
|
||||||
|
#define MISC_H
|
||||||
|
|
||||||
|
void print_str_array(const char **input_files);
|
||||||
|
|
||||||
|
#endif /* MISC_H */
|
||||||
Loading…
Add table
Add a link
Reference in a new issue