mirror of
https://github.com/openSUSE/libsolv.git
synced 2026-02-05 12:45:46 +01:00
290 lines
5.4 KiB
C
290 lines
5.4 KiB
C
/*
|
|
* Copyright (c) 2012, SUSE LLC
|
|
*
|
|
* This program is licensed under the BSD license, read LICENSE.BSD
|
|
* for further information
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <stdio.h>
|
|
|
|
#include "util.h"
|
|
#include "tarhead.h"
|
|
|
|
#define MAX_LINE_SIZE 0x1000000
|
|
|
|
static long long parsenum(unsigned char *p, int cnt)
|
|
{
|
|
long long x = 0;
|
|
if (!cnt)
|
|
return -1;
|
|
if (*p & 0x80)
|
|
{
|
|
/* binary format */
|
|
x = *p & 0x40 ? (-1 << 8 | *p) : (*p ^ 0x80);
|
|
while (--cnt > 0)
|
|
x = (x << 8) | *p++;
|
|
return x;
|
|
}
|
|
while (cnt > 0 && (*p == ' ' || *p == '\t'))
|
|
cnt--, p++;
|
|
if (*p == '-')
|
|
return -1;
|
|
for (; cnt > 0 && *p >= '0' && *p < '8'; cnt--, p++)
|
|
x = (x << 3) | (*p - '0');
|
|
return x;
|
|
}
|
|
|
|
static int readblock(FILE *fp, unsigned char *blk)
|
|
{
|
|
int r, l = 0;
|
|
while (l < 512)
|
|
{
|
|
r = fread(blk + l, 1, 512 - l, fp);
|
|
if (r <= 0)
|
|
return -1;
|
|
l += r;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void tarhead_skip(struct tarhead *th)
|
|
{
|
|
for (; th->length > 0; th->length -= 512)
|
|
{
|
|
if (readblock(th->fp, th->blk))
|
|
{
|
|
th->eof = 1;
|
|
th->length = 0;
|
|
return;
|
|
}
|
|
}
|
|
th->length = 0;
|
|
th->off = th->end = 0;
|
|
}
|
|
|
|
void tarhead_init(struct tarhead *th, FILE *fp)
|
|
{
|
|
memset(th, 0, sizeof(*th));
|
|
th->fp = fp;
|
|
}
|
|
|
|
void tarhead_free(struct tarhead *th)
|
|
{
|
|
solv_free(th->path);
|
|
}
|
|
|
|
int tarhead_next(struct tarhead *th)
|
|
{
|
|
int l, type;
|
|
long long length;
|
|
|
|
th->path = solv_free(th->path);
|
|
th->ispax = 0;
|
|
th->type = 0;
|
|
th->length = 0;
|
|
th->off = 0;
|
|
th->end = 0;
|
|
if (th->eof)
|
|
return 0;
|
|
for (;;)
|
|
{
|
|
int r = readblock(th->fp, th->blk);
|
|
if (r)
|
|
{
|
|
if (feof(th->fp))
|
|
{
|
|
th->eof = 1;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
if (th->blk[0] == 0)
|
|
{
|
|
th->eof = 1;
|
|
return 0;
|
|
}
|
|
length = parsenum(th->blk + 124, 12);
|
|
if (length < 0)
|
|
return -1;
|
|
type = 0;
|
|
switch (th->blk[156])
|
|
{
|
|
case 'S': case '0':
|
|
type = 1; /* file */
|
|
break;
|
|
case '1':
|
|
/* hard link, special length magic... */
|
|
if (!th->ispax)
|
|
length = 0;
|
|
break;
|
|
case '5':
|
|
type = 2; /* dir */
|
|
break;
|
|
case '2': case '3': case '4': case '6':
|
|
length = 0;
|
|
break;
|
|
case 'X': case 'x': case 'L':
|
|
{
|
|
char *data, *pp;
|
|
if (length < 1 || length >= 1024 * 1024)
|
|
return -1;
|
|
data = pp = solv_malloc(length + 512);
|
|
for (l = length; l > 0; l -= 512, pp += 512)
|
|
if (readblock(th->fp, (unsigned char *)pp))
|
|
{
|
|
solv_free(data);
|
|
return -1;
|
|
}
|
|
data[length] = 0;
|
|
type = 3; /* extension */
|
|
if (th->blk[156] == 'L')
|
|
{
|
|
solv_free(th->path);
|
|
th->path = data;
|
|
length = 0;
|
|
break;
|
|
}
|
|
pp = data;
|
|
while (length > 0)
|
|
{
|
|
int ll = 0;
|
|
for (l = 0; l < length && pp[l] >= '0' && pp[l] <= '9'; l++)
|
|
ll = ll * 10 + (pp[l] - '0');
|
|
if (l == length || pp[l] != ' ' || ll < 1 || ll > length || pp[ll - 1] != '\n')
|
|
{
|
|
solv_free(data);
|
|
return -1;
|
|
}
|
|
length -= ll;
|
|
pp += l + 1;
|
|
ll -= l + 1;
|
|
pp[ll - 1] = 0;
|
|
if (!strncmp(pp, "path=", 5))
|
|
{
|
|
solv_free(th->path);
|
|
th->path = solv_strdup(pp + 5);
|
|
}
|
|
pp += ll;
|
|
}
|
|
solv_free(data);
|
|
th->ispax = 1;
|
|
length = 0;
|
|
break;
|
|
}
|
|
default:
|
|
type = 3; /* extension */
|
|
break;
|
|
}
|
|
if ((type == 1 || type == 2) && !th->path)
|
|
{
|
|
char path[157];
|
|
memcpy(path, th->blk, 156);
|
|
path[156] = 0;
|
|
if (!memcmp(th->blk + 257, "ustar\0\060\060", 8) && !th->path && th->blk[345])
|
|
{
|
|
/* POSIX ustar with prefix */
|
|
char prefix[156];
|
|
memcpy(prefix, th->blk + 345, 155);
|
|
prefix[155] = 0;
|
|
l = strlen(prefix);
|
|
if (l && prefix[l - 1] == '/')
|
|
prefix[l - 1] = 0;
|
|
th->path = solv_dupjoin(prefix, "/", path);
|
|
}
|
|
else
|
|
th->path = solv_dupjoin(path, 0, 0);
|
|
}
|
|
if (type == 1 || type == 2)
|
|
{
|
|
l = strlen(th->path);
|
|
if (l && th->path[l - 1] == '/')
|
|
{
|
|
if (l > 1)
|
|
th->path[l - 1] = 0;
|
|
type = 2;
|
|
}
|
|
}
|
|
if (type != 3)
|
|
break;
|
|
while (length > 0)
|
|
{
|
|
r = readblock(th->fp, th->blk);
|
|
if (r)
|
|
return r;
|
|
length -= 512;
|
|
}
|
|
}
|
|
th->type = type;
|
|
th->length = length;
|
|
return 1;
|
|
}
|
|
|
|
size_t tarhead_gets(struct tarhead *th, char **linep , size_t *allocsizep)
|
|
{
|
|
char *line = *linep;
|
|
size_t lsize = allocsizep ? *allocsizep : 0, size = 0;
|
|
int i;
|
|
|
|
if (th->eof)
|
|
return 0;
|
|
for (;;)
|
|
{
|
|
size_t fsize = lsize - size;
|
|
if (fsize < 2)
|
|
{
|
|
if (lsize >= MAX_LINE_SIZE)
|
|
{
|
|
th->eof = 1;
|
|
return 0;
|
|
}
|
|
line = *linep = solv_realloc(line, lsize += 1024);
|
|
if (allocsizep)
|
|
*allocsizep = lsize;
|
|
fsize = lsize - size;
|
|
}
|
|
for (i = th->off; i < th->end && fsize > 1;)
|
|
{
|
|
fsize--;
|
|
if ((line[size++] = th->blk[i++]) == '\n')
|
|
{
|
|
th->off = i;
|
|
line[size] = 0;
|
|
return size;
|
|
}
|
|
}
|
|
/* end of block reached, read next block */
|
|
th->off = i;
|
|
if (th->off < th->end)
|
|
continue;
|
|
if (!th->path)
|
|
{
|
|
/* fake entry */
|
|
th->off = 0;
|
|
th->end = fread(th->blk, 1, 512, th->fp);
|
|
if (th->end <= 0)
|
|
{
|
|
th->eof = 1;
|
|
if (th->end < 0)
|
|
return 0;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
if (th->length <= 0)
|
|
break; /* reached end of entry */
|
|
if (readblock(th->fp, th->blk))
|
|
{
|
|
th->eof = 1;
|
|
return 0;
|
|
}
|
|
th->off = 0;
|
|
th->end = th->length > 512 ? 512 : th->length;
|
|
th->length -= th->end;
|
|
}
|
|
line[size] = 0;
|
|
return size;
|
|
}
|
|
|