diff --git a/.clang-format b/.clang-format deleted file mode 100644 index 7ed8115..0000000 --- a/.clang-format +++ /dev/null @@ -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 diff --git a/epitar/Makefile b/epitar/Makefile index 10068ef..b3430bf 100644 --- a/epitar/Makefile +++ b/epitar/Makefile @@ -21,18 +21,13 @@ $(TARGET): $(OBJS) $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) @echo $(OBJS) -all: $(OBJS) - $(CC) -o $(TARGET) $^ $(LDFLAGS) $(LDLIBS) - debug: CFLAGS += $(DBG_CFLAGS) debug: LDFLAGS += $(DBG_LDFLAGS) debug: $(OBJS) $(CC) -o $(TARGET) $^ $(LDFLAGS) $(LDLIBS) -check: $(TARGET) - bash ./tests/functional/old_testsuite.sh - bash ./tests/functional/run.sh - bash ./tests/functional/warning.sh +check: + dash ./tests/run.sh clean: $(RM) $(TARGET) diff --git a/epitar/src/main.c b/epitar/src/main.c index 92171d5..3e1f5d9 100644 --- a/epitar/src/main.c +++ b/epitar/src/main.c @@ -1,58 +1,6 @@ #include "tar/archive.h" #include "tar/extract.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) { @@ -83,7 +31,22 @@ int main(int argc, char **argv) err = archive(config.archive_file, config.input_files, config.verbose); } - int ret_code = handle_err_code(err, &config); - destroy_config(&config); - return ret_code; + switch (err) + { + 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; + } } diff --git a/epitar/src/tar/archive.c b/epitar/src/tar/archive.c index ed0b247..014bcd1 100644 --- a/epitar/src/tar/archive.c +++ b/epitar/src/tar/archive.c @@ -1,235 +1,8 @@ #include "archive.h" -#include -#include -#include -#include -#include -#include - -#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; - 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; +enum error_code archive(char *archive_name, char **files, bool verbose) { + (void) archive_name; + (void) files; + (void) verbose; + return UNKNOWN_ERROR; } diff --git a/epitar/src/tar/archive.h b/epitar/src/tar/archive.h index f9dd21f..546f4ff 100644 --- a/epitar/src/tar/archive.h +++ b/epitar/src/tar/archive.h @@ -1,10 +1,10 @@ #ifndef ARCHIVE_H #define ARCHIVE_H -#include - #include "../utils/errors.h" +#include + /* @brief Archives the listed `files` into `archive_name` * @return 0 on success, the corresponding error code otherwise */ diff --git a/epitar/src/tar/extract.c b/epitar/src/tar/extract.c index 28f2677..1de4b9d 100644 --- a/epitar/src/tar/extract.c +++ b/epitar/src/tar/extract.c @@ -5,7 +5,6 @@ #include #include "../utils/filesystem.h" -#include "tar.h" /** * @@ -14,6 +13,7 @@ */ static enum error_code extract_ustar(FILE *stream, struct ustar_header *header) { + struct file_stats stats = { .name = header->name, .length = strtol(header->size, NULL, 8), .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, struct unixtar_header *header) { - struct file_stats stats = { .name = header->name, - .length = strtol(header->size, NULL, 8), - .content_stream = stream, - .mode = header->mode, - .uid = header->uid, - .gid = header->gid, - .mtime = header->mtime }; - return create_file(&stats); + (void)stream; + (void)header; + return NOT_IMPLEMENTED; } /** @@ -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 (verbose) - printf("%s\n", ustar_header->name); + printf("./%s\n", ustar_header->name); return extract_ustar(content, ustar_header); } // UNIXTAR else if ((unixtar_header = get_unixtar_header(header)) != NULL) { if (verbose) - printf("%s\n", unixtar_header->name); + printf("./%s", unixtar_header->name); return extract_unixtar(content, unixtar_header); } // Unsupported format @@ -118,7 +113,6 @@ enum error_code extract(char *archive_name, bool verbose) if (bytes_read < sizeof(union tar_header)) { read_state = INVALID_FORMAT; // Unexpected EOF - continue; } // Check if empty @@ -136,12 +130,6 @@ enum error_code extract(char *archive_name, bool verbose) got_empty_block = false; } - if (!check_checksum(&header)) - { - fclose(stream); - return BAD_CHECKSUM; - } - // Extract file enum error_code err = extract_file(stream, &header, verbose); 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 size_t file_size = strtol(header.ustar.size, NULL, 8); - size_t padded_size = - ((file_size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE; + size_t padded_size = ((file_size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE; size_t skip_bytes = padded_size - file_size; if (skip_bytes > 0 && fseek(stream, skip_bytes, SEEK_CUR) != 0) diff --git a/epitar/src/tar/tar.c b/epitar/src/tar/tar.c index ee9fd7a..5defa56 100644 --- a/epitar/src/tar/tar.c +++ b/epitar/src/tar/tar.c @@ -1,6 +1,6 @@ #include "tar.h" -#include +#include #include 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 unixtar_header *unixtar_header; - char actual_checksum[TCHKSUMLEN]; + int actual_checksum; // USTAR if ((ustar_header = get_ustar_header(header)) != NULL) { - strcpy(actual_checksum, ustar_header->chksum); - return strcmp(actual_checksum, header->ustar.chksum) == 0; + actual_checksum = atoi(ustar_header->chksum); } // UNIXTAR else if ((unixtar_header = get_unixtar_header(header)) != NULL) { - strcpy(actual_checksum, unixtar_header->chksum); - return strcmp(actual_checksum, header->unixtar.chksum) == 0; + actual_checksum = atoi(unixtar_header->chksum); } // Unsupported format else { 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 - struct unixtar_header *unixtar_header = get_unixtar_header(header); - 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; + char *block = header->raw_block; + int sum = 0; for (int i = 0; i < BLOCK_SIZE; i++) sum += block[i]; - // Update checksum - 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] = ' '; - } + return sum; } diff --git a/epitar/src/tar/tar.h b/epitar/src/tar/tar.h index 82dab5f..ebf660c 100644 --- a/epitar/src/tar/tar.h +++ b/epitar/src/tar/tar.h @@ -40,11 +40,6 @@ #define TOWRITE 00002 /* write 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 struct ustar_header @@ -65,7 +60,6 @@ struct ustar_header char devmajor[8]; char devminor[8]; char prefix[155]; - char padding[12]; }; struct unixtar_header @@ -77,7 +71,7 @@ struct unixtar_header char size[12]; char mtime[12]; char chksum[8]; - char link; + char link[1]; char linkname[100]; char padding[255]; // Should be empty }; @@ -86,7 +80,7 @@ union tar_header { struct unixtar_header unixtar; struct ustar_header ustar; - unsigned char raw_block[BLOCK_SIZE]; + char raw_block[BLOCK_SIZE]; }; // === 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); /* @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 */ 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 */ -void compute_checksum(union tar_header *header); +int compute_checksum(union tar_header *header); #endif // TAR_H diff --git a/epitar/src/utils/config.c b/epitar/src/utils/config.c index a46a5a7..e2d82c6 100644 --- a/epitar/src/utils/config.c +++ b/epitar/src/utils/config.c @@ -81,7 +81,7 @@ enum arg_error_code args_handler(int argc, char **argv, struct config *config) // Parse option flags int opt; - while ((opt = getopt(argc, argv, ":cvxh")) != -1) + while ((opt = getopt(argc, argv, "cxvh")) != -1) { int err = handle_opt(opt, config); if (err == ARG_HELP) @@ -190,7 +190,7 @@ void print_invalid_option(void) puts("Try './epitar -h' for more information"); } -void destroy_config(struct config *config) +void config_destroy(struct config *config) { if (config == NULL) return; @@ -203,5 +203,5 @@ void destroy_config(struct config *config) // Note: archive_file points to argv, so we don't free it - // free(config); + free(config); } diff --git a/epitar/src/utils/config.h b/epitar/src/utils/config.h index a79bfc9..c429c96 100644 --- a/epitar/src/utils/config.h +++ b/epitar/src/utils/config.h @@ -55,7 +55,7 @@ void print_invalid_option(void); */ bool validate_config(struct config *cfg); -/* @brief frees all data allocated by config +/* */ void destroy_config(struct config *config); diff --git a/epitar/src/utils/errors.h b/epitar/src/utils/errors.h index aeb20c3..eaf81ea 100644 --- a/epitar/src/utils/errors.h +++ b/epitar/src/utils/errors.h @@ -6,15 +6,11 @@ enum error_code SUCCESS = 0, PENDING, UNKNOWN_ERROR, - GENERIC_ERROR, FILE_NOT_FOUND, BAD_CHECKSUM, INVALID_FORMAT, CANNOT_CREATE_TARGET, TARGET_ALREADY_EXISTS, - NO_TARGET_PROVIDED, - EMPTY_ARCHIVE, - CANNOT_STAT, NOT_IMPLEMENTED }; diff --git a/epitar/src/utils/filesystem.c b/epitar/src/utils/filesystem.c index 9116034..aac2b70 100644 --- a/epitar/src/utils/filesystem.c +++ b/epitar/src/utils/filesystem.c @@ -1,8 +1,8 @@ #define _POSIX_C_SOURCE 200809L #include "filesystem.h" +#include "errors.h" -#include #include #include #include @@ -11,15 +11,9 @@ #include #include -#include "errors.h" - -static char *current_dir_path = NULL; -static DIR *current_dir = NULL; - 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) + 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 }; @@ -32,13 +26,17 @@ enum error_code create_directory(struct file_stats *stats) } else { - // return TARGET_ALREADY_EXISTS; - return SUCCESS; + return TARGET_ALREADY_EXISTS; } // Set stats - setowner(stats->name, stats->uid, stats->gid); - setmtime(stats->name, stats->mtime); + 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; } @@ -53,12 +51,17 @@ enum error_code create_link(struct file_stats *stats, char *target) return CANNOT_CREATE_TARGET; // Set stats - setowner(stats->name, stats->uid, stats->gid); - setmtime(stats->name, stats->mtime); + 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) + // 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; @@ -80,15 +83,31 @@ enum error_code create_file(struct file_stats *stats) if (file_stream == NULL) return CANNOT_CREATE_TARGET; // 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 fclose(file_stream); // Set stats - setowner(stats->name, stats->uid, stats->gid); - setmtime(stats->name, stats->mtime); - setmode(stats->name, stats->mode); + 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; } @@ -96,13 +115,13 @@ enum error_code create_file(struct file_stats *stats) enum error_code setowner(char *path, char *uid, char *gid) { if (path == NULL || uid == NULL || gid == NULL) - return UNKNOWN_ERROR; + return UNKNOWN_ERROR; - uid_t uid_val = strtol(uid, NULL, 10); - gid_t gid_val = strtol(gid, NULL, 10); + uid_t uid_val = strtol(uid, NULL, 10); + gid_t gid_val = strtol(gid, NULL, 10); - int err = chown(path, uid_val, gid_val); - return err == 0 ? SUCCESS : CANNOT_CREATE_TARGET; + int err = chown(path, uid_val, gid_val); + return err == 0 ? SUCCESS : CANNOT_CREATE_TARGET; } enum error_code setmtime(char *path, char *mtime) @@ -136,76 +155,3 @@ enum error_code setmode(char *path, char *mode) int err = chmod(path, mode_val); 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; - } -} diff --git a/epitar/src/utils/filesystem.h b/epitar/src/utils/filesystem.h index 160c800..6b16787 100644 --- a/epitar/src/utils/filesystem.h +++ b/epitar/src/utils/filesystem.h @@ -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_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 setmtime(char *path, char *mtime); 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 diff --git a/epitar/src/utils/misc.c b/epitar/src/utils/misc.c new file mode 100644 index 0000000..0271150 --- /dev/null +++ b/epitar/src/utils/misc.c @@ -0,0 +1,3 @@ +#include "utils.h" + +#include diff --git a/epitar/src/utils/misc.h b/epitar/src/utils/misc.h new file mode 100644 index 0000000..3d20ecc --- /dev/null +++ b/epitar/src/utils/misc.h @@ -0,0 +1,6 @@ +#ifndef MISC_H +#define MISC_H + +void print_str_array(const char **input_files); + +#endif /* MISC_H */ diff --git a/epitar/tests/functional/old_testsuite.sh b/epitar/tests/functional/old_testsuite.sh deleted file mode 100644 index 5e16a27..0000000 --- a/epitar/tests/functional/old_testsuite.sh +++ /dev/null @@ -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] []" -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 - diff --git a/epitar/tests/functional/run.sh b/epitar/tests/functional/run.sh deleted file mode 100644 index 70b07f4..0000000 --- a/epitar/tests/functional/run.sh +++ /dev/null @@ -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 diff --git a/epitar/tests/functional/warning.sh b/epitar/tests/functional/warning.sh deleted file mode 100644 index 6b66c3b..0000000 --- a/epitar/tests/functional/warning.sh +++ /dev/null @@ -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)"