Иногда очень хочется поменять что-нибудь в базе RPM, и так, чтобы rpm -V заткнулся. Делается это так:
int update_file(char *fn, char *new_md5, int new_len)
{
int r = 1;
Header h;
rpmts ts = rpmtsCreate();
rpmtsOpenDB(ts, O_RDWR);
rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, fn, 0);
rpmdbSetIteratorRewrite(mi, 1);
while ((h = (Header)rpmdbNextIterator(mi)) != NULL) {
int_32 count, *sizes, *diri;
char **name, **md5s, **dirs;
headerGetEntry(h, RPMTAG_BASENAMES, NULL, (void**)&name, &count);
headerGetEntry(h, RPMTAG_FILEMD5S, NULL, (void**)&md5s, NULL);
headerGetEntry(h, RPMTAG_FILESIZES, NULL, (void**)&sizes, NULL);
headerGetEntry(h, RPMTAG_DIRNAMES, NULL, (void**)&dirs, NULL);
headerGetEntry(h, RPMTAG_DIRINDEXES, NULL, (void**)&diri, NULL);
int i;
for (i = 0; i < count; i++) {
char *file = malloc(strlen(dirs[diri[i]]) + strlen(name[i]) + 1);
strcpy(file, dirs[diri[i]]);
strcat(file, name[i]);
if (! strcmp(file, fn)) {
sizes[i] = new_len;
strcpy(md5s[i], new_md5);
headerModifyEntry(h, RPMTAG_FILEMD5S, RPM_STRING_ARRAY_TYPE, (void**)md5s, count);
headerModifyEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE, (void**)sizes, count);
if (headerIsEntry(h, RPMTAG_SHA1HEADER)) {
DIGEST_CTX ctx;
void *uh;
int_32 uhc;
char *digest = NULL, *p;
uint32_t header_magic[2];
header_magic[0] = 0x01e8ad8e;
header_magic[1] = 0;
headerGetEntry(h, RPMTAG_HEADERIMMUTABLE, NULL, &uh, &uhc);
ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE);
rpmDigestUpdate(ctx, header_magic, sizeof(header_magic));
rpmDigestUpdate(ctx, uh, uhc);
rpmDigestFinal(ctx, (void*)&digest, NULL, 1);
headerGetEntryMinMemory(h, RPMTAG_SHA1HEADER, NULL, (void*)&p, NULL);
strcpy(p, digest);
}
free(file);
rpmdbSetIteratorModified(mi, 1);
r = 0;
break;
}
free(file);
}
}
rpmdbFreeIterator(mi);
rpmtsFree(ts);
return r;
}
Скачать RiPM (форум, регистрация)
Основная проблема была с SHA1 заголовка. rpmlib суровая штука - ни документации, ни примеров, ни проверки ошибок: шаг влево, шаг вправо - SIGSEGV (например, если заменить headerGetEntryMinMemory на headerModifyEntry ;-) А "right thing" - это сделать на headerCopy/Unload/Reload/Unload, вообщем я так и не понял, что с ним нужно сделать, чтобы "закомитить" все изменения и не оторвать его от transaction set. А так работает.
rpm -Uvh /usr/src/redhat/RPMS/i386/foobar-0.1-1.i386.rpm
Preparing... ########################################### [100%]
1:foobar ########################################### [100%]
rpm -Vf /bin/foobar
sleep 1
echo "xxx" >> /bin/foobar
rpm -Vf /bin/foobar
S.5....T /bin/foobar
make: [all] Error 1 (ignored)
gcc -Wall -ggdb -lrpm ripm.c
./a.out /bin/foobar
RPMDB patched
rpm -Vf /bin/foobar
.......T /bin/foobar
make: [all] Error 1 (ignored)
"T", то есть mod. time лучше править не в базе RPM, восстанавливать на самом файле.
Кстати, если кто пользуется integrity check, то базу полагается хранить на ro-носителе.
P.S. Это я к чему. Вот есть у меня звери, которые используют libc, но раз есть libc, значит есть и все остальное ;-)
Типа того. |