Last active
December 16, 2015 01:39
-
-
Save vodik/5357198 to your computer and use it in GitHub Desktop.
Load archlinux package metadata from a *.pkg.tar.* file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "alpm-simple.h" | |
#include <stdlib.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <err.h> | |
#include <fcntl.h> | |
#include <sys/stat.h> | |
#include <sys/mman.h> | |
#include <alpm.h> | |
#include <archive.h> | |
#include <archive_entry.h> | |
/* {{{ MAGIC */ | |
struct archive_read_buffer { | |
char *line; | |
char *line_offset; | |
size_t line_size; | |
size_t real_line_size; | |
char *block; | |
char *block_offset; | |
size_t block_size; | |
long ret; | |
}; | |
static int archive_fgets(struct archive *a, struct archive_read_buffer *b, size_t entry_size) | |
{ | |
/* ensure we start populating our line buffer at the beginning */ | |
b->line_offset = b->line; | |
while(1) { | |
size_t new, block_remaining; | |
char *eol; | |
/* have we processed this entire block? */ | |
if(b->block + b->block_size == b->block_offset) { | |
int64_t offset; | |
if(b->ret == ARCHIVE_EOF) { | |
/* reached end of archive on the last read, now we are out of data */ | |
return b->ret; | |
} | |
/* zero-copy - this is the entire next block of data. */ | |
b->ret = archive_read_data_block(a, (const void **)&b->block, | |
&b->block_size, &offset); | |
b->block_offset = b->block; | |
block_remaining = b->block_size; | |
/* error, cleanup */ | |
if(b->ret != ARCHIVE_OK) { | |
return b->ret; | |
} | |
} else { | |
block_remaining = b->block + b->block_size - b->block_offset; | |
} | |
/* look through the block looking for EOL characters */ | |
eol = memchr(b->block_offset, '\n', block_remaining); | |
if(!eol) { | |
eol = memchr(b->block_offset, '\0', block_remaining); | |
} | |
/* note: we know eol > b->block_offset and b->line_offset > b->line, | |
* so we know the result is unsigned and can fit in size_t */ | |
new = eol ? (size_t)(eol - b->block_offset) : block_remaining; | |
if((b->line_offset - b->line + new + 1) > entry_size) { | |
return -ERANGE; | |
} | |
if(eol) { | |
size_t len = (size_t)(eol - b->block_offset); | |
memcpy(b->line_offset, b->block_offset, len); | |
b->line_offset[len] = '\0'; | |
b->block_offset = eol + 1; | |
b->real_line_size = b->line_offset + len - b->line; | |
/* this is the main return point; from here you can read b->line */ | |
return ARCHIVE_OK; | |
} else { | |
/* we've looked through the whole block but no newline, copy it */ | |
size_t len = (size_t)(b->block + b->block_size - b->block_offset); | |
b->line_offset = mempcpy(b->line_offset, b->block_offset, len); | |
b->block_offset = b->block + b->block_size; | |
/* there was no new data, return what is left; saved ARCHIVE_EOF will be | |
* returned on next call */ | |
if(len == 0) { | |
b->line_offset[0] = '\0'; | |
b->real_line_size = b->line_offset - b->line; | |
return ARCHIVE_OK; | |
} | |
} | |
} | |
return b->ret; | |
} /* }}} */ | |
static void read_metadata_line(char *buf, alpm_pkg_meta_t *pkg) | |
{ | |
char *var; | |
if (strchr(buf, '=') == NULL) | |
return; | |
var = strsep(&buf, " = "); | |
buf += 2; | |
if (strcmp(var, "pkgname") == 0) | |
pkg->name = strdup(buf); | |
else if (strcmp(var, "pkgver") == 0) | |
pkg->version = strdup(buf); | |
else if (strcmp(var, "pkgdesc") == 0) | |
pkg->desc = strdup(buf); | |
else if (strcmp(var, "url") == 0) | |
pkg->url = strdup(buf); | |
else if (strcmp(var, "builddate") == 0) | |
pkg->builddate = atol(buf); | |
else if (strcmp(var, "packager") == 0) | |
pkg->packager = strdup(buf); | |
else if (strcmp(var, "size") == 0) | |
pkg->isize = atol(buf); | |
else if (strcmp(var, "arch") == 0) | |
pkg->arch = strdup(buf); | |
else if (strcmp(var, "license") == 0) | |
pkg->license = alpm_list_add(pkg->license, strdup(buf)); | |
else if (strcmp(var, "depend") == 0) | |
pkg->depends = alpm_list_add(pkg->depends, strdup(buf)); | |
else if (strcmp(var, "conflict") == 0) | |
pkg->conflicts = alpm_list_add(pkg->conflicts, strdup(buf)); | |
else if (strcmp(var, "provides") == 0) | |
pkg->provides = alpm_list_add(pkg->provides, strdup(buf)); | |
else if (strcmp(var, "optdepend") == 0) | |
pkg->optdepends = alpm_list_add(pkg->optdepends, strdup(buf)); | |
else if (strcmp(var, "makedepend") == 0) | |
pkg->makedepends = alpm_list_add(pkg->makedepends, strdup(buf)); | |
} | |
static void read_metadata(struct archive *a, struct archive_entry *ae, alpm_pkg_meta_t *pkg) | |
{ | |
off_t entry_size = archive_entry_size(ae); | |
struct archive_read_buffer buf = { | |
.line = malloc(entry_size) | |
}; | |
while(archive_fgets(a, &buf, entry_size) == ARCHIVE_OK && buf.real_line_size > 0) { | |
read_metadata_line(buf.line, pkg); | |
} | |
free(buf.line); | |
} | |
int alpm_pkg_load_metadata(const char *filename, alpm_pkg_meta_t **_pkg) | |
{ | |
struct archive *archive = NULL; | |
alpm_pkg_meta_t *pkg; | |
struct stat st; | |
char *memblock = MAP_FAILED; | |
int fd = 0, rc = 0; | |
fd = open(filename, O_RDONLY); | |
if (fd < 0) { | |
if(errno == ENOENT) | |
err(EXIT_FAILURE, "failed to open %s", filename); | |
rc = -errno; | |
goto cleanup; | |
} | |
fstat(fd, &st); | |
memblock = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0); | |
if (memblock == MAP_FAILED) | |
err(EXIT_FAILURE, "failed to mmap package %s", filename); | |
archive = archive_read_new(); | |
archive_read_support_filter_all(archive); | |
archive_read_support_format_all(archive); | |
int r = archive_read_open_memory(archive, memblock, st.st_size); | |
if (r != ARCHIVE_OK) { | |
warnx("%s is not an archive", filename); | |
rc = -1; | |
goto cleanup; | |
} | |
pkg = calloc(1, sizeof(alpm_pkg_meta_t)); | |
for (;;) { | |
struct archive_entry *entry; | |
r = archive_read_next_header(archive, &entry); | |
if (r == ARCHIVE_EOF) { | |
break; | |
} else if (r != ARCHIVE_OK) { | |
errx(EXIT_FAILURE, "failed to read header: %s", archive_error_string(archive)); | |
} | |
const mode_t mode = archive_entry_mode(entry); | |
const char *entry_name = archive_entry_pathname(entry); | |
if (S_ISREG(mode) && strcmp(entry_name, ".PKGINFO") == 0) { | |
read_metadata(archive, entry, pkg); | |
break; | |
} | |
} | |
pkg->filename = strdup(filename); | |
pkg->size = st.st_size; | |
pkg->md5sum = alpm_compute_md5sum(filename); | |
pkg->sha256sum = alpm_compute_sha256sum(filename); | |
*_pkg = pkg; | |
cleanup: | |
if (fd >= 0) | |
close(fd); | |
if (memblock != MAP_FAILED) | |
munmap(memblock, st.st_size); | |
if (archive) { | |
archive_read_close(archive); | |
archive_read_free(archive); | |
} | |
return rc; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
alpm_pkg_meta_t *pkg; | |
if (argc < 2 || alpm_pkg_load_metadata(argv[1], &pkg) < 0) | |
return 1; | |
printf("%s-%s [%s]\n", pkg->name, pkg->version, pkg->arch); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef ALPM_SIMPLE_H | |
#define ALPM_SIMPLE_H | |
#include <alpm_list.h> | |
typedef struct alpm_pkg_meta { | |
char *filename; | |
char *name; | |
char *version; | |
char *desc; | |
char *url; | |
char *packager; | |
char *md5sum; | |
char *sha256sum; | |
char *arch; | |
off_t size; | |
off_t isize; | |
time_t builddate; | |
alpm_list_t *license; | |
alpm_list_t *depends; | |
alpm_list_t *conflicts; | |
alpm_list_t *provides; | |
alpm_list_t *optdepends; | |
alpm_list_t *makedepends; | |
} alpm_pkg_meta_t; | |
int alpm_pkg_load_metadata(const char *filename, alpm_pkg_meta_t **pkg); | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment