build/pack.c: pre-calculate cpio archive size, fail gracefully on >= 4G
The nearest goal is to be able to package files up to 4G, on both 32-bit 64-bit architectures, provided that cpio payload size also does not exceed 4G. More specifically, the limit is 2^32-2, while 2^32-1 remains special (this is not my own invention, I adopt it from rpm.org). If the limit is exceeded, rpmbuild should fail gracefully instead of writing malformed packages with truncated integer tags. (Supporting 4G+ files is a distant goal - neither rpm-4.0 nor apt-rpm can handle them yet. Besides, it's not clear if producing such big packages is even a good idea, unless one wishes to package p0rn.)
This commit is contained in:
parent
a1a075caa2
commit
d85641818b
@ -2023,9 +2023,8 @@ static int finalizeSize(TFI_t fi)
|
||||
return 0;
|
||||
int totalFileSize = 0;
|
||||
int partialHardlinkSets = 0;
|
||||
int i, j;
|
||||
for (i = 0; i < fi->fc; i++) {
|
||||
if (fi->actions[i] == FA_SKIP) // GHOST
|
||||
for (int i = 0; i < fi->fc; i++) {
|
||||
if (fi->actions[i] == FA_SKIP) // %ghost
|
||||
continue;
|
||||
if (!S_ISREG(fi->fsts[i].st_mode))
|
||||
continue;
|
||||
@ -2035,7 +2034,8 @@ static int finalizeSize(TFI_t fi)
|
||||
}
|
||||
assert(fi->fsts[i].st_nlink > 1);
|
||||
int found = 0;
|
||||
for (j = 0; j < i; j++) {
|
||||
// Look backwards, more likely in the same dir.
|
||||
for (int j = i - 1; j >= 0; j--) {
|
||||
if (fi->actions[j] == FA_SKIP)
|
||||
continue;
|
||||
if (fi->fsts[i].st_dev != fi->fsts[j].st_dev)
|
||||
@ -2050,7 +2050,7 @@ static int finalizeSize(TFI_t fi)
|
||||
// first hardlink occurrence
|
||||
totalFileSize += fi->fsts[i].st_size;
|
||||
int nlink = 1;
|
||||
for (j = i + 1; j < fi->fc; j++) {
|
||||
for (int j = i + 1; j < fi->fc; j++) {
|
||||
if (fi->actions[j] == FA_SKIP)
|
||||
continue;
|
||||
if (fi->fsts[i].st_dev != fi->fsts[j].st_dev)
|
||||
|
59
build/pack.c
59
build/pack.c
@ -380,6 +380,41 @@ static int rpmLeadVersion(void)
|
||||
return rpmlead_version;
|
||||
}
|
||||
|
||||
// Estimate the uncompressed size of cpio archive before it is actually written.
|
||||
static uint64_t calcArchiveSize(TFI_t fi)
|
||||
{
|
||||
uint64_t size = 124; // cpio trailer
|
||||
for (int i = 0; i < fi->fc; i++) {
|
||||
if (fi->actions[i] == FA_SKIP) // %ghost
|
||||
continue;
|
||||
size += 110; // cpio header
|
||||
size += strlen(fi->apath[i]) + 1;
|
||||
size = (size + 3) & ~(uint64_t)3;
|
||||
if (!S_ISREG(fi->fsts[i].st_mode) && !S_ISLNK(fi->fsts[i].st_mode))
|
||||
continue;
|
||||
if (fi->fsts[i].st_nlink > 1) {
|
||||
// Only the first hardlink occurrence counts.
|
||||
int found = 0;
|
||||
for (int j = i - 1; j >= 0; j--) {
|
||||
if (fi->actions[j] == FA_SKIP)
|
||||
continue;
|
||||
if (fi->fsts[i].st_dev != fi->fsts[j].st_dev)
|
||||
continue;
|
||||
if (fi->fsts[i].st_ino != fi->fsts[j].st_ino)
|
||||
continue;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
if (found)
|
||||
continue;
|
||||
}
|
||||
// Valid for symlinks, target not null-terminated.
|
||||
size += fi->fsts[i].st_size;
|
||||
size = (size + 3) & ~(uint64_t)3;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int writeRPM(Header *hdrp, const char *fileName, int type,
|
||||
CSA_t csa, char *passPhrase, const char **cookie)
|
||||
{
|
||||
@ -394,6 +429,13 @@ int writeRPM(Header *hdrp, const char *fileName, int type,
|
||||
Header sig = NULL;
|
||||
int rc = 0;
|
||||
|
||||
uint64_t archiveSize = calcArchiveSize(csa->cpioList);
|
||||
if (archiveSize >= UINT32_MAX) { // sic, UINT32_MAX proper is kind of special
|
||||
rpmError(RPMERR_FWRITE, "cpio archive too big - %uM\n",
|
||||
(unsigned)(archiveSize>>20));
|
||||
return RPMERR_FWRITE;
|
||||
}
|
||||
|
||||
/* Transfer header reference form *hdrp to h. */
|
||||
h = headerLink(*hdrp);
|
||||
*hdrp = headerFree(*hdrp);
|
||||
@ -516,10 +558,21 @@ int writeRPM(Header *hdrp, const char *fileName, int type,
|
||||
* archive size within the header region.
|
||||
*/
|
||||
if (Fileno(csa->cpioFdIn) < 0) {
|
||||
if (archiveSize != csa->cpioArchiveSize) {
|
||||
rpmError(RPMERR_FWRITE, "wrong archive size: %" PRIu64 " -> %u\n",
|
||||
archiveSize, csa->cpioArchiveSize);
|
||||
rc = RPMERR_FWRITE;
|
||||
goto exit;
|
||||
}
|
||||
HGE_t hge = (HGE_t)headerGetEntryMinMemory;
|
||||
int_32 * archiveSize;
|
||||
if (hge(h, RPMTAG_ARCHIVESIZE, NULL, (void *)&archiveSize, NULL))
|
||||
*archiveSize = csa->cpioArchiveSize;
|
||||
uint32_t *sizep;
|
||||
if (hge(h, RPMTAG_ARCHIVESIZE, NULL, (void *)&sizep, NULL))
|
||||
*sizep = csa->cpioArchiveSize;
|
||||
else {
|
||||
rpmError(RPMERR_FWRITE, "cannot add RPMTAG_ARCHIVESIZE\n");
|
||||
rc = RPMERR_FWRITE;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
(void) Fflush(fd);
|
||||
|
Loading…
Reference in New Issue
Block a user