mirror of https://github.com/pygos/pkg-utils.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
6.0 KiB
316 lines
6.0 KiB
/* SPDX-License-Identifier: ISC */ |
|
#include "pack.h" |
|
|
|
static char *skipspace(char *str) |
|
{ |
|
while (isspace(*str)) |
|
++str; |
|
return str; |
|
} |
|
|
|
static void oom(const char *filename, size_t linenum) |
|
{ |
|
input_file_complain(filename, linenum, "out of memory"); |
|
} |
|
|
|
static image_entry_t *filelist_mkentry(char *line, const char *filename, |
|
size_t linenum, mode_t filetype) |
|
{ |
|
image_entry_t *ent; |
|
size_t i; |
|
|
|
ent = calloc(1, sizeof(*ent)); |
|
if (ent == NULL) { |
|
oom(filename, linenum); |
|
return NULL; |
|
} |
|
|
|
/* name */ |
|
for (i = 0; !isspace(line[i]) && line[i] != '\0'; ++i) |
|
; |
|
|
|
if (!isspace(line[i])) { |
|
input_file_complain(filename, linenum, |
|
"expected space after file name"); |
|
goto fail; |
|
} |
|
|
|
ent->name = calloc(1, i + 1); |
|
if (ent->name == NULL) { |
|
oom(filename, linenum); |
|
goto fail; |
|
} |
|
|
|
memcpy(ent->name, line, i); |
|
if (canonicalize_name(ent->name)) { |
|
input_file_complain(filename, linenum, "invalid file name"); |
|
goto fail; |
|
} |
|
|
|
if (ent->name[0] == '\0') { |
|
input_file_complain(filename, linenum, |
|
"refusing to add entry for '/'"); |
|
goto fail; |
|
} |
|
|
|
if (strlen(ent->name) > 0xFFFF) { |
|
input_file_complain(filename, linenum, "name too long"); |
|
goto fail; |
|
} |
|
|
|
line = skipspace(line + i); |
|
|
|
/* mode */ |
|
if (!isdigit(*line)) { |
|
input_file_complain(filename, linenum, |
|
"expected numeric mode after file name"); |
|
goto fail; |
|
} |
|
|
|
while (isdigit(*line)) { |
|
if (*line > '7') { |
|
input_file_complain(filename, linenum, |
|
"mode must be octal number"); |
|
goto fail; |
|
} |
|
ent->mode = (ent->mode << 3) | (*(line++) - '0'); |
|
} |
|
|
|
if (!isspace(*line)) { |
|
input_file_complain(filename, linenum, |
|
"expected space after file mode"); |
|
goto fail; |
|
} |
|
|
|
line = skipspace(line); |
|
|
|
ent->mode = (ent->mode & ~S_IFMT) | filetype; |
|
|
|
/* uid */ |
|
if (!isdigit(*line)) { |
|
input_file_complain(filename, linenum, |
|
"expected numeric UID after mode"); |
|
goto fail; |
|
} |
|
|
|
while (isdigit(*line)) |
|
ent->uid = (ent->uid * 10) + (*(line++) - '0'); |
|
|
|
if (!isspace(*line)) { |
|
input_file_complain(filename, linenum, |
|
"expected space after UID"); |
|
goto fail; |
|
} |
|
|
|
line = skipspace(line); |
|
|
|
/* gid */ |
|
if (!isdigit(*line)) { |
|
input_file_complain(filename, linenum, |
|
"expected numeric GID after UID"); |
|
goto fail; |
|
} |
|
|
|
while (isdigit(*line)) |
|
ent->gid = (ent->gid * 10) + (*(line++) - '0'); |
|
|
|
line = skipspace(line); |
|
|
|
/* remove processed data */ |
|
memmove(line, line, strlen(line) + 1); |
|
return ent; |
|
fail: |
|
free(ent->name); |
|
free(ent); |
|
return NULL; |
|
} |
|
|
|
int filelist_mkdir(char *line, const char *filename, |
|
size_t linenum, void *obj) |
|
{ |
|
image_entry_t *ent = filelist_mkentry(line,filename,linenum,S_IFDIR); |
|
image_entry_t **listptr = obj; |
|
|
|
if (ent == NULL) |
|
return -1; |
|
|
|
ent->next = *listptr; |
|
*listptr = ent; |
|
return 0; |
|
} |
|
|
|
int filelist_mkslink(char *line, const char *filename, |
|
size_t linenum, void *obj) |
|
{ |
|
image_entry_t *ent = filelist_mkentry(line,filename,linenum,S_IFLNK); |
|
image_entry_t **listptr = obj; |
|
|
|
if (ent == NULL) |
|
return -1; |
|
|
|
ent->data.symlink.target = strdup(line); |
|
if (ent->data.symlink.target == NULL) { |
|
oom(filename, linenum); |
|
goto fail; |
|
} |
|
|
|
if (strlen(ent->data.symlink.target) > 0xFFFF) { |
|
input_file_complain(filename, linenum, |
|
"symlink target too long"); |
|
goto fail; |
|
} |
|
|
|
ent->next = *listptr; |
|
*listptr = ent; |
|
return 0; |
|
fail: |
|
image_entry_free(ent); |
|
return -1; |
|
} |
|
|
|
int filelist_mkfile(char *line, const char *filename, |
|
size_t linenum, void *obj) |
|
{ |
|
image_entry_t *ent = filelist_mkentry(line,filename,linenum,S_IFREG); |
|
image_entry_t **listptr = obj; |
|
const char *ptr; |
|
struct stat sb; |
|
|
|
if (ent == NULL) |
|
return -1; |
|
|
|
if (line[0] == '\0') { |
|
ptr = strrchr(filename, '/'); |
|
|
|
if (ptr == NULL) { |
|
ent->data.file.location = strdup(ent->name); |
|
} else { |
|
asprintf(&ent->data.file.location, "%.*s/%s", |
|
(int)(ptr - filename), filename, |
|
ent->name); |
|
} |
|
} else { |
|
ent->data.file.location = strdup(line); |
|
} |
|
|
|
if (ent->data.file.location == NULL) { |
|
oom(filename, linenum); |
|
goto fail; |
|
} |
|
|
|
if (stat(ent->data.file.location, &sb) != 0) { |
|
perror(ent->data.file.location); |
|
goto fail; |
|
} |
|
|
|
if (sizeof(off_t) > sizeof(uint64_t) && |
|
sb.st_size > (off_t)(~((uint64_t)0))) { |
|
input_file_complain(filename, linenum, |
|
"input file is too big"); |
|
goto fail; |
|
} |
|
|
|
ent->data.file.size = sb.st_size; |
|
|
|
ent->next = *listptr; |
|
*listptr = ent; |
|
return 0; |
|
fail: |
|
image_entry_free(ent); |
|
return -1; |
|
} |
|
|
|
static int filelist_mknod(char *line, const char *filename, |
|
size_t linenum, void *obj) |
|
{ |
|
image_entry_t **listptr = obj, *ent; |
|
unsigned int maj, min; |
|
char *ptr; |
|
|
|
ent = filelist_mkentry(line, filename, linenum, S_IFCHR); |
|
if (ent == NULL) |
|
return -1; |
|
|
|
switch (line[0]) { |
|
case 'c': |
|
case 'C': |
|
break; |
|
case 'b': |
|
case 'B': |
|
ent->mode = (ent->mode & (~S_IFMT)) | S_IFBLK; |
|
break; |
|
default: |
|
goto fail; |
|
} |
|
|
|
if (!isspace(line[1])) |
|
goto fail; |
|
|
|
ptr = line + 1; |
|
while (isspace(*ptr)) |
|
++ptr; |
|
|
|
if (sscanf(ptr, "%u %u", &maj, &min) != 2) |
|
goto fail; |
|
|
|
ent->data.device.devno = makedev(maj, min); |
|
|
|
ent->next = *listptr; |
|
*listptr = ent; |
|
return 0; |
|
fail: |
|
image_entry_free(ent); |
|
input_file_complain(filename, linenum, |
|
"error in device specification"); |
|
return -1; |
|
} |
|
|
|
static const keyword_handler_t line_hooks[] = { |
|
{ "file", filelist_mkfile }, |
|
{ "dir", filelist_mkdir }, |
|
{ "slink", filelist_mkslink }, |
|
{ "nod", filelist_mknod }, |
|
}; |
|
|
|
#define NUM_LINE_HOOKS (sizeof(line_hooks) / sizeof(line_hooks[0])) |
|
|
|
static int alloc_file_ids(image_entry_t *list) |
|
{ |
|
image_entry_t *ent; |
|
uint64_t file_id = 0; |
|
|
|
for (ent = list; ent != NULL; ent = ent->next) { |
|
if (!S_ISREG(ent->mode)) |
|
continue; |
|
|
|
if (file_id > 0x00000000FFFFFFFFUL) { |
|
fprintf(stderr, "too many input files\n"); |
|
return -1; |
|
} |
|
|
|
ent->data.file.id = file_id++; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
int filelist_read(const char *filename, image_entry_t **out) |
|
{ |
|
image_entry_t *list = NULL; |
|
|
|
if (process_file(filename, line_hooks, NUM_LINE_HOOKS, &list)) |
|
goto fail; |
|
|
|
if (list != NULL) { |
|
list = image_entry_sort(list); |
|
|
|
if (alloc_file_ids(list)) |
|
goto fail; |
|
} |
|
|
|
*out = list; |
|
return 0; |
|
fail: |
|
image_entry_free_list(list); |
|
return -1; |
|
}
|
|
|