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