// // hfstools - a Macintosh filesystem access tool // (C) Copyright 1993 by Equivalence // // This file part of hfs. // // hfs is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2, or (at your option) // any later version. // // hfs is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with hfs; see the file COPYING. If not, write to // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // // // $Id: volume.cxx,v 1.12 1994/07/08 12:00:59 craigs Exp $ // $Log: volume.cxx,v $ // Revision 1.12 1994/07/08 12:00:59 craigs // Fixed problem with displaying misleading error messages // Changed part command to display drive name rather than partition name // // Revision 1.11 1994/07/02 05:04:21 craigs // Added support for CDROM drives under MSDOS // // Revision 1.10 1994/06/30 14:59:42 craigs // Removed return statements in EnumeratePartitions // // Revision 1.9 1994/06/30 14:54:31 craigs // Removed return value to EnumeratePartitions // // Revision 1.8 1994/06/30 14:39:24 craigs // Changes for bigger blocks and partitions // // Revision 1.7 1994/01/11 02:39:55 craigs // Added newline to end of cannot open file message // // Revision 1.6 1994/01/11 00:45:48 craigs // Added InvalidateCache call so Filesystem::Unmount will work properly // // Revision 1.5 1994/01/06 03:05:08 craigs // Final checkin to include GNU header // // Revision 1.4 1993/12/23 15:19:30 craigs // Changed to accept A: and B: as device names under Unix // // Revision 1.3 1993/11/24 21:36:05 craigs // Various changes remove warnings under MSDOS/NT // by robertj // // Revision 1.2 1993/11/23 20:18:51 craigs // Added MSDOS/Windows compatibility // by robertj // // Revision 1.1 1993/11/22 22:25:55 craigs // Initial revision // // // #include "config.h" #include #include #include #include #include #include #include #if defined(_MSDOS) || defined(WIN32) #include #include #if defined(WIN32) inline int open(const char * n, int m) { return _open(n, m); } inline int close(int f) { return _close(f); } inline long lseek(int f, long p, int b) { return _lseek(f, p, b); } inline int read(int f, void * p, unsigned int l) { return _read(f, p, l); } inline int access(const char * f, int m) { return _access(f, m); } #endif #else #include #include #endif #include "mac.h" #include "volume.h" #include "error.h" #include "misc.h" ////////////////////////////////////// // // Volume::Volume // Volume::Volume (const char * devname) { deviceName = strdup(devname); partitionOffset = 0; partitionName = NULL; } ////////////////////////////////////// // // Volume::Mount // int Volume::Mount (int part_num) { DriverDescriptorMap driverDesc; NewPartitionMap partitionMap; // read the driver descriptor for the partition (if it has one) if (!Read(M_DRIVER_DESCRIPTOR_LBLOCK, &driverDesc)) return E_FAULTYDESCRIPTOR; // if this is a partition descriptor, then process it if ((UINT)driverDesc.sbSig == DriverDescriptorMap::Sig) { // read the first partition map entry if (!Read(M_PARTITION_MAP_ENTRY_LBLOCK, &partitionMap)) return E_FAULTYPARTITION; // if not correcty partitioned, finish if (partitionMap.pmSig != NewPartitionMap::Sig) return E_FAULTYPARTITION; // if there are no partitions, finish if (partitionMap.pmMapBlkCnt <= 1) return E_NOHFSPARTITIONS; // find either the first HFS partition or the one requested int i; int found = -1; int count = 0; ULONG offset = 0; int partitionCount = (int)partitionMap.pmMapBlkCnt; for (i = 0; i < partitionCount; i++) { if (!Read(M_PARTITION_MAP_ENTRY_LBLOCK+i, &partitionMap)) return E_FAULTYPARTITION; if (strcmp((const char *)partitionMap.pmPartType, HFS_PART_ID) == 0) { count++; if ((found < 0) && ((part_num < 0) || (i >= part_num))) { found = i; offset = partitionMap.pmPyPartStart; partitionName = strdup((const char *)partitionMap.pmPartName); } } } // check for errors int msg = 0; if (found < 0) { cout << "No HFS partitions on device" << deviceName << "!\n"; exit(0); } else { if ((part_num > 0) && (part_num != found)) { cout << "Specified partition " << part_num << " is not an HFS partition\n"; cout << "Partition " << found << " (" << partitionMap.pmPartName << ") used instead\n"; msg = 1; } else if ((count > 1) && (part_num < 0)) { cout << "warning: " << count << " HFS partitions found\n"; cout << "Using partition " << found << " (" << partitionMap.pmPartName << ")\n"; msg = 1; } } if (msg) cout << "Use \"hfs part\" to display all HFS partitions on device " << deviceName << endl; partitionOffset = offset; } // Invalidate cache as partition offset has changed and block numbers are wrong InvalidateCache(); // read the MDB if (!Read(M_MASTER_MDB_LBLOCK, &mdb)) return E_FAULTYMDB; // check the signature if (mdb.drSigWord != MDB::Sig_HFS) return E_NOTHFS; // all is OK return E_OK; } static char * inttosize(ULONG size) { static char buffer[20]; const ULONG kilobyte = 1024; const ULONG megabyte = 1024 * kilobyte; const ULONG gigabyte = 1024 * megabyte; if (size < 2 * kilobyte) sprintf (buffer, "%ul", size); else if (size < 2 * megabyte) sprintf (buffer, "%.1fK", size / (float)kilobyte); else if (size < 2 * gigabyte) sprintf (buffer, "%.1fM", size / (float)megabyte); else sprintf (buffer, "%.1fG", size / (float)gigabyte); return buffer; } ////////////////////////////////////// // // Volume::EnumeratePartitions // int Volume::EnumeratePartitions(BOOL showAll) { DriverDescriptorMap driverDesc; NewPartitionMap partitionMap; OldPartitionMap *oldPartitionMap; MDB mdb; // read the driver descriptor for the partition (if it has one) if (!Read(M_DRIVER_DESCRIPTOR_LBLOCK, &driverDesc)) return E_READERROR; // if this is a driver descriptor, then process it if (driverDesc.sbSig != DriverDescriptorMap::Sig) return E_NOHFSPARTITIONS; // read the first partition map entry if (!Read(M_PARTITION_MAP_ENTRY_LBLOCK, &partitionMap)) return E_READERROR; // we cannot handle old style partitions - yet!! if ((int)partitionMap.pmSig == OldPartitionMap::Sig) { oldPartitionMap = (OldPartitionMap *)&partitionMap; cout << deviceName << " uses an old format parition table which is not surrent supported\n"; exit (2); } else if (partitionMap.pmSig != NewPartitionMap::Sig) { cout << deviceName << " contains a driver descriptor but not partition map!!\n"; exit (2); } // if there are no partitions, return 0 if (partitionMap.pmMapBlkCnt <= 1) return E_NOHFSPARTITIONS; // enumerate the partitions int first = 1; int displayed = 0; int j; BOOL is_hfs_partition; int partitionCount = (int)partitionMap.pmMapBlkCnt; for (int i = 0; i < partitionCount; i++) { if (!Read(M_PARTITION_MAP_ENTRY_LBLOCK+i, &partitionMap)) return E_FAULTYPARTITION; if (is_hfs_partition = (strcmp((const char *)partitionMap.pmPartType, HFS_PART_ID) == 0)) if (!Read(M_MASTER_MDB_LBLOCK + (ULONG)partitionMap.pmPyPartStart, &mdb)) return E_FAULTYMDB; if (showAll || is_hfs_partition) { if (first) { cout << "No. Size Name"; if (showAll) { cout << " Type" << endl; j = 3+2+6+2+32+2+19; } else { cout << endl; j = 3+2+6+2+32; } for (;j > 0; j--) cout << "-"; cout << endl; first = 0; } cout << setw(3) << i << " " << setw(6) << inttosize(partitionMap.pmPartBlkCnt*Volume::LogicalBlockSize) << " " << setw(32) << setiosflags(ios::left); if (is_hfs_partition) cout << ptocstr(mdb.drVN); else cout << partitionMap.pmPartName; if (showAll) cout << " " << partitionMap.pmPartType; cout << endl; displayed++; } } if (displayed == 0) cout << "No" << (showAll ? "" : " HFS") << " partitions found\n"; return E_OK; } ////////////////////////////////////// // // FileVolume::FileVolume // FileVolume::FileVolume (const char * devname) : Volume(devname) { if ((fd = open (devname, O_RDONLY)) < 0) { cerr << "error: cannot open the file \"" << devname << "\" for use as a Macintosh volume\n"; exit (1); } } ////////////////////////////////////// // // FileVolume::~FileVolume // FileVolume::~FileVolume () { close(fd); } ////////////////////////////////////// // // FileVolume::Read // int FileVolume::Read (ULONG logical_block, void * buffer) { if (lseek (fd, (partitionOffset + logical_block) << LogicalBlockShift, 0) < 0) return 0; return read (fd, buffer, LogicalBlockSize) == LogicalBlockSize; } ////////////////////////////////////// // // FileVolume::Write // int FileVolume::Write (ULONG , void * ) { // if (lseek (fd, logical_block << LogicalBlockShift, 0) < 0) // return 0; // return write (fd, buffer, LogicalBlockSize) == LogicalBlockSize; return -1; } ////////////////////////////////////// // // DeviceVolume::DeviceVolume // DeviceVolume::DeviceVolume (const char * devname) : Volume (devname) { memset(cache, 0, sizeof(cache)); countLRU = 1; #if defined(_MSDOS) BYTE drive = (BYTE)(toupper(devname[0]) - 'A'); _asm { mov ax,0x150b mov bx,0 mov cl,drive mov ch,0 int 0x2f cmp bx,0xadad jnz noCDROM cmp ax,0 jz noCDROM } isCDROM = TRUE; driveNum = drive; // Put disk parameters into the object variables return; noCDROM: isCDROM = FALSE; BYTE maxHead; BYTE maxSector; short maxTrack; _asm { mov ah,8 // Code to get the disk parameters mov dl,drive int 0x13 jc invalid_drive // Disk does not exist mov maxHead,dh // Get the disk parameters to where C can get at them mov ah,cl and cl,0x3f mov maxSector,cl mov al,5 shr ah,cl mov al,ch mov maxTrack,ax } driveNum = drive; // Put disk parameters into the object variables numHeads = maxHead + 1; numTracks = maxTrack + 1; numSectors = maxSector; return; invalid_drive: #elif defined(WIN32) char drivename[20]; if (isalpha(devname[0])) { sprintf(drivename, "\\\\.\\%s", devname); devname = drivename; } hDrive = CreateFile(devname, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hDrive != INVALID_HANDLE_VALUE) { DWORD c; if (DeviceIoControl(hDrive, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &c, NULL)) return; } #else if ((fd = open ( (tolower(devname[0]) == 'a') ? DRIVE_A_DEVICE : DRIVE_B_DEVICE, O_RDWR)) > 0) return; #endif cerr << "error: cannot open the device \"" << devname << "\" for use as a Macintosh volume\n"; exit (1); } #if defined(_MSDOS) #if defined(_WINDOWS) extern "C" ULONG __pascal GlobalDosAlloc(ULONG); extern "C" UINT __pascal GlobalDosFree(ULONG); #endif static BOOL ReadCDROM(UINT drive, ULONG phys_block, void * buffer) { BOOL retVal = FALSE; char __far * _buffer; ULONG sector = phys_block >> 2; #if defined(_WINDOWS) ULONG dosAlloc = GlobalDosAlloc(2048); _buffer = (char __far *)((dosAlloc&0xffff) << 16); #else _buffer = (char __far *)_fmalloc(2048); #endif _asm { mov ax,0x1508; les bx,_buffer // address to read into mov cx,drive mov dx,1 mov si,word ptr [sector+2] mov di,word ptr sector int 0x2f jc read_bad } _fmemcpy(buffer, &_buffer[(phys_block&3)<<9], 512); retVal = TRUE; read_bad: #if defined(_WINDOWS) GlobalDosFree(dosAlloc); #else _ffree(_buffer); #endif return retVal; } static BOOL ReadDisk(BYTE drive, BYTE sector, BYTE head, UINT track, void * buffer) { _asm { mov ah,2 // function code mov al,1 // count of sectors les bx,buffer // address to read into mov dx,track // track number mov ch,dl mov cl,5 shl dh,5 mov cl,sector // sector number or cl,dh mov dh,head // head number mov dl,drive // drive number int 0x13 jc read_bad } return TRUE; read_bad: return FALSE; } #endif ////////////////////////////////////// // // DeviceVolume::Read // BOOL DeviceVolume::Read (ULONG logical_block, void * buffer) { int i; Cache * c = cache; for (i = 0; i < CacheSize; i++, c++) { if (c->lastUsed != 0 && c->block == logical_block) { memcpy(buffer, c->buffer, LogicalBlockSize); c->lastUsed = countLRU++; return TRUE; } } #if defined(_MSDOS) ULONG phys_block = logical_block + partitionOffset; if (isCDROM) { if (!ReadCDROM(driveNum, phys_block, buffer)) return FALSE; } else { if (!ReadDisk((BYTE)driveNum, (BYTE)(phys_block%numSectors + 1), (BYTE)((phys_block/numSectors)%numHeads), (UINT)(phys_block/numSectors/numHeads), buffer)) return FALSE; } #elif defined(WIN32) if (SetFilePointer(hDrive, (logical_block + partitionOffset) << LogicalBlockShift, NULL, FILE_BEGIN) == 0xffffffff) return FALSE; DWORD bytesRead; if (!ReadFile(hDrive, buffer, LogicalBlockSize, &bytesRead, NULL)) return FALSE; #else if (lseek (fd, (logical_block + partitionOffset) << LogicalBlockShift, 0) < 0) return FALSE; if (read (fd, buffer, LogicalBlockSize) != LogicalBlockSize) return FALSE; #endif Cache * cLRU = c = cache; for (i = 1; i < CacheSize; i++) { c++; if (cLRU->lastUsed > c->lastUsed) cLRU = c; } cLRU->block = logical_block; cLRU->lastUsed = countLRU++; memcpy(cLRU->buffer, buffer, LogicalBlockSize); return TRUE; } ////////////////////////////////////// // // DeviceVolume::Write // int DeviceVolume::Write (ULONG logical_block, void * ) { // invalidate the read cache for this block Cache * c = cache; for (int i = 0; i < CacheSize; i++, c++) { if (c->lastUsed != 0 && c->block == logical_block) { c->lastUsed = 0; i = CacheSize; } } cerr << "error: DeviceVolume::Write not yet implemented\n"; exit(0); return 0; } ////////////////////////////////////// // // DeviceVolume::InvalidateCache // void DeviceVolume::InvalidateCache() { memset(cache, 0, sizeof(cache)); countLRU = 1; } ////////////////////////////////////// // // DeviceVolume::IsDeviceName // BOOL DeviceVolume::IsDeviceName(const char * devname) { #if defined(_MSDOS) return isalpha(devname[0]) && devname[1] == ':' && devname[2] == '\0'; #elif defined(WIN32) if (devname[0] == '\\' && devname[1] == '\\') { if ((devname = strchr(devname+2, '\\')) == NULL) return FALSE; devname++; } return isalpha(devname[0]) && devname[1] == ':' && devname[2] == '\0'; #else return devname[1] == ':' && devname[2] == '\0' && strchr("ab", tolower(devname[0])) != NULL; #endif // struct stat s; // return stat(devname, &s) == 0 && S_ISBLK(s.st_mode); }