Compare commits
No commits in common. "master" and "epitar-dementor-1" have entirely different histories.
master
...
epitar-dem
18 changed files with 107 additions and 1029 deletions
|
|
@ -1,79 +0,0 @@
|
||||||
AccessModifierOffset: -4
|
|
||||||
AlignAfterOpenBracket: Align
|
|
||||||
AlignConsecutiveAssignments: false
|
|
||||||
AlignConsecutiveDeclarations: false
|
|
||||||
AlignEscapedNewlines: Right
|
|
||||||
AlignOperands: false
|
|
||||||
AlignTrailingComments: false
|
|
||||||
AllowAllParametersOfDeclarationOnNextLine: false
|
|
||||||
AllowShortBlocksOnASingleLine: false
|
|
||||||
AllowShortCaseLabelsOnASingleLine: false
|
|
||||||
AllowShortFunctionsOnASingleLine: None
|
|
||||||
AllowShortIfStatementsOnASingleLine: false
|
|
||||||
AlwaysBreakAfterReturnType: None
|
|
||||||
AlwaysBreakBeforeMultilineStrings: false
|
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
|
||||||
BinPackArguments: true
|
|
||||||
BinPackParameters: true
|
|
||||||
BreakBeforeBraces: Custom
|
|
||||||
BraceWrapping:
|
|
||||||
AfterEnum: true
|
|
||||||
AfterClass: true
|
|
||||||
AfterControlStatement: true
|
|
||||||
AfterFunction: true
|
|
||||||
AfterNamespace: true
|
|
||||||
AfterStruct: true
|
|
||||||
AfterUnion: true
|
|
||||||
AfterExternBlock: true
|
|
||||||
BeforeCatch: true
|
|
||||||
BeforeElse: true
|
|
||||||
IndentBraces: false
|
|
||||||
SplitEmptyFunction: false
|
|
||||||
SplitEmptyRecord: false
|
|
||||||
SplitEmptyNamespace: false
|
|
||||||
BreakBeforeBinaryOperators: NonAssignment
|
|
||||||
BreakBeforeTernaryOperators: true
|
|
||||||
BreakConstructorInitializers: BeforeComma
|
|
||||||
BreakInheritanceList: BeforeComma
|
|
||||||
BreakStringLiterals: true
|
|
||||||
ColumnLimit: 80
|
|
||||||
CompactNamespaces: false
|
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
|
||||||
ConstructorInitializerIndentWidth: 4
|
|
||||||
Cpp11BracedListStyle: false
|
|
||||||
DerivePointerAlignment: false
|
|
||||||
FixNamespaceComments: true
|
|
||||||
ForEachMacros: ['ILIST_FOREACH', 'ILIST_FOREACH_ENTRY']
|
|
||||||
IncludeBlocks: Regroup
|
|
||||||
IncludeCategories:
|
|
||||||
- Regex: '<.*>'
|
|
||||||
Priority: 1
|
|
||||||
- Regex: '.*'
|
|
||||||
Priority: 2
|
|
||||||
IndentCaseLabels: false
|
|
||||||
IndentPPDirectives: AfterHash
|
|
||||||
IndentWidth: 4
|
|
||||||
IndentWrappedFunctionNames: false
|
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
|
||||||
Language: Cpp
|
|
||||||
NamespaceIndentation: All
|
|
||||||
PointerAlignment: Right
|
|
||||||
ReflowComments: true
|
|
||||||
SortIncludes: true
|
|
||||||
SortUsingDeclarations: false
|
|
||||||
SpaceAfterCStyleCast: false
|
|
||||||
SpaceAfterTemplateKeyword: true
|
|
||||||
SpaceBeforeAssignmentOperators: true
|
|
||||||
SpaceBeforeCpp11BracedList: false
|
|
||||||
SpaceBeforeCtorInitializerColon: true
|
|
||||||
SpaceBeforeParens: ControlStatements
|
|
||||||
SpaceBeforeRangeBasedForLoopColon: true
|
|
||||||
SpaceInEmptyParentheses: false
|
|
||||||
SpacesBeforeTrailingComments: 1
|
|
||||||
SpacesInAngles: false
|
|
||||||
SpacesInCStyleCastParentheses: false
|
|
||||||
SpacesInContainerLiterals: false
|
|
||||||
SpacesInParentheses: false
|
|
||||||
SpacesInSquareBrackets: false
|
|
||||||
TabWidth: 4
|
|
||||||
UseTab: Never
|
|
||||||
|
|
@ -21,18 +21,13 @@ $(TARGET): $(OBJS)
|
||||||
$(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS)
|
$(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS)
|
||||||
@echo $(OBJS)
|
@echo $(OBJS)
|
||||||
|
|
||||||
all: $(OBJS)
|
|
||||||
$(CC) -o $(TARGET) $^ $(LDFLAGS) $(LDLIBS)
|
|
||||||
|
|
||||||
debug: CFLAGS += $(DBG_CFLAGS)
|
debug: CFLAGS += $(DBG_CFLAGS)
|
||||||
debug: LDFLAGS += $(DBG_LDFLAGS)
|
debug: LDFLAGS += $(DBG_LDFLAGS)
|
||||||
debug: $(OBJS)
|
debug: $(OBJS)
|
||||||
$(CC) -o $(TARGET) $^ $(LDFLAGS) $(LDLIBS)
|
$(CC) -o $(TARGET) $^ $(LDFLAGS) $(LDLIBS)
|
||||||
|
|
||||||
check: $(TARGET)
|
check:
|
||||||
bash ./tests/functional/old_testsuite.sh
|
dash ./tests/run.sh
|
||||||
bash ./tests/functional/run.sh
|
|
||||||
bash ./tests/functional/warning.sh
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) $(TARGET)
|
$(RM) $(TARGET)
|
||||||
|
|
|
||||||
|
|
@ -1,58 +1,6 @@
|
||||||
#include "tar/archive.h"
|
#include "tar/archive.h"
|
||||||
#include "tar/extract.h"
|
#include "tar/extract.h"
|
||||||
#include "utils/config.h"
|
#include "utils/config.h"
|
||||||
#include "utils/errors.h"
|
|
||||||
|
|
||||||
static int handle_err_code(enum error_code err, struct config *config)
|
|
||||||
{
|
|
||||||
int ret_code = 0;
|
|
||||||
switch (err)
|
|
||||||
{
|
|
||||||
case SUCCESS:
|
|
||||||
ret_code = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BAD_CHECKSUM:
|
|
||||||
puts("epitar: bad checksum");
|
|
||||||
ret_code = 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NOT_IMPLEMENTED:
|
|
||||||
puts("epitar: Function not implemented");
|
|
||||||
ret_code = 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FILE_NOT_FOUND:
|
|
||||||
if (config->mode == ARCHIVE)
|
|
||||||
{
|
|
||||||
// Rest of code should have printed the first part of the line
|
|
||||||
printf(" to tarball %s\n", config->archive_file);
|
|
||||||
ret_code = 3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
__attribute__((fallthrough));
|
|
||||||
|
|
||||||
case EMPTY_ARCHIVE:
|
|
||||||
if (config->mode == ARCHIVE)
|
|
||||||
{
|
|
||||||
puts("epitar: cowardly refusing to create an empty archive");
|
|
||||||
ret_code = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("epitar: error extracting tarball %s\n",
|
|
||||||
config->archive_file);
|
|
||||||
ret_code = 3;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
printf("epitar: error extracting tarball %s\n", config->archive_file);
|
|
||||||
ret_code = 3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return ret_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
|
@ -83,7 +31,22 @@ int main(int argc, char **argv)
|
||||||
err = archive(config.archive_file, config.input_files, config.verbose);
|
err = archive(config.archive_file, config.input_files, config.verbose);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ret_code = handle_err_code(err, &config);
|
switch (err)
|
||||||
destroy_config(&config);
|
{
|
||||||
return ret_code;
|
case SUCCESS:
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// case FILE_NOT_FOUND:
|
||||||
|
// case UNKNOWN_ERROR:
|
||||||
|
// printf("epitar: error extracting tarball %s\n", config.archive_file);
|
||||||
|
// return 3;
|
||||||
|
|
||||||
|
case BAD_CHECKSUM:
|
||||||
|
puts("epitar: bad checksum");
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf("epitar: error extracting tarball %s\n", config.archive_file);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,235 +1,8 @@
|
||||||
#include "archive.h"
|
#include "archive.h"
|
||||||
|
|
||||||
#include <grp.h>
|
enum error_code archive(char *archive_name, char **files, bool verbose) {
|
||||||
#include <pwd.h>
|
(void) archive_name;
|
||||||
#include <stddef.h>
|
(void) files;
|
||||||
#include <stdio.h>
|
(void) verbose;
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "../utils/filesystem.h"
|
|
||||||
#include "tar.h"
|
|
||||||
|
|
||||||
static size_t getfilesize(FILE *file)
|
|
||||||
{
|
|
||||||
fseek(file, 0L, SEEK_END);
|
|
||||||
size_t file_size = ftell(file);
|
|
||||||
fseek(file, 0L, SEEK_SET); // Go back
|
|
||||||
return file_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Creates and returns the header for the file located at `file_path` and
|
|
||||||
* with content `file_stream`
|
|
||||||
* @param file_stream Pointer to the content of the file (as a stream) if it's a
|
|
||||||
* file. If it's NULL file will be considered as a directory
|
|
||||||
*/
|
|
||||||
static struct ustar_header create_header(FILE *file_stream, char *file_path)
|
|
||||||
{
|
|
||||||
struct ustar_header header = { 0 };
|
|
||||||
|
|
||||||
// Name
|
|
||||||
strncpy(header.name, file_path, TNAMELEN);
|
|
||||||
// Magic tag
|
|
||||||
strncpy(header.magic, USTAR_MAGIC, TMAGLEN);
|
|
||||||
// Versions
|
|
||||||
memcpy(header.version, TVERSION, TVERSLEN);
|
|
||||||
|
|
||||||
// uid, gid, mtime and mode
|
|
||||||
struct stat st;
|
|
||||||
if (stat(file_path, &st) == -1)
|
|
||||||
{
|
|
||||||
printf("Could not stat file %s\n", file_path);
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
// TODO links
|
|
||||||
// UID
|
|
||||||
snprintf(header.uid, sizeof(header.uid), "%07o", st.st_uid);
|
|
||||||
// GID
|
|
||||||
snprintf(header.gid, sizeof(header.gid), "%07o", st.st_gid);
|
|
||||||
// Mtime
|
|
||||||
snprintf(header.mtime, sizeof(header.mtime), "%011lo", st.st_mtime);
|
|
||||||
// Mode
|
|
||||||
snprintf(header.mode, sizeof(header.mode), "%07o", st.st_mode & 0777);
|
|
||||||
|
|
||||||
// User name
|
|
||||||
struct passwd *pw = getpwuid(st.st_uid);
|
|
||||||
if (pw != NULL)
|
|
||||||
strncpy(header.uname, pw->pw_name, 32);
|
|
||||||
// Group name
|
|
||||||
struct group *gr = getgrgid(st.st_gid);
|
|
||||||
if (gr != NULL)
|
|
||||||
strncpy(header.gname, gr->gr_name, 32);
|
|
||||||
|
|
||||||
// File or directory
|
|
||||||
if (file_stream == NULL)
|
|
||||||
{
|
|
||||||
// === Directory
|
|
||||||
header.name[strlen(header.name)] = '/';
|
|
||||||
// Type
|
|
||||||
header.typeflag = DIRTYPE;
|
|
||||||
// Size
|
|
||||||
snprintf(header.size, 12, "%011o", 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// === File
|
|
||||||
// Size
|
|
||||||
snprintf(header.size, 12, "%011lo", st.st_size);
|
|
||||||
// Type
|
|
||||||
header.typeflag = REGTYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checksum
|
|
||||||
union tar_header header_union = { .ustar = header };
|
|
||||||
compute_checksum(&header_union);
|
|
||||||
return header_union.ustar;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Adds a file (header + content) to the archive stream
|
|
||||||
* @param archive The target archive stream
|
|
||||||
* @param file_path The path where the file to be archived is located
|
|
||||||
* @return SUCCESS or the corresponding error code
|
|
||||||
*/
|
|
||||||
static enum error_code archivefile(FILE *archive, char *file_path, bool verbose)
|
|
||||||
{
|
|
||||||
if (verbose)
|
|
||||||
puts(file_path);
|
|
||||||
|
|
||||||
FILE *file = fopen(file_path, "r");
|
|
||||||
if (file == NULL)
|
|
||||||
{
|
|
||||||
printf("unable to add file %s", file_path);
|
|
||||||
return FILE_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create header (ustar)
|
|
||||||
struct ustar_header header = create_header(file, file_path);
|
|
||||||
size_t file_size = getfilesize(file);
|
|
||||||
|
|
||||||
// Write header
|
|
||||||
size_t nwritten = fwrite(&header, sizeof(struct ustar_header), 1, archive);
|
|
||||||
if (nwritten < 1)
|
|
||||||
{
|
|
||||||
fclose(file);
|
|
||||||
return CANNOT_CREATE_TARGET;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy file content
|
|
||||||
fcat(file, archive, file_size);
|
|
||||||
|
|
||||||
// Add padding
|
|
||||||
size_t total_size =
|
|
||||||
(((file_size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE);
|
|
||||||
size_t padding = total_size - file_size;
|
|
||||||
|
|
||||||
char empty_buf[BLOCK_SIZE] = { 0 };
|
|
||||||
size_t remaining = padding;
|
|
||||||
nwritten = fwrite(empty_buf, sizeof(char), remaining, archive);
|
|
||||||
if (nwritten < remaining)
|
|
||||||
{
|
|
||||||
fclose(file);
|
|
||||||
return CANNOT_CREATE_TARGET;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Archives a hole directory (recursively)
|
|
||||||
* @warn Assumes that the director path is valid
|
|
||||||
* @return SUCCESS or the corresponding error
|
|
||||||
*/
|
|
||||||
static enum error_code archivedir(FILE *archive, char *dir_path, bool verbose)
|
|
||||||
{
|
|
||||||
// Add the directory itself
|
|
||||||
if (verbose)
|
|
||||||
printf("%s/\n", dir_path);
|
|
||||||
|
|
||||||
// Header
|
|
||||||
struct ustar_header header = create_header(NULL, dir_path);
|
|
||||||
size_t nwritten = fwrite(&header, sizeof(struct ustar_header), 1, archive);
|
|
||||||
if (nwritten < 1)
|
|
||||||
return CANNOT_CREATE_TARGET;
|
|
||||||
|
|
||||||
// Archive members
|
|
||||||
char *filename;
|
|
||||||
while ((filename = listdirectory(dir_path)) != NULL)
|
|
||||||
{
|
|
||||||
enum error_code err;
|
|
||||||
enum error_code isdir = isdirectory(filename);
|
|
||||||
if (isdir == FILE_NOT_FOUND)
|
|
||||||
{
|
|
||||||
// Does not exists
|
|
||||||
printf("unable to add file %s", dir_path);
|
|
||||||
err = FILE_NOT_FOUND;
|
|
||||||
}
|
|
||||||
else if (isdir == SUCCESS)
|
|
||||||
{
|
|
||||||
// Directory
|
|
||||||
err = archivedir(archive, filename, verbose);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// File
|
|
||||||
err = archivefile(archive, filename, verbose);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err != SUCCESS)
|
|
||||||
{
|
|
||||||
freedirectory();
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
freedirectory(); // Just in case
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum error_code archive(char *archive_name, char **files, bool verbose)
|
|
||||||
{
|
|
||||||
if (archive_name == NULL || files == NULL)
|
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
if (*files == NULL)
|
|
||||||
return EMPTY_ARCHIVE;
|
|
||||||
|
|
||||||
FILE *archive_stream = fopen(archive_name, "w+");
|
|
||||||
|
|
||||||
char **current_file = files;
|
|
||||||
while (*current_file != NULL)
|
|
||||||
{
|
|
||||||
enum error_code err;
|
|
||||||
enum error_code isdir = isdirectory(*current_file);
|
|
||||||
if (isdir == FILE_NOT_FOUND)
|
|
||||||
{
|
|
||||||
// Does not exists
|
|
||||||
printf("unable to add file %s", *current_file);
|
|
||||||
err = FILE_NOT_FOUND;
|
|
||||||
}
|
|
||||||
else if (isdir == SUCCESS)
|
|
||||||
{
|
|
||||||
// Directory
|
|
||||||
err = archivedir(archive_stream, *current_file, verbose);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// File
|
|
||||||
err = archivefile(archive_stream, *current_file, verbose);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err != SUCCESS)
|
|
||||||
{
|
|
||||||
fclose(archive_stream);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
current_file++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Terminating NULL blocks
|
|
||||||
char empty_buf[BLOCK_SIZE * 2] = { 0 };
|
|
||||||
fwrite(empty_buf, sizeof(char), BLOCK_SIZE * 2, archive_stream);
|
|
||||||
fclose(archive_stream);
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
#ifndef ARCHIVE_H
|
#ifndef ARCHIVE_H
|
||||||
#define ARCHIVE_H
|
#define ARCHIVE_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#include "../utils/errors.h"
|
#include "../utils/errors.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
/* @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
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "../utils/filesystem.h"
|
#include "../utils/filesystem.h"
|
||||||
#include "tar.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -14,6 +13,7 @@
|
||||||
*/
|
*/
|
||||||
static enum error_code extract_ustar(FILE *stream, struct ustar_header *header)
|
static enum error_code extract_ustar(FILE *stream, struct ustar_header *header)
|
||||||
{
|
{
|
||||||
|
|
||||||
struct file_stats stats = { .name = header->name,
|
struct file_stats stats = { .name = header->name,
|
||||||
.length = strtol(header->size, NULL, 8),
|
.length = strtol(header->size, NULL, 8),
|
||||||
.content_stream = stream,
|
.content_stream = stream,
|
||||||
|
|
@ -50,14 +50,9 @@ static enum error_code extract_ustar(FILE *stream, struct ustar_header *header)
|
||||||
static enum error_code extract_unixtar(FILE *stream,
|
static enum error_code extract_unixtar(FILE *stream,
|
||||||
struct unixtar_header *header)
|
struct unixtar_header *header)
|
||||||
{
|
{
|
||||||
struct file_stats stats = { .name = header->name,
|
(void)stream;
|
||||||
.length = strtol(header->size, NULL, 8),
|
(void)header;
|
||||||
.content_stream = stream,
|
return NOT_IMPLEMENTED;
|
||||||
.mode = header->mode,
|
|
||||||
.uid = header->uid,
|
|
||||||
.gid = header->gid,
|
|
||||||
.mtime = header->mtime };
|
|
||||||
return create_file(&stats);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -75,14 +70,14 @@ static enum error_code extract_file(FILE *content, union tar_header *header,
|
||||||
if ((ustar_header = get_ustar_header(header)) != NULL)
|
if ((ustar_header = get_ustar_header(header)) != NULL)
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("%s\n", ustar_header->name);
|
printf("./%s\n", ustar_header->name);
|
||||||
return extract_ustar(content, ustar_header);
|
return extract_ustar(content, ustar_header);
|
||||||
}
|
}
|
||||||
// UNIXTAR
|
// UNIXTAR
|
||||||
else if ((unixtar_header = get_unixtar_header(header)) != NULL)
|
else if ((unixtar_header = get_unixtar_header(header)) != NULL)
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("%s\n", unixtar_header->name);
|
printf("./%s", unixtar_header->name);
|
||||||
return extract_unixtar(content, unixtar_header);
|
return extract_unixtar(content, unixtar_header);
|
||||||
}
|
}
|
||||||
// Unsupported format
|
// Unsupported format
|
||||||
|
|
@ -118,7 +113,6 @@ enum error_code extract(char *archive_name, bool verbose)
|
||||||
if (bytes_read < sizeof(union tar_header))
|
if (bytes_read < sizeof(union tar_header))
|
||||||
{
|
{
|
||||||
read_state = INVALID_FORMAT; // Unexpected EOF
|
read_state = INVALID_FORMAT; // Unexpected EOF
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if empty
|
// Check if empty
|
||||||
|
|
@ -136,12 +130,6 @@ enum error_code extract(char *archive_name, bool verbose)
|
||||||
got_empty_block = false;
|
got_empty_block = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!check_checksum(&header))
|
|
||||||
{
|
|
||||||
fclose(stream);
|
|
||||||
return BAD_CHECKSUM;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract file
|
// Extract file
|
||||||
enum error_code err = extract_file(stream, &header, verbose);
|
enum error_code err = extract_file(stream, &header, verbose);
|
||||||
if (err != SUCCESS)
|
if (err != SUCCESS)
|
||||||
|
|
@ -152,8 +140,7 @@ enum error_code extract(char *archive_name, bool verbose)
|
||||||
|
|
||||||
// Calculate how many padding bytes need to be skipped
|
// Calculate how many padding bytes need to be skipped
|
||||||
size_t file_size = strtol(header.ustar.size, NULL, 8);
|
size_t file_size = strtol(header.ustar.size, NULL, 8);
|
||||||
size_t padded_size =
|
size_t padded_size = ((file_size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE;
|
||||||
((file_size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE;
|
|
||||||
size_t skip_bytes = padded_size - file_size;
|
size_t skip_bytes = padded_size - file_size;
|
||||||
|
|
||||||
if (skip_bytes > 0 && fseek(stream, skip_bytes, SEEK_CUR) != 0)
|
if (skip_bytes > 0 && fseek(stream, skip_bytes, SEEK_CUR) != 0)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include "tar.h"
|
#include "tar.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
struct unixtar_header *get_unixtar_header(union tar_header *header)
|
struct unixtar_header *get_unixtar_header(union tar_header *header)
|
||||||
|
|
@ -26,58 +26,34 @@ bool check_checksum(union tar_header *header)
|
||||||
{
|
{
|
||||||
struct ustar_header *ustar_header;
|
struct ustar_header *ustar_header;
|
||||||
struct unixtar_header *unixtar_header;
|
struct unixtar_header *unixtar_header;
|
||||||
char actual_checksum[TCHKSUMLEN];
|
int actual_checksum;
|
||||||
|
|
||||||
// USTAR
|
// USTAR
|
||||||
if ((ustar_header = get_ustar_header(header)) != NULL)
|
if ((ustar_header = get_ustar_header(header)) != NULL)
|
||||||
{
|
{
|
||||||
strcpy(actual_checksum, ustar_header->chksum);
|
actual_checksum = atoi(ustar_header->chksum);
|
||||||
return strcmp(actual_checksum, header->ustar.chksum) == 0;
|
|
||||||
}
|
}
|
||||||
// UNIXTAR
|
// UNIXTAR
|
||||||
else if ((unixtar_header = get_unixtar_header(header)) != NULL)
|
else if ((unixtar_header = get_unixtar_header(header)) != NULL)
|
||||||
{
|
{
|
||||||
strcpy(actual_checksum, unixtar_header->chksum);
|
actual_checksum = atoi(unixtar_header->chksum);
|
||||||
return strcmp(actual_checksum, header->unixtar.chksum) == 0;
|
|
||||||
}
|
}
|
||||||
// Unsupported format
|
// Unsupported format
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int computed_checksum = compute_checksum(header);
|
||||||
|
return computed_checksum == actual_checksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
void compute_checksum(union tar_header *header)
|
int compute_checksum(union tar_header *header)
|
||||||
{
|
{
|
||||||
// Set to spaces
|
char *block = header->raw_block;
|
||||||
struct unixtar_header *unixtar_header = get_unixtar_header(header);
|
int sum = 0;
|
||||||
struct ustar_header *ustar_header = get_ustar_header(header);
|
|
||||||
if (ustar_header)
|
|
||||||
{
|
|
||||||
memset(ustar_header->chksum, ' ', TCHKSUMLEN);
|
|
||||||
}
|
|
||||||
else if (unixtar_header)
|
|
||||||
{
|
|
||||||
memset(unixtar_header->chksum, ' ', TCHKSUMLEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute
|
|
||||||
unsigned char *block = header->raw_block;
|
|
||||||
unsigned int sum = 0;
|
|
||||||
for (int i = 0; i < BLOCK_SIZE; i++)
|
for (int i = 0; i < BLOCK_SIZE; i++)
|
||||||
sum += block[i];
|
sum += block[i];
|
||||||
|
|
||||||
// Update checksum
|
return sum;
|
||||||
if (ustar_header)
|
|
||||||
{
|
|
||||||
snprintf(ustar_header->chksum, TCHKSUMLEN - 1, "%06o", sum);
|
|
||||||
ustar_header->chksum[TCHKSUMLEN - 2] = '\0';
|
|
||||||
ustar_header->chksum[TCHKSUMLEN - 1] = ' ';
|
|
||||||
}
|
|
||||||
else if (unixtar_header)
|
|
||||||
{
|
|
||||||
snprintf(unixtar_header->chksum, TCHKSUMLEN - 1, "%06o", sum);
|
|
||||||
unixtar_header->chksum[TCHKSUMLEN - 2] = '\0';
|
|
||||||
unixtar_header->chksum[TCHKSUMLEN - 1] = ' ';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,6 @@
|
||||||
#define TOWRITE 00002 /* write by other */
|
#define TOWRITE 00002 /* write by other */
|
||||||
#define TOEXEC 00001 /* execute/search by other */
|
#define TOEXEC 00001 /* execute/search by other */
|
||||||
|
|
||||||
// Non standard definitions
|
|
||||||
#define TNAMELEN 100 /* Length of the name field */
|
|
||||||
#define TSIZELEN 12 /* Length of the size field */
|
|
||||||
#define TCHKSUMLEN 8 /* Length of the size field */
|
|
||||||
|
|
||||||
// === Structures
|
// === Structures
|
||||||
|
|
||||||
struct ustar_header
|
struct ustar_header
|
||||||
|
|
@ -65,7 +60,6 @@ struct ustar_header
|
||||||
char devmajor[8];
|
char devmajor[8];
|
||||||
char devminor[8];
|
char devminor[8];
|
||||||
char prefix[155];
|
char prefix[155];
|
||||||
char padding[12];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct unixtar_header
|
struct unixtar_header
|
||||||
|
|
@ -77,7 +71,7 @@ struct unixtar_header
|
||||||
char size[12];
|
char size[12];
|
||||||
char mtime[12];
|
char mtime[12];
|
||||||
char chksum[8];
|
char chksum[8];
|
||||||
char link;
|
char link[1];
|
||||||
char linkname[100];
|
char linkname[100];
|
||||||
char padding[255]; // Should be empty
|
char padding[255]; // Should be empty
|
||||||
};
|
};
|
||||||
|
|
@ -86,7 +80,7 @@ union tar_header
|
||||||
{
|
{
|
||||||
struct unixtar_header unixtar;
|
struct unixtar_header unixtar;
|
||||||
struct ustar_header ustar;
|
struct ustar_header ustar;
|
||||||
unsigned char raw_block[BLOCK_SIZE];
|
char raw_block[BLOCK_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
// === Functions
|
// === Functions
|
||||||
|
|
@ -103,14 +97,13 @@ struct unixtar_header *get_unixtar_header(union tar_header *header);
|
||||||
struct ustar_header *get_ustar_header(union tar_header *header);
|
struct ustar_header *get_ustar_header(union tar_header *header);
|
||||||
|
|
||||||
/* @brief checks if file has not been corrupted via checksum
|
/* @brief checks if file has not been corrupted via checksum
|
||||||
* @warn Alters header checksum with the new one
|
|
||||||
* @return true if member file is valid, false otherwise
|
* @return true if member file is valid, false otherwise
|
||||||
*/
|
*/
|
||||||
bool check_checksum(union tar_header *header);
|
bool check_checksum(union tar_header *header);
|
||||||
|
|
||||||
/* @brief computes the checksum of the given header and updates it
|
/* @brief computes the checksum of the given file
|
||||||
* @return the checksum
|
* @return the checksum
|
||||||
*/
|
*/
|
||||||
void compute_checksum(union tar_header *header);
|
int compute_checksum(union tar_header *header);
|
||||||
|
|
||||||
#endif // TAR_H
|
#endif // TAR_H
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ enum arg_error_code args_handler(int argc, char **argv, struct config *config)
|
||||||
|
|
||||||
// Parse option flags
|
// Parse option flags
|
||||||
int opt;
|
int opt;
|
||||||
while ((opt = getopt(argc, argv, ":cvxh")) != -1)
|
while ((opt = getopt(argc, argv, "cxvh")) != -1)
|
||||||
{
|
{
|
||||||
int err = handle_opt(opt, config);
|
int err = handle_opt(opt, config);
|
||||||
if (err == ARG_HELP)
|
if (err == ARG_HELP)
|
||||||
|
|
@ -190,7 +190,7 @@ void print_invalid_option(void)
|
||||||
puts("Try './epitar -h' for more information");
|
puts("Try './epitar -h' for more information");
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy_config(struct config *config)
|
void config_destroy(struct config *config)
|
||||||
{
|
{
|
||||||
if (config == NULL)
|
if (config == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
@ -203,5 +203,5 @@ void destroy_config(struct config *config)
|
||||||
|
|
||||||
// Note: archive_file points to argv, so we don't free it
|
// Note: archive_file points to argv, so we don't free it
|
||||||
|
|
||||||
// free(config);
|
free(config);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ void print_invalid_option(void);
|
||||||
*/
|
*/
|
||||||
bool validate_config(struct config *cfg);
|
bool validate_config(struct config *cfg);
|
||||||
|
|
||||||
/* @brief frees all data allocated by config
|
/*
|
||||||
*/
|
*/
|
||||||
void destroy_config(struct config *config);
|
void destroy_config(struct config *config);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,11 @@ enum error_code
|
||||||
SUCCESS = 0,
|
SUCCESS = 0,
|
||||||
PENDING,
|
PENDING,
|
||||||
UNKNOWN_ERROR,
|
UNKNOWN_ERROR,
|
||||||
GENERIC_ERROR,
|
|
||||||
FILE_NOT_FOUND,
|
FILE_NOT_FOUND,
|
||||||
BAD_CHECKSUM,
|
BAD_CHECKSUM,
|
||||||
INVALID_FORMAT,
|
INVALID_FORMAT,
|
||||||
CANNOT_CREATE_TARGET,
|
CANNOT_CREATE_TARGET,
|
||||||
TARGET_ALREADY_EXISTS,
|
TARGET_ALREADY_EXISTS,
|
||||||
NO_TARGET_PROVIDED,
|
|
||||||
EMPTY_ARCHIVE,
|
|
||||||
CANNOT_STAT,
|
|
||||||
NOT_IMPLEMENTED
|
NOT_IMPLEMENTED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
#include "filesystem.h"
|
#include "filesystem.h"
|
||||||
|
#include "errors.h"
|
||||||
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
@ -11,15 +11,9 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <utime.h>
|
#include <utime.h>
|
||||||
|
|
||||||
#include "errors.h"
|
|
||||||
|
|
||||||
static char *current_dir_path = NULL;
|
|
||||||
static DIR *current_dir = NULL;
|
|
||||||
|
|
||||||
enum error_code create_directory(struct file_stats *stats)
|
enum error_code create_directory(struct file_stats *stats)
|
||||||
{
|
{
|
||||||
if (stats == NULL || stats->name == NULL || stats->mode == NULL
|
if (stats == NULL || stats->name == NULL || stats->mode == NULL || stats->uid == NULL || stats->gid == NULL || stats->mtime == NULL)
|
||||||
|| stats->uid == NULL || stats->gid == NULL || stats->mtime == NULL)
|
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
// Create dir
|
// Create dir
|
||||||
struct stat st = { 0 };
|
struct stat st = { 0 };
|
||||||
|
|
@ -32,13 +26,17 @@ enum error_code create_directory(struct file_stats *stats)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// return TARGET_ALREADY_EXISTS;
|
return TARGET_ALREADY_EXISTS;
|
||||||
return SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set stats
|
// Set stats
|
||||||
setowner(stats->name, stats->uid, stats->gid);
|
enum error_code errc;
|
||||||
setmtime(stats->name, stats->mtime);
|
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;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,12 +51,17 @@ enum error_code create_link(struct file_stats *stats, char *target)
|
||||||
return CANNOT_CREATE_TARGET;
|
return CANNOT_CREATE_TARGET;
|
||||||
|
|
||||||
// Set stats
|
// Set stats
|
||||||
setowner(stats->name, stats->uid, stats->gid);
|
enum error_code errc;
|
||||||
setmtime(stats->name, stats->mtime);
|
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
|
// May change the target mode instead of the limk itsel
|
||||||
// also lchmod is not available on all systems so just ignoring this
|
// also lchmod is not available on all systems so just ignoring this property here
|
||||||
// property here errc = setmode(stats->name, stats->mode); if (errc !=
|
// errc = setmode(stats->name, stats->mode);
|
||||||
// SUCCESS)
|
// if (errc != SUCCESS)
|
||||||
// return errc;
|
// return errc;
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
|
|
@ -80,15 +83,31 @@ enum error_code create_file(struct file_stats *stats)
|
||||||
if (file_stream == NULL)
|
if (file_stream == NULL)
|
||||||
return CANNOT_CREATE_TARGET;
|
return CANNOT_CREATE_TARGET;
|
||||||
// Copy content
|
// Copy content
|
||||||
fcat(stats->content_stream, file_stream, stats->length);
|
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
|
// Close file
|
||||||
fclose(file_stream);
|
fclose(file_stream);
|
||||||
|
|
||||||
// Set stats
|
// Set stats
|
||||||
setowner(stats->name, stats->uid, stats->gid);
|
enum error_code errc;
|
||||||
setmtime(stats->name, stats->mtime);
|
errc = setowner(stats->name, stats->uid, stats->gid);
|
||||||
setmode(stats->name, stats->mode);
|
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;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
@ -136,76 +155,3 @@ enum error_code setmode(char *path, char *mode)
|
||||||
int err = chmod(path, mode_val);
|
int err = chmod(path, mode_val);
|
||||||
return err == 0 ? SUCCESS : CANNOT_CREATE_TARGET;
|
return err == 0 ? SUCCESS : CANNOT_CREATE_TARGET;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum error_code isdirectory(char *path)
|
|
||||||
{
|
|
||||||
struct stat st;
|
|
||||||
if (stat(path, &st) == -1)
|
|
||||||
return FILE_NOT_FOUND;
|
|
||||||
return S_ISREG(st.st_mode) ? GENERIC_ERROR : SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *listdirectory(char *path)
|
|
||||||
{
|
|
||||||
struct dirent *de;
|
|
||||||
|
|
||||||
// Check for path changes
|
|
||||||
if (current_dir == NULL || current_dir_path == NULL
|
|
||||||
|| strcmp(current_dir_path, path) != 0)
|
|
||||||
{
|
|
||||||
// Close previous dir
|
|
||||||
freedirectory();
|
|
||||||
// Open new dir
|
|
||||||
current_dir_path = strdup(path);
|
|
||||||
current_dir = opendir(current_dir_path);
|
|
||||||
if (current_dir == NULL)
|
|
||||||
{
|
|
||||||
freedirectory();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get following file
|
|
||||||
de = readdir(current_dir);
|
|
||||||
if (de == NULL)
|
|
||||||
{
|
|
||||||
freedirectory();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for '.' or '..'
|
|
||||||
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
|
|
||||||
return listdirectory(path);
|
|
||||||
|
|
||||||
return de->d_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void freedirectory(void)
|
|
||||||
{
|
|
||||||
if (current_dir != NULL)
|
|
||||||
{
|
|
||||||
closedir(current_dir);
|
|
||||||
current_dir = NULL;
|
|
||||||
}
|
|
||||||
if (current_dir_path != NULL)
|
|
||||||
{
|
|
||||||
free(current_dir_path);
|
|
||||||
current_dir_path = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void fcat(FILE *src_stream, FILE *dst_stream, size_t size)
|
|
||||||
{
|
|
||||||
char buffer[4096];
|
|
||||||
size_t remaining = size;
|
|
||||||
while (remaining > 0)
|
|
||||||
{
|
|
||||||
size_t to_read =
|
|
||||||
(remaining < sizeof(buffer)) ? remaining : sizeof(buffer);
|
|
||||||
size_t bytes_read = fread(buffer, 1, to_read, src_stream);
|
|
||||||
if (bytes_read == 0)
|
|
||||||
break; // Reached EOF
|
|
||||||
fwrite(buffer, bytes_read, 1, dst_stream);
|
|
||||||
remaining -= bytes_read;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -21,44 +21,8 @@ enum error_code create_directory(struct file_stats *stats);
|
||||||
enum error_code create_link(struct file_stats *stats, char *target);
|
enum error_code create_link(struct file_stats *stats, char *target);
|
||||||
enum error_code create_file(struct file_stats *stats);
|
enum error_code create_file(struct file_stats *stats);
|
||||||
|
|
||||||
void fcat(FILE *src_stream, FILE *dst_stream, size_t size);
|
|
||||||
|
|
||||||
enum error_code setowner(char *path, char *uid, char *gid);
|
enum error_code setowner(char *path, char *uid, char *gid);
|
||||||
enum error_code setmtime(char *path, char *mtime);
|
enum error_code setmtime(char *path, char *mtime);
|
||||||
enum error_code setmode(char *path, char *mode);
|
enum error_code setmode(char *path, char *mode);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Retrieves the file informations at path and build the corresponding
|
|
||||||
* file_stats struct
|
|
||||||
* @arg path The given path
|
|
||||||
* @arg stats Pointer to where the function should write file informations
|
|
||||||
* @return FILE_NOT_FOUND if doesn's exists, GENERIC_ERROR if .
|
|
||||||
*/
|
|
||||||
enum error_code getstats(char *path, struct file_stats *stats);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if given path exists and is a directory
|
|
||||||
* @option path The given path
|
|
||||||
* @return FILE_NOT_FOUND if doesn's exists, GENERIC_ERROR if it is not a
|
|
||||||
* directory, SUCCESS otherwise.
|
|
||||||
*/
|
|
||||||
enum error_code isdirectory(char *path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Lists a directory content. Returns a new member name after each
|
|
||||||
* iteration and NULL when there are no more to list.
|
|
||||||
* @arg path The directory path
|
|
||||||
* @return The current item name or NULL
|
|
||||||
* @warn The return item statically allocated and freed after finishing to list
|
|
||||||
* the directory or when freedirectory() is called.
|
|
||||||
*/
|
|
||||||
char *listdirectory(char *path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Closes the last opened (and not closed) directory and frees the
|
|
||||||
* related allocated memory
|
|
||||||
* @warn Use this function if you only partially listed a directory previously.
|
|
||||||
*/
|
|
||||||
void freedirectory(void);
|
|
||||||
|
|
||||||
#endif // FILESYSTEM_H
|
#endif // FILESYSTEM_H
|
||||||
|
|
|
||||||
3
epitar/src/utils/misc.c
Normal file
3
epitar/src/utils/misc.c
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <stdio.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 */
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Old testsuite but still pertinent
|
|
||||||
|
|
||||||
# Colors
|
|
||||||
GREEN='\033[0;32m'
|
|
||||||
RED='\033[0;31m'
|
|
||||||
BLUE='\033[0;34m'
|
|
||||||
NC='\033[0m' # No Color
|
|
||||||
|
|
||||||
PASSED=0
|
|
||||||
FAILED=0
|
|
||||||
TOTAL=0
|
|
||||||
|
|
||||||
# Ensure executable exists
|
|
||||||
if [ ! -x "./epitar" ]; then
|
|
||||||
echo -e "${RED}Error: ./epitar not found or not executable.${NC}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# assert "test name" "command" "expected_exit_code" "expected_output_substring"
|
|
||||||
assert() {
|
|
||||||
((TOTAL++))
|
|
||||||
local label=$1
|
|
||||||
local cmd=$2
|
|
||||||
local expected_code=$3
|
|
||||||
local expected_msg=$4
|
|
||||||
|
|
||||||
echo -n "Test $TOTAL: $label... "
|
|
||||||
|
|
||||||
# Run
|
|
||||||
output=$(eval "$cmd" 2>&1)
|
|
||||||
exit_code=$?
|
|
||||||
|
|
||||||
if [ "$exit_code" -eq "$expected_code" ] && [[ "$output" == *"$expected_msg"* ]]; then
|
|
||||||
echo -e "${GREEN}PASS${NC}"
|
|
||||||
((PASSED++))
|
|
||||||
else
|
|
||||||
echo -e "${RED}FAIL${NC}"
|
|
||||||
echo -e " Expected code: $expected_code, Got: $exit_code"
|
|
||||||
echo -e " Expected msg contains: '$expected_msg'"
|
|
||||||
echo -e " Actual output: '$output'"
|
|
||||||
((FAILED++))
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# === Clean Environment ===
|
|
||||||
rm -f *.tar tmp_file1 tmp_file2
|
|
||||||
touch tmp_file1 tmp_file2
|
|
||||||
echo "corrupt data" > bad_checksum.tar
|
|
||||||
|
|
||||||
# === Testsuite ===
|
|
||||||
|
|
||||||
echo -e "${BLUE}Starting epitar test suite...${NC}\n"
|
|
||||||
|
|
||||||
# Help and Basic Usage
|
|
||||||
assert "Help flag -h" "./epitar -h" 0 "Usage: epitar -[xcvh] <file.tar> [<files>]"
|
|
||||||
assert "No arguments" "./epitar" 1 "invalid or missing option"
|
|
||||||
assert "Invalid flag -z" "./epitar -z" 1 "invalid or missing option"
|
|
||||||
assert "Invalid flag -a" "./epitar -a toto.tar titi" 1 "invalid or missing option"
|
|
||||||
|
|
||||||
# Creation Errors (-c)
|
|
||||||
assert "Refuse empty archive" "./epitar -c empty.tar" 1 "cowardly refusing"
|
|
||||||
assert "Missing archive name for -c" "./epitar -c" 1 "invalid or missing option"
|
|
||||||
assert "Archive adding itself" "./epitar -c self.tar self.tar" 0 "is the archive; not dumped"
|
|
||||||
assert "Unable to add missing file" "./epitar -cv missing.tar non_existent_file" 3 "unable to add file"
|
|
||||||
|
|
||||||
# Extraction Errors (-x)
|
|
||||||
assert "Bad checksum" "./epitar -x bad_checksum.tar" 2 "bad checksum"
|
|
||||||
assert "Extraction of non-existent file" "./epitar -x nowhere.tar" 3 "error extracting tarball"
|
|
||||||
assert "Missing archive name for -x" "./epitar -x" 1 "invalid or missing option"
|
|
||||||
|
|
||||||
# Combinations & Complex flags
|
|
||||||
assert "Multiple invalid flags" "./epitar -xz archive.tar" 1 "invalid or missing option"
|
|
||||||
assert "Help priority" "./epitar -h -x nonexistent.tar" 0 "Usage:"
|
|
||||||
assert "Multiple files for creation (failure case)" "./epitar -c fail.tar tmp_file1 missing_file" 3 "unable to add file"
|
|
||||||
|
|
||||||
# Path and Environment edge cases
|
|
||||||
assert "Relative path to non-existent" "./epitar -x ./subdir/fake.tar" 3 "error extracting tarball"
|
|
||||||
assert "Creation with dot (.)" "./epitar -c current.tar ." 0 "current.tar is the archive; not dumped"
|
|
||||||
assert "List content (hypothetical -v without -c or -x)" "./epitar -v" 1 "invalid or missing option"
|
|
||||||
assert "Wrong order of arguments" "./epitar archive.tar -c" 1 "invalid or missing option"
|
|
||||||
|
|
||||||
# === Summary ===
|
|
||||||
echo -e "\n------------------------------------"
|
|
||||||
echo -e "Results: ${GREEN}$PASSED Passed${NC}, ${RED}$FAILED Failed${NC} / $TOTAL Total"
|
|
||||||
echo -e "------------------------------------"
|
|
||||||
|
|
||||||
# Cleanup
|
|
||||||
rm -f *.tar tmp_file1 tmp_file2
|
|
||||||
|
|
||||||
|
|
@ -1,345 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Testsuitator (mon bien-aimé) le retour <3
|
|
||||||
|
|
||||||
###################
|
|
||||||
# Variables #
|
|
||||||
###################
|
|
||||||
|
|
||||||
executable="$BIN_PATH"
|
|
||||||
ref_executable="tar"
|
|
||||||
|
|
||||||
total_tests=0
|
|
||||||
errors_count=0
|
|
||||||
timeouts_count=0
|
|
||||||
|
|
||||||
# Create temp directory for tests
|
|
||||||
test_dir=$(mktemp -d)
|
|
||||||
trap "rm -rf $test_dir" EXIT
|
|
||||||
|
|
||||||
|
|
||||||
##################
|
|
||||||
# Colors #
|
|
||||||
##################
|
|
||||||
|
|
||||||
# Reset
|
|
||||||
Color_Off='\033[0m' # Text Reset
|
|
||||||
|
|
||||||
# Regular Colors
|
|
||||||
Black='\033[0;30m' # Black
|
|
||||||
Red='\033[0;31m' # Red
|
|
||||||
Green='\033[0;32m' # Green
|
|
||||||
Yellow='\033[0;33m' # Yellow
|
|
||||||
Blue='\033[0;34m' # Blue
|
|
||||||
Purple='\033[0;35m' # Purple
|
|
||||||
Cyan='\033[0;36m' # Cyan
|
|
||||||
White='\033[0;37m' # White
|
|
||||||
|
|
||||||
# Bold
|
|
||||||
BBlack='\033[1;30m' # Black
|
|
||||||
BRed='\033[1;31m' # Red
|
|
||||||
BGreen='\033[1;32m' # Green
|
|
||||||
BYellow='\033[1;33m' # Yellow
|
|
||||||
BBlue='\033[1;34m' # Blue
|
|
||||||
BPurple='\033[1;35m' # Purple
|
|
||||||
BCyan='\033[1;36m' # Cyan
|
|
||||||
BWhite='\033[1;37m' # White
|
|
||||||
|
|
||||||
|
|
||||||
##################
|
|
||||||
# Wrappers #
|
|
||||||
##################
|
|
||||||
|
|
||||||
# @arg test name
|
|
||||||
# @arg command
|
|
||||||
check_result() {
|
|
||||||
local test_name="$1"
|
|
||||||
local actual_code="$2"
|
|
||||||
local ref_code="$3"
|
|
||||||
|
|
||||||
test_failed=0
|
|
||||||
|
|
||||||
# Check return code
|
|
||||||
if [[ "$actual_code" -eq 124 ]]; then
|
|
||||||
echo -e $BRed "TIMEOUT" $Color_Off
|
|
||||||
echo -e ' ' "on '$test_name'"
|
|
||||||
echo -e ' ' "Expected code $ref_code but got $actual_code"
|
|
||||||
((timeouts_count++))
|
|
||||||
test_failed=1
|
|
||||||
elif [[ "$actual_code" -ne "$ref_code" ]]; then
|
|
||||||
echo -e $BRed "FAILED" $Color_Off
|
|
||||||
echo -e ' ' "on '$test_name'"
|
|
||||||
echo -e ' ' "Expected code $ref_code but got $actual_code"
|
|
||||||
test_failed=1
|
|
||||||
else
|
|
||||||
echo -e $BGreen "OK" $Color_Off
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$test_failed" -eq 1 ]]; then
|
|
||||||
((errors_count++))
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# @arg test name
|
|
||||||
# @arg test command
|
|
||||||
test_archive_creation() {
|
|
||||||
local test_name="$1"
|
|
||||||
local archive_name="$2"
|
|
||||||
local input_files="$3"
|
|
||||||
|
|
||||||
echo -e $BBlue "========== $test_name ==========" $Color_Off
|
|
||||||
|
|
||||||
# Create test files
|
|
||||||
local test_subdir="$test_dir/$test_name"
|
|
||||||
mkdir -p "$test_subdir"
|
|
||||||
cd "$test_subdir"
|
|
||||||
|
|
||||||
# Prepare input files
|
|
||||||
local file_list=""
|
|
||||||
for file in $input_files; do
|
|
||||||
echo "content of $file" > "$file"
|
|
||||||
file_list="$file_list $file"
|
|
||||||
done
|
|
||||||
|
|
||||||
# Test with epitar
|
|
||||||
echo -e -n $Blue "= [ARCHIVE]" $Color_Off " "
|
|
||||||
timeout 2 $executable -c "$archive_name" $file_list > /dev/null 2>&1
|
|
||||||
actual_code=$?
|
|
||||||
|
|
||||||
if [[ $actual_code -eq 0 && -f "$archive_name" ]]; then
|
|
||||||
echo -e $BGreen "OK" $Color_Off
|
|
||||||
else
|
|
||||||
echo -e $BRed "FAILED" $Color_Off
|
|
||||||
((errors_count++))
|
|
||||||
fi
|
|
||||||
((total_tests++))
|
|
||||||
|
|
||||||
# Test extraction
|
|
||||||
if [[ -f "$archive_name" ]]; then
|
|
||||||
echo -e -n $Blue "= [EXTRACT] " $Color_Off " "
|
|
||||||
extract_dir="extract_test"
|
|
||||||
mkdir "$extract_dir"
|
|
||||||
cd "$extract_dir"
|
|
||||||
timeout 2 $executable -x "../$archive_name" > /dev/null 2>&1
|
|
||||||
actual_code=$?
|
|
||||||
|
|
||||||
if [[ $actual_code -eq 0 ]]; then
|
|
||||||
# Check if files were extracted
|
|
||||||
all_extracted=1
|
|
||||||
for file in $file_list; do
|
|
||||||
if [[ ! -f "$file" ]]; then
|
|
||||||
all_extracted=0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if [[ $all_extracted -eq 1 ]]; then
|
|
||||||
echo -e $BGreen "OK" $Color_Off
|
|
||||||
else
|
|
||||||
echo -e $BRed "FAILED (files not extracted)" $Color_Off
|
|
||||||
((errors_count++))
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo -e $BRed "FAILED (exit code $actual_code)" $Color_Off
|
|
||||||
((errors_count++))
|
|
||||||
fi
|
|
||||||
((total_tests++))
|
|
||||||
cd ..
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd /
|
|
||||||
echo -e "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
# @arg test name
|
|
||||||
# @arg files to test
|
|
||||||
test_verbose_output() {
|
|
||||||
local test_name="$1"
|
|
||||||
local files="$2"
|
|
||||||
|
|
||||||
echo -e $BBlue "========== $test_name ==========" $Color_Off
|
|
||||||
|
|
||||||
local test_subdir="$test_dir/${test_name}_verbose"
|
|
||||||
mkdir -p "$test_subdir"
|
|
||||||
cd "$test_subdir"
|
|
||||||
|
|
||||||
# Create test files
|
|
||||||
for file in $files; do
|
|
||||||
echo "test content" > "$file"
|
|
||||||
done
|
|
||||||
|
|
||||||
echo -e -n $Blue "= [VERBOSE]" $Color_Off " "
|
|
||||||
archive="test_verbose.tar"
|
|
||||||
timeout 2 $executable -cv "$archive" $files > output.txt 2>&1
|
|
||||||
actual_code=$?
|
|
||||||
|
|
||||||
# Check if output contains filenames
|
|
||||||
local all_found=1
|
|
||||||
for file in $files; do
|
|
||||||
if ! grep -q "$file" output.txt; then
|
|
||||||
all_found=0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ $actual_code -eq 0 && $all_found -eq 1 ]]; then
|
|
||||||
echo -e $BGreen "OK" $Color_Off
|
|
||||||
else
|
|
||||||
echo -e $BRed "FAILED" $Color_Off
|
|
||||||
((errors_count++))
|
|
||||||
fi
|
|
||||||
((total_tests++))
|
|
||||||
|
|
||||||
cd /
|
|
||||||
echo -e "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
# @arg test name
|
|
||||||
test_help() {
|
|
||||||
local test_name="$1"
|
|
||||||
|
|
||||||
echo -e $BBlue "========== $test_name ==========" $Color_Off
|
|
||||||
|
|
||||||
echo -e -n $Blue "= [HELP]" $Color_Off " "
|
|
||||||
timeout 1 $executable -h > /dev/null 2>&1
|
|
||||||
actual_code=$?
|
|
||||||
|
|
||||||
if [[ $actual_code -eq 0 ]]; then
|
|
||||||
echo -e $BGreen "OK" $Color_Off
|
|
||||||
else
|
|
||||||
echo -e $BRed "FAILED" $Color_Off
|
|
||||||
((errors_count++))
|
|
||||||
fi
|
|
||||||
((total_tests++))
|
|
||||||
|
|
||||||
echo -e "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
# @arg test name
|
|
||||||
test_invalid_args() {
|
|
||||||
local test_name="$1"
|
|
||||||
local args="$2"
|
|
||||||
|
|
||||||
echo -e $BBlue "========== $test_name ==========" $Color_Off
|
|
||||||
|
|
||||||
echo -e -n $Blue "= [INVALID]" $Color_Off " "
|
|
||||||
timeout 1 $executable $args > /dev/null 2>&1
|
|
||||||
actual_code=$?
|
|
||||||
|
|
||||||
# Should fail
|
|
||||||
if [[ $actual_code -ne 0 ]]; then
|
|
||||||
echo -e $BGreen "OK (correctly rejected)" $Color_Off
|
|
||||||
else
|
|
||||||
echo -e $BRed "FAILED (should have been rejected)" $Color_Off
|
|
||||||
((errors_count++))
|
|
||||||
fi
|
|
||||||
((total_tests++))
|
|
||||||
|
|
||||||
echo -e "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
summarize() {
|
|
||||||
# Compute statistics
|
|
||||||
(( passed_tests = $total_tests - $errors_count ))
|
|
||||||
if [[ $total_tests -gt 0 ]]; then
|
|
||||||
(( tests_percentage = 100 * $passed_tests / $total_tests ))
|
|
||||||
else
|
|
||||||
tests_percentage=0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Adapt color depending on results
|
|
||||||
if [[ tests_percentage -gt 80 ]]; then
|
|
||||||
coverage_color=$BGreen
|
|
||||||
elif [[ tests_percentage -gt 50 ]]; then
|
|
||||||
coverage_color=$BYellow
|
|
||||||
else
|
|
||||||
coverage_color=$BRed
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Print
|
|
||||||
echo -e $BWhite "\n\n""===========" $BWhite"Summary"$Color_Off "\n"
|
|
||||||
echo -e " Passed $coverage_color$passed_tests/$total_tests$Color_Off tests ($coverage_color$tests_percentage%$Color_Off)"
|
|
||||||
echo -e " Got $timeouts_count timeout(s)"
|
|
||||||
if [ "$OUTPUT_FILE" != "" ]; then
|
|
||||||
echo $tests_percentage > "$OUTPUT_FILE"
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#################
|
|
||||||
# Tests #
|
|
||||||
#################
|
|
||||||
|
|
||||||
echo -e "\n\n""===$BGreen TestsuitatorX 2.0 Ultra Pro Max+ 365 Premium Gris Sidéral" "\n\n"$Color_Off
|
|
||||||
|
|
||||||
echo -e "\n$BBlue=== Basic Operations ===$Color_Off"
|
|
||||||
test_archive_creation "Single file" "single.tar" "file1.txt"
|
|
||||||
test_archive_creation "Multiple files" "multi.tar" "file1.txt file2.txt file3.txt"
|
|
||||||
test_archive_creation "Files with spaces" "spaces.tar" "file_with_underscore.txt"
|
|
||||||
|
|
||||||
echo -e "\n$BBlue=== Verbose Output ===$Color_Off"
|
|
||||||
test_verbose_output "Verbose creation" "file1.txt file2.txt"
|
|
||||||
|
|
||||||
echo -e "\n$BBlue=== Help and Invalid Arguments ===$Color_Off"
|
|
||||||
test_help "Help flag"
|
|
||||||
test_invalid_args "No arguments" ""
|
|
||||||
test_invalid_args "Invalid flag" "-z"
|
|
||||||
test_invalid_args "Invalid flagS" "-zbk"
|
|
||||||
test_invalid_args "Invalid position" "test.tar -h"
|
|
||||||
test_invalid_args "Invalid flags and position" "test.tar -z"
|
|
||||||
test_invalid_args "Invalid flags and position mix" "test.tar -zhx"
|
|
||||||
test_invalid_args "Invalid position" "test.tar -z"
|
|
||||||
test_invalid_args "Invalid flag mix" "-zh"
|
|
||||||
test_invalid_args "Invalid flag mix 2" "-hnh"
|
|
||||||
test_invalid_args "Invalid flag mix 3" "-cnc"
|
|
||||||
test_invalid_args "Invalid flag mix 3" "-hhhz"
|
|
||||||
test_invalid_args "Invalid flag mix 3" "-hnc"
|
|
||||||
test_invalid_args "Invalid flag mix 3" "-cch"
|
|
||||||
test_invalid_args "Invalid flag mix 3" "-cnc"
|
|
||||||
test_invalid_args "Valid flags multiple" "-cc"
|
|
||||||
test_invalid_args "Incompatible flag" "-cx"
|
|
||||||
test_invalid_args "Missing op flag" "-v"
|
|
||||||
test_invalid_args "Missing archive (c)" "-c"
|
|
||||||
test_invalid_args "Missing archive (x)" "-x"
|
|
||||||
test_invalid_args "Extract with files" "-x archive.tar file.txt"
|
|
||||||
|
|
||||||
echo -e "\n$BBlue=== Edge Cases ===$Color_Off"
|
|
||||||
|
|
||||||
# Test with empty archive
|
|
||||||
test_subdir="$test_dir/empty_archive"
|
|
||||||
mkdir -p "$test_subdir"
|
|
||||||
cd "$test_subdir"
|
|
||||||
echo -e $BBlue "========== Empty archive ==========" $Color_Off
|
|
||||||
echo -e -n $Blue "= [CREATE_EMPTY]" $Color_Off " "
|
|
||||||
touch dummy
|
|
||||||
timeout 2 $executable -c "empty.tar" dummy > /dev/null 2>&1
|
|
||||||
actual_code=$?
|
|
||||||
rm dummy
|
|
||||||
if [[ $actual_code -eq 0 ]]; then
|
|
||||||
echo -e $BGreen "OK" $Color_Off
|
|
||||||
else
|
|
||||||
echo -e $BRed "FAILED" $Color_Off
|
|
||||||
((errors_count++))
|
|
||||||
fi
|
|
||||||
((total_tests++))
|
|
||||||
cd /
|
|
||||||
echo -e "\n"
|
|
||||||
|
|
||||||
# Test combined flags
|
|
||||||
test_subdir="$test_dir/combined_flags"
|
|
||||||
mkdir -p "$test_subdir"
|
|
||||||
cd "$test_subdir"
|
|
||||||
echo -e $BBlue "========== Combined flags (-cv) ==========" $Color_Off
|
|
||||||
echo -e -n $Blue "= [COMBINED]" $Color_Off " "
|
|
||||||
echo "content" > test.txt
|
|
||||||
timeout 2 $executable -cv "test.tar" test.txt > /dev/null 2>&1
|
|
||||||
actual_code=$?
|
|
||||||
if [[ $actual_code -eq 0 && -f "test.tar" ]]; then
|
|
||||||
echo -e $BGreen "OK" $Color_Off
|
|
||||||
else
|
|
||||||
echo -e $BRed "FAILED" $Color_Off
|
|
||||||
((errors_count++))
|
|
||||||
fi
|
|
||||||
((total_tests++))
|
|
||||||
cd /
|
|
||||||
echo -e "\n"
|
|
||||||
|
|
||||||
summarize
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
Color_Off='\033[0m' # Text Reset
|
|
||||||
BYellow='\033[1;33m' # Yellow
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo -e $BYellow "WARNING:" $Color_Off " Two testsuites have been run, don't just check the last summary"
|
|
||||||
echo -e $BYellow "WARNING:" $Color_Off " Archive/Extract tests will be partly hidden if their first part doesn't succeed"
|
|
||||||
echo " (So if you want the accurate number of tests just add the number of failed tests to the total)"
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue