pcq a un moment faut bien commit

This commit is contained in:
Gu://em_ 2026-04-07 23:07:01 +02:00
parent d5a774a533
commit 6456f05c2c
15 changed files with 391 additions and 27 deletions

View file

@ -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
View 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;
}

View file

@ -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
View 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;
}

View file

@ -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

View file

@ -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

View file

@ -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
View 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

View 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;
}

View 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
View file

@ -0,0 +1,6 @@
#ifndef MISC_H
#define MISC_H
void print_str_array(const char **input_files);
#endif /* MISC_H */