1 /* $NetBSD: rpm2pkg.c,v 1.10 2010/06/14 11:24:48 tron Exp $ */
4 * Copyright (c) 2004-2009 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/types.h>
47 * Lead of an RPM archive as described here:
48 * http://www.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html
50 static const unsigned char RPMMagic[] = { 0xed, 0xab, 0xee, 0xdb };
52 #define RPM_LEAD_SIZE 96
54 /* Magic bytes for "bzip2" and "gzip" compressed files. */
55 static const unsigned char BZipMagic[] = { 'B', 'Z', 'h' };
56 static const unsigned char GZipMagic[] = { 0x1f, 0x8b, 0x08 };
58 /* Structure of a cpio(1) archive. */
59 #define C_IRUSR 0000400
60 #define C_IWUSR 0000200
61 #define C_IXUSR 0000100
62 #define C_IRGRP 0000040
63 #define C_IWGRP 0000020
64 #define C_IXGRP 0000010
65 #define C_IROTH 0000004
66 #define C_IWOTH 0000002
67 #define C_IXOTH 0000001
68 #define C_ISUID 0004000
69 #define C_ISGID 0002000
70 #define C_ISVTX 0001000
71 #define C_ISDIR 0040000
72 #define C_ISREG 0100000
73 #define C_ISCHR 0020000
74 #define C_ISLNK 0120000
76 static const unsigned char CPIOMagic[] = {'0','7','0','7','0','1'};
78 #define CPIO_END_MARKER "TRAILER!!!"
79 #define CPIO_FIELD_LENGTH 8
81 #define CPIO_HDR_INODE 0
82 #define CPIO_HDR_MODE 1
83 #define CPIO_HDR_FILESIZE 6
84 #define CPIO_HDR_NAMESIZE 11
85 #define CPIO_NUM_HEADERS 13
87 #define CP_IFMT 0170000
89 typedef struct ModeMapStruct {
90 unsigned long mm_CPIOMode;
94 ModeMap ModeMapTab[] = {
110 typedef struct PListEntryStruct PListEntry;
111 struct PListEntryStruct {
112 PListEntry *pe_Childs[2];
115 unsigned long pe_INode;
120 #define pe_Left pe_Childs[0]
121 #define pe_Right pe_Childs[1]
123 typedef void PListEntryFunc(PListEntry *,FILE *);
125 #define PLIST_ORDER_FORWARD 0
126 #define PLIST_ORDER_BACKWARD 1
128 #define INVERT_PLIST_ORDER(o) (1 - (o))
130 typedef struct FileHandleStruct {
138 InitBuffer(void **Buffer, size_t *BufferSizePtr)
140 if (*Buffer == NULL) {
143 BufferSize = sysconf(_SC_PAGESIZE) * 256;
144 while ((*Buffer = malloc(BufferSize)) == NULL) {
149 *BufferSizePtr = BufferSize;
155 Close(FileHandle *fh)
157 if (fh->fh_BZFile != NULL) {
160 (void)BZ2_bzReadClose(&bzerror, fh->fh_BZFile);
161 (void)fclose(fh->fh_File);
163 (void)gzclose(fh->fh_GZFile);
171 char buffer[RPM_LEAD_SIZE];
173 if (read(fd, buffer, sizeof(buffer)) != sizeof(buffer))
176 return (memcmp(buffer, RPMMagic, sizeof(RPMMagic)) == 0);
182 unsigned char buffer[16384];
183 size_t bzMatch, gzMatch;
195 bytes = read(fd, buffer, sizeof(buffer));
199 for (i = 0; i < bytes; i++) {
200 unsigned char cur_char;
202 cur_char = buffer[i];
204 /* Look for bzip2 header. */
205 if (cur_char == BZipMagic[bzMatch]) {
207 if (bzMatch == sizeof(BZipMagic)) {
210 sizeof(BZipMagic) + 1;
214 bzMatch = (cur_char == BZipMagic[0]) ? 1 : 0;
217 /* Look for gzip header. */
218 if (cur_char == GZipMagic[gzMatch]) {
220 if (gzMatch == sizeof(GZipMagic)) {
223 sizeof(GZipMagic) + 1;
227 gzMatch = (cur_char == GZipMagic[0]) ? 1 : 0;
230 } while (archive_type == 0);
232 /* Go back to the beginning of the archive. */
233 if (lseek(fd, offset, SEEK_CUR) < RPM_LEAD_SIZE)
236 if ((fh = calloc(1, sizeof (FileHandle))) == NULL)
239 if (archive_type == 1) {
243 if ((fd = dup(fd)) < 0) {
247 if ((fh->fh_File = fdopen(fd, "rb")) == NULL) {
253 if ((fh->fh_BZFile = BZ2_bzReadOpen(&bzerror, fh->fh_File, 0,
254 0, NULL, 0)) == NULL) {
255 (void)fclose(fh->fh_File);
261 if ((fh->fh_GZFile = gzdopen(fd, "r")) == NULL) {
271 Read(FileHandle *fh, void *buffer, int length)
275 bytes = (fh->fh_BZFile != NULL) ?
276 BZ2_bzRead(&bzerror, fh->fh_BZFile, buffer, length) :
277 gzread(fh->fh_GZFile, buffer, length);
281 return (bytes == length);
285 SkipAndAlign(FileHandle *fh, off_t Skip)
290 NewPos = (fh->fh_Pos + Skip + 3) & ~3;
291 if (fh->fh_Pos == NewPos)
294 if (fh->fh_GZFile != NULL) {
295 if (gzseek(fh->fh_GZFile, NewPos, SEEK_SET) == NewPos) {
301 static void *Buffer = NULL;
302 static size_t BufferSize = 0;
304 if (!InitBuffer(&Buffer, &BufferSize))
307 while (fh->fh_Pos < NewPos) {
311 Length = NewPos - fh->fh_Pos;
312 Chunk = (Length > (off_t)BufferSize) ?
313 (off_t)BufferSize : Length;
314 if (!Read(fh, Buffer, Chunk))
323 InsertPListEntry(PListEntry **Tree,char *Name)
327 while ((Node = *Tree) != NULL) {
328 Tree = (strcmp(Name, Node->pe_Name) <0) ?
329 &Node->pe_Left : &Node->pe_Right;
332 if ((Node = calloc(1, sizeof (PListEntry) + strlen(Name))) == NULL) {
337 (void)strcpy(Node->pe_Name, Name);
343 FindPListEntry(PListEntry *Tree, char *Name)
345 while (Tree != NULL) {
348 if ((Result = strcmp(Name, Tree->pe_Name)) == 0) break;
349 Tree = (Result < 0) ? Tree->pe_Left : Tree->pe_Right;
356 PListEntryFile(PListEntry *Node, FILE *Out)
359 (void)fputs(Node->pe_Name, Out);
360 (void)fputc('\n', Out);
364 StrCat(char *Prefix, char *Suffix)
369 Length = strlen(Prefix);
370 if ((Str = malloc(Length + strlen(Suffix) + 1)) == NULL) {
375 (void)memcpy(Str, Prefix, Length);
376 (void)strcpy(&Str[Length], Suffix);
382 PListEntryLink(PListEntry *Node, FILE *Out)
389 if ((Ptr = strrchr(Node->pe_Name, '/')) != NULL) {
390 char Old, *Targetname;
394 Targetname = StrCat(Node->pe_Name, Node->pe_Link);
397 Result = stat(Targetname, &Stat);
400 Result = stat(Node->pe_Link, &Stat);
403 if ((Result == 0) && ((Stat.st_mode & S_IFMT) == S_IFREG)) {
404 PListEntryFile(Node, Out);
408 (void)fprintf(Out, "@exec ln -fs %s %%D/%s\n@unexec rm -f %%D/%s\n",
409 Node->pe_Link, Node->pe_Name, Node->pe_Name);
413 PListEntryMakeDir(PListEntry *Node, FILE *Out)
416 if (Node->pe_DirEmpty) {
417 (void)fprintf(Out, "@exec mkdir -m %o -p %%D/%s\n",
418 Node->pe_DirMode, Node->pe_Name);
423 ProcessPList(PListEntry *Tree, PListEntryFunc Func, int Order, FILE *Out)
426 while (Tree != NULL) {
427 if (Tree->pe_Childs[Order] != NULL)
428 ProcessPList(Tree->pe_Childs[Order], Func, Order, Out);
430 Tree = Tree->pe_Childs[INVERT_PLIST_ORDER(Order)];
435 ArrayAdd(char **Array, char *String)
442 while (Array[Old] != NULL)
445 if ((Array = realloc(Array, sizeof (char *) * (Old + 2))) == NULL) {
450 Array[Old++] = String;
457 Usage(char *Progname)
459 (void)fprintf(stderr,
460 "Usage: %s [-d directory] [-f packlist] [[-i ignorepath] ...]\n"
461 " [-p prefix] [-s stripcount] rpmfile [...]\n",
467 GetData(FileHandle *In, unsigned long Length)
471 if ((Ptr = malloc(Length + 1)) != NULL) {
472 if (Read(In, Ptr, Length) && SkipAndAlign(In, 0)) {
483 GetCPIOHeader(FileHandle *In, unsigned long *Fields, char **Name)
485 char Buffer[CPIO_NUM_HEADERS * CPIO_FIELD_LENGTH], *Ptr;
491 if (!Read(In, Buffer, sizeof (CPIOMagic)))
493 if (memcmp(Buffer, CPIOMagic, sizeof (CPIOMagic)) != 0)
496 if (!Read(In, Buffer, sizeof (Buffer)))
500 Index = sizeof (Buffer);
502 while (Index-- > 0) {
504 if ((*Ptr >= '0') && (*Ptr <= '9')) {
505 Value += (unsigned long)(*Ptr++-'0');
506 } else if ((*Ptr >= 'A') && (*Ptr <= 'F')) {
507 Value += (unsigned long)(*Ptr++-'A') + 10;
508 } else if ((*Ptr >= 'a') && (*Ptr <= 'f')) {
509 Value += (unsigned long)(*Ptr++-'a') + 10;
514 if ((Index % CPIO_FIELD_LENGTH) == 0) {
520 Value = Fields[CPIO_HDR_NAMESIZE - CPIO_NUM_HEADERS];
521 if ((*Name = GetData(In, Value)) == NULL)
523 return ((*Name)[Value -1 ] == '\0');
527 ConvertMode(unsigned long CPIOMode)
534 while (Ptr->mm_CPIOMode != 0) {
535 if ((CPIOMode & Ptr->mm_CPIOMode) != 0)
536 Mode |= Ptr->mm_SysMode;
544 MakeTargetDir(char *Name, PListEntry **Dirs, int MarkNonEmpty)
551 if ((Basename = strrchr(Name, '/')) == NULL)
555 if ((Dir = FindPListEntry(*Dirs, Name)) != NULL) {
557 Dir->pe_DirEmpty = !MarkNonEmpty;
561 if (!MakeTargetDir(Name, Dirs, true)) {
566 if (stat(Name, &Stat) == 0) {
567 Result = S_ISDIR(Stat.st_mode);
568 } else if (errno != ENOENT) {
570 } else if ((Result = (mkdir(Name, S_IRWXU|S_IRWXG|S_IRWXO) == 0))) {
571 InsertPListEntry(Dirs, Name)->pe_DirMode =
572 S_IRWXU|S_IRWXG|S_IRWXO;
580 MakeDir(char *Name, mode_t Mode, int *OldDir)
585 if (mkdir(Name, Mode) == 0)
588 if ((errno != EEXIST) || (lstat(Name, &Stat) < 0) ||
589 !S_ISDIR(Stat.st_mode)) {
598 MakeSymLink(char *Link, char *Name)
602 if (symlink(Link, Name) == 0) return true;
604 if ((errno != EEXIST) || (lstat(Name, &Stat) < 0) ||
605 !S_ISLNK(Stat.st_mode)) {
609 return ((unlink(Name) == 0) && (symlink(Link, Name) == 0));
613 WriteFile(FileHandle *In, char *Name, mode_t Mode, unsigned long Length,
618 static void *Buffer = NULL;
619 static size_t BufferSize = 0;
621 if ((lstat(Name, &Stat) == 0) &&
622 (!S_ISREG(Stat.st_mode) || (unlink(Name) < 0))) {
626 if (!InitBuffer(&Buffer, &BufferSize))
630 if (link(Link, Name) < 0)
632 Out = open(Name, O_WRONLY, Mode);
634 Out = open(Name, O_WRONLY|O_CREAT, Mode);
642 Chunk = (Length > BufferSize) ? BufferSize : Length;
643 if (!Read(In, Buffer, Chunk) ||
644 (write(Out, Buffer, Chunk) != Chunk))
649 if ((close(Out) == 0) && (Length == 0))
650 return SkipAndAlign(In, 0);
657 CheckSymLinks(PListEntry **Links, PListEntry **Files, PListEntry **Dirs)
661 while ((Link = *Links) != NULL) {
666 if (Link->pe_Left != NULL)
667 CheckSymLinks(&Link->pe_Left, Files, Dirs);
669 if ((stat(Link->pe_Name, &Stat) < 0) ||
670 !S_ISREG(Stat.st_mode)) {
671 Links = &Link->pe_Right;
675 (void)InsertPListEntry(Files, Link->pe_Name);
676 if ((Basename = strrchr(Link->pe_Name, '/')) != NULL) {
678 if ((Ptr = FindPListEntry(*Dirs,
679 Link->pe_Name)) != NULL)
680 Ptr->pe_DirEmpty = false;
683 if (Link->pe_Right == NULL) {
684 *Links = Link->pe_Left;
689 *Links = Link->pe_Right;
697 while (Link->pe_Left != NULL)
698 Link = Link->pe_Left;
704 CheckPrefix(char *Prefix, char *Name)
708 Length = strlen(Prefix);
709 return ((strncmp(Prefix, Name, Length) == 0) &&
710 ((Name[Length] == '\0') || (Name[Length] == '/')));
714 StripPrefix(char *Name, int Count)
722 while (Count-- > 0) {
723 NewName = strchr(NewName, '/');
728 (void)memmove(Name, NewName, strlen(NewName) + 1);
734 main(int argc, char **argv)
738 char **Ignore, *Prefix;
739 int Opt, Index, FD, StripCount;
740 PListEntry *Files, *Links, *Dirs;
743 Progname = strrchr(argv[0], '/');
744 if (Progname == NULL)
753 while ((Opt = getopt(argc, argv, "s:d:f:i:p:")) != -1) {
756 StripCount = atoi(optarg);
757 if (StripCount <= 0) {
758 (void)fprintf(stderr,
759 "%s: -s argument \"%s\" "
760 "must be a positive integer.\n",
766 if (PListFile != NULL)
767 (void)fclose(PListFile);
768 if ((PListFile = fopen(optarg, "a")) == NULL) {
774 Ignore = ArrayAdd(Ignore, optarg);
783 if (strlen(optarg) > 0)
796 if ((Prefix != NULL) && (Prefix[strlen(Prefix) - 1] != '/'))
797 Prefix = StrCat(Prefix, "/");
802 for (Index = 0; Index < argc ; Index++) {
805 if ((FD = open(argv[Index], O_RDONLY, 0)) < 0) {
810 if (!IsRPMFile(FD)) {
811 (void)fprintf(stderr,
812 "%s: file is not an RPM package.\n", argv[Index]);
816 if ((In = Open(FD)) == NULL) {
817 (void)fprintf(stderr,
818 "%s: cannot read cpio data.\n", argv[Index]);
824 unsigned long Fields[CPIO_NUM_HEADERS];
827 unsigned long Length;
829 if (!GetCPIOHeader(In, Fields, &Name)) {
830 (void)fprintf(stderr,
831 "%s: error in cpio header.\n",
835 if (strcmp(Name, CPIO_END_MARKER) == 0) {
840 Fields[CPIO_HDR_MODE] = 0;
842 if (Ignore != NULL) {
846 while (*Ptr != NULL) {
847 if (CheckPrefix(*Ptr, Name)) {
848 Fields[CPIO_HDR_MODE] = 0;
855 if ((Name = StripPrefix(Name, StripCount)) == NULL) {
856 (void)fprintf(stderr,
857 "%s: Leading path to strip too "
859 argv[Index], StripCount);
863 if (Prefix != NULL) {
866 Fullname = StrCat(Prefix, Name);
871 Mode = ConvertMode(Fields[CPIO_HDR_MODE]);
872 Length = Fields[CPIO_HDR_FILESIZE];
873 switch (Fields[CPIO_HDR_MODE] & CP_IFMT) {
879 (void)fprintf(stderr,
880 "%s: error in cpio file.\n",
885 if (!MakeTargetDir(Name, &Dirs, true)) {
886 (void)fprintf(stderr,
887 "%s: can't create parent "
888 "directories for \"%s\".\n",
893 if (!MakeDir(Name, Mode, &OldDir)) {
894 (void)fprintf(stderr,
895 "%s: can't create directory "
896 "\"%s\".\n", argv[Index], Name);
901 Dir = InsertPListEntry(&Dirs, Name);
902 Dir->pe_DirEmpty = true;
903 Dir->pe_DirMode = Mode;
910 if ((Link = GetData(In, Length)) == NULL) {
911 (void)fprintf(stderr,
912 "%s: error in cpio file.\n",
917 if (!MakeTargetDir(Name, &Dirs, true)) {
918 (void)fprintf(stderr,
919 "%s: can't create parent "
920 "directories for \"%s\".\n",
928 (void)memmove(Link, Link + 1,
929 strlen(Link + 1) + 1);
932 Ptr += strlen(Prefix);
934 while ((Ptr = strchr(Ptr, '/'))
938 NewLink = StrCat("../", Link);
945 if (!MakeSymLink(Link, Name)) {
946 (void)fprintf(stderr,
947 "%s: can't create symbolic link "
948 "\"%s\".\n", argv[Index], Name);
952 InsertPListEntry(&Links, Name)->pe_Link = Link;
956 if (!MakeTargetDir(Name, &Dirs, true)) {
957 (void)fprintf(stderr,
958 "%s: can't create parent "
959 "directories for \"%s\".\n",
965 if ((Last != NULL) && (Last->pe_INode !=
966 Fields[CPIO_HDR_INODE])) {
970 if (!WriteFile(In, Name, Mode, Length,
971 (Last != NULL)? Last->pe_Name : NULL)) {
972 (void)fprintf(stderr,
973 "%s: can't write file \"%s\".\n",
979 Last = InsertPListEntry(&Files, Name);
980 Last->pe_INode = Fields[CPIO_HDR_INODE];
984 !SkipAndAlign(In, Length)) {
985 (void)fprintf(stderr,
986 "%s: error in cpio file.\n",
1000 if (PListFile != NULL) {
1001 ProcessPList(Files, PListEntryFile, PLIST_ORDER_FORWARD,
1003 ProcessPList(Dirs, PListEntryMakeDir, PLIST_ORDER_FORWARD,
1005 ProcessPList(Links, PListEntryLink, PLIST_ORDER_FORWARD,
1007 (void)fclose(PListFile);
1010 return EXIT_SUCCESS;