|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * The contents of this file constitute Original Code as defined in and ! 7: * are subject to the Apple Public Source License Version 1.1 (the ! 8: * "License"). You may not use this file except in compliance with the ! 9: * License. Please obtain a copy of the License at ! 10: * http://www.apple.com/publicsource and read it before using this file. ! 11: * ! 12: * This Original Code and all software distributed under the License are ! 13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 17: * License for the specific language governing rights and limitations ! 18: * under the License. ! 19: * ! 20: * @APPLE_LICENSE_HEADER_END@ ! 21: */ ! 22: /* ! 23: File: VolumeRequests.c ! 24: ! 25: Contains: MountVolume and related utility routines for HFS & HFS Plus ! 26: ! 27: Version: HFS Plus 1.0 ! 28: ! 29: Written by: Deric Horn ! 30: ! 31: Copyright: � 1996-1998 by Apple Computer, Inc., all rights reserved. ! 32: ! 33: File Ownership: ! 34: ! 35: DRI: Deric Horn ! 36: ! 37: Other Contacts: Mark Day, Don Brady ! 38: ! 39: Technology: File Systems ! 40: ! 41: Writers: ! 42: ! 43: (JL) Jim Luther ! 44: (msd) Mark Day ! 45: (DSH) Deric Horn ! 46: (djb) Don Brady ! 47: ! 48: Change History (most recent first): ! 49: <MacOSX> 7/28/98 djb GetDiskBlocks is now implemented in MacOSStubs.c (radar #2258148). ! 50: <MacOSX> 4/3/98 djb Conditionally remove FSVars reference from GetVolumeNameFromCatalog. ! 51: <MacOSX> 3/31/98 djb Sync up with final HFSVolumes.h header file. ! 52: <CS47> 1/29/98 DSH TrashAllFSCaches is responsible for trashing all file system and ! 53: disk caches. Called from FlushVol when the HFS bit is set. ! 54: <CS46> 12/12/97 DSH 2003877, when vcbAllocPtr was copied to nextAllocation it was ! 55: getting sign extended. ! 56: <CS45> 11/26/97 DSH 2003459, fcbs was not being initialized if volume was offline ! 57: and we are executing an unconditional unmount. ! 58: <CS44> 11/24/97 DSH 2005507, FlushVolumeControlBlock() keeps MDB drCrDate in sync ! 59: with VolumeHeader createDate. ! 60: <CS43> 11/11/97 DSH 1685873, RemountWrappedVolumes was only remounting the first ! 61: HFS+ volume in the queue, causing HFS wrappers to be mounted if ! 62: multiple volumes had been mounted before InitHFSPlus. ! 63: <CS42> 11/4/97 DSH Clear FCB when getting a new one. ! 64: <CS41> 11/3/97 JL #2001483 - Removed unneeded parameters from MountVolume, ! 65: MountHFSVolume, MountHFSPlusVolume, GetVolumeInformation, ! 66: GetXVolumeInformation and AddVCB (and added local variables as ! 67: needed). Return WDCBRecPtr from UnMountVolume. Set wdcb ! 68: parameter to NULL in GetXVolumeInformation if working directory ! 69: was not specified. ! 70: <CS40> 10/31/97 DSH Added consistencyStatus parameter to MountCheck ! 71: <CS39> 10/23/97 msd Bug 1685113. The VolumeHeader's createDate should be in local ! 72: time (not GMT) and identical to the MDB's drCrDate (and VCB's ! 73: vcbCrDate). When checking for a remount of an offline HFS Plus ! 74: volume, compare write counts instead of mod dates (which could ! 75: be fooled by the user changing time zones). Force MountCheck to ! 76: run if the volume was last mounted by Bride 1.0b2 or earlier. ! 77: <CS38> 10/17/97 msd Conditionalize DebugStrs. ! 78: <CS37> 10/13/97 djb Update volumeNameEncodingHint when updating the volume name. ! 79: <CS36> 10/10/97 msd Bug 1683571. The dates in the volume header are in GMT, so be ! 80: sure to convert them when mounting a volume or flushing the ! 81: volume header. ! 82: <CS35> 10/2/97 DSH In UnmountVolume() check that the drive is on line before ! 83: determining if wrapper volume needs to be renamed causing IO. ! 84: <CS34> 10/1/97 DSH Run on disk version of MountCheck instead of ROM version for ! 85: boot volumes1682475. ! 86: <CS33> 10/1/97 djb Add calls to InvalidateCatalogCache (part of radar #1678833). ! 87: <CS32> 9/26/97 DSH Removed debugging code: support for 'W' key wrapper mounting. ! 88: <CS31> 9/17/97 DSH hfsPlusIOPosOffset was uninitialized for Wrapperless volumes. ! 89: <CS30> 9/5/97 djb In MountVol initialize Catalog cache before calling Catalog! ! 90: <CS29> 9/4/97 msd PropertyCloseVolume renamed to AttributesCloseVolume. Remove ! 91: call to AttributesOpenVolume (it no longer exists). ! 92: <CS28> 9/2/97 DSH VolumeHeader is now 3rd sector in partition, altVH is 2nd to ! 93: last cor compatability. Initial support for wrapperless ! 94: volumes. ! 95: <CS27> 8/26/97 djb Only call CountRootFiles during MountVol. ! 96: <CS26> 8/20/97 msd If the HFS Plus volume version doesn't match, mount the wrapper ! 97: instead. ! 98: <CS25> 8/19/97 djb Add error handling to RenameWrapperVolume. ! 99: <CS24> 8/15/97 msd Bug 1673999. In MakeVCBsExtendedVCBs, copy old VCB's vcbAllocPtr ! 100: to new VCB's nextAllocation field. ! 101: <CS23> 8/12/97 djb Fixed GetXVolInfo to only use extended vcb fields for local ! 102: volumes (radar# 1673177) ! 103: <CS22> 8/11/97 DSH vcbNmAlBlks is now taken from the embededExtent.blockCount ! 104: (1669121). ! 105: <CS21> 8/11/97 djb Return actual count of files in root directory for HFS Plus ! 106: volumes (Radar #1669118). Added local CountRootFiles routine. ! 107: 8/5/97 msd Make sure version field in VolumeHeader is exactly ! 108: kHFSPlusVersion. 8/1/97 djb GetXVolumeInformation now returns ! 109: extFSErr when FSID is nonzero (Radar #1649503). ! 110: <CS20> 7/25/97 DSH Init and Dispose of GenericMRUCache within ExtendedVCB. ! 111: <CS19> 7/16/97 DSH FilesInternal.x -> FileMgrInternal.x to avoid name collision ! 112: <CS18> 7/15/97 DSH Remount Wrapper volumes mounted before HFS+ initialization ! 113: (166729) ! 114: <CS17> 7/15/97 djb Remove ioXVersion checking in GetXVolInfo (radar #1666217). ! 115: <CS16> 7/8/97 DSH Loading PrecompiledHeaders from define passed in on C line ! 116: <CS15> 7/7/97 djb Add GetVolumeNameFromCatalog routine. ! 117: <CS14> 7/7/97 DSH GetNewVRefNum now get's a recycled vRefNum. Bug 1664445 in ! 118: Installer was cacheing the vRefNum while CheckDisk unmounts and ! 119: remounts disk. ! 120: <CS13> 6/30/97 DSH shadowing values obsoleteVCBXTRef, and obsoleteVCBCTRef when ! 121: HFS+ volume is mounted. ! 122: <CS12> 6/26/97 DSH GetVolInfo returns HFS signature for HFS+ volumes, GetXVolInfo ! 123: returns real signature. ! 124: <CS11> 6/24/97 DSH MakeVCBsExtendedVCBs was using wdcb->count as count not byte ! 125: count. ! 126: <CS10> 6/18/97 djb Set/get volume encodingsBitmap. ! 127: <CS9> 6/16/97 msd Include String.h and Disks.h. ! 128: <CS8> 6/12/97 djb Get in sync with HFS Plus format changes. ! 129: <CS7> 6/11/97 msd Make GetXVolumeInformation return true allocation block size. It ! 130: now checks the ioXVersion field. ! 131: <CS6> 5/28/97 msd When flushing the volume header, write out the allocation file's ! 132: clump size (from the FCB). When mounting an HFS Plus volume, ! 133: zero the entire FCB extent record, not just the first extent, ! 134: for the various volume control files. ! 135: <CS5> 5/19/97 djb Add calls to CreateVolumeCatalogCache, ! 136: DisposeVolumeCatalogCache. ! 137: <CS4> 5/9/97 djb Get in sync with new FilesInternal.i ! 138: <CS3> 5/8/97 DSH Only mount HFS+ volumes with version < 2.0 in the VolumeHeader. ! 139: Return wrgVolTypErr if too new. ! 140: <CS2> 5/2/97 djb Disable Manual Eject code since its buggy! ! 141: <CS1> 4/25/97 djb first checked in ! 142: ! 143: <HFS32> 4/11/97 DSH MountHFSPlusVolume gets volume name from catalog, and ! 144: UnmountVolume shadows the name back to the wrapper partition. ! 145: <HFS31> 4/8/97 msd Once volume is mounted, call AttributesOpenVolume to allow a ! 146: buffer to be allocated. ! 147: <HFS30> 4/7/97 msd In FlushVolumeControlBlock, don't update the attributes BTree ! 148: fields in the Volume Header unless an attributes BTree was ! 149: already open. ! 150: <HFS29> 4/7/97 msd In SetupFCB, add case for attributes BTree. Add code to set up ! 151: the attributes BTree. Remove call to PropertyOpenVolume. In ! 152: FlushVolumeControlBlock, write out any changes to the attributes ! 153: BTree. ! 154: <HFS28> 4/4/97 djb Get in sync with volume format changes. ! 155: <HFS27> 3/31/97 djb Added catalogDataCache to VCB; Remove ClearMem routine. ! 156: <HFS26> 3/18/97 msd In MountHFSPlusVolume, the free blocks calculation can overflow, ! 157: setting vcbFreeBks to a too-small value. ! 158: <HFS25> 3/17/97 DSH Added some utility functions AddVCB, GetParallelFCBFromRefNum, ! 159: casting for SC, and made some functions extern for DFA. ! 160: <HFS24> 3/5/97 msd Add calls to Property Manager to open and close the volume. When ! 161: unmounting an HFS+ volume, the allocation (bitmap) file now gets ! 162: closed. ! 163: <HFS23> 2/19/97 djb Update to 16-bit HFS Plus signature. ! 164: <HFS22> 2/12/97 msd In GetXVolumeInformation, the result code could be ! 165: uninitialized. ! 166: <HFS21> 1/23/97 DSH UpdateAlternateVoumeControlBlock() ! 167: <HFS20> 1/15/97 djb Remove MountCheckStub. Add file names to fcbs for debugging. ! 168: <HFS19> 1/13/97 DSH Use ExtendedVCB nextAllocation instead of vcbAllocPtr through ! 169: all code. ! 170: <HFS18> 1/9/97 djb Get in sync with new VolumeHeader and Extended VCB. ! 171: <HFS17> 1/6/97 djb Changed API to ParallelFCBFromRefnum (pragma parameter was ! 172: broken). ! 173: <HFS16> 1/6/97 msd Set only the defined bits in the MDB drAtrb field (when copying ! 174: from VCB vcbAtrb field). ! 175: <HFS15> 1/6/97 DSH CloseFile requires VCB to be passed in. ! 176: <HFS14> 1/6/97 djb FlushVolumeControlBlock was writing to absolute block 0 instead ! 177: of to block zero of the embedded volume. ! 178: <HFS13> 12/20/96 msd A comparison was using "=" instead of "=="; might have caused ! 179: the wrong volume to be set as the default. ! 180: <HFS12> 12/19/96 DSH Setting up ExtendedVCBs ! 181: <HFS11> 12/19/96 djb Updated for new B-tree Manager interface. ! 182: <HFS10> 12/18/96 msd Change GetVCBRefNum so it can actually return a VCB pointer. ! 183: <HFS9> 12/12/96 djb Use new SPI for GetCatalogNode. ! 184: <HFS8> 12/12/96 msd Fix a bunch of errors (mostly type mismatch) when compiling with ! 185: Metrowerks. ! 186: <HFS7> 12/12/96 DSH adding some util functions ! 187: <HFS6> 12/10/96 msd Check PRAGMA_LOAD_SUPPORTED before loading precompiled headers. ! 188: <HFS5> 12/4/96 DSH Ported GetVolumeInformation & GetXVolumeInformation. ! 189: <3*> 11/20/96 DSH HFS Plus support to MountVolume ! 190: <HFS3> 11/20/96 DSH Added UnmountVol and related routines, also backed out <2> ! 191: because C_FXMKeyCmp is passed as a parameter from C but called ! 192: from Asm in BTOpen so we need a Case ON Asm entry point. ! 193: <HFS2> 11/20/96 msd Use CompareExtentKeys() instead of CFXMKeyCmp(). ! 194: <HFS1> 11/19/96 DSH first checked in ! 195: <1> 11/19/96 DSH first checked in ! 196: ! 197: */ ! 198: #include <sys/param.h> ! 199: #include <sys/systm.h> ! 200: #include <sys/kernel.h> ! 201: #include <sys/vnode.h> ! 202: ! 203: #include "../../hfs.h" ! 204: ! 205: #include "../headers/FileMgrInternal.h" ! 206: ! 207: #define kIDSectorOffset 2 ! 208: ! 209: OSErr GetNewFCB( ExtendedVCB *vcb, FileReference* fRefPtr); ! 210: ! 211: OSErr AccessBTree( ExtendedVCB *vcb, FileReference refNum, UInt32 fileID, UInt32 fileClumpSize, void *CompareRoutine ); ! 212: ! 213: UInt16 DivUp( UInt32 byteRun, UInt32 blockSize ); ! 214: ! 215: Boolean IsARamDiskDriver( void ); ! 216: ! 217: OSErr GetVCBRefNum( ExtendedVCB **vcb, short vRefNum ); ! 218: ! 219: OSErr ValidMasterDirectoryBlock( HFSMasterDirectoryBlock *mdb ); ! 220: ! 221: void RenameWrapperVolume( Str27 newVolumeName, UInt16 driveNumber ); ! 222: ! 223: OSErr CheckExternalFileSystem( ExtendedVCB *vcb ); ! 224: ! 225: OSErr FlushVolume( ExtendedVCB *vcb ); ! 226: ! 227: FCB *SetupFCB( ExtendedVCB *vcb, FileReference refNum, UInt32 fileID, UInt32 fileClumpSize ); ! 228: ! 229: void AddVCB( ExtendedVCB *vcb, short driveNumber, short ioDRefNum ); ! 230: ! 231: short IsPressed( unsigned short k ); ! 232: ! 233: FileReference GetNewVRefNum(); ! 234: ! 235: OSErr GetVolumeNameFromCatalog(ExtendedVCB *vcb); ! 236: ! 237: #if TARGET_API_MAC_OS8 ! 238: static UInt16 CountRootFiles(ExtendedVCB *vcb); ! 239: #endif /* TARGET_API_MAC_OS8 */ ! 240: ! 241: ! 242: #if ( hasHFSManualEject ) ! 243: static void SetVCBManEject(ExtendedVCB *vcb); ! 244: #endif ! 245: ! 246: // External routines ! 247: ! 248: extern OSErr C_FlushMDB( ExtendedVCB *volume ); ! 249: ! 250: extern OSErr DisposeVolumeCacheBlocks( ExtendedVCB *vcb ); ! 251: ! 252: extern void DisposeVolumeControlBlock( ExtendedVCB *vcb ); ! 253: ! 254: extern OSErr FlushVolumeBuffers( ExtendedVCB *vcb ); ! 255: ! 256: extern void MultiplyUInt32IntoUInt64( UInt64 *wideResult, UInt32 num1, UInt32 num2 ); ! 257: ! 258: extern void TrashCatalogNodeCache( void ); ! 259: ! 260: ! 261: //������������������������������������������������������������������������������� ! 262: // Routine: VolumeWritable Asm: CVFlgs ! 263: // ! 264: // Function: Check the volume's flags to see if modify requests are allowed. ! 265: // ! 266: //������������������������������������������������������������������������������� ! 267: OSErr VolumeWritable( ExtendedVCB *vcb ) ! 268: { ! 269: if ( !(vcb->vcbAtrb & 0x8000) ) // if the volume is not locked ! 270: { ! 271: if ( ! (*((Ptr)&(vcb->vcbAtrb) + 1) & kHFSVolumeHardwareLockMask) ) // if it's not write protected ! 272: return( noErr ); ! 273: else ! 274: return( wPrErr ); ! 275: } ! 276: else ! 277: { ! 278: return( vLckdErr ); ! 279: } ! 280: } ! 281: ! 282: ! 283: //������������������������������������������������������������������������������� ! 284: // Routine: DivUp from Asm: DivUp ! 285: // ! 286: // Function: Given a number of bytes and block size, calculate the number of ! 287: // blocks needd to hold all the bytes. ! 288: // ! 289: // Result: Number of physical blocks needed ! 290: //������������������������������������������������������������������������������� ! 291: UInt16 DivUp( UInt32 byteRun, UInt32 blockSize ) ! 292: { ! 293: UInt32 blocks; ! 294: ! 295: blocks = (byteRun + blockSize - 1) / blockSize; // Divide up, remember this is integer math. ! 296: ! 297: if ( blocks > 0xffff ) // maximum 16 bit value ! 298: blocks = 0xffff; ! 299: ! 300: return( (UInt16) blocks ); ! 301: } ! 302: ! 303: ! 304: ! 305: ! 306: //������������������������������������������������������������������������������� ! 307: // Routine: HFSBlocksFromTotalSectors ! 308: // ! 309: // Function: Given the total number of sectors on the volume, calculate ! 310: // the 16Bit number of allocation blocks, and allocation block size. ! 311: // ! 312: // Result: none ! 313: //������������������������������������������������������������������������������� ! 314: void HFSBlocksFromTotalSectors( UInt32 totalSectors, UInt32 *blockSize, UInt16 *blockCount ) ! 315: { ! 316: UInt16 newBlockSizeInSectors = 1; ! 317: UInt32 newBlockCount = totalSectors; ! 318: ! 319: while ( newBlockCount > 0XFFFF ) ! 320: { ! 321: newBlockSizeInSectors++; ! 322: newBlockCount = totalSectors / newBlockSizeInSectors; ! 323: } ! 324: ! 325: *blockSize = newBlockSizeInSectors * 512; ! 326: *blockCount = newBlockCount; ! 327: } ! 328: ! 329: ! 330: ! 331: ! 332: //������������������������������������������������������������������������������� ! 333: // Routine: ValidMasterDirectoryBlock ! 334: // ! 335: // Function: Run some sanity checks to make sure the MDB is valid ! 336: // ! 337: // Result: error ! 338: //������������������������������������������������������������������������������� ! 339: OSErr ValidMasterDirectoryBlock( HFSMasterDirectoryBlock *mdb ) ! 340: { ! 341: OSErr err; ! 342: ! 343: if ( (mdb->drSigWord == kHFSPlusSigWord) || (mdb->drSigWord == kHFSSigWord) ) // if HFS or HFS Plus volume ! 344: { ! 345: if ( (mdb->drAlBlkSiz != 0) && ((mdb->drAlBlkSiz & 0x01FF) == 0) ) // non zero multiple of 512 ! 346: err = noErr; ! 347: else ! 348: err = badMDBErr; ! 349: } ! 350: else ! 351: { ! 352: err = noMacDskErr; ! 353: } ! 354: ! 355: return( err ); ! 356: } ! 357: ! 358: ! 359: //������������������������������������������������������������������������������� ! 360: // Routine: ValidVolumeHeader ! 361: // ! 362: // Function: Run some sanity checks to make sure the VolumeHeader is valid ! 363: // ! 364: // Result: error ! 365: //������������������������������������������������������������������������������� ! 366: OSErr ValidVolumeHeader( HFSPlusVolumeHeader *volumeHeader ) ! 367: { ! 368: OSErr err; ! 369: ! 370: if ( volumeHeader->signature == kHFSPlusSigWord && volumeHeader->version == kHFSPlusVersion ) ! 371: { ! 372: if ( (volumeHeader->blockSize != 0) && ((volumeHeader->blockSize & 0x01FF) == 0) ) // non zero multiple of 512 ! 373: err = noErr; ! 374: else ! 375: err = badMDBErr; //�� I want badVolumeHeaderErr in Errors.i ! 376: } ! 377: else ! 378: { ! 379: err = noMacDskErr; ! 380: } ! 381: ! 382: return( err ); ! 383: } ! 384: ! 385: ! 386: ! 387: ! 388: //_______________________________________________________________________ ! 389: // ! 390: // Routine: CountRootFiles ! 391: // ! 392: // Input: pointer to VCB ! 393: // ! 394: // Function: Return a count of the number of files and folders in ! 395: // the root directory of a volume. For HFS volumes, this ! 396: // is maintained in the VCB (and MDB). For HFS Plus volumes, ! 397: // we get the valence of the root directory from its catalog ! 398: // record. ! 399: //_______________________________________________________________________ ! 400: UInt16 CountRootFiles(ExtendedVCB *vcb) ! 401: { ! 402: OSErr err; ! 403: FSSpec spec; ! 404: CatalogNodeData catNodeData; ! 405: UInt32 hint; ! 406: UInt16 rootCount; ! 407: ! 408: // if (vcb->vcbSigWord == kHFSSigWord || vcb->vcbFSID != 0) { ! 409: // return vcb->vcbNmFls; ! 410: // } ! 411: ! 412: // Here, it's an HFS Plus volume, so get the valence from the root ! 413: // directory's catalog record. ! 414: ! 415: rootCount = 0; ! 416: ! 417: err = GetCatalogNode( vcb, kHFSRootFolderID, nil, kUndefinedStrLen, kNoHint, &spec, &catNodeData, &hint ); ! 418: if ( err == noErr ) { ! 419: if (catNodeData.cnd_valence < 65536) ! 420: rootCount = catNodeData.cnd_valence; ! 421: else ! 422: rootCount = 65535; // if the value is too large, pin it ! 423: } ! 424: ! 425: return rootCount; ! 426: } ! 427: ! 428: ! 429: ! 430: //_______________________________________________________________________ ! 431: // ! 432: // Routine: FlushVolumeControlBlock ! 433: // Arguments: ExtendedVCB *vcb ! 434: // Output: OSErr err ! 435: // ! 436: // Function: Flush volume information to either the VolumeHeader of the Master Directory Block ! 437: //_______________________________________________________________________ ! 438: ! 439: OSErr FlushVolumeControlBlock( ExtendedVCB *vcb ) ! 440: { ! 441: OSErr err; ! 442: ! 443: if ( ! IsVCBDirty( vcb ) ) // if it's not dirty ! 444: return( noErr ); ! 445: ! 446: if ( vcb->vcbSigWord == kHFSPlusSigWord ) ! 447: { ! 448: err = C_FlushMDB( vcb ); // Go flush the VCB info BEFORE close ! 449: } ! 450: else ! 451: { ! 452: // This routine doesn't really return an error!!! ! 453: // So for now, we will just return noErr ! 454: err = C_FlushMDB( vcb ); // Go flush the VCB info BEFORE close ! 455: return( noErr ); ! 456: } ! 457: ! 458: return( err ); ! 459: } ! 460: ! 461: ! 462: //������������������������������������������������������������������������������� ! 463: ! 464: OSErr GetVolumeNameFromCatalog( ExtendedVCB *vcb ) ! 465: { ! 466: FSSpec spec; ! 467: CatalogNodeData nodeData; ! 468: UInt32 hint; ! 469: OSErr err; ! 470: ! 471: if ( true ) ! 472: { ! 473: err = GetCatalogNode( vcb, kHFSRootFolderID, NULL, kUndefinedStrLen, kNoHint, &spec, &nodeData, &hint ); ! 474: ! 475: if ( err == noErr ) ! 476: { ! 477: BlockMoveData( spec.name, vcb->vcbVN, strlen(spec.name)); ! 478: vcb->volumeNameEncodingHint = nodeData.cnd_textEncoding; // save encoding for FSFindTextEncodingForVolume ! 479: } ! 480: } ! 481: else ! 482: { ! 483: err = paramErr; ! 484: } ! 485: ! 486: return err; ! 487: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.