ustarfs.c revision 1.30 1 /* $NetBSD: ustarfs.c,v 1.30 2007/11/11 13:23:07 isaki Exp $ */
2
3 /* [Notice revision 2.2]
4 * Copyright (c) 1997, 1998 Avalon Computer Systems, Inc.
5 * All rights reserved.
6 *
7 * Author: Ross Harvey
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright and
13 * author notice, this list of conditions, and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of Avalon Computer Systems, Inc. nor the names of
18 * its contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 * 4. This copyright will be assigned to The NetBSD Foundation on
21 * 1/1/2000 unless these terms (including possibly the assignment
22 * date) are updated in writing by Avalon prior to the latest specified
23 * assignment date.
24 *
25 * THIS SOFTWARE IS PROVIDED BY AVALON COMPUTER SYSTEMS, INC. AND CONTRIBUTORS
26 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AVALON OR THE CONTRIBUTORS
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38
39 /*
40 ******************************* USTAR FS *******************************
41 */
42
43 /*
44 * Implement an ROFS with an 8K boot area followed by ustar-format data.
45 * The point: minimal FS overhead, and it's easy (well, `possible') to
46 * split files over multiple volumes.
47 *
48 * XXX - TODO LIST
49 * --- - ---- ----
50 * XXX - tag volume numbers and verify that the correct volume is
51 * inserted after volume swaps.
52 *
53 * XXX - stop hardwiring FS metadata for floppies...embed it in a file,
54 * file name, or something. (Remember __SYMDEF? :-)
55 *
56 * XXX Does not currently implement:
57 * XXX
58 * XXX LIBSA_NO_FS_CLOSE
59 * XXX LIBSA_NO_FS_SEEK
60 * XXX LIBSA_NO_FS_WRITE
61 * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?)
62 * XXX LIBSA_FS_SINGLECOMPONENT
63 */
64
65 #ifdef _STANDALONE
66 #include <lib/libkern/libkern.h>
67 #else
68 #include <string.h>
69 #endif
70 #include "stand.h"
71 #include "ustarfs.h"
72
73 #define BBSIZE 8192
74 #define USTAR_NAME_BLOCK 512
75
76 /*
77 * Virtual offset: relative to start of ustar archive
78 * Logical offset: volume-relative
79 * Physical offset: the usual meaning
80 */
81
82 /* virtual offset to volume number */
83
84 #define vda2vn(_v,_volsize) ((_v) / (_volsize))
85
86 /* conversions between the three different levels of disk addresses */
87
88 #define vda2lda(_v,_volsize) ((_v) % (_volsize))
89 #define lda2vda(_v,_volsize,_volnumber) ((_v) + (_volsize) * (_volnumber))
90
91 #define lda2pda(_lda) ((_lda) + ustarfs_mode_offset)
92 #define pda2lda(_pda) ((_pda) - ustarfs_mode_offset)
93 /*
94 * Change this to off_t if you want to support big volumes. If we only use
95 * ustarfs on floppies it can stay int for libsa code density.
96 *
97 * It needs to be signed.
98 */
99 typedef int ustoffs;
100
101 typedef struct ustar_struct {
102 char ust_name[100],
103 ust_mode[8],
104 ust_uid[8],
105 ust_gid[8],
106 ust_size[12],
107 ust_misc[12 + 8 + 1 + 100],
108 ust_magic[6],
109 /* there is more, but we don't care */
110 ust_pad[1]; /* make it aligned */
111 } ustar_t;
112
113 /*
114 * We buffer one even cylinder of data...it's actually only really one
115 * cyl on a 1.44M floppy, but on other devices it's fast enough with any
116 * kind of block buffering, so we optimize for the slowest device.
117 */
118
119 #ifndef USTAR_SECT_PER_CYL
120 #define USTAR_SECT_PER_CYL (18 * 2)
121 #endif
122
123 typedef struct ust_active_struct {
124 ustar_t uas_active;
125 char uas_1cyl[USTAR_SECT_PER_CYL * 512];
126 ustoffs uas_volsize; /* XXX this is hardwired now */
127 ustoffs uas_windowbase; /* relative to volume 0 */
128 ustoffs uas_filestart; /* relative to volume 0 */
129 ustoffs uas_fseek; /* relative to file */
130 ustoffs uas_filesize; /* relative to volume 0 */
131 int uas_init_window; /* data present in window */
132 int uas_init_fs; /* ust FS actually found */
133 int uas_volzerosig; /* ID volume 0 by signature */
134 int uas_sigdone; /* did sig already */
135 int uas_offset; /* amount of cylinder below lba 0 */
136 } ust_active_t;
137
138 static const char formatid[] = "USTARFS",
139 metaname[] = "USTAR.volsize.";
140
141 static const int ustarfs_mode_offset = BBSIZE;
142
143 static int checksig __P((ust_active_t *));
144 static int convert __P((const char *, int, int));
145 static int get_volume __P((struct open_file *, int));
146 static void setwindow(ust_active_t *, ustoffs, ustoffs);
147 static int real_fs_cylinder_read __P((struct open_file *, ustoffs, int));
148 static int ustarfs_cylinder_read __P((struct open_file *, ustoffs, int));
149 static void ustarfs_sscanf __P((const char *, const char *, int *));
150 static int read512block __P((struct open_file *, ustoffs, char block[512]));
151 static int init_volzero_sig __P((struct open_file *));
152
153 #ifdef HAVE_CHANGEDISK_HOOK
154 /*
155 * Called when the next volume is prompted.
156 * Machine dependent code can eject the medium etc.
157 * The new medium must be ready when this hook returns.
158 */
159 void changedisk_hook __P((struct open_file *));
160 #endif
161
162 static int
163 convert(f, base, fw)
164 const char *f;
165 int base, fw;
166 {
167 int i, c, result = 0;
168
169 while(fw > 0 && *f == ' ') {
170 --fw;
171 ++f;
172 }
173 for(i = 0; i < fw; ++i) {
174 c = f[i];
175 if ('0' <= c && c < '0' + base) {
176 c -= '0';
177 result = result * base + c;
178 } else break;
179 }
180 return result;
181 }
182
183 static void
184 ustarfs_sscanf(s,f,xi)
185 const char *s,*f;
186 int *xi;
187 {
188 *xi = convert(s, 8, convert(f + 1, 10, 99));
189 }
190
191 static int
192 ustarfs_cylinder_read(f, seek2, forcelabel)
193 struct open_file *f;
194 ustoffs seek2;
195 int forcelabel;
196 {
197 int i, e;
198
199 for (i = 0; i < 3; ++i) {
200 e = real_fs_cylinder_read(f, seek2, forcelabel);
201 if (e == 0)
202 return 0;
203 }
204 return e;
205 }
206
207 static int
208 real_fs_cylinder_read(f, seek2, forcelabel)
209 struct open_file *f;
210 ustoffs seek2;
211 int forcelabel;
212 {
213 int i;
214 int e = 0; /* XXX work around gcc warning */
215 ustoffs lda;
216 char *xferbase;
217 ust_active_t *ustf;
218 size_t xferrqst, xfercount;
219
220 ustf = f->f_fsdata;
221 xferrqst = sizeof ustf->uas_1cyl;
222 xferbase = ustf->uas_1cyl;
223 lda = pda2lda(seek2);
224 if (lda < 0) {
225 lda = -lda;
226 ustf->uas_offset = lda;
227 /*
228 * don't read the label unless we have to. (Preserve
229 * sequential block access so tape boot works.)
230 */
231 if (!forcelabel) {
232 memset(xferbase, 0, lda);
233 xferrqst -= lda;
234 xferbase += lda;
235 seek2 += lda;
236 }
237 } else
238 ustf->uas_offset = 0;
239 while(xferrqst > 0) {
240 #if !defined(LIBSA_NO_TWIDDLE)
241 twiddle();
242 #endif
243 for (i = 0; i < 3; ++i) {
244 e = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
245 seek2 / 512, xferrqst, xferbase, &xfercount);
246 if (e == 0)
247 break;
248 printf("@");
249 }
250 if (e)
251 break;
252 if (xfercount != xferrqst)
253 printf("Warning, unexpected short transfer %d/%d\n",
254 (int)xfercount, (int)xferrqst);
255 xferrqst -= xfercount;
256 xferbase += xfercount;
257 seek2 += xfercount;
258 }
259 return e;
260 }
261
262 static int
263 checksig(ustf)
264 ust_active_t *ustf;
265 {
266 int i, rcs;
267
268 for(i = rcs = 0; i < (int)(sizeof ustf->uas_1cyl); ++i)
269 rcs += ustf->uas_1cyl[i];
270 return rcs;
271 }
272
273 static int
274 get_volume(f, vn)
275 struct open_file *f;
276 int vn;
277 {
278 int e, needvolume, havevolume;
279 ust_active_t *ustf;
280
281 ustf = f->f_fsdata;
282 havevolume = vda2vn(ustf->uas_windowbase, ustf->uas_volsize);
283 needvolume = vn;
284 while(havevolume != needvolume) {
285 printf("\nPlease ");
286 if (havevolume >= 0)
287 printf("remove disk %d, ", havevolume + 1);
288 printf("insert disk %d, and press return...",
289 needvolume + 1);
290 #ifdef HAVE_CHANGEDISK_HOOK
291 changedisk_hook(f);
292 #else
293 for (;;) {
294 int c = getchar();
295 if ((c == '\n') || (c == '\r'))
296 break;
297 }
298 #endif
299 printf("\n");
300 e = ustarfs_cylinder_read(f, 0, needvolume != 0);
301 if (e)
302 return e;
303 if(strncmp(formatid, ustf->uas_1cyl, strlen(formatid))) {
304 /* no magic, might be OK if we want volume 0 */
305 if (ustf->uas_volzerosig == checksig(ustf)) {
306 havevolume = 0;
307 continue;
308 }
309 printf("Disk is not from the volume set?!\n");
310 havevolume = -2;
311 continue;
312 }
313 ustarfs_sscanf(ustf->uas_1cyl + strlen(formatid), "%9o",
314 &havevolume);
315 --havevolume;
316 }
317 return 0;
318 }
319
320 static void
321 setwindow(ust_active_t *ustf, ustoffs pda, ustoffs vda)
322 {
323 ustf->uas_windowbase = lda2vda(pda2lda(pda), ustf->uas_volsize,
324 vda2vn(vda, ustf->uas_volsize))
325 + ustf->uas_offset;
326 ustf->uas_init_window = 1;
327 }
328
329 static int
330 read512block(f, vda, block)
331 struct open_file *f;
332 ustoffs vda;
333 char block[512];
334 {
335 ustoffs pda;
336 ssize_t e;
337 int dienow;
338 ust_active_t *ustf;
339
340 dienow = 0;
341 ustf = f->f_fsdata;
342
343 /*
344 * if (vda in window)
345 * copy out and return data
346 * if (vda is on some other disk)
347 * do disk swap
348 * get physical disk address
349 * round down to cylinder boundary
350 * read cylinder
351 * set window (in vda space) and try again
352 * [ there is an implicit assumption that windowbase always identifies
353 * the current volume, even if initwindow == 0. This way, a
354 * windowbase of 0 causes the initial volume to be disk 0 ]
355 */
356 tryagain:
357 if(ustf->uas_init_window
358 && ustf->uas_windowbase <= vda && vda <
359 ustf->uas_windowbase +
360 (int)(sizeof ustf->uas_1cyl) - ustf->uas_offset) {
361 memcpy(block, ustf->uas_1cyl
362 + (vda - ustf->uas_windowbase)
363 + ustf->uas_offset, 512);
364 return 0;
365 }
366 if (dienow++)
367 panic("ustarfs read512block");
368 ustf->uas_init_window = 0;
369 e = get_volume(f, vda2vn(vda, ustf->uas_volsize));
370 if (e)
371 return e;
372 pda = lda2pda(vda2lda(vda, ustf->uas_volsize));
373 pda-= pda % sizeof ustf->uas_1cyl;
374 e = ustarfs_cylinder_read(f, pda, 0);
375 if (e)
376 return e;
377 setwindow(ustf, pda, vda);
378 goto tryagain;
379 }
380
381 static int
382 init_volzero_sig(f)
383 struct open_file *f;
384 {
385 int e;
386 ust_active_t *ustf;
387
388 ustf = f->f_fsdata;
389 if (!ustf->uas_sigdone) {
390 e = ustarfs_cylinder_read(f, 0, 0);
391 if (e)
392 return e;
393 ustf->uas_volzerosig = checksig(ustf);
394 setwindow(ustf, 0, 0);
395 }
396 return 0;
397 }
398
399 int
400 ustarfs_open(path, f)
401 const char *path;
402 struct open_file *f;
403
404 {
405 ust_active_t *ustf;
406 ustoffs offset;
407 char block[512];
408 int filesize;
409 int e, e2;
410 int newvolblocks;
411
412 if (*path == '/')
413 ++path;
414 f->f_fsdata = ustf = alloc(sizeof *ustf);
415 memset(ustf, 0, sizeof *ustf);
416 offset = 0;
417 /* default to 2880 sector floppy */
418 ustf->uas_volsize = 80 * 2 * 18 * 512 - lda2pda(0);
419 ustf->uas_fseek = 0;
420 e = init_volzero_sig(f);
421 if (e)
422 return e;
423 e2 = EINVAL;
424 for(;;) {
425 ustf->uas_filestart = offset;
426 e = read512block(f, offset, block);
427 if (e)
428 break;
429 memcpy(&ustf->uas_active, block, sizeof ustf->uas_active);
430 if(strncmp(ustf->uas_active.ust_magic, "ustar", 5)) {
431 e = e2;
432 break;
433 }
434 e2 = ENOENT; /* it must be an actual ustarfs */
435 ustf->uas_init_fs = 1;
436 /* if volume metadata is found, use it */
437 if(strncmp(ustf->uas_active.ust_name, metaname,
438 strlen(metaname)) == 0) {
439 ustarfs_sscanf(ustf->uas_active.ust_name
440 + strlen(metaname), "%99o", &newvolblocks);
441 ustf->uas_volsize = newvolblocks * 512
442 - lda2pda(0);
443 }
444 ustarfs_sscanf(ustf->uas_active.ust_size,"%12o",&filesize);
445 if(strncmp(ustf->uas_active.ust_name, path,
446 sizeof ustf->uas_active.ust_name) == 0) {
447 ustf->uas_filesize = filesize;
448 break;
449 }
450 offset += USTAR_NAME_BLOCK + filesize;
451 filesize %= 512;
452 if (filesize)
453 offset += 512 - filesize;
454 }
455 if (e) {
456 dealloc(ustf, sizeof *ustf);
457 f->f_fsdata = 0;
458 }
459 return e;
460 }
461
462 #ifndef LIBSA_NO_FS_WRITE
463 int
464 ustarfs_write(f, start, size, resid)
465 struct open_file *f;
466 void *start;
467 size_t size;
468 size_t *resid;
469 {
470 return (EROFS);
471 }
472 #endif /* !LIBSA_NO_FS_WRITE */
473
474 #ifndef LIBSA_NO_FS_SEEK
475 off_t
476 ustarfs_seek(f, offs, whence)
477 struct open_file *f;
478 off_t offs;
479 int whence;
480 {
481 ust_active_t *ustf;
482
483 ustf = f->f_fsdata;
484 switch (whence) {
485 case SEEK_SET:
486 ustf->uas_fseek = offs;
487 break;
488 case SEEK_CUR:
489 ustf->uas_fseek += offs;
490 break;
491 case SEEK_END:
492 ustf->uas_fseek = ustf->uas_filesize - offs;
493 break;
494 default:
495 return -1;
496 }
497 return ustf->uas_fseek;
498 }
499 #endif /* !LIBSA_NO_FS_SEEK */
500
501 int
502 ustarfs_read(f, start, size, resid)
503 struct open_file *f;
504 void *start;
505 size_t size;
506 size_t *resid;
507 {
508 ust_active_t *ustf;
509 int e;
510 char *space512;
511 int blkoffs,
512 readoffs,
513 bufferoffset;
514 size_t seg;
515 size_t infile,
516 inbuffer;
517
518 e = 0;
519 space512 = alloc(512);
520 ustf = f->f_fsdata;
521 while(size != 0) {
522 if (ustf->uas_fseek >= ustf->uas_filesize)
523 break;
524 bufferoffset = ustf->uas_fseek % 512;
525 blkoffs = ustf->uas_fseek - bufferoffset;
526 readoffs = ustf->uas_filestart + 512 + blkoffs;
527 e = read512block(f, readoffs, space512);
528 if (e)
529 break;
530 seg = size;
531 inbuffer = 512 - bufferoffset;
532 if (inbuffer < seg)
533 seg = inbuffer;
534 infile = ustf->uas_filesize - ustf->uas_fseek;
535 if (infile < seg)
536 seg = infile;
537 memcpy(start, space512 + bufferoffset, seg);
538 ustf->uas_fseek += seg;
539 start = (char *)start + seg;
540 size -= seg;
541 }
542 if (resid)
543 *resid = size;
544 dealloc(space512, 512);
545 return e;
546 }
547
548 int
549 ustarfs_stat(f, sb)
550 struct open_file *f;
551 struct stat *sb;
552 {
553 int mode, uid, gid;
554 ust_active_t *ustf;
555
556 if (f == NULL)
557 return EINVAL;
558 ustf = f->f_fsdata;
559 memset(sb, 0, sizeof *sb);
560 ustarfs_sscanf(ustf->uas_active.ust_mode, "%8o", &mode);
561 ustarfs_sscanf(ustf->uas_active.ust_uid, "%8o", &uid);
562 ustarfs_sscanf(ustf->uas_active.ust_gid, "%8o", &gid);
563 sb->st_mode = mode;
564 sb->st_uid = uid;
565 sb->st_gid = gid;
566 sb->st_size = ustf->uas_filesize;
567 return 0;
568 }
569
570 #ifndef LIBSA_NO_FS_CLOSE
571 int
572 ustarfs_close(f)
573 struct open_file *f;
574 {
575 if (f == NULL || f->f_fsdata == NULL)
576 return EINVAL;
577 dealloc(f->f_fsdata, sizeof(ust_active_t));
578 f->f_fsdata = 0;
579 return 0;
580 }
581 #endif /* !LIBSA_NO_FS_CLOSE */
582