ustarfs.c revision 1.7
1/*	$NetBSD: ustarfs.c,v 1.7 1999/03/26 15:41:38 dbj 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 */
57
58#ifdef _STANDALONE
59#include <lib/libkern/libkern.h>
60#else
61#include <string.h>
62#endif
63#include "stand.h"
64#include "ustarfs.h"
65
66#define	BBSIZE	8192
67#define	USTAR_NAME_BLOCK 512
68
69/*
70 * Virtual offset: relative to start of ustar archive
71 * Logical offset: volume-relative
72 * Physical offset: the usual meaning
73 */
74
75/* virtual offset to volume number */
76
77#define	vda2vn(_v,_volsize) ((_v) / (_volsize))
78
79/* conversions between the three different levels of disk addresses */
80
81#define	vda2lda(_v,_volsize) ((_v) % (_volsize))
82#define	lda2vda(_v,_volsize,_volnumber) ((_v) + (_volsize) * (_volnumber))
83
84#define	lda2pda(_lda) ((_lda) + ustarfs_mode_offset)
85#define	pda2lda(_pda) ((_pda) - ustarfs_mode_offset)
86/*
87 * Change this to off_t if you want to support big volumes. If we only use
88 * ustarfs on floppies it can stay int for libsa code density.
89 *
90 * It needs to be signed.
91 */
92typedef	int ustoffs;
93
94typedef struct ustar_struct {
95	char	ust_name[100],
96		ust_mode[8],
97		ust_uid[8],
98		ust_gid[8],
99		ust_size[12],
100		ust_misc[12 + 8 + 1 + 100],
101		ust_magic[6];
102	/* there is more, but we don't care */
103} ustar_t;
104
105/*
106 * We buffer one even cylindar of data...it's actually only really one
107 * cyl on a 1.44M floppy, but on other devices it's fast enough with any
108 * kind of block buffering, so we optimize for the slowest device.
109 */
110
111typedef struct ust_active_struct {
112	ustar_t	uas_active;
113	char	uas_1cyl[18 * 2 * 512];
114	ustoffs	uas_volsize;		/* XXX this is hardwired now */
115	ustoffs	uas_windowbase;		/* relative to volume 0 */
116	ustoffs	uas_filestart;		/* relative to volume 0 */
117	ustoffs	uas_fseek;		/* relative to file */
118	ustoffs	uas_filesize;		/* relative to volume 0 */
119	int	uas_init_window;	/* data present in window */
120	int	uas_init_fs;		/* ust FS actually found */
121	int	uas_volzerosig;		/* ID volume 0 by signature */
122	int	uas_offset;		/* amount of cylinder below lba 0 */
123} ust_active_t;
124
125static const char formatid[] = "USTARFS",
126		  metaname[] = "USTAR.volsize.";
127
128static int ustarfs_mode_offset = BBSIZE;
129
130static int checksig __P((ust_active_t *));
131static int convert __P((const char *, int, int));
132static int get_volume __P((struct open_file *, int));
133static void setwindow(ust_active_t *, ustoffs, ustoffs);
134static int ustarfs_cylinder_read __P((struct open_file *, ustoffs, int));
135static void ustarfs_sscanf __P((const char *, const char *, int *));
136static int read512block __P((struct open_file *, ustoffs, char block[512]));
137
138static int
139convert(f, base, fw)
140	const char *f;
141	int base, fw;
142{
143	int	i, c, result = 0;
144
145	while(fw > 0 && *f == ' ') {
146		--fw;
147		++f;
148	}
149	for(i = 0; i < fw; ++i) {
150		c = f[i];
151		if ('0' <= c && c < '0' + base) {
152			c -= '0';
153			result = result * base + c;
154		} else	break;
155	}
156	return result;
157}
158
159static void
160ustarfs_sscanf(s,f,xi)
161	const char *s,*f;
162	int *xi;
163{
164	*xi = convert(s, 8, convert(f + 1, 10, 99));
165}
166
167static int
168ustarfs_cylinder_read(f, seek2, forcelabel)
169	struct open_file *f;
170	ustoffs seek2;
171{
172	int e = 0;	/* XXX work around gcc warning */
173	ustoffs	lda;
174	char *xferbase;
175	ust_active_t *ustf;
176	size_t	xferrqst, xfercount;
177
178	ustf = f->f_fsdata;
179	xferrqst = sizeof ustf->uas_1cyl;
180	xferbase = ustf->uas_1cyl;
181	lda = pda2lda(seek2);
182	if (lda < 0) {
183		lda = -lda;
184		ustf->uas_offset = lda;
185		/*
186		 * don't read the label unless we have to. (Preserve
187		 * sequential block access so tape boot works.)
188		 */
189		if (!forcelabel) {
190			memset(xferbase, 0, lda);
191			xferrqst -= lda;
192			xferbase += lda;
193			seek2    += lda;
194		}
195	} else
196		ustf->uas_offset = 0;
197	while(xferrqst > 0) {
198		e = f->f_dev->dv_strategy(f->f_devdata, F_READ, seek2 / 512,
199			xferrqst, xferbase, &xfercount);
200		if (e)
201			break;
202		if (xfercount != xferrqst)
203			printf("Warning, unexpected short transfer %d/%d\n",
204				(int)xfercount, (int)xferrqst);
205		xferrqst -= xfercount;
206		xferbase += xfercount;
207	}
208	return e;
209}
210
211static int
212checksig(ustf)
213	ust_active_t *ustf;
214{
215	int	i, rcs;
216
217	for(i = rcs = 0; i < sizeof ustf->uas_1cyl; ++i)
218		rcs += ustf->uas_1cyl[i];
219	return rcs;
220}
221
222static int
223get_volume(f, vn)
224	struct open_file *f;
225	int vn;
226{
227	int	e, needvolume, havevolume;
228	ust_active_t *ustf;
229
230	ustf = f->f_fsdata;
231	havevolume = vda2vn(ustf->uas_windowbase, ustf->uas_volsize);
232	needvolume = vn;
233	while(havevolume != needvolume) {
234		printf("\nPlease ");
235		if (havevolume >= 0)
236			printf("remove disk %d, ", havevolume + 1);
237		printf("insert disk %d, and type return...",
238			needvolume + 1);
239		getchar();
240		printf("\n");
241		e = ustarfs_cylinder_read(f, 0, 1);
242		if (e)		/* Try again on error, needed on i386 */
243			e = ustarfs_cylinder_read(f, 0, 1);
244		if (e)
245			return e;
246		if(strncmp(formatid, ustf->uas_1cyl, strlen(formatid))) {
247			/* no magic, might be OK if we want volume 0 */
248			if (ustf->uas_volzerosig == checksig(ustf)) {
249				havevolume = 0;
250				continue;
251			}
252			printf("Disk is not from the volume set?!\n");
253			havevolume = -2;
254			continue;
255		}
256		ustarfs_sscanf(ustf->uas_1cyl + strlen(formatid), "%9o",
257			&havevolume);
258		--havevolume;
259	}
260	return 0;
261}
262
263static void
264setwindow(ust_active_t *ustf, ustoffs pda, ustoffs vda)
265{
266	ustf->uas_windowbase = lda2vda(pda2lda(pda), ustf->uas_volsize,
267					vda2vn(vda, ustf->uas_volsize))
268			     + ustf->uas_offset;
269	ustf->uas_init_window = 1;
270}
271
272static int
273read512block(f, vda, block)
274	struct open_file *f;
275	ustoffs vda;
276	char block[512];
277{
278	ustoffs pda;
279	ssize_t	e;
280	int	dienow;
281	ust_active_t *ustf;
282
283	dienow = 0;
284	ustf = f->f_fsdata;
285
286	if (!ustf->uas_init_window
287	&&   ustf->uas_windowbase == 0) {
288		/*
289		 * The algorithm doesn't require this, but without it we would
290		 * need some trick to get the cylinder zero signature computed.
291		 * That signature is used to identify volume zero, which we
292		 * don't give a USTARFS label to. (It's platform-dependent.)
293		 */
294		e = ustarfs_cylinder_read(f, 0, 0);
295		if (e)
296			return e;
297		ustf->uas_volzerosig = checksig(ustf);
298		setwindow(ustf, 0, 0);
299	}
300	/*
301	 * if (vda in window)
302	 * 	copy out and return data
303	 * if (vda is on some other disk)
304	 * 	do disk swap
305	 * get physical disk address
306	 * round down to cylinder boundary
307	 * read cylindar
308	 * set window (in vda space) and try again
309	 * [ there is an implicit assumption that windowbase always identifies
310	 *    the current volume, even if initwindow == 0. This way, a
311	 *    windowbase of 0 causes the initial volume to be disk 0 ]
312	 */
313tryagain:
314	if(ustf->uas_init_window
315	&& ustf->uas_windowbase <= vda && vda <
316	   ustf->uas_windowbase + sizeof ustf->uas_1cyl - ustf->uas_offset) {
317		memcpy(block, ustf->uas_1cyl
318				+ (vda - ustf->uas_windowbase)
319				+ ustf->uas_offset, 512);
320		return 0;
321	}
322	if (dienow++)
323		panic("ustarfs read512block");
324	ustf->uas_init_window = 0;
325	e = get_volume(f, vda2vn(vda, ustf->uas_volsize));
326	if (e)
327		return e;
328	pda = lda2pda(vda2lda(vda, ustf->uas_volsize));
329	pda-= pda % sizeof ustf->uas_1cyl;
330	e = ustarfs_cylinder_read(f, pda, 0);
331	if (e)
332		return e;
333	setwindow(ustf, pda, vda);
334	goto tryagain;
335}
336
337int
338ustarfs_open(path, f)
339	char *path;
340	struct open_file *f;
341
342{
343	ust_active_t *ustf;
344	ustoffs offset;
345	char	block[512];
346	int	filesize;
347	int	e, e2;
348	int	newvolblocks;
349
350	if (*path == '/')
351		++path;
352	e = EINVAL;
353	f->f_fsdata = ustf = alloc(sizeof *ustf);
354	memset(ustf, 0, sizeof *ustf);
355	offset = 0;
356	/* default to 2880 sector floppy */
357	ustf->uas_volsize = 80 * 2 * 18 * 512 - lda2pda(0);
358	ustf->uas_fseek = 0;
359	for(;;) {
360		ustf->uas_filestart = offset;
361		e2 = read512block(f, offset, block);
362		if (e2) {
363			e = e2;
364			break;
365		}
366		memcpy(&ustf->uas_active, block, sizeof ustf->uas_active);
367		if(strncmp(ustf->uas_active.ust_magic, "ustar", 5))
368			break;
369		e = ENOENT;	/* it must be an actual ustarfs */
370		ustf->uas_init_fs = 1;
371		/* if volume metadata is found, use it */
372		if(strncmp(ustf->uas_active.ust_name, metaname,
373		    strlen(metaname)) == 0) {
374			ustarfs_sscanf(ustf->uas_active.ust_name
375				+ strlen(metaname), "%99o", &newvolblocks);
376			ustf->uas_volsize = newvolblocks * 512
377					  - lda2pda(0);
378		}
379		ustarfs_sscanf(ustf->uas_active.ust_size,"%12o",&filesize);
380		if(strncmp(ustf->uas_active.ust_name, path,
381		    sizeof ustf->uas_active.ust_name) == 0) {
382			ustf->uas_filesize = filesize;
383			e = 0;
384			break;
385		}
386		offset += USTAR_NAME_BLOCK + filesize;
387		filesize %= 512;
388		if (filesize)
389			offset += 512 - filesize;
390	}
391	if (e) {
392		free(ustf, sizeof *ustf);
393		f->f_fsdata = 0;
394	}
395	return e;
396}
397
398int
399ustarfs_write(f, start, size, resid)
400	struct open_file *f;
401	void *start;
402	size_t size;
403	size_t *resid;
404{
405	return (EROFS);
406}
407
408off_t
409ustarfs_seek(f, offs, whence)
410	struct open_file *f;
411	off_t offs;
412	int whence;
413{
414	ust_active_t *ustf;
415
416	ustf = f->f_fsdata;
417	switch (whence) {
418	    case SEEK_SET:
419		ustf->uas_fseek = offs;
420		break;
421	    case SEEK_CUR:
422		ustf->uas_fseek += offs;
423		break;
424	    case SEEK_END:
425		ustf->uas_fseek = ustf->uas_filesize - offs;
426		break;
427	    default:
428		return -1;
429	}
430	return ustf->uas_fseek;
431}
432
433int
434ustarfs_read(f, start, size, resid)
435	struct open_file *f;
436	void *start;
437	size_t size;
438	size_t *resid;
439{
440	ust_active_t *ustf;
441	int	e;
442	char	*space512;
443	int	blkoffs,
444		readoffs,
445		bufferoffset;
446	size_t	seg;
447	int	infile,
448		inbuffer;
449
450	e = 0;
451	space512 = alloc(512);
452	ustf = f->f_fsdata;
453	while(size != 0) {
454		if (ustf->uas_fseek >= ustf->uas_filesize)
455			break;
456		bufferoffset = ustf->uas_fseek % 512;
457		blkoffs  = ustf->uas_fseek - bufferoffset;
458		readoffs = ustf->uas_filestart + 512 + blkoffs;
459		e = read512block(f, readoffs, space512);
460		if (e)
461			break;
462		seg = size;
463		inbuffer = 512 - bufferoffset;
464		if (inbuffer < seg)
465			seg = inbuffer;
466		infile = ustf->uas_filesize - ustf->uas_fseek;
467		if (infile < seg)
468			seg = infile;
469		memcpy(start, space512 + bufferoffset, seg);
470		ustf->uas_fseek += seg;
471		start = (caddr_t)start + seg;
472		size  -= seg;
473	}
474	if (resid)
475		*resid = size;
476	free(space512, 512);
477	return e;
478}
479
480int
481ustarfs_stat(f, sb)
482	struct open_file *f;
483	struct stat *sb;
484{
485	int	mode, uid, gid;
486	ust_active_t *ustf;
487
488	if (f == NULL)
489		return EINVAL;
490	ustf = f->f_fsdata;
491	memset(sb, 0, sizeof *sb);
492	ustarfs_sscanf(ustf->uas_active.ust_mode, "%8o", &mode);
493	ustarfs_sscanf(ustf->uas_active.ust_uid, "%8o", &uid);
494	ustarfs_sscanf(ustf->uas_active.ust_gid, "%8o", &gid);
495	sb->st_mode = mode;
496	sb->st_uid  = uid;
497	sb->st_gid  = gid;
498	sb->st_size = ustf->uas_filesize;
499	return 0;
500}
501
502int
503ustarfs_close(f)
504	struct open_file *f;
505{
506	if (f == NULL || f->f_fsdata == NULL)
507		return EINVAL;
508	free(f->f_fsdata, sizeof(ust_active_t));
509	f->f_fsdata = 0;
510	return 0;
511}
512