1 /*-
2 * Copyright (c) 2010
3 * Thorsten Glaser <tg@mirbsd.org>
4 *
5 * Provided that these terms and disclaimer and all copyright notices
6 * are retained or reproduced in an accompanying document, permission
7 * is granted to deal in this work without restriction, including un-
8 * limited rights to use, publicly perform, distribute, sell, modify,
9 * merge, give away, or sublicence.
10 *
11 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
12 * the utmost extent permitted by applicable law, neither express nor
13 * implied; without malicious intent or gross negligence. In no event
14 * may a licensor, author or contributor be held liable for indirect,
15 * direct, other damage, loss, or other issues arising in any way out
16 * of dealing in the work, even if advised of the possibility of such
17 * damage or existence of a defect, except proven that it results out
18 * of said person's immediate fault when using the work as intended.
19 */
20
21 #include <sys/param.h>
22 #include <sys/time.h>
23 #include <sys/stat.h>
24 #include <sys/slibkern.h>
25 #include <lib/libsa/stand.h>
26 #include <lib/libsa/ustar.h>
27 #include <lib/libsa/ustarfs.h>
28
29 __RCSID("$MirOS: src/sys/lib/libsa/ustarfs.c,v 1.3 2010/01/30 14:57:44 tg Exp $");
30
31 /* This is intentionally simple. We don’t support directories. */
32
33 struct ustarfs_file {
34 struct open_file *open_file; /* our "parent" structure */
35 struct stat sb; /* file properties */
36 uint32_t sector; /* header sector */
37 #define SEC_VDIR ((uint32_t)-1)
38 uint32_t nodeseekp; /* current file offset */
39 };
40
41 #undef DEBUG
42 #ifdef DEBUG
43 char assertion_size__ustar_hdr_t[sizeof(ustar_hdr_t) == 512 ? 1 : -1];
44 /* #define D(fmt, ...) printf("D: %s[%u]:%s: " fmt "\n", __FILE__, __LINE__, \
45 __func__, ## __VA_ARGS__) */
46 #define D(fmt, ...) printf("D: [%u]:%s: " fmt "\n", __LINE__, \
47 __func__, ## __VA_ARGS__)
48 #else
49 #define D(fmt, ...) /* nothing */
50 #endif
51
52 #ifdef __i386__
53 extern uint8_t bounce_buf[4096];
54 #else
55 uint8_t bounce_buf[1536];
56 #endif
57 static ustar_hdr_t *hp = (ustar_hdr_t *)bounce_buf;
58 #define ISAFILE() (!hp->typeflag || hp->typeflag == '0' || \
59 hp->typeflag == '7')
60 #define ISALINK() (hp->typeflag == '1')
61 #define UNOCT(x) unoct((x), sizeof(x))
62
63 static int rd(struct open_file *, uint32_t);
64 static int ustar_check(int);
65 static int ustar_cmpfn(const char *);
66 static int ustar_search(struct ustarfs_file *, const char *);
67 static void ustar_fillsb(struct ustarfs_file *);
68 static uint32_t unoct(char *, size_t);
69 static char *fn_normalise(const char *, const char *);
70
71
72 /* low-level helper routine to load one sector from disc */
73 static int
rd(struct open_file * f,uint32_t blk)74 rd(struct open_file *f, uint32_t blk)
75 {
76 size_t buflen;
77 int rv;
78
79 twiddle();
80 D("block %u", blk);
81 if ((rv = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
82 blk, 512, (void *)hp, &buflen)))
83 D("error code %u", rv);
84 else if (buflen != 512) {
85 D("short read %u", (unsigned)buflen);
86 rv = EIO;
87 }
88 return (rv);
89 }
90
91 /* low-level routine to check a loaded sector for ustar */
92 int
ustar_check(int chk_typeflag)93 ustar_check(int chk_typeflag)
94 {
95 ustar_hdr_t *h2 = hp + 1;
96 uint32_t sum = 0, i = 0;
97 int rv = 1;
98
99 if (memcmp((char *)hp + offsetof(ustar_hdr_t, magic),
100 ustar_magic_version, sizeof(ustar_magic_version)))
101 goto out;
102 if (chk_typeflag) {
103 if (hp->typeflag && (hp->typeflag < '0' || hp->typeflag > '7'))
104 goto out;
105 }
106 memcpy(h2, hp, 512);
107 memset(h2->chksum, ' ', sizeof(h2->chksum));
108 while (i < 512)
109 sum += ((unsigned char *)h2)[i++];
110 if (sum != UNOCT(hp->chksum))
111 goto out;
112 rv = 0;
113 out:
114 D("rv = %d", rv);
115 return (rv);
116 }
117
118 uint32_t
unoct(char * s,size_t n)119 unoct(char *s, size_t n)
120 {
121 uint32_t rv = 0;
122 size_t i = 0;
123
124 while (i < n && s[i] && s[i] != ' ') {
125 if (s[i] < '0' || s[i] > '7')
126 return ((uint32_t)-1);
127 rv = (rv << 3) | (s[i++] & 7);
128 }
129 return (rv);
130 }
131
132 static char *
fn_normalise(const char * p1,const char * p2)133 fn_normalise(const char *p1, const char *p2)
134 {
135 char *rv, *buf = (char *)(void *)(hp + 1);
136 size_t n = 0, i = 260;
137
138 D("s1=%s%s%s s2='%s'", p1 ? "'" : "", p1 ? p1 : "(null)",
139 p1 ? "'" : "", p2);
140
141 /* construct filename at [buf+260;buf+1024[ and NUL-terminate */
142 buf[260] = '/';
143 if (p1 == NULL) {
144 /* external filename */
145 strlcpy(buf + 261, p2, 1024 - 261);
146 } else {
147 if (*p1) {
148 /* ustar header prefix */
149 memcpy(buf + 261, p1, 155);
150 buf[261 + 155] = '\0';
151 } else
152 /* empty string */
153 buf[261] = '\0';
154 /* ustar header name or linkname */
155 memcpy(buf + 600, p2, 100);
156 buf[600 + 100] = '\0';
157 strlcat(buf + 261, "/", 512);
158 strlcat(buf + 261, buf + 600, 512);
159 }
160 /* D("s1+s2='%s'", buf + 260); */
161
162 /* normalise filename from buf+261 to [buf+1;buf+257] */
163 /* buf[0] will always contain a slash from buf[260] */
164 expect_slash:
165 while (buf[i] == '/')
166 i++;
167 if (!buf[i])
168 goto got_NUL;
169 buf[n++] = '/';
170 expect_nonslash:
171 if (n == 257) /* overflow check */
172 goto got_NUL;
173 if (buf[i] == '/')
174 goto expect_slash;
175 else if (buf[i]) {
176 buf[n++] = buf[i++];
177 goto expect_nonslash;
178 }
179 got_NUL:
180 buf[n++] = '\0';
181
182 rv = alloc(n);
183 memcpy(rv, buf, n);
184 D("rv = '%s'", rv);
185 return (rv);
186 }
187
188 static int
ustar_search(struct ustarfs_file * ff,const char * ofn)189 ustar_search(struct ustarfs_file *ff, const char *ofn)
190 {
191 uint32_t i;
192 int rv, lastwasNUL = 0;
193
194 D("@%u, ofn=%s%s%s", ff->sector, ofn ? "'" : "",
195 ofn ? ofn : "(null)", ofn ? "'" : "");
196
197 if (ff->sector == SEC_VDIR) {
198 /* search from start */
199 ff->sector = USTARFS_FIRSTSEC;
200 goto loadsec;
201 }
202
203 skipdata:
204 /* skip data sectors, if any */
205 if (ISAFILE())
206 ff->sector += (UNOCT(hp->size) + 511) / 512;
207 skiphdr:
208 /* skip (current sector) ustar header block */
209 ff->sector++;
210
211 loadsec:
212 /* load next (hopefully) ustar header block */
213 if ((rv = rd(ff->open_file, ff->sector))) {
214 D("I/O error %d", rv);
215 return (rv);
216 }
217
218 /* check for header (or end of such) */
219 if (ustar_check(1)) {
220 /* check for end of archive */
221 for (i = 0; i < 512; ++i)
222 if (((char *)(void *)hp)[i])
223 break;
224 if (i == 512) {
225 /* sector was all NULs */
226 if (lastwasNUL++) {
227 D("end of archive");
228 return (ENOENT);
229 }
230 } else
231 /* sector was not all NULs */
232 lastwasNUL = 0;
233 goto skiphdr;
234 }
235 /* sector contains a valid and known ustar header */
236 lastwasNUL = 0;
237
238 if (ofn && ustar_cmpfn(ofn))
239 /* filename does not match expectations */
240 goto skipdata;
241
242 /* found ustar header which, if given, matches filename */
243 D("found");
244 return (0);
245 }
246
247 static int
ustar_cmpfn(const char * ofn)248 ustar_cmpfn(const char *ofn)
249 {
250 char *hfn;
251 int rv = 0;
252
253 hfn = fn_normalise(hp->prefix, hp->name);
254 D("ofn '%s' with hfn '%s'", ofn, hfn);
255 if (strcmp(ofn, hfn))
256 rv = 1;
257 free(hfn, strlen(hfn) + 1);
258 return (rv);
259 }
260
261 static void
ustar_fillsb(struct ustarfs_file * ff)262 ustar_fillsb(struct ustarfs_file *ff)
263 {
264 bzero(&ff->sb, sizeof(ff->sb));
265
266 ff->sb.st_mode = UNOCT(hp->mode);
267 ff->sb.st_uid = UNOCT(hp->uid);
268 ff->sb.st_gid = UNOCT(hp->gid);
269 #if 0
270 ff->sb.st_rdev = makedev(UNOCT(hp->devmajor), UNOCT(hp->devminor));
271 ff->sb.st_mtime = UNOCT(hp->mtime);
272 #endif
273 if (ISAFILE())
274 ff->sb.st_size = UNOCT(hp->size);
275 }
276
277 int
ustarfs_open(char * path,struct open_file * f)278 ustarfs_open(char *path, struct open_file *f)
279 {
280 int rv = 0;
281 struct ustarfs_file *ff;
282
283 D("path='%s'", path);
284 if (path[0] == '/' && path[1] == '.' && path[2] == '/')
285 path += 3;
286
287 /* check first sector for valid ustar header */
288 if ((rv = rd(f, USTARFS_FIRSTSEC)) || ustar_check(0))
289 return (rv ? rv : EUNLAB);
290
291 /* allocate fs specific data structure */
292 ff = alloc(sizeof(struct ustarfs_file));
293 bzero(ff, sizeof(struct ustarfs_file));
294 f->f_fsdata = ff;
295 ff->open_file = f;
296
297 /* check if we want “the directory” (e.g. for “ls”) */
298 if (!path[0] || (path[0] == '/' && (!path[1] ||
299 (path[1] == '.' && !path[2])))) {
300 /* “” or “/” or “/.” */
301 ff->sector = SEC_VDIR;
302 ff->nodeseekp = SEC_VDIR;
303 D("got directory");
304 return (0);
305 }
306
307 path = fn_normalise(NULL, path);
308 ff->sector = USTARFS_FIRSTSEC;
309 ff->nodeseekp = 0;
310 if (ustar_cmpfn(path)) {
311 do_search:
312 rv = ustar_search(ff, path);
313 }
314 if (rv) {
315 D("cannot find, error %d", rv);
316 free(ff, sizeof(struct ustarfs_file));
317 } else if (ISALINK()) {
318 D("got link, redoing");
319 free(path, strlen(path) + 1);
320 path = fn_normalise("", hp->linkname);
321 /* redo from start */
322 ff->sector = SEC_VDIR;
323 goto do_search;
324 } else
325 ustar_fillsb(ff);
326 free(path, strlen(path) + 1);
327 D("success");
328 return (rv);
329 }
330
331 int
ustarfs_close(struct open_file * f)332 ustarfs_close(struct open_file *f)
333 {
334 struct ustarfs_file *ff;
335
336 if (f == NULL)
337 return (0);
338
339 ff = f->f_fsdata;
340 if (ff) {
341 free(ff, sizeof(struct ustarfs_file));
342 }
343 return (0);
344 }
345
346 int
ustarfs_read(struct open_file * f,void * buf,size_t size,size_t * resid)347 ustarfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
348 {
349 struct ustarfs_file *ff = f->f_fsdata;
350 uint32_t datasec, dataofs, n;
351 int rv = 0;
352
353 D("%u @ %u", (unsigned)size, ff->nodeseekp);
354
355 /* are we at or past EOF already? */
356 if (ff->nodeseekp >= ff->sb.st_size)
357 goto out;
358
359 datasec = ff->sector + 1 + (ff->nodeseekp / 512);
360 dataofs = ff->nodeseekp & 511;
361
362 /* remaining octets in the sector */
363 n = 512 - dataofs;
364
365 /* amount we actually want to read */
366 if (n > size)
367 n = size;
368
369 /* bounds check against file size */
370 if (ff->nodeseekp + n > ff->sb.st_size)
371 n = ff->sb.st_size - ff->nodeseekp;
372
373 /* n is now amount of bytes we actually CAN read from one sector */
374
375 /* try to read the sector we’re in */
376 if ((rv = rd(ff->open_file, datasec)))
377 goto out;
378
379 /* copy (part) of the sector */
380 memcpy(buf, ((char *)hp) + dataofs, n);
381 ff->nodeseekp += n;
382 size -= n;
383 D("success, read %u bytes", n);
384
385 out:
386 if (resid)
387 *resid = size;
388 return (rv);
389 }
390
391 int
ustarfs_write(struct open_file * f,void * buf,size_t size,size_t * resid)392 ustarfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
393 {
394 return (EROFS);
395 }
396
397 off_t
ustarfs_seek(struct open_file * f,off_t offset,int where)398 ustarfs_seek(struct open_file *f, off_t offset, int where)
399 {
400 struct ustarfs_file *ff = f->f_fsdata;
401
402 if (ff->sector == SEC_VDIR) {
403 if (where == SEEK_SET && offset == 0) {
404 ff->nodeseekp = SEC_VDIR;
405 return (0);
406 }
407 return (-1);
408 }
409
410 switch (where) {
411 case SEEK_SET:
412 ff->nodeseekp = offset;
413 break;
414 case SEEK_CUR:
415 ff->nodeseekp += offset;
416 break;
417 case SEEK_END:
418 ff->nodeseekp = ff->sb.st_size + offset;
419 break;
420 default:
421 return (-1);
422 }
423 return (ff->nodeseekp);
424 }
425
426 int
ustarfs_stat(struct open_file * f,struct stat * sb)427 ustarfs_stat(struct open_file *f, struct stat *sb)
428 {
429 struct ustarfs_file *ff = f->f_fsdata;
430
431 if (ff->sector == SEC_VDIR) {
432 /* quick and dirty */
433 bzero(sb, sizeof(struct stat));
434 sb->st_mode = 040555;
435 } else
436 memcpy(sb, &ff->sb, sizeof(struct stat));
437
438 return (0);
439 }
440
441 int
ustarfs_readdir(struct open_file * f,char * name)442 ustarfs_readdir(struct open_file *f, char *name)
443 {
444 struct ustarfs_file *ff = f->f_fsdata;
445 int rv;
446
447 if (ff->sector != SEC_VDIR)
448 return (EINVAL);
449
450 /* reset? */
451 if (name == NULL) {
452 ff->nodeseekp = SEC_VDIR;
453 return (0);
454 }
455
456 ff->sector = ff->nodeseekp;
457 if (ff->sector != SEC_VDIR)
458 if ((rv = rd(ff->open_file, ff->sector)))
459 goto oops;
460 rv = ustar_search(ff, NULL);
461 oops:
462 ff->nodeseekp = ff->sector;
463 ff->sector = SEC_VDIR;
464
465 if (!rv) {
466 char *fn;
467
468 fn = fn_normalise(hp->prefix, hp->name);
469 memcpy(name, fn, strlen(fn) + 1);
470 free(fn, strlen(fn) + 1);
471 } else
472 *name = '\0';
473
474 return (rv);
475 }
476