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