1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 2003-2011 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21
22 /*
23 * pax rar format
24 */
25
26 #include <paxlib.h>
27 #include <codex.h>
28 #include <swap.h>
29 #include <tm.h>
30
31 #define SUM "sum-crc-0xedb88320-init-done"
32
33 typedef struct Ar_s
34 {
35 Codexdisc_t codexdisc;
36 Pax_t* pax;
37 Paxarchive_t* ap;
38 Sfio_t* sum;
39 char method[128];
40 unsigned long checksum;
41 int solid;
42
43 unsigned short level;
44 } Ar_t;
45
46 static int
rar_done(Pax_t * pax,register Paxarchive_t * ap)47 rar_done(Pax_t* pax, register Paxarchive_t* ap)
48 {
49 register Ar_t* ar = (Ar_t*)ap->data;
50
51 if (!ar)
52 return -1;
53 if (ar->sum)
54 sfdisc(ar->sum, SF_POPDISC);
55 free(ar);
56 ap->data = 0;
57 return 0;
58 }
59
60 static int
rar_getprologue(Pax_t * pax,Paxformat_t * fp,register Paxarchive_t * ap,Paxfile_t * f,unsigned char * buf,size_t size)61 rar_getprologue(Pax_t* pax, Paxformat_t* fp, register Paxarchive_t* ap, Paxfile_t* f, unsigned char* buf, size_t size)
62 {
63 register Ar_t* ar;
64
65 if (size < 7 || buf[0] != 0x52 || buf[1] != 0x61 || buf[2] != 0x72 || buf[3] != 0x21 || buf[4] != 0x1a || buf[5] != 0x07 || buf[6] != 0x00)
66 return 0;
67 if (!(ar = newof(0, Ar_t, 1, 0)))
68 {
69 if (ar)
70 free(ar);
71 return paxnospace(pax);
72 }
73 ap->data = ar;
74 ar->pax = pax;
75 ar->ap = ap;
76 codexinit(&ar->codexdisc, pax->errorf);
77 ar->codexdisc.passphrase = pax->passphrase;
78 if (!(ar->sum = codexnull()) || codex(ar->sum, NiL, SUM, 0, &ar->codexdisc, NiL) <= 0)
79 {
80 ar->sum = 0;
81 rar_done(pax, ap);
82 return -1;
83 }
84 return 1;
85 }
86
87 static int
rar_getheader(Pax_t * pax,register Paxarchive_t * ap,register Paxfile_t * f)88 rar_getheader(Pax_t* pax, register Paxarchive_t* ap, register Paxfile_t* f)
89 {
90 register Ar_t* ar = (Ar_t*)ap->data;
91 register unsigned char* buf;
92 char* s;
93 Tm_t tm;
94 unsigned short checksum;
95 unsigned short flags;
96 unsigned short type;
97 unsigned short size;
98 unsigned long data;
99 unsigned long dostime;
100 int i;
101 char* msg;
102 Codexdata_t sum;
103
104 msg = 0;
105 for (;;)
106 {
107 if (!(buf = (unsigned char*)paxget(pax, ap, -7, NiL)))
108 return 0;
109 checksum = swapget(3, buf+0, 2);
110 type = buf[2];
111 flags = swapget(3, buf+3, 2);
112 if (flags & 0x0008)
113 ar->solid = 1;
114 if ((size = swapget(3, buf+5, 2)) < 7)
115 break;
116 if (!(size -= 7))
117 continue;
118 sfsync(ar->sum);
119 sfwrite(ar->sum, buf+2, 5);
120 if (!(buf = (unsigned char*)paxget(pax, ap, size, NiL)))
121 {
122 msg = "unexpected EOF";
123 break;
124 }
125 data = (flags & 0x8000) ? swapget(3, buf+0, 4) : 0;
126 if (type != 0x74)
127 {
128 if (data && paxseek(pax, ap, data, SEEK_CUR, 0) < 0)
129 break;
130 continue;
131 }
132 sfwrite(ar->sum, buf, size);
133 codexdata(ar->sum, &sum);
134 if (paxchecksum(pax, ap, NiL, checksum, sum.num & 0xffff))
135 return -1;
136 f->uncompressed = swapget(3, buf+4, 4);
137 if (flags & 0x0100)
138 {
139 data += swapget(3, buf+25, 4) << 32;
140 f->uncompressed += swapget(3, buf+29, 4);
141 }
142 f->st->st_size = data;
143 ar->checksum = swapget(3, buf+9, 4);
144 dostime = swapget(3, buf+13, 4);
145 i = 0;
146 if (flags & 0x0004)
147 i += sfsprintf(ar->method, sizeof(ar->method), "crypt-rar-%u%s+SIZE=%I*u|", buf[17], ar->solid ? "-RETAIN" : "", sizeof(f->st->st_size), f->st->st_size);
148 if ((ar->level = buf[18]) == 0x30)
149 i += sfsprintf(ar->method + i, sizeof(ar->method) - i, "copy");
150 else
151 i += sfsprintf(ar->method + i, sizeof(ar->method) - i, "rar-%u%s+SIZE=%I*u", buf[17], ar->solid ? "-RETAIN" : "", sizeof(f->uncompressed), f->uncompressed);
152 sfsprintf(ar->method + i, sizeof(ar->method) - i, "|%s", SUM);
153 i = swapget(3, buf+19, 2);
154 f->name = paxstash(pax, &ap->stash.head, NiL, i + 1);
155 memcpy(f->name, buf + ((flags & 0x0100) ? 33 : 25), i);
156 f->name[i] = 0;
157 f->linkpath = 0;
158 f->st->st_dev = 0;
159 f->st->st_ino = 0;
160 i = swapget(3, buf+21, 4);
161 if (buf[8] <= 2)
162 {
163 if (i & 0x10)
164 i = X_IFDIR|X_IRUSR|X_IWUSR|X_IXUSR|X_IRGRP|X_IWGRP|X_IXGRP|X_IROTH|X_IWOTH|X_IXOTH;
165 else if (i & 0x01)
166 i = X_IFREG|X_IRUSR|X_IRGRP|X_IROTH;
167 else
168 {
169 i = X_IFREG|X_IRUSR|X_IWUSR|X_IRGRP|X_IWGRP|X_IROTH|X_IWOTH;
170 if ((s = strrchr(f->name, '.')) && (s[1]=='e' || s[1]=='E') && (s[1]=='x' || s[1]=='X') && (s[1]=='e' || s[1]=='E'))
171 i |= X_IXUSR|X_IXGRP|X_IXOTH;
172 }
173 }
174 if ((flags & 0x00e0) == 0x00e0 && (i & X_IFMT) != X_IFDIR)
175 {
176 i = X_IFDIR|(i & ~X_IFMT)|X_IRUSR|X_IWUSR|X_IXUSR;
177 if (i & (X_IROTH|X_IWOTH))
178 i |= X_IROTH|X_IXOTH;
179 else
180 {
181 i &= ~(X_IROTH|X_IXOTH);
182 if (i & (X_IRGRP|X_IWGRP))
183 i |= X_IRGRP|X_IXGRP;
184 else
185 i &= ~(X_IRGRP|X_IXGRP);
186 }
187 }
188 f->st->st_mode = i & pax->modemask;
189 f->st->st_uid = pax->uid;
190 f->st->st_gid = pax->gid;
191 f->st->st_nlink = 1;
192 IDEVICE(f->st, 0);
193 memset(&tm, 0, sizeof(tm));
194 tm.tm_year = ((dostime >> (16+9)) & 0x7f) + 80;
195 tm.tm_mon = ((dostime >> (16+5)) & 0x0f) - 1;
196 tm.tm_mday = ((dostime >> (16+0)) & 0x1f);
197 tm.tm_hour = ((dostime >> 11) & 0x1f);
198 tm.tm_min = ((dostime >> 5) & 0x3f);
199 tm.tm_sec = ((dostime ) & 0x1f) * 2;
200 f->st->st_mtime = f->st->st_ctime = f->st->st_atime = tmtime(&tm, TM_LOCALZONE);
201 return 1;
202 }
203 return paxcorrupt(pax, ap, f, msg);
204 }
205
206 static int
rar_getdata(Pax_t * pax,register Paxarchive_t * ap,register Paxfile_t * f,int fd)207 rar_getdata(Pax_t* pax, register Paxarchive_t* ap, register Paxfile_t* f, int fd)
208 {
209 register Ar_t* ar = (Ar_t*)ap->data;
210 Sfio_t* sp;
211 off_t pos;
212 ssize_t n;
213 int r;
214 int pop;
215 Codexdata_t sum;
216
217 if (!f->st->st_size)
218 return 1;
219 pos = paxseek(pax, ap, 0, SEEK_CUR, 0) + f->st->st_size;
220 r = -1;
221 if (fd < 0 && !ar->solid)
222 r = 1;
223 else if (sp = paxpart(pax, ap, f->st->st_size))
224 {
225 if ((pop = codex(sp, NiL, ar->method, 0, &ar->codexdisc, NiL)) < 0)
226 (*pax->errorf)(NiL, pax, 2, "%s: %s: cannot decode method %s", ap->name, f->name, ar->method);
227 else
228 {
229 for (;;)
230 {
231 if ((n = sfread(sp, pax->buf, sizeof(pax->buf))) < 0)
232 {
233 (*pax->errorf)(NiL, pax, 2, "%s: %s: unexpected EOF", ap->name, f->name);
234 break;
235 }
236 else if (n == 0)
237 {
238 if (codexdata(sp, &sum) <= 0)
239 (*pax->errorf)(NiL, pax, 2, "%s: %s: checksum discipline error", ap->name, f->name);
240 else if (!paxchecksum(pax, ap, f, ar->checksum, sum.num))
241 r = 1;
242 break;
243 }
244 if (fd >= 0 && paxdata(pax, ap, f, fd, pax->buf, n))
245 break;
246 }
247 codexpop(sp, NiL, pop);
248 }
249 }
250 if (paxseek(pax, ap, pos, SEEK_SET, 0) != pos)
251 {
252 (*pax->errorf)(NiL, pax, 2, "%s: %s: cannot seek past %s format data", ap->name, f->name, ap->format->name);
253 r = -1;
254 }
255 return r;
256 }
257
258 Paxformat_t pax_rar_format =
259 {
260 "rar",
261 0,
262 "rar archive",
263 0,
264 PAX_ARCHIVE|PAX_DOS|PAX_NOHARDLINKS|PAX_IN,
265 PAX_DEFBUFFER,
266 PAX_DEFBLOCKS,
267 0,
268 PAXNEXT(rar),
269 0,
270 rar_done,
271 rar_getprologue,
272 rar_getheader,
273 rar_getdata,
274 };
275
276 PAXLIB(rar)
277