| 1 | // -*- mode: cpp; mode: fold -*- |
| 2 | // Description /*{{{*/ |
| 3 | // $Id: arfile.cc,v 1.6.2.1 2004/01/16 18:58:50 mdz Exp $ |
| 4 | /* ###################################################################### |
| 5 | |
| 6 | AR File - Handle an 'AR' archive |
| 7 | |
| 8 | AR Archives have plain text headers at the start of each file |
| 9 | section. The headers are aligned on a 2 byte boundry. |
| 10 | |
| 11 | Information about the structure of AR files can be found in ar(5) |
| 12 | on a BSD system, or in the binutils source. |
| 13 | |
| 14 | ##################################################################### */ |
| 15 | /*}}}*/ |
| 16 | // Include Files /*{{{*/ |
| 17 | #include <apt-pkg/arfile.h> |
| 18 | #include <apt-pkg/strutl.h> |
| 19 | #include <apt-pkg/error.h> |
| 20 | |
| 21 | #include <stdlib.h> |
| 22 | /*}}}*/ |
| 23 | #include <apti18n.h> |
| 24 | |
| 25 | struct ARArchive::MemberHeader |
| 26 | { |
| 27 | char Name[16]; |
| 28 | char MTime[12]; |
| 29 | char UID[6]; |
| 30 | char GID[6]; |
| 31 | char Mode[8]; |
| 32 | char Size[10]; |
| 33 | char Magic[2]; |
| 34 | }; |
| 35 | |
| 36 | // ARArchive::ARArchive - Constructor /*{{{*/ |
| 37 | // --------------------------------------------------------------------- |
| 38 | /* */ |
| 39 | ARArchive::ARArchive(FileFd &File) : List(0), File(File) |
| 40 | { |
| 41 | LoadHeaders(); |
| 42 | } |
| 43 | /*}}}*/ |
| 44 | // ARArchive::~ARArchive - Destructor /*{{{*/ |
| 45 | // --------------------------------------------------------------------- |
| 46 | /* */ |
| 47 | ARArchive::~ARArchive() |
| 48 | { |
| 49 | while (List != 0) |
| 50 | { |
| 51 | Member *Tmp = List; |
| 52 | List = List->Next; |
| 53 | delete Tmp; |
| 54 | } |
| 55 | } |
| 56 | /*}}}*/ |
| 57 | // ARArchive::LoadHeaders - Load the headers from each file /*{{{*/ |
| 58 | // --------------------------------------------------------------------- |
| 59 | /* AR files are structured with a 8 byte magic string followed by a 60 |
| 60 | byte plain text header then the file data, another header, data, etc */ |
| 61 | bool ARArchive::LoadHeaders() |
| 62 | { |
| 63 | signed long Left = File.Size(); |
| 64 | |
| 65 | // Check the magic byte |
| 66 | char Magic[8]; |
| 67 | if (File.Read(Magic,sizeof(Magic)) == false) |
| 68 | return false; |
| 69 | if (memcmp(Magic,"!<arch>\012",sizeof(Magic)) != 0) |
| 70 | return _error->Error(_("Invalid archive signature")); |
| 71 | Left -= sizeof(Magic); |
| 72 | |
| 73 | // Read the member list |
| 74 | while (Left > 0) |
| 75 | { |
| 76 | MemberHeader Head; |
| 77 | if (File.Read(&Head,sizeof(Head)) == false) |
| 78 | return _error->Error(_("Error reading archive member header")); |
| 79 | Left -= sizeof(Head); |
| 80 | |
| 81 | // Convert all of the integer members |
| 82 | Member *Memb = new Member(); |
| 83 | if (StrToNum(Head.MTime,Memb->MTime,sizeof(Head.MTime)) == false || |
| 84 | StrToNum(Head.UID,Memb->UID,sizeof(Head.UID)) == false || |
| 85 | StrToNum(Head.GID,Memb->GID,sizeof(Head.GID)) == false || |
| 86 | StrToNum(Head.Mode,Memb->Mode,sizeof(Head.Mode),8) == false || |
| 87 | StrToNum(Head.Size,Memb->Size,sizeof(Head.Size)) == false) |
| 88 | { |
| 89 | delete Memb; |
| 90 | return _error->Error(_("Invalid archive member header")); |
| 91 | } |
| 92 | |
| 93 | // Check for an extra long name string |
| 94 | if (memcmp(Head.Name,"#1/",3) == 0) |
| 95 | { |
| 96 | char S[300]; |
| 97 | unsigned long Len; |
| 98 | if (StrToNum(Head.Name+3,Len,sizeof(Head.Size)-3) == false || |
| 99 | Len >= strlen(S)) |
| 100 | { |
| 101 | delete Memb; |
| 102 | return _error->Error(_("Invalid archive member header")); |
| 103 | } |
| 104 | if (File.Read(S,Len) == false) |
| 105 | return false; |
| 106 | S[Len] = 0; |
| 107 | Memb->Name = S; |
| 108 | Memb->Size -= Len; |
| 109 | Left -= Len; |
| 110 | } |
| 111 | else |
| 112 | { |
| 113 | unsigned int I = sizeof(Head.Name) - 1; |
| 114 | for (; Head.Name[I] == ' '; I--); |
| 115 | Memb->Name = string(Head.Name,I+1); |
| 116 | } |
| 117 | |
| 118 | // Account for the AR header alignment |
| 119 | unsigned Skip = Memb->Size % 2; |
| 120 | |
| 121 | // Add it to the list |
| 122 | Memb->Next = List; |
| 123 | List = Memb; |
| 124 | Memb->Start = File.Tell(); |
| 125 | if (File.Skip(Memb->Size + Skip) == false) |
| 126 | return false; |
| 127 | if (Left < (signed)(Memb->Size + Skip)) |
| 128 | return _error->Error(_("Archive is too short")); |
| 129 | Left -= Memb->Size + Skip; |
| 130 | } |
| 131 | if (Left != 0) |
| 132 | return _error->Error(_("Failed to read the archive headers")); |
| 133 | |
| 134 | return true; |
| 135 | } |
| 136 | /*}}}*/ |
| 137 | // ARArchive::FindMember - Find a name in the member list /*{{{*/ |
| 138 | // --------------------------------------------------------------------- |
| 139 | /* Find a member with the given name */ |
| 140 | const ARArchive::Member *ARArchive::FindMember(const char *Name) const |
| 141 | { |
| 142 | const Member *Res = List; |
| 143 | while (Res != 0) |
| 144 | { |
| 145 | if (Res->Name == Name) |
| 146 | return Res; |
| 147 | Res = Res->Next; |
| 148 | } |
| 149 | |
| 150 | return 0; |
| 151 | } |
| 152 | /*}}}*/ |