5e63f2a5e7
- Remove unused CheckSymLinks() function to fix build problems under DragonFly BSD. This fixes PR pkg/44862 by Francois Tigeot.
658 lines
14 KiB
C
658 lines
14 KiB
C
/* $NetBSD: rpm2pkg.c,v 1.21 2011/04/12 22:12:42 tron Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2001-2011 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Matthias Scheler.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "fileio.h"
|
|
#include "package-list.h"
|
|
#include "parse-rpm.h"
|
|
|
|
/* Structure of a cpio(1) archive. */
|
|
#define C_IRUSR 0000400
|
|
#define C_IWUSR 0000200
|
|
#define C_IXUSR 0000100
|
|
#define C_IRGRP 0000040
|
|
#define C_IWGRP 0000020
|
|
#define C_IXGRP 0000010
|
|
#define C_IROTH 0000004
|
|
#define C_IWOTH 0000002
|
|
#define C_IXOTH 0000001
|
|
#define C_ISUID 0004000
|
|
#define C_ISGID 0002000
|
|
#define C_ISVTX 0001000
|
|
#define C_ISDIR 0040000
|
|
#define C_ISREG 0100000
|
|
#define C_ISCHR 0020000
|
|
#define C_ISLNK 0120000
|
|
|
|
static const unsigned char CPIOMagic[] = {'0','7','0','7','0','1'};
|
|
|
|
#define CPIO_END_MARKER "TRAILER!!!"
|
|
#define CPIO_FIELD_LENGTH 8
|
|
|
|
#define CPIO_HDR_INODE 0
|
|
#define CPIO_HDR_MODE 1
|
|
#define CPIO_HDR_FILESIZE 6
|
|
#define CPIO_HDR_NAMESIZE 11
|
|
#define CPIO_NUM_HEADERS 13
|
|
|
|
#define CP_IFMT 0170000
|
|
|
|
typedef struct ModeMapStruct {
|
|
unsigned long mm_CPIOMode;
|
|
mode_t mm_SysMode;
|
|
} ModeMap;
|
|
|
|
ModeMap ModeMapTab[] = {
|
|
{C_IRUSR, S_IRUSR},
|
|
{C_IWUSR, S_IWUSR},
|
|
{C_IXUSR, S_IXUSR},
|
|
{C_IRGRP, S_IRGRP},
|
|
{C_IWGRP, S_IWGRP},
|
|
{C_IXGRP, S_IXGRP},
|
|
{C_IROTH, S_IROTH},
|
|
{C_IWOTH, S_IWOTH},
|
|
{C_IXOTH, S_IXOTH},
|
|
{C_ISUID, S_ISUID},
|
|
{C_ISGID, S_ISGID},
|
|
{C_ISVTX, S_ISVTX},
|
|
{0, 0}
|
|
};
|
|
|
|
static bool
|
|
SkipAndAlign(FileHandle *fh, off_t skip)
|
|
{
|
|
off_t old_pos, new_pos;
|
|
char buffer[1 << 16];
|
|
|
|
old_pos = FileHandleGetPos(fh);
|
|
new_pos = (old_pos + skip + 3) & ~3;
|
|
if (old_pos == new_pos)
|
|
return true;
|
|
|
|
while (old_pos < new_pos) {
|
|
off_t length;
|
|
size_t chunk;
|
|
|
|
length = new_pos - old_pos;
|
|
chunk = (length > (off_t)sizeof(buffer)) ?
|
|
(off_t)sizeof(buffer) : length;
|
|
if (!FileHandleRead(fh, buffer, chunk))
|
|
return false;
|
|
|
|
old_pos = FileHandleGetPos(fh);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static char *
|
|
StrCat(const char *prefix, const char *suffix)
|
|
{
|
|
size_t prefix_length;
|
|
char *str;
|
|
|
|
prefix_length = strlen(prefix);
|
|
if ((str = malloc(prefix_length + strlen(suffix) + 1)) == NULL) {
|
|
perror("malloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
(void)memcpy(str, prefix, prefix_length);
|
|
(void)strcpy(&str[prefix_length], suffix);
|
|
|
|
return str;
|
|
}
|
|
|
|
static char **
|
|
ArrayAdd(char **array, char *string)
|
|
{
|
|
size_t old;
|
|
|
|
old = 0;
|
|
if (array != NULL) {
|
|
while (array[old] != NULL)
|
|
old++;
|
|
}
|
|
|
|
if ((array = realloc(array, sizeof(char *) * (old + 2))) == NULL) {
|
|
perror("realloc");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
array[old++] = string;
|
|
array[old] = NULL;
|
|
|
|
return array;
|
|
}
|
|
|
|
static void
|
|
Usage(char *Progname)
|
|
{
|
|
(void)fprintf(stderr,
|
|
"Usage: %s [-d directory] [-f packlist] [[-i ignorepath] ...]\n"
|
|
" [-p prefix] [-s stripcount] rpmfile [...]\n",
|
|
Progname);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
static char *
|
|
GetData(FileHandle *In, unsigned long Length)
|
|
{
|
|
char *Ptr;
|
|
|
|
if ((Ptr = malloc(Length + 1)) != NULL) {
|
|
if (FileHandleRead(In, Ptr, Length) && SkipAndAlign(In, 0)) {
|
|
Ptr[Length] = '\0';
|
|
return Ptr;
|
|
}
|
|
free(Ptr);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static bool
|
|
GetCPIOHeader(FileHandle *in, unsigned long *fields, char **name)
|
|
{
|
|
char buffer[CPIO_NUM_HEADERS][CPIO_FIELD_LENGTH];
|
|
char header[CPIO_FIELD_LENGTH + 1];
|
|
size_t i, j;
|
|
unsigned long namelen;
|
|
|
|
*name = NULL;
|
|
|
|
if (!FileHandleRead(in, buffer, sizeof(CPIOMagic)))
|
|
return false;
|
|
if (memcmp(buffer, CPIOMagic, sizeof(CPIOMagic)) != 0)
|
|
return false;
|
|
|
|
if (!FileHandleRead(in, buffer, sizeof(buffer)))
|
|
return false;
|
|
|
|
header[CPIO_FIELD_LENGTH] = '\0';
|
|
for (i = 0; i < CPIO_NUM_HEADERS; i++) {
|
|
for (j = 0; j < CPIO_FIELD_LENGTH; j++) {
|
|
if (!isxdigit((unsigned char)buffer[i][j]))
|
|
return false;
|
|
header[j] = buffer[i][j];
|
|
}
|
|
|
|
fields[i] = strtoul(header, NULL, 16);
|
|
}
|
|
|
|
namelen = fields[CPIO_HDR_NAMESIZE];
|
|
if ((*name = GetData(in, namelen)) == NULL)
|
|
return false;
|
|
return ((*name)[namelen - 1] == '\0');
|
|
}
|
|
|
|
static mode_t
|
|
ConvertMode(unsigned long cpio_mode)
|
|
{
|
|
mode_t mode;
|
|
ModeMap *ptr;
|
|
|
|
for (ptr = ModeMapTab, mode = 0; ptr->mm_CPIOMode != 0; ptr++) {
|
|
if (cpio_mode & ptr->mm_CPIOMode)
|
|
mode |= ptr->mm_SysMode;
|
|
}
|
|
|
|
return mode;
|
|
}
|
|
|
|
static bool
|
|
MakeTargetDir(char *Name, PListEntry **Dirs)
|
|
{
|
|
char *Basename;
|
|
PListEntry *Dir;
|
|
struct stat Stat;
|
|
int Result;
|
|
|
|
if ((Basename = strrchr(Name, '/')) == NULL)
|
|
return true;
|
|
|
|
*Basename = '\0';
|
|
if ((Dir = PListFind(*Dirs, Name)) != NULL) {
|
|
*Basename = '/';
|
|
Dir->pe_DirEmpty = false;
|
|
return true;
|
|
}
|
|
|
|
if (!MakeTargetDir(Name, Dirs)) {
|
|
*Basename = '/';
|
|
return false;
|
|
}
|
|
|
|
if (stat(Name, &Stat) == 0) {
|
|
Result = S_ISDIR(Stat.st_mode);
|
|
} else if (errno != ENOENT) {
|
|
Result = false;
|
|
} else if ((Result = (mkdir(Name, S_IRWXU|S_IRWXG|S_IRWXO) == 0))) {
|
|
(void)PListInsert(Dirs, Name);
|
|
}
|
|
|
|
*Basename = '/';
|
|
return Result;
|
|
}
|
|
|
|
static bool
|
|
MakeDir(char *name, mode_t mode, bool *old_dir)
|
|
{
|
|
struct stat sb;
|
|
|
|
*old_dir = false;
|
|
if (mkdir(name, mode) == 0)
|
|
return true;
|
|
|
|
if (errno != EEXIST || lstat(name, &sb) < 0 || !S_ISDIR(sb.st_mode))
|
|
return false;
|
|
|
|
*old_dir = true;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
MakeSymLink(char *Link, char *Name)
|
|
{
|
|
struct stat Stat;
|
|
|
|
if (symlink(Link, Name) == 0) return true;
|
|
|
|
if ((errno != EEXIST) || (lstat(Name, &Stat) < 0) ||
|
|
!S_ISLNK(Stat.st_mode)) {
|
|
return false;
|
|
}
|
|
|
|
return ((unlink(Name) == 0) && (symlink(Link, Name) == 0));
|
|
}
|
|
|
|
static bool
|
|
WriteFile(FileHandle *in, char *name, mode_t mode, unsigned long length,
|
|
const char *link_target)
|
|
{
|
|
int outfd;
|
|
struct stat sb;
|
|
char buffer[1 << 16];
|
|
|
|
if ((lstat(name, &sb) == 0) &&
|
|
(!S_ISREG(sb.st_mode) || (unlink(name) < 0))) {
|
|
return false;
|
|
}
|
|
|
|
if (link_target != NULL) {
|
|
if (link(link_target, name) < 0)
|
|
return false;
|
|
outfd = open(name, O_WRONLY, mode);
|
|
} else {
|
|
outfd = open(name, O_WRONLY|O_CREAT, mode);
|
|
}
|
|
if (outfd < 0)
|
|
return false;
|
|
|
|
while (length > 0) {
|
|
ssize_t chunk;
|
|
|
|
chunk = (length > sizeof(buffer)) ? sizeof(buffer) : length;
|
|
if (!FileHandleRead(in, buffer, chunk) ||
|
|
write(outfd, buffer, chunk) != chunk)
|
|
break;
|
|
length -= chunk;
|
|
}
|
|
|
|
if (close(outfd) == 0 && length == 0)
|
|
return SkipAndAlign(in, 0);
|
|
|
|
(void)unlink(name);
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
CheckPrefix(const char *prefix, char *name)
|
|
{
|
|
size_t length;
|
|
|
|
length = strlen(prefix);
|
|
return ((strncmp(prefix, name, length) == 0) &&
|
|
((name[length] == '\0') || (name[length] == '/')));
|
|
}
|
|
|
|
static bool
|
|
StripPrefix(char *name, int stripcount)
|
|
{
|
|
char *new_name;
|
|
|
|
if (stripcount <= 0)
|
|
return true;
|
|
|
|
new_name = name;
|
|
while (stripcount-- > 0) {
|
|
new_name = strchr(new_name, '/');
|
|
if (new_name == NULL)
|
|
return false;
|
|
new_name++;
|
|
}
|
|
(void)memmove(name, new_name, strlen(new_name) + 1);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
ProcessRPM(const char *filename, PListEntry **files, PListEntry **dirs,
|
|
char **ignore, const char *prefix, int stripcount)
|
|
{
|
|
int fd;
|
|
FileHandle *in;
|
|
PListEntry *last;
|
|
|
|
if ((fd = open(filename, O_RDONLY, 0)) < 0) {
|
|
perror(filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (!IsRPMFile(fd)) {
|
|
(void)fprintf(stderr, "%s: file is not an RPM package.\n",
|
|
filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if ((in = OpenRPM(&fd)) == NULL) {
|
|
(void)fprintf(stderr, "%s: cannot get RPM data.\n", filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (fd >= 0) {
|
|
(void)close(fd);
|
|
fd = -1;
|
|
}
|
|
|
|
last = NULL;
|
|
for (;;) {
|
|
unsigned long fields[CPIO_NUM_HEADERS];
|
|
char *name;
|
|
mode_t mode;
|
|
unsigned long length;
|
|
|
|
if (!GetCPIOHeader(in, fields, &name)) {
|
|
(void)fprintf(stderr,
|
|
"%s: error in cpio header.\n",
|
|
filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (strcmp(name, CPIO_END_MARKER) == 0) {
|
|
free(name);
|
|
break;
|
|
}
|
|
if (*name == '\0')
|
|
fields[CPIO_HDR_MODE] = 0;
|
|
|
|
if (ignore != NULL) {
|
|
char **ptr;
|
|
|
|
for (ptr = ignore; *ptr != NULL; ptr++) {
|
|
if (CheckPrefix(*ptr, name)) {
|
|
fields[CPIO_HDR_MODE] = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fields[CPIO_HDR_MODE] != 0 &&
|
|
!StripPrefix(name, stripcount)) {
|
|
(void)fprintf(stderr,
|
|
"%s: Leading path to strip too "
|
|
"big (-s %d)\n",
|
|
filename, stripcount);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (prefix != NULL) {
|
|
char *fullname;
|
|
|
|
fullname = StrCat(prefix, name);
|
|
free(name);
|
|
name = fullname;
|
|
}
|
|
|
|
mode = ConvertMode(fields[CPIO_HDR_MODE]);
|
|
length = fields[CPIO_HDR_FILESIZE];
|
|
switch (fields[CPIO_HDR_MODE] & CP_IFMT) {
|
|
case C_ISDIR: {
|
|
PListEntry *dir;
|
|
bool old_dir;
|
|
|
|
if (length != 0) {
|
|
(void)fprintf(stderr,
|
|
"%s: error in cpio file.\n",
|
|
filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (!MakeTargetDir(name, dirs)) {
|
|
(void)fprintf(stderr,
|
|
"%s: can't create parent "
|
|
"directories for \"%s\".\n",
|
|
filename, name);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (!MakeDir(name, mode, &old_dir)) {
|
|
(void)fprintf(stderr,
|
|
"%s: can't create directory "
|
|
"\"%s\".\n", filename, name);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (!old_dir) {
|
|
dir = PListInsert(dirs, name);
|
|
dir->pe_DirEmpty = true;
|
|
}
|
|
break;
|
|
}
|
|
case C_ISLNK: {
|
|
char *link_target;
|
|
|
|
if ((link_target = GetData(in, length)) == NULL) {
|
|
(void)fprintf(stderr,
|
|
"%s: error in cpio file.\n",
|
|
filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (!MakeTargetDir(name, dirs)) {
|
|
(void)fprintf(stderr,
|
|
"%s: can't create parent "
|
|
"directories for \"%s\".\n",
|
|
filename, name);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (*link_target == '/') {
|
|
char *ptr;
|
|
|
|
(void)memmove(link_target, link_target + 1,
|
|
strlen(link_target));
|
|
ptr = name;
|
|
if (prefix != NULL)
|
|
ptr += strlen(prefix);
|
|
|
|
while ((ptr = strchr(ptr, '/')) != NULL) {
|
|
char *new_link_target;
|
|
|
|
new_link_target = StrCat("../",
|
|
link_target);
|
|
free(link_target);
|
|
link_target = new_link_target;
|
|
ptr++;
|
|
}
|
|
}
|
|
|
|
if (!MakeSymLink(link_target, name)) {
|
|
(void)fprintf(stderr,
|
|
"%s: can't create symbolic link "
|
|
"\"%s\".\n", filename, name);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
PListInsert(files, name)->pe_Link = link_target;
|
|
break;
|
|
}
|
|
case C_ISREG:
|
|
if (!MakeTargetDir(name, dirs)) {
|
|
(void)fprintf(stderr,
|
|
"%s: can't create parent "
|
|
"directories for \"%s\".\n",
|
|
filename, name);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
|
|
if ((last != NULL) && (last->pe_INode !=
|
|
fields[CPIO_HDR_INODE])) {
|
|
last = NULL;
|
|
}
|
|
|
|
if (!WriteFile(in, name, mode, length,
|
|
(last != NULL)? last->pe_Name : NULL)) {
|
|
(void)fprintf(stderr,
|
|
"%s: can't write file \"%s\".\n",
|
|
filename, name);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
last = PListInsert(files, name);
|
|
last->pe_INode = fields[CPIO_HDR_INODE];
|
|
break;
|
|
default:
|
|
if (length > 0 && !SkipAndAlign(in, length)) {
|
|
(void)fprintf(stderr,
|
|
"%s: error in cpio file.\n",
|
|
filename);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
}
|
|
|
|
free(name);
|
|
}
|
|
|
|
FileHandleClose(in);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
char *progname;
|
|
FILE *plist_file;
|
|
char **ignore, *prefix;
|
|
int opt, stripcount, i;
|
|
PListEntry *files, *dirs;
|
|
|
|
progname = strrchr(argv[0], '/');
|
|
if (progname == NULL)
|
|
progname = argv[0];
|
|
else
|
|
progname++;
|
|
|
|
plist_file = NULL;
|
|
ignore = NULL;
|
|
prefix = NULL;
|
|
stripcount = 0;
|
|
while ((opt = getopt(argc, argv, "d:f:i:p:s:")) != -1) {
|
|
switch (opt) {
|
|
case 'd':
|
|
if (chdir(optarg)) {
|
|
perror(optarg);
|
|
return EXIT_FAILURE;
|
|
}
|
|
break;
|
|
|
|
case 'f':
|
|
if (plist_file != NULL)
|
|
(void)fclose(plist_file);
|
|
if ((plist_file = fopen(optarg, "a")) == NULL) {
|
|
perror(optarg);
|
|
return EXIT_FAILURE;
|
|
}
|
|
break;
|
|
|
|
case 'i':
|
|
ignore = ArrayAdd(ignore, optarg);
|
|
break;
|
|
|
|
case 'p':
|
|
if (strlen(optarg) > 0)
|
|
prefix = optarg;
|
|
break;
|
|
|
|
case 's':
|
|
stripcount = atoi(optarg);
|
|
if (stripcount <= 0) {
|
|
(void)fprintf(stderr,
|
|
"%s: -s argument \"%s\" "
|
|
"must be a positive integer.\n",
|
|
progname, optarg);
|
|
return EXIT_FAILURE;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Usage(progname);
|
|
}
|
|
}
|
|
|
|
if (argc == optind)
|
|
Usage(progname);
|
|
|
|
if (prefix != NULL && prefix[strlen(prefix) - 1] != '/')
|
|
prefix = StrCat(prefix, "/");
|
|
|
|
files = NULL;
|
|
dirs = NULL;
|
|
for (i = optind; i < argc ; i++)
|
|
ProcessRPM(argv[i], &files, &dirs, ignore, prefix, stripcount);
|
|
|
|
if (plist_file != NULL) {
|
|
PListWrite(files, dirs, plist_file);
|
|
(void)fclose(plist_file);
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|