Home | History | Annotate | Line # | Download | only in libsa
ustarfs.c revision 1.3
      1 /*	$NetBSD: ustarfs.c,v 1.3 1998/10/15 01:11:46 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 		  metaname[] = "USTAR.volsize.";
    123 
    124 static int ustarfs_mode_offset = BBSIZE;
    125 
    126 static int checksig __P((ust_active_t *));
    127 static int convert __P((const char *, int, int));
    128 static int get_volume __P((struct open_file *, int));
    129 static void setwindow(ust_active_t *, ustoffs, ustoffs);
    130 static int ustarfs_cylinder_read __P((struct open_file *, ustoffs, int));
    131 static void ustarfs_sscanf __P((const char *, const char *, int *));
    132 static int read512block __P((struct open_file *, ustoffs, char block[512]));
    133 
    134 static int
    135 convert(f, base, fw)
    136 	const char *f;
    137 	int base, fw;
    138 {
    139 	int	i, c, result = 0;
    140 
    141 	while(fw > 0 && *f == ' ') {
    142 		--fw;
    143 		++f;
    144 	}
    145 	for(i = 0; i < fw; ++i) {
    146 		c = f[i];
    147 		if ('0' <= c && c < '0' + base) {
    148 			c -= '0';
    149 			result = result * base + c;
    150 		} else	break;
    151 	}
    152 	return result;
    153 }
    154 
    155 static void
    156 ustarfs_sscanf(s,f,xi)
    157 	const char *s,*f;
    158 	int *xi;
    159 {
    160 	*xi = convert(s, 8, convert(f + 1, 10, 99));
    161 }
    162 
    163 static int
    164 ustarfs_cylinder_read(f, seek2, forcelabel)
    165 	struct open_file *f;
    166 	ustoffs seek2;
    167 {
    168 	int e;
    169 	ustoffs	lda;
    170 	char *xferbase;
    171 	ust_active_t *ustf;
    172 	size_t	xferrqst, xfercount;
    173 
    174 	ustf = f->f_fsdata;
    175 	xferrqst = sizeof ustf->uas_1cyl;
    176 	xferbase = ustf->uas_1cyl;
    177 	lda = pda2lda(seek2);
    178 	if (lda < 0) {
    179 		lda = -lda;
    180 		ustf->uas_offset = lda;
    181 		/*
    182 		 * don't read the label unless we have to. (Preserve
    183 		 * sequential block access so tape boot works.)
    184 		 */
    185 		if (!forcelabel) {
    186 			memset(xferbase, 0, lda);
    187 			xferrqst -= lda;
    188 			xferbase += lda;
    189 			seek2    += lda;
    190 		}
    191 	} else
    192 		ustf->uas_offset = 0;
    193 	while(xferrqst > 0) {
    194 		e = f->f_dev->dv_strategy(f->f_devdata, F_READ, seek2 / 512,
    195 			xferrqst, xferbase, &xfercount);
    196 		if (e)
    197 			break;
    198 		if (xfercount != xferrqst)
    199 			printf("Warning, unexpected short transfer %d/%d\n",
    200 				(int)xfercount, (int)xferrqst);
    201 		xferrqst -= xfercount;
    202 		xferbase += xfercount;
    203 	}
    204 	return e;
    205 }
    206 
    207 static int
    208 checksig(ustf)
    209 	ust_active_t *ustf;
    210 {
    211 	int	i, rcs;
    212 
    213 	for(i = rcs = 0; i < sizeof ustf->uas_1cyl; ++i)
    214 		rcs += ustf->uas_1cyl[i];
    215 	return rcs;
    216 }
    217 
    218 static int
    219 get_volume(f, vn)
    220 	struct open_file *f;
    221 	int vn;
    222 {
    223 	int	e, needvolume, havevolume;
    224 	ust_active_t *ustf;
    225 
    226 	ustf = f->f_fsdata;
    227 	havevolume = vda2vn(ustf->uas_windowbase, ustf->uas_volsize);
    228 	needvolume = vn;
    229 	while(havevolume != needvolume) {
    230 		printf("\nPlease ");
    231 		if (havevolume >= 0)
    232 			printf("remove disk %d, ", havevolume + 1);
    233 		printf("insert disk %d, and type return...",
    234 			needvolume + 1);
    235 		getchar();
    236 		printf("\n");
    237 		e = ustarfs_cylinder_read(f, 0, 1);
    238 		if (e)
    239 			return e;
    240 		if(strncmp(formatid, ustf->uas_1cyl, strlen(formatid))) {
    241 			/* no magic, might be OK if we want volume 0 */
    242 			if (ustf->uas_volzerosig == checksig(ustf)) {
    243 				havevolume = 0;
    244 				continue;
    245 			}
    246 			printf("Disk is not from the volume set?!\n");
    247 			havevolume = -2;
    248 			continue;
    249 		}
    250 		ustarfs_sscanf(ustf->uas_1cyl + strlen(formatid), "%9o",
    251 			&havevolume);
    252 		--havevolume;
    253 	}
    254 	return 0;
    255 }
    256 
    257 static void
    258 setwindow(ust_active_t *ustf, ustoffs pda, ustoffs vda)
    259 {
    260 	ustf->uas_windowbase = lda2vda(pda2lda(pda), ustf->uas_volsize,
    261 					vda2vn(vda, ustf->uas_volsize))
    262 			     + ustf->uas_offset;
    263 	ustf->uas_init_window = 1;
    264 }
    265 
    266 static int
    267 read512block(f, vda, block)
    268 	struct open_file *f;
    269 	ustoffs vda;
    270 	char block[512];
    271 {
    272 	ustoffs pda;
    273 	ssize_t	e;
    274 	int	dienow;
    275 	ust_active_t *ustf;
    276 
    277 	dienow = 0;
    278 	ustf = f->f_fsdata;
    279 
    280 	if (!ustf->uas_init_window
    281 	&&   ustf->uas_windowbase == 0) {
    282 		/*
    283 		 * The algorithm doesn't require this, but without it we would
    284 		 * need some trick to get the cylinder zero signature computed.
    285 		 * That signature is used to identify volume zero, which we
    286 		 * don't give a USTARFS label to. (It's platform-dependent.)
    287 		 */
    288 		e = ustarfs_cylinder_read(f, 0, 0);
    289 		if (e)
    290 			return e;
    291 		ustf->uas_volzerosig = checksig(ustf);
    292 		setwindow(ustf, 0, 0);
    293 	}
    294 	/*
    295 	 * if (vda in window)
    296 	 * 	copy out and return data
    297 	 * if (vda is on some other disk)
    298 	 * 	do disk swap
    299 	 * get physical disk address
    300 	 * round down to cylinder boundary
    301 	 * read cylindar
    302 	 * set window (in vda space) and try again
    303 	 * [ there is an implicit assumption that windowbase always identifies
    304 	 *    the current volume, even if initwindow == 0. This way, a
    305 	 *    windowbase of 0 causes the initial volume to be disk 0 ]
    306 	 */
    307 tryagain:
    308 	if(ustf->uas_init_window
    309 	&& ustf->uas_windowbase <= vda && vda <
    310 	   ustf->uas_windowbase + sizeof ustf->uas_1cyl - ustf->uas_offset) {
    311 		memcpy(block, ustf->uas_1cyl
    312 				+ (vda - ustf->uas_windowbase)
    313 				+ ustf->uas_offset, 512);
    314 		return 0;
    315 	}
    316 	if (dienow++)
    317 		panic("ustarfs read512block");
    318 	ustf->uas_init_window = 0;
    319 	e = get_volume(f, vda2vn(vda, ustf->uas_volsize));
    320 	if (e)
    321 		return e;
    322 	pda = lda2pda(vda2lda(vda, ustf->uas_volsize));
    323 	pda-= pda % sizeof ustf->uas_1cyl;
    324 	e = ustarfs_cylinder_read(f, pda, 0);
    325 	if (e)
    326 		return e;
    327 	setwindow(ustf, pda, vda);
    328 	goto tryagain;
    329 }
    330 
    331 int
    332 ustarfs_open(path, f)
    333 	char *path;
    334 	struct open_file *f;
    335 
    336 {
    337 	ust_active_t *ustf;
    338 	ustoffs offset;
    339 	char	block[512];
    340 	int	filesize;
    341 	int	e, e2;
    342 	int	newvolblocks;
    343 
    344 	if (*path == '/')
    345 		++path;
    346 	e = EINVAL;
    347 	f->f_fsdata = ustf = alloc(sizeof *ustf);
    348 	memset(ustf, 0, sizeof *ustf);
    349 	offset = 0;
    350 	/* default to 2880 sector floppy */
    351 	ustf->uas_volsize = 80 * 2 * 18 * 512 - lda2pda(0);
    352 	ustf->uas_fseek = 0;
    353 	for(;;) {
    354 		ustf->uas_filestart = offset;
    355 		e2 = read512block(f, offset, block);
    356 		if (e2) {
    357 			e = e2;
    358 			break;
    359 		}
    360 		memcpy(&ustf->uas_active, block, sizeof ustf->uas_active);
    361 		if(strncmp(ustf->uas_active.ust_magic, "ustar", 5))
    362 			break;
    363 		e = ENOENT;	/* it must be an actual ustarfs */
    364 		ustf->uas_init_fs = 1;
    365 		/* if volume metadata is found, use it */
    366 		if(strncmp(ustf->uas_active.ust_name, metaname,
    367 		    strlen(metaname)) == 0) {
    368 			ustarfs_sscanf(ustf->uas_active.ust_name
    369 				+ strlen(metaname), "%99o", &newvolblocks);
    370 			ustf->uas_volsize = newvolblocks * 512
    371 					  - lda2pda(0);
    372 		}
    373 		ustarfs_sscanf(ustf->uas_active.ust_size,"%12o",&filesize);
    374 		if(strncmp(ustf->uas_active.ust_name, path,
    375 		    sizeof ustf->uas_active.ust_name) == 0) {
    376 			ustf->uas_filesize = filesize;
    377 			e = 0;
    378 			break;
    379 		}
    380 		offset += USTAR_NAME_BLOCK + filesize;
    381 		filesize %= 512;
    382 		if (filesize)
    383 			offset += 512 - filesize;
    384 	}
    385 	if (e) {
    386 		free(ustf, sizeof *ustf);
    387 		f->f_fsdata = 0;
    388 	}
    389 	return e;
    390 }
    391 
    392 int
    393 ustarfs_write(f, start, size, resid)
    394 	struct open_file *f;
    395 	void *start;
    396 	size_t size;
    397 	size_t *resid;
    398 {
    399 	return (EROFS);
    400 }
    401 
    402 off_t
    403 ustarfs_seek(f, offs, whence)
    404 	struct open_file *f;
    405 	off_t offs;
    406 	int whence;
    407 {
    408 	ust_active_t *ustf;
    409 
    410 	ustf = f->f_fsdata;
    411 	switch (whence) {
    412 	    case SEEK_SET:
    413 		ustf->uas_fseek = offs;
    414 		break;
    415 	    case SEEK_CUR:
    416 		ustf->uas_fseek += offs;
    417 		break;
    418 	    case SEEK_END:
    419 		ustf->uas_fseek = ustf->uas_filesize - offs;
    420 		break;
    421 	    default:
    422 		return -1;
    423 	}
    424 	return ustf->uas_fseek;
    425 }
    426 
    427 int
    428 ustarfs_read(f, start, size, resid)
    429 	struct open_file *f;
    430 	void *start;
    431 	size_t size;
    432 	size_t *resid;
    433 {
    434 	ust_active_t *ustf;
    435 	int	e;
    436 	char	*space512;
    437 	int	blkoffs,
    438 		readoffs,
    439 		bufferoffset;
    440 	size_t	seg;
    441 	int	infile,
    442 		inbuffer;
    443 
    444 	e = 0;
    445 	space512 = alloc(512);
    446 	ustf = f->f_fsdata;
    447 	while(size != 0) {
    448 		if (ustf->uas_fseek >= ustf->uas_filesize)
    449 			break;
    450 		bufferoffset = ustf->uas_fseek % 512;
    451 		blkoffs  = ustf->uas_fseek - bufferoffset;
    452 		readoffs = ustf->uas_filestart + 512 + blkoffs;
    453 		e = read512block(f, readoffs, space512);
    454 		if (e)
    455 			break;
    456 		seg = size;
    457 		inbuffer = 512 - bufferoffset;
    458 		if (inbuffer < seg)
    459 			seg = inbuffer;
    460 		infile = ustf->uas_filesize - ustf->uas_fseek;
    461 		if (infile < seg)
    462 			seg = infile;
    463 		memcpy(start, space512 + bufferoffset, seg);
    464 		ustf->uas_fseek += seg;
    465 		start += seg;
    466 		size  -= seg;
    467 	}
    468 	if (resid)
    469 		*resid = size;
    470 	free(space512, 512);
    471 	return e;
    472 }
    473 
    474 int
    475 ustarfs_stat(f, sb)
    476 	struct open_file *f;
    477 	struct stat *sb;
    478 {
    479 	int	mode, uid, gid;
    480 	ust_active_t *ustf;
    481 
    482 	if (f == NULL)
    483 		return EINVAL;
    484 	ustf = f->f_fsdata;
    485 	memset(sb, 0, sizeof *sb);
    486 	ustarfs_sscanf(ustf->uas_active.ust_mode, "%8o", &mode);
    487 	ustarfs_sscanf(ustf->uas_active.ust_uid, "%8o", &uid);
    488 	ustarfs_sscanf(ustf->uas_active.ust_gid, "%8o", &gid);
    489 	sb->st_mode = mode;
    490 	sb->st_uid  = uid;
    491 	sb->st_gid  = gid;
    492 	sb->st_size = ustf->uas_filesize;
    493 	return 0;
    494 }
    495 
    496 int
    497 ustarfs_close(f)
    498 	struct open_file *f;
    499 {
    500 	if (f == NULL || f->f_fsdata == NULL)
    501 		return EINVAL;
    502 	free(f->f_fsdata, sizeof(ust_active_t));
    503 	f->f_fsdata = 0;
    504 	return 0;
    505 }
    506