#include #include #include #include #include "sha256.h" #include "vieter_package_internal.h" #define ADD_STRING(section, field) \ if (pkg_info->field != 0) { \ size_to_be_written = \ snprintf(aux, small_buff_size, section, pkg_info->field); \ if (size_to_be_written > small_buff_size) { \ aux = realloc(aux, size_to_be_written + 1); \ small_buff_size = size_to_be_written + 1; \ snprintf(aux, small_buff_size, section, pkg_info->field); \ } \ if (buff_size < strlen(description) + small_buff_size + 1) { \ description = realloc(description, buff_size * 2); \ buff_size *= 2; \ } \ strcat(description, aux); \ } #define ADD_ARRAY(section, field) \ i = 0; \ if (pkg_info->field != NULL) { \ ADD_STRING(section, field->array[i]); \ i++; \ while (pkg_info->field->array[i] != NULL) { \ ADD_STRING("\n%s", field->array[i]); \ i++; \ } \ } static char *ignored_names[5] = {".BUILDINFO", ".INSTALL", ".MTREE", ".PKGINFO", ".CHANGELOG"}; static size_t ignored_words_len = sizeof(ignored_names) / sizeof(char *); vieter_package *vieter_package_init() { return calloc(sizeof(vieter_package_info), 1); } vieter_package_error vieter_package_read_archive(vieter_package *pkg, const char *pkg_path) { struct archive *a = archive_read_new(); struct archive_entry *entry; // These three are the most commonly used compression methods archive_read_support_filter_zstd(a); archive_read_support_filter_gzip(a); archive_read_support_filter_xz(a); // Contents should always be a tarball archive_read_support_format_tar(a); // TODO where does this 10240 come from? int r = archive_read_open_filename(a, pkg_path, 10240); // Exit early if we weren't able to successfully open the archive for reading if (r != ARCHIVE_OK) { return vieter_package_unarchive_error; } int compression_code = archive_filter_code(a, 0); const char *path_name; vieter_package_info *pkg_info = NULL; vieter_dynarray *files = vieter_dynarray_init(16); vieter_dynarray_add(files, "%FILES%"); while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { path_name = archive_entry_pathname(entry); bool ignore = false; for (size_t i = 0; i < ignored_words_len; i++) { if (strcmp(path_name, ignored_names[i]) == 0) { ignore = true; break; } } if (!ignore) { vieter_dynarray_add(files, path_name); } if (strcmp(path_name, ".PKGINFO") == 0) { // Read data of file into memory buffer int size = archive_entry_size(entry); char *buf = malloc(size + 1); archive_read_data(a, buf, size); buf[size] = '\0'; // Parse package vieter_package_info string into a struct pkg_info = vieter_package_info_init(); vieter_package_info_parse(pkg_info, buf); free(buf); } else { archive_read_data_skip(a); } } // Get size of file struct stat stats; if (stat(pkg_path, &stats) != 0) { // errno is set if stat() fails; the calling function should check // the value of errno in case vieter_package_stat_error is returned return vieter_package_stat_error; } pkg_info->csize = stats.st_size; archive_read_free(a); // Create final return value pkg->path = strdup(pkg_path); pkg->info = pkg_info; pkg->files = files; pkg->compression = compression_code; return vieter_package_ok; } void vieter_package_sha256sum(vieter_package *pkg, char *res) { FILE *f = fopen(pkg->path, "r"); // Try to read 100KiB at a time unsigned char *in = malloc(102400); // Actual number of bytes read size_t read_size; SHA256_CTX *ctx = malloc(sizeof(SHA256_CTX)); sha256_init(ctx); while ((read_size = fread(in, 1, 102400, f)) != 0) { sha256_update(ctx, in, read_size); } unsigned char hash[SHA256_BLOCK_SIZE]; sha256_final(ctx, hash); fclose(f); free(in); free(ctx); // We need to convert the bytes in the hash to get a string representation of // its hex values i.e. turn 1001 1111 into the string "9f" Each byte of the // hash is going to turn into two bytes in the final string so we are going to // convert each half byte into a char unsigned int half_byte = 0; int j = 0; // We advance 2 bytes in the string for every one byte of the hash for (int i = 0; i < SHA256_BLOCK_SIZE; i++) { // We transform the first half byte into the second character to keep // each byte from becoming reversed in the final string half_byte = hash[i] & 0b1111; if (half_byte < 10) { res[j + 1] = half_byte + 48; } else { res[j + 1] = half_byte + 87; } hash[i] = hash[i] >> 4; half_byte = hash[i] & 0b1111; if (half_byte < 10) { res[j] = half_byte + 48; } else { res[j] = half_byte + 87; } j += 2; } res[j] = '\0'; } char *vieter_package_to_description(vieter_package *pkg) { vieter_package_info *pkg_info = pkg->info; size_t buff_size = 1024; int small_buff_size = 128; int size_to_be_written; char *aux = malloc(sizeof(char) * small_buff_size); char *description = malloc(sizeof(char) * buff_size); // Helper variable for ADD_ARRAY macro int i; // special case for FILENAME char *ext = NULL; switch (pkg->compression) { case 0: ext = ".tar"; break; case 1: ext = ".tar.gz"; break; case 6: ext = ".tar.xz"; break; case 14: ext = ".tar.zst"; break; } size_to_be_written = snprintf(aux, small_buff_size, "%%FILENAME%%\n%s-%s-%s.pkg%s", pkg_info->name, pkg_info->version, pkg_info->arch, ext); // We neither want to let an arbritrarily long input to overflow the buffer // nor to truncate perfectly valid inputs if (size_to_be_written > small_buff_size) { aux = realloc(aux, size_to_be_written + 1); small_buff_size = size_to_be_written + 1; snprintf(aux, small_buff_size, "%%FILENAME%%\n%s-%s-%s.pkg.tar.zst", pkg_info->name, pkg_info->version, pkg_info->arch); } strcpy(description, aux); ADD_STRING("\n\n%%NAME%%\n%s", name); ADD_STRING("\n\n%%BASE%%\n%s", base); ADD_STRING("\n\n%%VERSION%%\n%s", version); ADD_STRING("\n\n%%DESC%%\n%s", description); ADD_ARRAY("\n\n%%GROUPS%%\n%s", groups); ADD_STRING("\n\n%%CSIZE%%\n%ld", csize); ADD_STRING("\n\n%%ISIZE%%\n%ld", size); char checksum[SHA256_BLOCK_SIZE * 2 + 1]; vieter_package_sha256sum(pkg, checksum); snprintf(aux, small_buff_size, "\n\n%%SHA256SUM%%\n%s", checksum); if (buff_size < strlen(description) + small_buff_size + 1) { description = realloc(description, buff_size * 2); buff_size *= 2; } strcat(description, aux); ADD_STRING("\n\n%%URL%%\n%s", url); ADD_ARRAY("\n\n%%LICENSE%%\n%s", licenses); ADD_STRING("\n\n%%ARCH%%\n%s", arch); ADD_STRING("\n\n%%BUILDDATE%%\n%ld", build_date); ADD_STRING("\n\n%%PACKAGER%%\n%s", packager); ADD_ARRAY("\n\n%%REPLACES%%\n%s", replaces); ADD_ARRAY("\n\n%%CONFLICTS%%\n%s", conflicts); ADD_ARRAY("\n\n%%PROVIDES%%\n%s", provides); ADD_ARRAY("\n\n%%DEPENDS%%\n%s", depends); ADD_ARRAY("\n\n%%OPTDEPENDS%%\n%s", optdepends); ADD_ARRAY("\n\n%%MAKEDEPENDS%%\n%s", makedepends); ADD_ARRAY("\n\n%%CHECKDEPENDS%%\n%s", checkdepends); strcat(description, "\n\n"); free(aux); return description; } void vieter_package_free(vieter_package **ptp) { FREE_STRING((*ptp)->path); vieter_package_info_free((*ptp)->info); vieter_dynarray_free((*ptp)->files); free(*ptp); *ptp = NULL; }