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