ustarfs.c revision 1.21
1/*	$NetBSD: ustarfs.c,v 1.21 2003/03/18 19:20:09 mycroft 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 */
99typedef	int ustoffs;
100
101typedef 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
119typedef struct ust_active_struct {
120	ustar_t	uas_active;
121	char	uas_1cyl[18 * 2 * 512];
122	ustoffs	uas_volsize;		/* XXX this is hardwired now */
123	ustoffs	uas_windowbase;		/* relative to volume 0 */
124	ustoffs	uas_filestart;		/* relative to volume 0 */
125	ustoffs	uas_fseek;		/* relative to file */
126	ustoffs	uas_filesize;		/* relative to volume 0 */
127	int	uas_init_window;	/* data present in window */
128	int	uas_init_fs;		/* ust FS actually found */
129	int	uas_volzerosig;		/* ID volume 0 by signature */
130	int	uas_sigdone;		/* did sig already */
131	int	uas_offset;		/* amount of cylinder below lba 0 */
132} ust_active_t;
133
134static const char formatid[] = "USTARFS",
135		  metaname[] = "USTAR.volsize.";
136
137static const int ustarfs_mode_offset = BBSIZE;
138
139static int checksig __P((ust_active_t *));
140static int convert __P((const char *, int, int));
141static int get_volume __P((struct open_file *, int));
142static void setwindow(ust_active_t *, ustoffs, ustoffs);
143static int real_fs_cylinder_read __P((struct open_file *, ustoffs, int));
144static int ustarfs_cylinder_read __P((struct open_file *, ustoffs, int));
145static void ustarfs_sscanf __P((const char *, const char *, int *));
146static int read512block __P((struct open_file *, ustoffs, char block[512]));
147static int init_volzero_sig __P((struct open_file *));
148
149#ifdef HAVE_CHANGEDISK_HOOK
150/*
151 * Called when the next volume is prompted.
152 * Machine dependent code can eject the medium etc.
153 * The new medium must be ready when this hook returns.
154 */
155void changedisk_hook __P((struct open_file *));
156#endif
157
158static int
159convert(f, base, fw)
160	const char *f;
161	int base, fw;
162{
163	int	i, c, result = 0;
164
165	while(fw > 0 && *f == ' ') {
166		--fw;
167		++f;
168	}
169	for(i = 0; i < fw; ++i) {
170		c = f[i];
171		if ('0' <= c && c < '0' + base) {
172			c -= '0';
173			result = result * base + c;
174		} else	break;
175	}
176	return result;
177}
178
179static void
180ustarfs_sscanf(s,f,xi)
181	const char *s,*f;
182	int *xi;
183{
184	*xi = convert(s, 8, convert(f + 1, 10, 99));
185}
186
187static int
188ustarfs_cylinder_read(f, seek2, forcelabel)
189	struct open_file *f;
190	ustoffs seek2;
191{
192	int i, e;
193
194	for (i = 0; i < 3; ++i) {
195		e = real_fs_cylinder_read(f, seek2, forcelabel);
196		if (e == 0)
197			return 0;
198	}
199	return e;
200}
201
202static int
203real_fs_cylinder_read(f, seek2, forcelabel)
204	struct open_file *f;
205	ustoffs seek2;
206{
207	int i;
208	int e = 0;	/* XXX work around gcc warning */
209	ustoffs	lda;
210	char *xferbase;
211	ust_active_t *ustf;
212	size_t	xferrqst, xfercount;
213
214	ustf = f->f_fsdata;
215	xferrqst = sizeof ustf->uas_1cyl;
216	xferbase = ustf->uas_1cyl;
217	lda = pda2lda(seek2);
218	if (lda < 0) {
219		lda = -lda;
220		ustf->uas_offset = lda;
221		/*
222		 * don't read the label unless we have to. (Preserve
223		 * sequential block access so tape boot works.)
224		 */
225		if (!forcelabel) {
226			memset(xferbase, 0, lda);
227			xferrqst -= lda;
228			xferbase += lda;
229			seek2    += lda;
230		}
231	} else
232		ustf->uas_offset = 0;
233	while(xferrqst > 0) {
234#if !defined(LIBSA_NO_TWIDDLE)
235		twiddle();
236#endif
237		for (i = 0; i < 3; ++i) {
238			e = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
239			    seek2 / 512, xferrqst, xferbase, &xfercount);
240			if (e == 0)
241				break;
242			printf("@");
243		}
244		if (e)
245			break;
246		if (xfercount != xferrqst)
247			printf("Warning, unexpected short transfer %d/%d\n",
248				(int)xfercount, (int)xferrqst);
249		xferrqst -= xfercount;
250		xferbase += xfercount;
251		seek2    += xfercount;
252	}
253	return e;
254}
255
256static int
257checksig(ustf)
258	ust_active_t *ustf;
259{
260	int	i, rcs;
261
262	for(i = rcs = 0; i < sizeof ustf->uas_1cyl; ++i)
263		rcs += ustf->uas_1cyl[i];
264	return rcs;
265}
266
267static int
268get_volume(f, vn)
269	struct open_file *f;
270	int vn;
271{
272	int	e, needvolume, havevolume;
273	ust_active_t *ustf;
274
275	ustf = f->f_fsdata;
276	havevolume = vda2vn(ustf->uas_windowbase, ustf->uas_volsize);
277	needvolume = vn;
278	while(havevolume != needvolume) {
279		printf("\nPlease ");
280		if (havevolume >= 0)
281			printf("remove disk %d, ", havevolume + 1);
282		printf("insert disk %d, and press return...",
283			needvolume + 1);
284#ifdef HAVE_CHANGEDISK_HOOK
285		changedisk_hook(f);
286#else
287		for (;;) {
288			int c = getchar();
289			if ((c == '\n') || (c == '\r'))
290				break;
291		}
292#endif
293		printf("\n");
294		e = ustarfs_cylinder_read(f, 0, needvolume != 0);
295		if (e)
296			return e;
297		if(strncmp(formatid, ustf->uas_1cyl, strlen(formatid))) {
298			/* no magic, might be OK if we want volume 0 */
299			if (ustf->uas_volzerosig == checksig(ustf)) {
300				havevolume = 0;
301				continue;
302			}
303			printf("Disk is not from the volume set?!\n");
304			havevolume = -2;
305			continue;
306		}
307		ustarfs_sscanf(ustf->uas_1cyl + strlen(formatid), "%9o",
308			&havevolume);
309		--havevolume;
310	}
311	return 0;
312}
313
314static void
315setwindow(ust_active_t *ustf, ustoffs pda, ustoffs vda)
316{
317	ustf->uas_windowbase = lda2vda(pda2lda(pda), ustf->uas_volsize,
318					vda2vn(vda, ustf->uas_volsize))
319			     + ustf->uas_offset;
320	ustf->uas_init_window = 1;
321}
322
323static int
324read512block(f, vda, block)
325	struct open_file *f;
326	ustoffs vda;
327	char block[512];
328{
329	ustoffs pda;
330	ssize_t	e;
331	int	dienow;
332	ust_active_t *ustf;
333
334	dienow = 0;
335	ustf = f->f_fsdata;
336
337	/*
338	 * if (vda in window)
339	 * 	copy out and return data
340	 * if (vda is on some other disk)
341	 * 	do disk swap
342	 * get physical disk address
343	 * round down to cylinder boundary
344	 * read cylinder
345	 * set window (in vda space) and try again
346	 * [ there is an implicit assumption that windowbase always identifies
347	 *    the current volume, even if initwindow == 0. This way, a
348	 *    windowbase of 0 causes the initial volume to be disk 0 ]
349	 */
350tryagain:
351	if(ustf->uas_init_window
352	&& ustf->uas_windowbase <= vda && vda <
353	   ustf->uas_windowbase + sizeof ustf->uas_1cyl - ustf->uas_offset) {
354		memcpy(block, ustf->uas_1cyl
355				+ (vda - ustf->uas_windowbase)
356				+ ustf->uas_offset, 512);
357		return 0;
358	}
359	if (dienow++)
360		panic("ustarfs read512block");
361	ustf->uas_init_window = 0;
362	e = get_volume(f, vda2vn(vda, ustf->uas_volsize));
363	if (e)
364		return e;
365	pda = lda2pda(vda2lda(vda, ustf->uas_volsize));
366	pda-= pda % sizeof ustf->uas_1cyl;
367	e = ustarfs_cylinder_read(f, pda, 0);
368	if (e)
369		return e;
370	setwindow(ustf, pda, vda);
371	goto tryagain;
372}
373
374static int
375init_volzero_sig(f)
376	struct open_file *f;
377{
378	int e;
379	ust_active_t *ustf;
380
381	ustf = f->f_fsdata;
382	if (!ustf->uas_sigdone) {
383		e = ustarfs_cylinder_read(f, 0, 0);
384		if (e)
385			return e;
386		ustf->uas_volzerosig = checksig(ustf);
387		setwindow(ustf, 0, 0);
388	}
389	return 0;
390}
391
392int
393ustarfs_open(path, f)
394	char *path;
395	struct open_file *f;
396
397{
398	ust_active_t *ustf;
399	ustoffs offset;
400	char	block[512];
401	int	filesize;
402	int	e, e2;
403	int	newvolblocks;
404
405	if (*path == '/')
406		++path;
407	f->f_fsdata = ustf = alloc(sizeof *ustf);
408	memset(ustf, 0, sizeof *ustf);
409	offset = 0;
410	/* default to 2880 sector floppy */
411	ustf->uas_volsize = 80 * 2 * 18 * 512 - lda2pda(0);
412	ustf->uas_fseek = 0;
413	e = init_volzero_sig(f);
414	if (e)
415		return e;
416	e2 = EINVAL;
417	for(;;) {
418		ustf->uas_filestart = offset;
419		e = read512block(f, offset, block);
420		if (e)
421			break;
422		memcpy(&ustf->uas_active, block, sizeof ustf->uas_active);
423		if(strncmp(ustf->uas_active.ust_magic, "ustar", 5)) {
424			e = e2;
425			break;
426		}
427		e2 = ENOENT;	/* it must be an actual ustarfs */
428		ustf->uas_init_fs = 1;
429		/* if volume metadata is found, use it */
430		if(strncmp(ustf->uas_active.ust_name, metaname,
431		    strlen(metaname)) == 0) {
432			ustarfs_sscanf(ustf->uas_active.ust_name
433				+ strlen(metaname), "%99o", &newvolblocks);
434			ustf->uas_volsize = newvolblocks * 512
435					  - lda2pda(0);
436		}
437		ustarfs_sscanf(ustf->uas_active.ust_size,"%12o",&filesize);
438		if(strncmp(ustf->uas_active.ust_name, path,
439		    sizeof ustf->uas_active.ust_name) == 0) {
440			ustf->uas_filesize = filesize;
441			break;
442		}
443		offset += USTAR_NAME_BLOCK + filesize;
444		filesize %= 512;
445		if (filesize)
446			offset += 512 - filesize;
447	}
448	if (e) {
449		free(ustf, sizeof *ustf);
450		f->f_fsdata = 0;
451	}
452	return e;
453}
454
455#ifndef LIBSA_NO_FS_WRITE
456int
457ustarfs_write(f, start, size, resid)
458	struct open_file *f;
459	void *start;
460	size_t size;
461	size_t *resid;
462{
463	return (EROFS);
464}
465#endif /* !LIBSA_NO_FS_WRITE */
466
467#ifndef LIBSA_NO_FS_SEEK
468off_t
469ustarfs_seek(f, offs, whence)
470	struct open_file *f;
471	off_t offs;
472	int whence;
473{
474	ust_active_t *ustf;
475
476	ustf = f->f_fsdata;
477	switch (whence) {
478	    case SEEK_SET:
479		ustf->uas_fseek = offs;
480		break;
481	    case SEEK_CUR:
482		ustf->uas_fseek += offs;
483		break;
484	    case SEEK_END:
485		ustf->uas_fseek = ustf->uas_filesize - offs;
486		break;
487	    default:
488		return -1;
489	}
490	return ustf->uas_fseek;
491}
492#endif /* !LIBSA_NO_FS_SEEK */
493
494int
495ustarfs_read(f, start, size, resid)
496	struct open_file *f;
497	void *start;
498	size_t size;
499	size_t *resid;
500{
501	ust_active_t *ustf;
502	int	e;
503	char	*space512;
504	int	blkoffs,
505		readoffs,
506		bufferoffset;
507	size_t	seg;
508	int	infile,
509		inbuffer;
510
511	e = 0;
512	space512 = alloc(512);
513	ustf = f->f_fsdata;
514	while(size != 0) {
515		if (ustf->uas_fseek >= ustf->uas_filesize)
516			break;
517		bufferoffset = ustf->uas_fseek % 512;
518		blkoffs  = ustf->uas_fseek - bufferoffset;
519		readoffs = ustf->uas_filestart + 512 + blkoffs;
520		e = read512block(f, readoffs, space512);
521		if (e)
522			break;
523		seg = size;
524		inbuffer = 512 - bufferoffset;
525		if (inbuffer < seg)
526			seg = inbuffer;
527		infile = ustf->uas_filesize - ustf->uas_fseek;
528		if (infile < seg)
529			seg = infile;
530		memcpy(start, space512 + bufferoffset, seg);
531		ustf->uas_fseek += seg;
532		start = (caddr_t)start + seg;
533		size  -= seg;
534	}
535	if (resid)
536		*resid = size;
537	free(space512, 512);
538	return e;
539}
540
541int
542ustarfs_stat(f, sb)
543	struct open_file *f;
544	struct stat *sb;
545{
546	int	mode, uid, gid;
547	ust_active_t *ustf;
548
549	if (f == NULL)
550		return EINVAL;
551	ustf = f->f_fsdata;
552	memset(sb, 0, sizeof *sb);
553	ustarfs_sscanf(ustf->uas_active.ust_mode, "%8o", &mode);
554	ustarfs_sscanf(ustf->uas_active.ust_uid, "%8o", &uid);
555	ustarfs_sscanf(ustf->uas_active.ust_gid, "%8o", &gid);
556	sb->st_mode = mode;
557	sb->st_uid  = uid;
558	sb->st_gid  = gid;
559	sb->st_size = ustf->uas_filesize;
560	return 0;
561}
562
563#ifndef LIBSA_NO_FS_CLOSE
564int
565ustarfs_close(f)
566	struct open_file *f;
567{
568	if (f == NULL || f->f_fsdata == NULL)
569		return EINVAL;
570	free(f->f_fsdata, sizeof(ust_active_t));
571	f->f_fsdata = 0;
572	return 0;
573}
574#endif /* !LIBSA_NO_FS_CLOSE */
575