show app icons on disk image chooser

This commit is contained in:
Jesús A. Álvarez 2016-05-21 15:01:57 +02:00
parent 6235aa8fe8
commit bac652d451
34 changed files with 9926 additions and 1 deletions

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "libmfs"]
path = libmfs
url = https://github.com/zydeco/libmfs.git
[submodule "libres"]
path = libres
url = https://github.com/zydeco/libres.git

View File

@ -39,8 +39,58 @@
28F676CB1CD15E0B00FC6FA6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 28F676C91CD15E0B00FC6FA6 /* Main.storyboard */; };
28F676CD1CD15E0B00FC6FA6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 28F676CC1CD15E0B00FC6FA6 /* Assets.xcassets */; };
28F676D01CD15E0B00FC6FA6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 28F676CE1CD15E0B00FC6FA6 /* LaunchScreen.storyboard */; };
28F6B4521CF07C48002D76D0 /* UIImage+DiskImageIcon.m in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4511CF07C48002D76D0 /* UIImage+DiskImageIcon.m */; };
28F6B4971CF07E00002D76D0 /* block.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4611CF07CC9002D76D0 /* block.c */; };
28F6B4981CF07E00002D76D0 /* btree.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4631CF07CC9002D76D0 /* btree.c */; };
28F6B4991CF07E00002D76D0 /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4661CF07CC9002D76D0 /* data.c */; };
28F6B49A1CF07E00002D76D0 /* file.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4681CF07CC9002D76D0 /* file.c */; };
28F6B49B1CF07E00002D76D0 /* hfs.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B46A1CF07CC9002D76D0 /* hfs.c */; };
28F6B49C1CF07E00002D76D0 /* low.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B46D1CF07CC9002D76D0 /* low.c */; };
28F6B49D1CF07E00002D76D0 /* medium.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B46F1CF07CC9002D76D0 /* medium.c */; };
28F6B49E1CF07E00002D76D0 /* memcmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4711CF07CC9002D76D0 /* memcmp.c */; };
28F6B49F1CF07E00002D76D0 /* node.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4721CF07CC9002D76D0 /* node.c */; };
28F6B4A01CF07E00002D76D0 /* record.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4771CF07CC9002D76D0 /* record.c */; };
28F6B4A11CF07E00002D76D0 /* version.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4791CF07CC9002D76D0 /* version.c */; };
28F6B4A21CF07E00002D76D0 /* volume.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B47B1CF07CC9002D76D0 /* volume.c */; };
28F6B4A31CF07E08002D76D0 /* unix.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4751CF07CC9002D76D0 /* unix.c */; };
28F6B4B11CF07ED9002D76D0 /* mfs.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B45C1CF07CBF002D76D0 /* mfs.c */; };
28F6B4BF1CF07F39002D76D0 /* res.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4571CF07CB3002D76D0 /* res.c */; };
28F6B4C01CF07F5C002D76D0 /* liblibhfs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 28F6B48E1CF07DDD002D76D0 /* liblibhfs.a */; };
28F6B4C11CF07F5C002D76D0 /* liblibmfs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 28F6B4A81CF07EC9002D76D0 /* liblibmfs.a */; };
28F6B4C21CF07F5C002D76D0 /* liblibres.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 28F6B4B61CF07F32002D76D0 /* liblibres.a */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
28F6B48C1CF07DDD002D76D0 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
28F6B4A61CF07EC9002D76D0 /* Copy Files */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
name = "Copy Files";
runOnlyForDeploymentPostprocessing = 0;
};
28F6B4B41CF07F32002D76D0 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
28848B601CDE97D600B86C45 /* InsertDiskViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InsertDiskViewController.h; sourceTree = "<group>"; };
28848B611CDE97D600B86C45 /* InsertDiskViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InsertDiskViewController.m; sourceTree = "<group>"; };
@ -113,10 +163,75 @@
28F676CC1CD15E0B00FC6FA6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
28F676CF1CD15E0B00FC6FA6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
28F676D11CD15E0B00FC6FA6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
28F6B4501CF07C48002D76D0 /* UIImage+DiskImageIcon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+DiskImageIcon.h"; sourceTree = "<group>"; };
28F6B4511CF07C48002D76D0 /* UIImage+DiskImageIcon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+DiskImageIcon.m"; sourceTree = "<group>"; };
28F6B4561CF07CB3002D76D0 /* libres_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libres_internal.h; sourceTree = "<group>"; };
28F6B4571CF07CB3002D76D0 /* res.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = res.c; sourceTree = "<group>"; };
28F6B4581CF07CB3002D76D0 /* res.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = res.h; sourceTree = "<group>"; };
28F6B45A1CF07CBF002D76D0 /* appledouble.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = appledouble.h; sourceTree = "<group>"; };
28F6B45B1CF07CBF002D76D0 /* fobj.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fobj.h; sourceTree = "<group>"; };
28F6B45C1CF07CBF002D76D0 /* mfs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mfs.c; sourceTree = "<group>"; };
28F6B45D1CF07CBF002D76D0 /* mfs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mfs.h; sourceTree = "<group>"; };
28F6B45F1CF07CC9002D76D0 /* acconfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = acconfig.h; sourceTree = "<group>"; };
28F6B4601CF07CC9002D76D0 /* apple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = apple.h; sourceTree = "<group>"; };
28F6B4611CF07CC9002D76D0 /* block.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = block.c; sourceTree = "<group>"; };
28F6B4621CF07CC9002D76D0 /* block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = block.h; sourceTree = "<group>"; };
28F6B4631CF07CC9002D76D0 /* btree.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = btree.c; sourceTree = "<group>"; };
28F6B4641CF07CC9002D76D0 /* btree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = btree.h; sourceTree = "<group>"; };
28F6B4651CF07CC9002D76D0 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; };
28F6B4661CF07CC9002D76D0 /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data.c; sourceTree = "<group>"; };
28F6B4671CF07CC9002D76D0 /* data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data.h; sourceTree = "<group>"; };
28F6B4681CF07CC9002D76D0 /* file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = file.c; sourceTree = "<group>"; };
28F6B4691CF07CC9002D76D0 /* file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file.h; sourceTree = "<group>"; };
28F6B46A1CF07CC9002D76D0 /* hfs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hfs.c; sourceTree = "<group>"; };
28F6B46B1CF07CC9002D76D0 /* hfs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hfs.h; sourceTree = "<group>"; };
28F6B46C1CF07CC9002D76D0 /* libhfs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libhfs.h; sourceTree = "<group>"; };
28F6B46D1CF07CC9002D76D0 /* low.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = low.c; sourceTree = "<group>"; };
28F6B46E1CF07CC9002D76D0 /* low.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = low.h; sourceTree = "<group>"; };
28F6B46F1CF07CC9002D76D0 /* medium.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = medium.c; sourceTree = "<group>"; };
28F6B4701CF07CC9002D76D0 /* medium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = medium.h; sourceTree = "<group>"; };
28F6B4711CF07CC9002D76D0 /* memcmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = memcmp.c; sourceTree = "<group>"; };
28F6B4721CF07CC9002D76D0 /* node.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = node.c; sourceTree = "<group>"; };
28F6B4731CF07CC9002D76D0 /* node.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = node.h; sourceTree = "<group>"; };
28F6B4751CF07CC9002D76D0 /* unix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unix.c; sourceTree = "<group>"; };
28F6B4761CF07CC9002D76D0 /* os.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = os.h; sourceTree = "<group>"; };
28F6B4771CF07CC9002D76D0 /* record.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = record.c; sourceTree = "<group>"; };
28F6B4781CF07CC9002D76D0 /* record.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = record.h; sourceTree = "<group>"; };
28F6B4791CF07CC9002D76D0 /* version.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = version.c; sourceTree = "<group>"; };
28F6B47A1CF07CC9002D76D0 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = "<group>"; };
28F6B47B1CF07CC9002D76D0 /* volume.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = volume.c; sourceTree = "<group>"; };
28F6B47C1CF07CC9002D76D0 /* volume.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = volume.h; sourceTree = "<group>"; };
28F6B48E1CF07DDD002D76D0 /* liblibhfs.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = liblibhfs.a; sourceTree = BUILT_PRODUCTS_DIR; };
28F6B4A81CF07EC9002D76D0 /* liblibmfs.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = liblibmfs.a; sourceTree = BUILT_PRODUCTS_DIR; };
28F6B4B61CF07F32002D76D0 /* liblibres.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = liblibres.a; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
28F676BA1CD15E0B00FC6FA6 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
28F6B4C01CF07F5C002D76D0 /* liblibhfs.a in Frameworks */,
28F6B4C11CF07F5C002D76D0 /* liblibmfs.a in Frameworks */,
28F6B4C21CF07F5C002D76D0 /* liblibres.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
28F6B48B1CF07DDD002D76D0 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
28F6B4A51CF07EC9002D76D0 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
28F6B4B31CF07F32002D76D0 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
@ -207,6 +322,9 @@
isa = PBXGroup;
children = (
28F676BD1CD15E0B00FC6FA6 /* Mini vMac.app */,
28F6B48E1CF07DDD002D76D0 /* liblibhfs.a */,
28F6B4A81CF07EC9002D76D0 /* liblibmfs.a */,
28F6B4B61CF07F32002D76D0 /* liblibres.a */,
);
name = Products;
sourceTree = "<group>";
@ -239,6 +357,8 @@
28CE8E8E1CD4C3B200FE25A8 /* mnvm_core */,
28CE8E871CD4C33E00FE25A8 /* mnvm_cfg */,
28F676C01CD15E0B00FC6FA6 /* Supporting Files */,
28F6B4501CF07C48002D76D0 /* UIImage+DiskImageIcon.h */,
28F6B4511CF07C48002D76D0 /* UIImage+DiskImageIcon.m */,
);
path = "Mini vMac";
sourceTree = "<group>";
@ -246,11 +366,82 @@
28F676C01CD15E0B00FC6FA6 /* Supporting Files */ = {
isa = PBXGroup;
children = (
28F6B4551CF07C9A002D76D0 /* libhfs */,
28F6B4541CF07C8D002D76D0 /* libmfs */,
28F6B4531CF07C83002D76D0 /* libres */,
28F676C11CD15E0B00FC6FA6 /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
28F6B4531CF07C83002D76D0 /* libres */ = {
isa = PBXGroup;
children = (
28F6B4561CF07CB3002D76D0 /* libres_internal.h */,
28F6B4571CF07CB3002D76D0 /* res.c */,
28F6B4581CF07CB3002D76D0 /* res.h */,
);
name = libres;
path = ../libres;
sourceTree = "<group>";
};
28F6B4541CF07C8D002D76D0 /* libmfs */ = {
isa = PBXGroup;
children = (
28F6B45A1CF07CBF002D76D0 /* appledouble.h */,
28F6B45B1CF07CBF002D76D0 /* fobj.h */,
28F6B45C1CF07CBF002D76D0 /* mfs.c */,
28F6B45D1CF07CBF002D76D0 /* mfs.h */,
);
name = libmfs;
path = ../libmfs;
sourceTree = "<group>";
};
28F6B4551CF07C9A002D76D0 /* libhfs */ = {
isa = PBXGroup;
children = (
28F6B45F1CF07CC9002D76D0 /* acconfig.h */,
28F6B4601CF07CC9002D76D0 /* apple.h */,
28F6B4611CF07CC9002D76D0 /* block.c */,
28F6B4621CF07CC9002D76D0 /* block.h */,
28F6B4631CF07CC9002D76D0 /* btree.c */,
28F6B4641CF07CC9002D76D0 /* btree.h */,
28F6B4651CF07CC9002D76D0 /* config.h */,
28F6B4661CF07CC9002D76D0 /* data.c */,
28F6B4671CF07CC9002D76D0 /* data.h */,
28F6B4681CF07CC9002D76D0 /* file.c */,
28F6B4691CF07CC9002D76D0 /* file.h */,
28F6B46A1CF07CC9002D76D0 /* hfs.c */,
28F6B46B1CF07CC9002D76D0 /* hfs.h */,
28F6B46C1CF07CC9002D76D0 /* libhfs.h */,
28F6B46D1CF07CC9002D76D0 /* low.c */,
28F6B46E1CF07CC9002D76D0 /* low.h */,
28F6B46F1CF07CC9002D76D0 /* medium.c */,
28F6B4701CF07CC9002D76D0 /* medium.h */,
28F6B4711CF07CC9002D76D0 /* memcmp.c */,
28F6B4721CF07CC9002D76D0 /* node.c */,
28F6B4731CF07CC9002D76D0 /* node.h */,
28F6B4741CF07CC9002D76D0 /* os */,
28F6B4761CF07CC9002D76D0 /* os.h */,
28F6B4771CF07CC9002D76D0 /* record.c */,
28F6B4781CF07CC9002D76D0 /* record.h */,
28F6B4791CF07CC9002D76D0 /* version.c */,
28F6B47A1CF07CC9002D76D0 /* version.h */,
28F6B47B1CF07CC9002D76D0 /* volume.c */,
28F6B47C1CF07CC9002D76D0 /* volume.h */,
);
name = libhfs;
path = ../libhfs;
sourceTree = "<group>";
};
28F6B4741CF07CC9002D76D0 /* os */ = {
isa = PBXGroup;
children = (
28F6B4751CF07CC9002D76D0 /* unix.c */,
);
path = os;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -271,6 +462,57 @@
productReference = 28F676BD1CD15E0B00FC6FA6 /* Mini vMac.app */;
productType = "com.apple.product-type.application";
};
28F6B48D1CF07DDD002D76D0 /* libhfs */ = {
isa = PBXNativeTarget;
buildConfigurationList = 28F6B4941CF07DDD002D76D0 /* Build configuration list for PBXNativeTarget "libhfs" */;
buildPhases = (
28F6B48A1CF07DDD002D76D0 /* Sources */,
28F6B48B1CF07DDD002D76D0 /* Frameworks */,
28F6B48C1CF07DDD002D76D0 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = libhfs;
productName = libhfs;
productReference = 28F6B48E1CF07DDD002D76D0 /* liblibhfs.a */;
productType = "com.apple.product-type.library.static";
};
28F6B4A71CF07EC9002D76D0 /* libmfs */ = {
isa = PBXNativeTarget;
buildConfigurationList = 28F6B4AE1CF07EC9002D76D0 /* Build configuration list for PBXNativeTarget "libmfs" */;
buildPhases = (
28F6B4A41CF07EC9002D76D0 /* Sources */,
28F6B4A51CF07EC9002D76D0 /* Frameworks */,
28F6B4A61CF07EC9002D76D0 /* Copy Files */,
);
buildRules = (
);
dependencies = (
);
name = libmfs;
productName = libmfs;
productReference = 28F6B4A81CF07EC9002D76D0 /* liblibmfs.a */;
productType = "com.apple.product-type.library.static";
};
28F6B4B51CF07F32002D76D0 /* libres */ = {
isa = PBXNativeTarget;
buildConfigurationList = 28F6B4BC1CF07F32002D76D0 /* Build configuration list for PBXNativeTarget "libres" */;
buildPhases = (
28F6B4B21CF07F32002D76D0 /* Sources */,
28F6B4B31CF07F32002D76D0 /* Frameworks */,
28F6B4B41CF07F32002D76D0 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = libres;
productName = libres;
productReference = 28F6B4B61CF07F32002D76D0 /* liblibres.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@ -284,6 +526,15 @@
CreatedOnToolsVersion = 7.3;
DevelopmentTeam = UJXNDZ5TNU;
};
28F6B48D1CF07DDD002D76D0 = {
CreatedOnToolsVersion = 7.3.1;
};
28F6B4A71CF07EC9002D76D0 = {
CreatedOnToolsVersion = 7.3.1;
};
28F6B4B51CF07F32002D76D0 = {
CreatedOnToolsVersion = 7.3.1;
};
};
};
buildConfigurationList = 28F676B81CD15E0B00FC6FA6 /* Build configuration list for PBXProject "Mini vMac" */;
@ -300,6 +551,9 @@
projectRoot = "";
targets = (
28F676BC1CD15E0B00FC6FA6 /* Mini vMac */,
28F6B48D1CF07DDD002D76D0 /* libhfs */,
28F6B4A71CF07EC9002D76D0 /* libmfs */,
28F6B4B51CF07F32002D76D0 /* libres */,
);
};
/* End PBXProject section */
@ -338,6 +592,7 @@
28CE8EB81CD4C3B200FE25A8 /* M68KITAB.c in Sources */,
28848B651CDE97E900B86C45 /* SettingsViewController.m in Sources */,
28BA89881CE73FBC00A98104 /* MNVMApplication.m in Sources */,
28F6B4521CF07C48002D76D0 /* UIImage+DiskImageIcon.m in Sources */,
28CE8EB71CD4C3B200FE25A8 /* KBRDEMDV.c in Sources */,
28CE8EBC1CD4C3B200FE25A8 /* ROMEMDEV.c in Sources */,
28BA897F1CE7315400A98104 /* KBKeyboardLayout.m in Sources */,
@ -354,6 +609,42 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
28F6B48A1CF07DDD002D76D0 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
28F6B49A1CF07E00002D76D0 /* file.c in Sources */,
28F6B4981CF07E00002D76D0 /* btree.c in Sources */,
28F6B49E1CF07E00002D76D0 /* memcmp.c in Sources */,
28F6B49D1CF07E00002D76D0 /* medium.c in Sources */,
28F6B4A11CF07E00002D76D0 /* version.c in Sources */,
28F6B49C1CF07E00002D76D0 /* low.c in Sources */,
28F6B49F1CF07E00002D76D0 /* node.c in Sources */,
28F6B4991CF07E00002D76D0 /* data.c in Sources */,
28F6B49B1CF07E00002D76D0 /* hfs.c in Sources */,
28F6B4A21CF07E00002D76D0 /* volume.c in Sources */,
28F6B4A01CF07E00002D76D0 /* record.c in Sources */,
28F6B4A31CF07E08002D76D0 /* unix.c in Sources */,
28F6B4971CF07E00002D76D0 /* block.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
28F6B4A41CF07EC9002D76D0 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
28F6B4B11CF07ED9002D76D0 /* mfs.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
28F6B4B21CF07F32002D76D0 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
28F6B4BF1CF07F39002D76D0 /* res.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
@ -466,8 +757,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
HEADER_SEARCH_PATHS = "$(SRCROOT)";
INFOPLIST_FILE = "Mini vMac/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_CFLAGS = "-DUSE_LIBRES";
PRODUCT_BUNDLE_IDENTIFIER = net.namedfork.minivmac;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
@ -480,14 +773,80 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
HEADER_SEARCH_PATHS = "$(SRCROOT)";
INFOPLIST_FILE = "Mini vMac/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_CFLAGS = "-DUSE_LIBRES";
PRODUCT_BUNDLE_IDENTIFIER = net.namedfork.minivmac;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
};
name = Release;
};
28F6B4951CF07DDD002D76D0 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = NO;
OTHER_CFLAGS = "-DHAVE_CONFIG_H";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Debug;
};
28F6B4961CF07DDD002D76D0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = NO;
OTHER_CFLAGS = "-DHAVE_CONFIG_H";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Release;
};
28F6B4AF1CF07EC9002D76D0 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_WARN_ABOUT_RETURN_TYPE = NO;
HEADER_SEARCH_PATHS = "$(SRCROOT)";
OTHER_CFLAGS = "-DUSE_LIBRES";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Debug;
};
28F6B4B01CF07EC9002D76D0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_WARN_ABOUT_RETURN_TYPE = NO;
HEADER_SEARCH_PATHS = "$(SRCROOT)";
OTHER_CFLAGS = "-DUSE_LIBRES";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Release;
};
28F6B4BD1CF07F32002D76D0 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Debug;
};
28F6B4BE1CF07F32002D76D0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@ -509,6 +868,30 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
28F6B4941CF07DDD002D76D0 /* Build configuration list for PBXNativeTarget "libhfs" */ = {
isa = XCConfigurationList;
buildConfigurations = (
28F6B4951CF07DDD002D76D0 /* Debug */,
28F6B4961CF07DDD002D76D0 /* Release */,
);
defaultConfigurationIsVisible = 0;
};
28F6B4AE1CF07EC9002D76D0 /* Build configuration list for PBXNativeTarget "libmfs" */ = {
isa = XCConfigurationList;
buildConfigurations = (
28F6B4AF1CF07EC9002D76D0 /* Debug */,
28F6B4B01CF07EC9002D76D0 /* Release */,
);
defaultConfigurationIsVisible = 0;
};
28F6B4BC1CF07F32002D76D0 /* Build configuration list for PBXNativeTarget "libres" */ = {
isa = XCConfigurationList;
buildConfigurations = (
28F6B4BD1CF07F32002D76D0 /* Debug */,
28F6B4BE1CF07F32002D76D0 /* Release */,
);
defaultConfigurationIsVisible = 0;
};
/* End XCConfigurationList section */
};
rootObject = 28F676B51CD15E0B00FC6FA6 /* Project object */;

View File

@ -8,6 +8,7 @@
#import "InsertDiskViewController.h"
#import "AppDelegate.h"
#import "UIImage+DiskImageIcon.h"
@interface InsertDiskViewController () <UITextFieldDelegate>
@ -420,7 +421,15 @@
NSDictionary *attributes = [[NSURL fileURLWithPath:filePath] resourceValuesForKeys:@[NSURLTotalFileSizeKey] error:NULL];
if (attributes && attributes[NSURLTotalFileSizeKey]) {
BOOL isDiskImage = [[AppDelegate sharedInstance].diskImageExtensions containsObject:fileName.pathExtension.lowercaseString];
self.imageView.image = [UIImage imageNamed:isDiskImage ? @"floppy" : @"document"];
if (isDiskImage) {
UIImage *icon = [UIImage imageWithIconForDiskImage:filePath];
if (icon == nil) {
icon = [UIImage imageNamed:@"floppy"];
}
self.imageView.image = icon;
} else {
self.imageView.image = [UIImage imageNamed:@"document"];
}
NSString *sizeString = [NSByteCountFormatter stringFromByteCount:[attributes[NSURLTotalFileSizeKey] longLongValue] countStyle:NSByteCountFormatterCountStyleBinary];
self.detailTextLabel.text = sizeString;
} else {

View File

@ -0,0 +1,15 @@
//
// UIImage+DiskImageIcon.h
// Mini vMac
//
// Created by Jesús A. Álvarez on 21/05/2016.
// Copyright © 2016 namedfork. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIImage (DiskImageIcon)
+ (UIImage *)imageWithIconForDiskImage:(NSString *)path;
@end

View File

@ -0,0 +1,602 @@
//
// UIImage+DiskImageIcon.m
// Mini vMac
//
// Created by Jesús A. Álvarez on 21/05/2016.
// Copyright © 2016 namedfork. All rights reserved.
//
#import "UIImage+DiskImageIcon.h"
#import "libhfs.h"
#import "res.h"
#import "mfs.h"
#define kDiskImageHasDC42Header 1 << 0
#define RSHORT(base, offset) ntohs(*((short *)((base) + (offset))))
#define RLONG(base, offset) ntohl(*((long *)((base) + (offset))))
#define RCSTR(base, offset) ((char *)((base) + (offset)))
@interface DiskImageIconReader : NSObject
- (UIImage *)iconForDiskImage:(NSString *)path;
@end
@implementation UIImage (DiskImageIcon)
+ (UIImage *)imageWithIconForDiskImage:(NSString *)path {
return [[DiskImageIconReader new] iconForDiskImage:path];
}
@end
// Mac OS 1 bit palette
static uint32_t ctb1[2] = {0xFFFFFF, 0x000000};
// Mac OS 4 bit palette
static uint32_t ctb4[16] = {
0xFFFFFF, 0xFFFF00, 0xFF6600, 0xDD0000, 0xFF0099, 0x330099, 0x0000DD, 0x0099FF,
0x00BB00, 0x006600, 0x663300, 0x996633, 0xCCCCCC, 0x888888, 0x444444, 0x000000};
// Mac OS 8 bit palette
static uint32_t ctb8[256] = {
0xFFFFFF, 0xFFFFCC, 0xFFFF99, 0xFFFF66, 0xFFFF33, 0xFFFF00, 0xFFCCFF, 0xFFCCCC,
0xFFCC99, 0xFFCC66, 0xFFCC33, 0xFFCC00, 0xFF99FF, 0xFF99CC, 0xFF9999, 0xFF9966,
0xFF9933, 0xFF9900, 0xFF66FF, 0xFF66CC, 0xFF6699, 0xFF6666, 0xFF6633, 0xFF6600,
0xFF33FF, 0xFF33CC, 0xFF3399, 0xFF3366, 0xFF3333, 0xFF3300, 0xFF00FF, 0xFF00CC,
0xFF0099, 0xFF0066, 0xFF0033, 0xFF0000, 0xCCFFFF, 0xCCFFCC, 0xCCFF99, 0xCCFF66,
0xCCFF33, 0xCCFF00, 0xCCCCFF, 0xCCCCCC, 0xCCCC99, 0xCCCC66, 0xCCCC33, 0xCCCC00,
0xCC99FF, 0xCC99CC, 0xCC9999, 0xCC9966, 0xCC9933, 0xCC9900, 0xCC66FF, 0xCC66CC,
0xCC6699, 0xCC6666, 0xCC6633, 0xCC6600, 0xCC33FF, 0xCC33CC, 0xCC3399, 0xCC3366,
0xCC3333, 0xCC3300, 0xCC00FF, 0xCC00CC, 0xCC0099, 0xCC0066, 0xCC0033, 0xCC0000,
0x99FFFF, 0x99FFCC, 0x99FF99, 0x99FF66, 0x99FF33, 0x99FF00, 0x99CCFF, 0x99CCCC,
0x99CC99, 0x99CC66, 0x99CC33, 0x99CC00, 0x9999FF, 0x9999CC, 0x999999, 0x999966,
0x999933, 0x999900, 0x9966FF, 0x9966CC, 0x996699, 0x996666, 0x996633, 0x996600,
0x9933FF, 0x9933CC, 0x993399, 0x993366, 0x993333, 0x993300, 0x9900FF, 0x9900CC,
0x990099, 0x990066, 0x990033, 0x990000, 0x66FFFF, 0x66FFCC, 0x66FF99, 0x66FF66,
0x66FF33, 0x66FF00, 0x66CCFF, 0x66CCCC, 0x66CC99, 0x66CC66, 0x66CC33, 0x66CC00,
0x6699FF, 0x6699CC, 0x669999, 0x669966, 0x669933, 0x669900, 0x6666FF, 0x6666CC,
0x666699, 0x666666, 0x666633, 0x666600, 0x6633FF, 0x6633CC, 0x663399, 0x663366,
0x663333, 0x663300, 0x6600FF, 0x6600CC, 0x660099, 0x660066, 0x660033, 0x660000,
0x33FFFF, 0x33FFCC, 0x33FF99, 0x33FF66, 0x33FF33, 0x33FF00, 0x33CCFF, 0x33CCCC,
0x33CC99, 0x33CC66, 0x33CC33, 0x33CC00, 0x3399FF, 0x3399CC, 0x339999, 0x339966,
0x339933, 0x339900, 0x3366FF, 0x3366CC, 0x336699, 0x336666, 0x336633, 0x336600,
0x3333FF, 0x3333CC, 0x333399, 0x333366, 0x333333, 0x333300, 0x3300FF, 0x3300CC,
0x330099, 0x330066, 0x330033, 0x330000, 0x00FFFF, 0x00FFCC, 0x00FF99, 0x00FF66,
0x00FF33, 0x00FF00, 0x00CCFF, 0x00CCCC, 0x00CC99, 0x00CC66, 0x00CC33, 0x00CC00,
0x0099FF, 0x0099CC, 0x009999, 0x009966, 0x009933, 0x009900, 0x0066FF, 0x0066CC,
0x006699, 0x006666, 0x006633, 0x006600, 0x0033FF, 0x0033CC, 0x003399, 0x003366,
0x003333, 0x003300, 0x0000FF, 0x0000CC, 0x000099, 0x000066, 0x000033, 0xEE0000,
0xDD0000, 0xBB0000, 0xAA0000, 0x880000, 0x770000, 0x550000, 0x440000, 0x220000,
0x110000, 0x00EE00, 0x00DD00, 0x00BB00, 0x00AA00, 0x008800, 0x007700, 0x005500,
0x004400, 0x002200, 0x001100, 0x0000EE, 0x0000DD, 0x0000BB, 0x0000AA, 0x000088,
0x000077, 0x000055, 0x000044, 0x000022, 0x000011, 0xEEEEEE, 0xDDDDDD, 0xBBBBBB,
0xAAAAAA, 0x888888, 0x777777, 0x555555, 0x444444, 0x222222, 0x111111, 0x000000};
@implementation DiskImageIconReader
- (UIImage *)iconForDiskImage:(NSString *)path {
// determine format and offset of disk image
NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:path];
if (fh == nil) {
return nil;
}
[fh seekToFileOffset:1024];
NSData *checkHeader = [fh readDataOfLength:128];
[fh closeFile];
const unsigned char *chb = [checkHeader bytes];
// determine type from header
if ((chb[0] == 0x42) && (chb[1] == 0x44)) {
/* hfs */
return [self iconForHFSDiskImage:path options:0];
} else if ((chb[0] == 0xD2) && (chb[1] == 0xD7)) {
/* mfs */
return [self iconForMFSDiskImage:path options:0];
} else if ((chb[84] == 0x42) && (chb[85] == 0x44)) {
/* hfs, dc42 header */
return [self iconForHFSDiskImage:path options:kDiskImageHasDC42Header];
} else if ((chb[84] == 0xD2) && (chb[85] == 0xD7)) {
/* mfs, dc42 header */
return [self iconForMFSDiskImage:path options:kDiskImageHasDC42Header];
}
return nil;
}
#pragma mark - MFS
- (UIImage *)iconForMFSDiskImage:(NSString *)path options:(int)options {
// open disk image
size_t offset = (options & kDiskImageHasDC42Header) ? 84 : 0;
MFSVolume *vol = mfs_vopen([path fileSystemRepresentation], (size_t)offset, 0);
if (vol == NULL) {
NSLog(@"Can't open MFS volume at %@", path);
return nil;
}
NSString *volName = [NSString stringWithCString:vol->name encoding:NSMacOSRomanStringEncoding];
NSString *volComment;
char *const volCommentBytes = mfs_comment(vol, NULL);
if (volCommentBytes) {
volComment = [NSString stringWithCString:volCommentBytes encoding:NSMacOSRomanStringEncoding];
free(volCommentBytes);
}
// find applications
MFSDirectoryRecord *rec;
NSMutableArray *apps = [NSMutableArray arrayWithCapacity:5];
for (int i = 0; vol->directory[i]; i++) {
rec = vol->directory[i];
if (ntohl(rec->flUsrWds.type) != 'APPL') {
continue;
}
[apps addObject:[NSNumber numberWithInt:i]];
}
// if there's more than one app, find one that looks matching
if ([apps count] == 0) {
return nil;
} else if ([apps count] > 1) {
rec = NULL;
for (NSNumber *num in apps) {
rec = vol->directory[[num intValue]];
NSString *appName = [[NSString alloc] initWithCString:rec->flCName encoding:NSMacOSRomanStringEncoding];
if (![self chooseApp:appName inVolume:volName hint:volComment]) {
rec = NULL;
}
if (rec) {
break;
}
}
if (rec == NULL) {
return nil;
}
} else {
rec = vol->directory[[[apps objectAtIndex:0] intValue]];
}
// open resource fork
MFSFork *rsrcFork = mfs_fkopen(vol, rec, kMFSForkRsrc, 0);
RFILE *rfile = res_open_funcs(rsrcFork, mfs_fkseek, mfs_fkread);
// get icon
CGImageRef iconImage = [self appIconForResourceFile:rfile creator:ntohl(rec->flUsrWds.creator)];
UIImage *icon = nil;
if (iconImage) {
icon = [UIImage imageWithCGImage:iconImage];
CGImageRelease(iconImage);
}
// close stuff
res_close(rfile);
mfs_fkclose(rsrcFork);
mfs_vclose(vol);
return icon;
}
#pragma mark - HFS
- (UIImage *)iconForHFSDiskImage:(NSString *)path options:(int)options {
// open disk image
int mountFlags = HFS_MODE_RDONLY;
if (options & kDiskImageHasDC42Header) {
mountFlags |= HFS_OPT_DC42HEADER;
}
hfsvol *vol = hfs_mount([path fileSystemRepresentation], 0, mountFlags);
if (vol == NULL) {
NSLog(@"Can't open HFS volume at %@ with flags %x", path, mountFlags);
return nil;
}
// try volume icon
UIImage *volumeIcon = [self iconFromHFSVolumeIcon:vol];
if (volumeIcon) {
hfs_umount(vol);
return volumeIcon;
}
// find best application
UIImage *icon = nil;
NSString *appPath = [self findAppInHFSVolume:vol];
hfsfile *hfile = NULL;
RFILE *rfile = NULL;
if (appPath == nil) {
hfs_umount(vol);
return nil;
}
// open resource fork
hfile = hfs_open(vol, [appPath cStringUsingEncoding:NSMacOSRomanStringEncoding]);
if (hfile == NULL) {
hfs_umount(vol);
return nil;
}
hfs_setfork(hfile, 1);
rfile = res_open_funcs(hfile, (res_seek_func)hfs_seek, (res_read_func)hfs_read);
if (rfile == NULL) {
hfs_close(hfile);
hfs_umount(vol);
return nil;
}
// get icon
hfsdirent ent;
if (hfs_stat(vol, [appPath cStringUsingEncoding:NSMacOSRomanStringEncoding], &ent)) {
res_close(rfile);
hfs_close(hfile);
hfs_umount(vol);
return nil;
}
CGImageRef iconImage = [self appIconForResourceFile:rfile creator:ntohl(*(uint32_t *)ent.u.file.creator)];
if (iconImage) {
icon = [UIImage imageWithCGImage:iconImage];
CGImageRelease(iconImage);
}
// close stuff
res_close(rfile);
hfs_close(hfile);
hfs_umount(vol);
return icon;
}
- (NSString *)findAppInHFSVolume:(hfsvol *)vol {
// get disk name
hfsvolent volEnt;
hfs_vstat(vol, &volEnt);
NSString *volName = [NSString stringWithCString:volEnt.name encoding:NSMacOSRomanStringEncoding];
NSString *volComment = [self commentForHFSVolume:vol];
// find apps
NSMutableArray *apps = [[NSMutableArray alloc] initWithCapacity:5];
[self findApps:apps inDirectory:HFS_CNID_ROOTDIR ofHFSVolume:vol skipFolder:volEnt.blessed];
// decide which one to use
NSString *myApp = nil;
NSString *appName = nil;
if ([apps count] == 1) {
myApp = [apps objectAtIndex:0];
} else if ([apps count] > 1) {
for (NSString *appPath in apps) {
// choose an app
appName = [appPath componentsSeparatedByString:@":"].lastObject;
if (![self chooseApp:appName inVolume:volName hint:volComment]) {
continue;
}
myApp = appPath;
}
}
return myApp;
}
- (void)findApps:(NSMutableArray *)apps inDirectory:(unsigned long)cnid ofHFSVolume:(hfsvol *)vol skipFolder:(unsigned long)skipCNID {
if (hfs_setcwd(vol, cnid)) {
return;
}
hfsdir *dir = hfs_opendir(vol, ":");
if (dir == NULL) {
return;
}
hfsdirent ent;
while (hfs_readdir(dir, &ent) == 0) {
if (ent.flags & HFS_ISDIR && ent.cnid != skipCNID) {
[self findApps:apps inDirectory:ent.cnid ofHFSVolume:vol skipFolder:skipCNID];
} else if (ntohl(*(uint32_t *)ent.u.file.type) == 'APPL') {
// Found an app
[apps addObject:[self pathToDirEntry:&ent ofHFSVolume:vol]];
}
}
hfs_closedir(dir);
}
- (NSString *)pathToDirEntry:(const hfsdirent *)ent ofHFSVolume:(hfsvol *)vol {
NSMutableString *path = [NSMutableString stringWithCString:ent->name encoding:NSMacOSRomanStringEncoding];
NSString *entName;
char name[HFS_MAX_FLEN + 1];
unsigned long cnid = ent->parid;
while (cnid != HFS_CNID_ROOTPAR) {
if (hfs_dirinfo(vol, &cnid, name)) {
return nil;
}
entName = [[NSString alloc] initWithCString:name encoding:NSMacOSRomanStringEncoding];
[path insertString:@":" atIndex:0];
[path insertString:entName atIndex:0];
}
return path;
}
- (NSString *)commentForHFSVolume:(hfsvol *)vol {
hfsvolent vent;
hfsdirent dent;
NSString *comment = nil;
// get comment ID
if (hfs_vstat(vol, &vent) || hfs_stat(vol, ":", &dent)) {
return nil;
}
unsigned short cmtID = dent.fdcomment;
// open desktop
hfsfile *hfile = NULL;
RFILE *rfile = NULL;
hfs_chdir(vol, vent.name);
hfile = hfs_open(vol, "Desktop");
if (hfile == NULL) {
return nil;
}
hfs_setfork(hfile, 1);
rfile = res_open_funcs(hfile, (res_seek_func)hfs_seek, (res_read_func)hfs_read);
if (rfile == NULL) {
hfs_close(hfile);
return nil;
}
// read resource
unsigned char cmtLen;
size_t readBytes;
res_read(rfile, 'FCMT', cmtID, &cmtLen, 0, 1, &readBytes, NULL);
if (readBytes == 0) {
res_close(rfile);
hfs_close(hfile);
return nil;
}
char cmtBytes[256];
res_read(rfile, 'FCMT', cmtID, cmtBytes, 1, cmtLen, &readBytes, NULL);
cmtBytes[cmtLen] = '\0';
comment = [NSString stringWithCString:cmtBytes encoding:NSMacOSRomanStringEncoding];
// close
res_close(rfile);
hfs_close(hfile);
return comment;
}
- (UIImage *)iconFromHFSVolumeIcon:(hfsvol *)vol {
UIImage *icon = nil;
hfsvolent vent;
if (hfs_vstat(vol, &vent)) {
return nil;
}
// open icon file
hfs_chdir(vol, vent.name);
hfsfile *hfile = NULL;
RFILE *rfile = NULL;
hfile = hfs_open(vol, "Icon\x0D");
if (hfile == NULL) {
res_close(rfile);
return nil;
}
hfs_setfork(hfile, 1);
rfile = res_open_funcs(hfile, (res_seek_func)hfs_seek, (res_read_func)hfs_read);
if (rfile == NULL) {
if (hfile) {
hfs_close(hfile);
}
return nil;
}
// read icon family
NSDictionary *iconFamily = [self iconFamilyID:-16455 inResourceFile:rfile];
// create image
CGImageRef iconImage = [self iconImageFromFamily:iconFamily];
if (iconImage) {
icon = [UIImage imageWithCGImage:iconImage];
CGImageRelease(iconImage);
}
res_close(rfile);
if (hfile) {
hfs_close(hfile);
}
return icon;
}
#pragma mark - App Selection
- (BOOL)chooseApp:(NSString *)appName inVolume:(NSString *)volName hint:(NSString *)hint {
return ([appName hasPrefix:volName] ||
[volName hasPrefix:appName] ||
[volName isEqualToString:appName] ||
[appName isEqualToString:hint]);
}
#pragma mark - Resource Access
- (CGImageRef)appIconForResourceFile:(RFILE *)rfile creator:(OSType)creator {
// load bundle
size_t numBundles;
ResAttr *bundles = res_list(rfile, 'BNDL', NULL, 0, 0, &numBundles, NULL);
void *bundle = NULL;
if (numBundles == 0 || bundles == NULL) {
return nil;
}
for (int i = 0; i < numBundles; i++) {
bundle = res_read(rfile, 'BNDL', bundles[i].ID, NULL, 0, 0, NULL, NULL);
if (bundle == NULL || ntohl(*(OSType *)bundle) == creator) {
break;
}
free(bundle);
bundle = NULL;
}
free(bundles);
if (bundle == NULL) {
return nil;
}
// read bundle
int iconID = [self iconFamilyIDForType:'APPL' inBundle:bundle inResourceFile:rfile];
free(bundle);
if (iconID == NSNotFound) {
return nil;
}
// read icon family
NSDictionary *iconFamily = [self iconFamilyID:iconID inResourceFile:rfile];
// create image
return [self iconImageFromFamily:iconFamily];
}
- (NSDictionary *)iconFamilyID:(int16_t)famID inResourceFile:(RFILE *)rfile {
NSMutableDictionary *iconFamily = [NSMutableDictionary dictionaryWithCapacity:6];
NSData *iconData, *maskData;
void *iconRsrc;
size_t resSize;
// separate resources
const uint32_t iconResourceTypes[] = {'ICN#', 'icl4', 'icl8', 'ics#', 'ics4', 'ics8', 0};
for (int i = 0; iconResourceTypes[i]; i++) {
iconRsrc = res_read(rfile, iconResourceTypes[i], famID, NULL, 0, 0, &resSize, NULL);
if (iconRsrc == NULL) {
continue;
}
[iconFamily setObject:[NSData dataWithBytes:iconRsrc length:resSize] forKey:[NSString stringWithFormat:@"%c%c%c%c", TYPECHARS(iconResourceTypes[i])]];
free(iconRsrc);
}
// mask pseudo-resources
if ((iconData = [iconFamily objectForKey:@"ICN#"])) {
maskData = [iconData subdataWithRange:NSMakeRange(0x80, 0x80)];
[iconFamily setObject:maskData forKey:@"IMK#"];
}
if ((iconData = [iconFamily objectForKey:@"ics#"])) {
maskData = [iconData subdataWithRange:NSMakeRange(0x20, 0x20)];
[iconFamily setObject:maskData forKey:@"imk#"];
}
return iconFamily;
}
- (int)iconFamilyIDForType:(OSType)type inBundle:(void *)bndl inResourceFile:(RFILE *)rfile {
short numIconFamilies = RSHORT(bndl, 0x0C) + 1;
short *iconFamily = (short *)(bndl + 0x0E);
short numFileRefs = RSHORT(bndl, (numIconFamilies * 4) + 0x12) + 1;
short *fileRef = (short *)(bndl + (numIconFamilies * 4) + 0x14);
// find FREF for APPL type
short localIconID;
void *FREF = NULL;
for (int i = 0; i < 2 * numFileRefs; i += 2) {
FREF = res_read(rfile, 'FREF', (int)ntohs(fileRef[i + 1]), NULL, 0, 0, NULL, NULL);
if (FREF == NULL || RLONG(FREF, 0) == 'APPL') {
break;
}
free(FREF);
FREF = NULL;
}
if (FREF == NULL) {
return NSNotFound;
}
// read FREF
localIconID = RSHORT(FREF, 4);
free(FREF);
// find resource ID for local ID
for (int i = 0; i < 2 * numIconFamilies; i += 2) {
if (ntohs(iconFamily[i]) == localIconID) {
return (int)ntohs(iconFamily[i + 1]);
}
}
return NSNotFound;
}
- (CGImageRef)iconImageFromFamily:(NSDictionary *)iconFamily {
NSData *iconData, *iconMask;
if ((iconMask = [iconFamily objectForKey:@"IMK#"])) {
// has large mask, find best large icon
if ((iconData = [iconFamily objectForKey:@"icl8"])) {
return [self iconImageWithData:iconData mask:iconMask size:32 depth:8];
} else if ((iconData = [iconFamily objectForKey:@"icl4"])) {
return [self iconImageWithData:iconData mask:iconMask size:32 depth:4];
} else {
iconData = [iconFamily objectForKey:@"ICN#"];
}
return [self iconImageWithData:iconData mask:iconMask size:32 depth:1];
} else if ((iconMask = [iconFamily objectForKey:@"imk#"])) {
// has small mask, find best small icon
if ((iconData = [iconFamily objectForKey:@"ics8"])) {
return [self iconImageWithData:iconData mask:iconMask size:32 depth:8];
} else if ((iconData = [iconFamily objectForKey:@"ics4"])) {
return [self iconImageWithData:iconData mask:iconMask size:32 depth:4];
} else {
iconData = [iconFamily objectForKey:@"ics#"];
}
return [self iconImageWithData:iconData mask:iconMask size:32 depth:1];
}
return NULL;
}
- (CGImageRef)iconImageWithData:(NSData *)iconData mask:(NSData *)iconMask size:(int)size depth:(int)depth {
if (iconData == nil || iconMask == nil) {
return NULL;
}
// convert to ARGB
#define _iSETPIXELRGB(px, py, sa, srgb) \
data[(4 * (px + (py * size))) + 0] = sa; \
data[(4 * (px + (py * size))) + 1] = ((srgb >> 16) & 0xFF); \
data[(4 * (px + (py * size))) + 2] = ((srgb >> 8) & 0xFF); \
data[(4 * (px + (py * size))) + 3] = (srgb & 0xFF)
CFMutableDataRef pixels = CFDataCreateMutable(kCFAllocatorDefault, 4 * size * size);
CFDataSetLength(pixels, 4 * size * size);
unsigned char *data = CFDataGetMutableBytePtr(pixels);
const unsigned char *pixelData = [iconData bytes];
const unsigned char *maskData = [iconMask bytes];
int m, mxy, pxy, rgb;
if (pixels == NULL) {
return NULL;
}
switch (depth) {
case 1:
// 1-bit
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
mxy = pxy = (y * (size / 8)) + (x / 8);
m = ((maskData[mxy] >> (7 - (x % 8))) & 0x01) ? 0xFF : 0x00;
rgb = ctb1[((pixelData[pxy] >> (7 - (x % 8))) & 0x01)];
_iSETPIXELRGB(x, y, m, rgb);
}
}
break;
case 4:
// 4-bit
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
mxy = (y * (size / 8)) + (x / 8);
pxy = (y * (size / 2)) + (x / 2);
m = ((maskData[mxy] >> (7 - (x % 8))) & 0x01) ? 0xFF : 0x00;
rgb = ctb4[(pixelData[pxy] >> 4 * (1 - x % 2)) & 0x0F];
_iSETPIXELRGB(x, y, m, rgb);
}
}
break;
case 8:
// 8-bit
for (int y = 0; y < size; y++) {
for (int x = 0; x < size; x++) {
mxy = (y * (size / 8)) + (x / 8);
pxy = (y * size) + x;
m = ((maskData[mxy] >> (7 - (x % 8))) & 0x01) ? 0xFF : 0x00;
rgb = ctb8[pixelData[pxy]];
_iSETPIXELRGB(x, y, m, rgb);
}
}
break;
}
// create image
CGDataProviderRef provider = CGDataProviderCreateWithCFData(pixels);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGImageRef image = CGImageCreate(size, size, 8, 32, size * 4, colorSpace, kCGImageAlphaFirst | kCGBitmapByteOrder32Big, provider, NULL, false, kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);
CFRelease(pixels);
return image;
}
@end

38
libhfs/acconfig.h Normal file
View File

@ -0,0 +1,38 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: acconfig.h,v 1.5 1998/04/11 08:27:11 rob Exp $
*/
/*****************************************************************************
* Definitions selected automatically by `configure' *
*****************************************************************************/
@TOP@
/* Define if you want to enable diagnostic debugging support. */
#undef DEBUG
@BOTTOM@
/*****************************************************************************
* End of automatically configured definitions *
*****************************************************************************/
# ifdef DEBUG
# include <stdio.h>
# endif

280
libhfs/apple.h Normal file
View File

@ -0,0 +1,280 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: apple.h,v 1.1 1998/04/11 08:27:11 rob Exp $
*/
#if defined(__APPLE__)
#define Str15 _Str15
#define Str31 _Str31
#define OSType _OSType
#define Rect _Rect
#define Point _Point
#endif
typedef signed char Char;
typedef unsigned char UChar;
typedef signed char SignedByte;
typedef signed short Integer;
typedef unsigned short UInteger;
typedef signed long LongInt;
typedef unsigned long ULongInt;
typedef char Str15[16];
typedef char Str31[32];
typedef long OSType;
typedef struct {
Integer sbSig; /* device signature (should be 0x4552) */
Integer sbBlkSize; /* block size of the device (in bytes) */
LongInt sbBlkCount; /* number of blocks on the device */
Integer sbDevType; /* reserved */
Integer sbDevId; /* reserved */
LongInt sbData; /* reserved */
Integer sbDrvrCount; /* number of driver descriptor entries */
LongInt ddBlock; /* first driver's starting block */
Integer ddSize; /* size of the driver, in 512-byte blocks */
Integer ddType; /* driver operating system type (MacOS = 1) */
Integer ddPad[243]; /* additional drivers, if any */
} Block0;
typedef struct {
Integer pmSig; /* partition signature (0x504d or 0x5453) */
Integer pmSigPad; /* reserved */
LongInt pmMapBlkCnt; /* number of blocks in partition map */
LongInt pmPyPartStart; /* first physical block of partition */
LongInt pmPartBlkCnt; /* number of blocks in partition */
Char pmPartName[33]; /* partition name */
Char pmParType[33]; /* partition type */
LongInt pmLgDataStart; /* first logical block of data area */
LongInt pmDataCnt; /* number of blocks in data area */
LongInt pmPartStatus; /* partition status information */
LongInt pmLgBootStart; /* first logical block of boot code */
LongInt pmBootSize; /* size of boot code, in bytes */
LongInt pmBootAddr; /* boot code load address */
LongInt pmBootAddr2; /* reserved */
LongInt pmBootEntry; /* boot code entry point */
LongInt pmBootEntry2; /* reserved */
LongInt pmBootCksum; /* boot code checksum */
Char pmProcessor[17];/* processor type */
Integer pmPad[188]; /* reserved */
} Partition;
typedef struct {
Integer bbID; /* boot blocks signature */
LongInt bbEntry; /* entry point to boot code */
Integer bbVersion; /* boot blocks version number */
Integer bbPageFlags; /* used internally */
Str15 bbSysName; /* System filename */
Str15 bbShellName; /* Finder filename */
Str15 bbDbg1Name; /* debugger filename */
Str15 bbDbg2Name; /* debugger filename */
Str15 bbScreenName; /* name of startup screen */
Str15 bbHelloName; /* name of startup program */
Str15 bbScrapName; /* name of system scrap file */
Integer bbCntFCBs; /* number of FCBs to allocate */
Integer bbCntEvts; /* number of event queue elements */
LongInt bb128KSHeap; /* system heap size on 128K Mac */
LongInt bb256KSHeap; /* used internally */
LongInt bbSysHeapSize; /* system heap size on all machines */
Integer filler; /* reserved */
LongInt bbSysHeapExtra; /* additional system heap space */
LongInt bbSysHeapFract; /* fraction of RAM for system heap */
} BootBlkHdr;
typedef struct {
UInteger xdrStABN; /* first allocation block */
UInteger xdrNumABlks; /* number of allocation blocks */
} ExtDescriptor;
typedef ExtDescriptor ExtDataRec[3];
typedef struct {
SignedByte xkrKeyLen; /* key length */
SignedByte xkrFkType; /* fork type (0x00/0xff == data/resource */
ULongInt xkrFNum; /* file number */
UInteger xkrFABN; /* starting file allocation block */
} ExtKeyRec;
typedef struct {
SignedByte ckrKeyLen; /* key length */
SignedByte ckrResrv1; /* reserved */
ULongInt ckrParID; /* parent directory ID */
Str31 ckrCName; /* catalog node name */
} CatKeyRec;
typedef struct {
Integer v; /* vertical coordinate */
Integer h; /* horizontal coordinate */
} Point;
typedef struct {
Integer top; /* top edge of rectangle */
Integer left; /* left edge */
Integer bottom; /* bottom edge */
Integer right; /* right edge */
} Rect;
typedef struct {
Rect frRect; /* folder's rectangle */
Integer frFlags; /* flags */
Point frLocation; /* folder's location */
Integer frView; /* folder's view */
} DInfo;
typedef struct {
Point frScroll; /* scroll position */
LongInt frOpenChain; /* directory ID chain of open folders */
Integer frUnused; /* reserved */
Integer frComment; /* comment ID */
LongInt frPutAway; /* directory ID */
} DXInfo;
typedef struct {
OSType fdType; /* file type */
OSType fdCreator; /* file's creator */
Integer fdFlags; /* flags */
Point fdLocation; /* file's location */
Integer fdFldr; /* file's window */
} FInfo;
typedef struct {
Integer fdIconID; /* icon ID */
Integer fdUnused[4]; /* reserved */
Integer fdComment; /* comment ID */
LongInt fdPutAway; /* home directory ID */
} FXInfo;
typedef struct {
Integer drSigWord; /* volume signature (0x4244 for HFS) */
LongInt drCrDate; /* date and time of volume creation */
LongInt drLsMod; /* date and time of last modification */
Integer drAtrb; /* volume attributes */
UInteger drNmFls; /* number of files in root directory */
UInteger drVBMSt; /* first block of volume bit map (always 3) */
UInteger drAllocPtr; /* start of next allocation search */
UInteger drNmAlBlks; /* number of allocation blocks in volume */
ULongInt drAlBlkSiz; /* size (in bytes) of allocation blocks */
ULongInt drClpSiz; /* default clump size */
UInteger drAlBlSt; /* first allocation block in volume */
LongInt drNxtCNID; /* next unused catalog node ID (dir/file ID) */
UInteger drFreeBks; /* number of unused allocation blocks */
char drVN[28]; /* volume name (1-27 chars) */
LongInt drVolBkUp; /* date and time of last backup */
Integer drVSeqNum; /* volume backup sequence number */
ULongInt drWrCnt; /* volume write count */
ULongInt drXTClpSiz; /* clump size for extents overflow file */
ULongInt drCTClpSiz; /* clump size for catalog file */
UInteger drNmRtDirs; /* number of directories in root directory */
ULongInt drFilCnt; /* number of files in volume */
ULongInt drDirCnt; /* number of directories in volume */
LongInt drFndrInfo[8]; /* information used by the Finder */
UInteger drEmbedSigWord; /* type of embedded volume */
ExtDescriptor drEmbedExtent; /* location of embedded volume */
ULongInt drXTFlSize; /* size (in bytes) of extents overflow file */
ExtDataRec drXTExtRec; /* first extent record for extents file */
ULongInt drCTFlSize; /* size (in bytes) of catalog file */
ExtDataRec drCTExtRec; /* first extent record for catalog file */
} MDB;
typedef enum {
cdrDirRec = 1,
cdrFilRec = 2,
cdrThdRec = 3,
cdrFThdRec = 4
} CatDataType;
typedef struct {
SignedByte cdrType; /* record type */
SignedByte cdrResrv2; /* reserved */
union {
struct { /* cdrDirRec */
Integer dirFlags; /* directory flags */
UInteger dirVal; /* directory valence */
ULongInt dirDirID; /* directory ID */
LongInt dirCrDat; /* date and time of creation */
LongInt dirMdDat; /* date and time of last modification */
LongInt dirBkDat; /* date and time of last backup */
DInfo dirUsrInfo; /* Finder information */
DXInfo dirFndrInfo; /* additional Finder information */
LongInt dirResrv[4]; /* reserved */
} dir;
struct { /* cdrFilRec */
SignedByte
filFlags; /* file flags */
SignedByte
filTyp; /* file type */
FInfo filUsrWds; /* Finder information */
ULongInt filFlNum; /* file ID */
UInteger filStBlk; /* first alloc block of data fork */
ULongInt filLgLen; /* logical EOF of data fork */
ULongInt filPyLen; /* physical EOF of data fork */
UInteger filRStBlk; /* first alloc block of resource fork */
ULongInt filRLgLen; /* logical EOF of resource fork */
ULongInt filRPyLen; /* physical EOF of resource fork */
LongInt filCrDat; /* date and time of creation */
LongInt filMdDat; /* date and time of last modification */
LongInt filBkDat; /* date and time of last backup */
FXInfo filFndrInfo; /* additional Finder information */
UInteger filClpSize; /* file clump size */
ExtDataRec
filExtRec; /* first data fork extent record */
ExtDataRec
filRExtRec; /* first resource fork extent record */
LongInt filResrv; /* reserved */
} fil;
struct { /* cdrThdRec */
LongInt thdResrv[2]; /* reserved */
ULongInt thdParID; /* parent ID for this directory */
Str31 thdCName; /* name of this directory */
} dthd;
struct { /* cdrFThdRec */
LongInt fthdResrv[2]; /* reserved */
ULongInt fthdParID; /* parent ID for this file */
Str31 fthdCName; /* name of this file */
} fthd;
} u;
} CatDataRec;
typedef struct {
ULongInt ndFLink; /* forward link */
ULongInt ndBLink; /* backward link */
SignedByte ndType; /* node type */
SignedByte ndNHeight; /* node level */
UInteger ndNRecs; /* number of records in node */
Integer ndResv2; /* reserved */
} NodeDescriptor;
enum {
ndIndxNode = (SignedByte) 0x00,
ndHdrNode = (SignedByte) 0x01,
ndMapNode = (SignedByte) 0x02,
ndLeafNode = (SignedByte) 0xff
};
typedef struct {
UInteger bthDepth; /* current depth of tree */
ULongInt bthRoot; /* number of root node */
ULongInt bthNRecs; /* number of leaf records in tree */
ULongInt bthFNode; /* number of first leaf node */
ULongInt bthLNode; /* number of last leaf node */
UInteger bthNodeSize; /* size of a node */
UInteger bthKeyLen; /* maximum length of a key */
ULongInt bthNNodes; /* total number of nodes in tree */
ULongInt bthFree; /* number of free nodes */
SignedByte bthResv[76]; /* reserved */
} BTHdrRec;

807
libhfs/block.c Normal file
View File

@ -0,0 +1,807 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: block.c,v 1.11 1998/11/02 22:08:52 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include "libhfs.h"
# include "volume.h"
# include "block.h"
# include "os.h"
# define INUSE(b) ((b)->flags & HFS_BUCKET_INUSE)
# define DIRTY(b) ((b)->flags & HFS_BUCKET_DIRTY)
/*
* NAME: block->init()
* DESCRIPTION: initialize a volume's block cache
*/
int b_init(hfsvol *vol)
{
bcache *cache;
int i;
ASSERT(vol->cache == 0);
cache = ALLOC(bcache, 1);
if (cache == 0)
ERROR(ENOMEM, 0);
vol->cache = cache;
cache->vol = vol;
cache->tail = &cache->chain[HFS_CACHESZ - 1];
cache->hits = 0;
cache->misses = 0;
for (i = 0; i < HFS_CACHESZ; ++i)
{
bucket *b = &cache->chain[i];
b->flags = 0;
b->count = 0;
b->bnum = 0;
b->data = &cache->pool[i];
b->cnext = b + 1;
b->cprev = b - 1;
b->hnext = 0;
b->hprev = 0;
}
cache->chain[0].cprev = cache->tail;
cache->tail->cnext = &cache->chain[0];
for (i = 0; i < HFS_HASHSZ; ++i)
cache->hash[i] = 0;
return 0;
fail:
return -1;
}
# ifdef DEBUG
/*
* NAME: block->showstats()
* DESCRIPTION: output cache hit/miss ratio
*/
void b_showstats(const bcache *cache)
{
fprintf(stderr, "BLOCK: CACHE vol 0x%lx \"%s\" hit/miss ratio = %.3f\n",
(unsigned long) cache->vol, cache->vol->mdb.drVN,
(float) cache->hits / (float) cache->misses);
}
/*
* NAME: block->dumpcache()
* DESCRIPTION: dump the cache tables for a volume
*/
void b_dumpcache(const bcache *cache)
{
const bucket *b;
int i;
fprintf(stderr, "BLOCK CACHE DUMP:\n");
for (i = 0, b = cache->tail->cnext; i < HFS_CACHESZ; ++i, b = b->cnext)
{
if (INUSE(b))
{
fprintf(stderr, "\t %lu", b->bnum);
if (DIRTY(b))
fprintf(stderr, "*");
fprintf(stderr, ":%u", b->count);
}
}
fprintf(stderr, "\n");
fprintf(stderr, "BLOCK HASH DUMP:\n");
for (i = 0; i < HFS_HASHSZ; ++i)
{
int seen = 0;
for (b = cache->hash[i]; b; b = b->hnext)
{
if (! seen)
fprintf(stderr, " %d:", i);
if (INUSE(b))
{
fprintf(stderr, " %lu", b->bnum);
if (DIRTY(b))
fprintf(stderr, "*");
fprintf(stderr, ":%u", b->count);
}
seen = 1;
}
if (seen)
fprintf(stderr, "\n");
}
}
# endif
/*
* NAME: fillchain()
* DESCRIPTION: fill a chain of bucket buffers with a single read
*/
static
int fillchain(hfsvol *vol, bucket **bptr, unsigned int *count)
{
bucket *blist[HFS_BLOCKBUFSZ], **start = bptr;
unsigned long bnum;
unsigned int len, i;
for (len = 0; len < HFS_BLOCKBUFSZ &&
(unsigned int) (bptr - start) < *count; ++bptr)
{
if (INUSE(*bptr))
continue;
if (len > 0 && (*bptr)->bnum != bnum)
break;
blist[len++] = *bptr;
bnum = (*bptr)->bnum + 1;
}
*count = bptr - start;
if (len == 0)
goto done;
else if (len == 1)
{
if (b_readpb(vol, vol->vstart + blist[0]->bnum,
blist[0]->data, 1) == -1)
goto fail;
}
else
{
block buffer[HFS_BLOCKBUFSZ];
if (b_readpb(vol, vol->vstart + blist[0]->bnum, buffer, len) == -1)
goto fail;
for (i = 0; i < len; ++i)
memcpy(blist[i]->data, buffer[i], HFS_BLOCKSZ);
}
for (i = 0; i < len; ++i)
{
blist[i]->flags |= HFS_BUCKET_INUSE;
blist[i]->flags &= ~HFS_BUCKET_DIRTY;
}
done:
return 0;
fail:
return -1;
}
/*
* NAME: flushchain()
* DESCRIPTION: store a chain of bucket buffers with a single write
*/
static
int flushchain(hfsvol *vol, bucket **bptr, unsigned int *count)
{
bucket *blist[HFS_BLOCKBUFSZ], **start = bptr;
unsigned long bnum;
unsigned int len, i;
for (len = 0; len < HFS_BLOCKBUFSZ &&
(unsigned int) (bptr - start) < *count; ++bptr)
{
if (! INUSE(*bptr) || ! DIRTY(*bptr))
continue;
if (len > 0 && (*bptr)->bnum != bnum)
break;
blist[len++] = *bptr;
bnum = (*bptr)->bnum + 1;
}
*count = bptr - start;
if (len == 0)
goto done;
else if (len == 1)
{
if (b_writepb(vol, vol->vstart + blist[0]->bnum,
blist[0]->data, 1) == -1)
goto fail;
}
else
{
block buffer[HFS_BLOCKBUFSZ];
for (i = 0; i < len; ++i)
memcpy(buffer[i], blist[i]->data, HFS_BLOCKSZ);
if (b_writepb(vol, vol->vstart + blist[0]->bnum, buffer, len) == -1)
goto fail;
}
for (i = 0; i < len; ++i)
blist[i]->flags &= ~HFS_BUCKET_DIRTY;
done:
return 0;
fail:
return -1;
}
/*
* NAME: compare()
* DESCRIPTION: comparison function for qsort of cache bucket pointers
*/
static
int compare(const bucket **b1, const bucket **b2)
{
long diff;
diff = (*b1)->bnum - (*b2)->bnum;
if (diff < 0)
return -1;
else if (diff > 0)
return 1;
else
return 0;
}
/*
* NAME: dobuckets()
* DESCRIPTION: fill or flush an array of cache buckets to a volume
*/
static
int dobuckets(hfsvol *vol, bucket **chain, unsigned int len,
int (*func)(hfsvol *, bucket **, unsigned int *))
{
unsigned int count, i;
int result = 0;
qsort(chain, len, sizeof(*chain),
(int (*)(const void *, const void *)) compare);
for (i = 0; i < len; i += count)
{
count = len - i;
if (func(vol, chain + i, &count) == -1)
result = -1;
}
return result;
}
# define fillbuckets(vol, chain, len) dobuckets(vol, chain, len, fillchain)
# define flushbuckets(vol, chain, len) dobuckets(vol, chain, len, flushchain)
/*
* NAME: block->flush()
* DESCRIPTION: commit dirty cache blocks to a volume
*/
int b_flush(hfsvol *vol)
{
bcache *cache = vol->cache;
bucket *chain[HFS_CACHESZ];
int i;
if (cache == 0 || (vol->flags & HFS_VOL_READONLY))
goto done;
for (i = 0; i < HFS_CACHESZ; ++i)
chain[i] = &cache->chain[i];
if (flushbuckets(vol, chain, HFS_CACHESZ) == -1)
goto fail;
done:
# ifdef DEBUG
if (cache)
b_showstats(cache);
# endif
return 0;
fail:
return -1;
}
/*
* NAME: block->finish()
* DESCRIPTION: commit and free a volume's block cache
*/
int b_finish(hfsvol *vol)
{
int result = 0;
if (vol->cache == 0)
goto done;
# ifdef DEBUG
b_dumpcache(vol->cache);
# endif
result = b_flush(vol);
FREE(vol->cache);
vol->cache = 0;
done:
return result;
}
/*
* NAME: findbucket()
* DESCRIPTION: locate a bucket in the cache, and/or its hash slot
*/
static
bucket *findbucket(bcache *cache, unsigned long bnum, bucket ***hslot)
{
bucket *b;
*hslot = &cache->hash[bnum & (HFS_HASHSZ - 1)];
for (b = **hslot; b; b = b->hnext)
{
if (INUSE(b) && b->bnum == bnum)
break;
}
return b;
}
/*
* NAME: reuse()
* DESCRIPTION: free a bucket for reuse, flushing if necessary
*/
static
int reuse(bcache *cache, bucket *b, unsigned long bnum)
{
bucket *chain[HFS_BLOCKBUFSZ], *bptr;
int i;
# ifdef DEBUG
if (INUSE(b))
fprintf(stderr, "BLOCK: CACHE reusing bucket containing "
"vol 0x%lx block %lu:%u\n",
(unsigned long) cache->vol, b->bnum, b->count);
# endif
if (INUSE(b) && DIRTY(b))
{
/* flush most recently unused buckets */
for (bptr = b, i = 0; i < HFS_BLOCKBUFSZ; ++i)
{
chain[i] = bptr;
bptr = bptr->cprev;
}
if (flushbuckets(cache->vol, chain, HFS_BLOCKBUFSZ) == -1)
goto fail;
}
b->flags &= ~HFS_BUCKET_INUSE;
b->count = 1;
b->bnum = bnum;
return 0;
fail:
return -1;
}
/*
* NAME: cplace()
* DESCRIPTION: move a bucket to an appropriate place near head of the chain
*/
static
void cplace(bcache *cache, bucket *b)
{
bucket *p;
for (p = cache->tail->cnext; p->count > 1; p = p->cnext)
--p->count;
b->cnext->cprev = b->cprev;
b->cprev->cnext = b->cnext;
if (cache->tail == b)
cache->tail = b->cprev;
b->cprev = p->cprev;
b->cnext = p;
p->cprev->cnext = b;
p->cprev = b;
}
/*
* NAME: hplace()
* DESCRIPTION: move a bucket to the head of its hash slot
*/
static
void hplace(bucket **hslot, bucket *b)
{
if (*hslot != b)
{
if (b->hprev)
*b->hprev = b->hnext;
if (b->hnext)
b->hnext->hprev = b->hprev;
b->hprev = hslot;
b->hnext = *hslot;
if (*hslot)
(*hslot)->hprev = &b->hnext;
*hslot = b;
}
}
/*
* NAME: getbucket()
* DESCRIPTION: fetch a bucket from the cache, or an empty one to be filled
*/
static
bucket *getbucket(bcache *cache, unsigned long bnum, int fill)
{
bucket **hslot, *b, *p, *bptr,
*chain[HFS_BLOCKBUFSZ], **slots[HFS_BLOCKBUFSZ];
b = findbucket(cache, bnum, &hslot);
if (b)
{
/* cache hit; move towards head of cache chain */
++cache->hits;
if (++b->count > b->cprev->count &&
b != cache->tail->cnext)
{
p = b->cprev;
p->cprev->cnext = b;
b->cnext->cprev = p;
p->cnext = b->cnext;
b->cprev = p->cprev;
p->cprev = b;
b->cnext = p;
if (cache->tail == b)
cache->tail = p;
}
}
else
{
/* cache miss; reuse least-used cache bucket */
++cache->misses;
b = cache->tail;
if (reuse(cache, b, bnum) == -1)
goto fail;
if (fill)
{
unsigned int len = 0;
chain[len] = b;
slots[len++] = hslot;
for (bptr = b->cprev;
len < (HFS_BLOCKBUFSZ >> 1) && ++bnum < cache->vol->vlen;
bptr = bptr->cprev)
{
if (findbucket(cache, bnum, &hslot))
break;
if (reuse(cache, bptr, bnum) == -1)
goto fail;
chain[len] = bptr;
slots[len++] = hslot;
}
if (fillbuckets(cache->vol, chain, len) == -1)
goto fail;
while (--len)
{
cplace(cache, chain[len]);
hplace(slots[len], chain[len]);
}
hslot = slots[0];
}
/* move bucket to appropriate place in chain */
cplace(cache, b);
}
/* insert at front of hash chain */
hplace(hslot, b);
return b;
fail:
return 0;
}
/*
* NAME: block->readpb()
* DESCRIPTION: read blocks from the physical medium (bypassing cache)
*/
int b_readpb(hfsvol *vol, unsigned long bnum, block *bp, unsigned int blen)
{
unsigned long nblocks;
# ifdef DEBUG
fprintf(stderr, "BLOCK: READ vol 0x%lx block %lu",
(unsigned long) vol, bnum);
if (blen > 1)
fprintf(stderr, "+%u[..%lu]\n", blen - 1, bnum + blen - 1);
else
fprintf(stderr, "\n");
# endif
nblocks = os_seek(&vol->priv, vol->base, bnum);
if (nblocks == (unsigned long) -1)
goto fail;
if (nblocks != bnum)
ERROR(EIO, "block seek failed for read");
nblocks = os_read(&vol->priv, bp, blen);
if (nblocks == (unsigned long) -1)
goto fail;
if (nblocks != blen)
ERROR(EIO, "incomplete block read");
return 0;
fail:
return -1;
}
/*
* NAME: block->writepb()
* DESCRIPTION: write blocks to the physical medium (bypassing cache)
*/
int b_writepb(hfsvol *vol, unsigned long bnum, const block *bp,
unsigned int blen)
{
unsigned long nblocks;
# ifdef DEBUG
fprintf(stderr, "BLOCK: WRITE vol 0x%lx block %lu",
(unsigned long) vol, bnum);
if (blen > 1)
fprintf(stderr, "+%u[..%lu]\n", blen - 1, bnum + blen - 1);
else
fprintf(stderr, "\n");
# endif
nblocks = os_seek(&vol->priv, vol->base, bnum);
if (nblocks == (unsigned long) -1)
goto fail;
if (nblocks != bnum)
ERROR(EIO, "block seek failed for write");
nblocks = os_write(&vol->priv, bp, blen);
if (nblocks == (unsigned long) -1)
goto fail;
if (nblocks != blen)
ERROR(EIO, "incomplete block write");
return 0;
fail:
return -1;
}
/*
* NAME: block->readlb()
* DESCRIPTION: read a logical block from a volume (or from the cache)
*/
int b_readlb(hfsvol *vol, unsigned long bnum, block *bp)
{
if (vol->vlen > 0 && bnum >= vol->vlen)
ERROR(EIO, "read nonexistent logical block");
if (vol->cache)
{
bucket *b;
b = getbucket(vol->cache, bnum, 1);
if (b == 0)
goto fail;
memcpy(bp, b->data, HFS_BLOCKSZ);
}
else
{
if (b_readpb(vol, vol->vstart + bnum, bp, 1) == -1)
goto fail;
}
return 0;
fail:
return -1;
}
/*
* NAME: block->writelb()
* DESCRIPTION: write a logical block to a volume (or to the cache)
*/
int b_writelb(hfsvol *vol, unsigned long bnum, const block *bp)
{
if (vol->vlen > 0 && bnum >= vol->vlen)
ERROR(EIO, "write nonexistent logical block");
if (vol->cache)
{
bucket *b;
b = getbucket(vol->cache, bnum, 0);
if (b == 0)
goto fail;
if (! INUSE(b) ||
memcmp(b->data, bp, HFS_BLOCKSZ) != 0)
{
memcpy(b->data, bp, HFS_BLOCKSZ);
b->flags |= HFS_BUCKET_INUSE | HFS_BUCKET_DIRTY;
}
}
else
{
if (b_writepb(vol, vol->vstart + bnum, bp, 1) == -1)
goto fail;
}
return 0;
fail:
return -1;
}
/*
* NAME: block->readab()
* DESCRIPTION: read a block from an allocation block from a volume
*/
int b_readab(hfsvol *vol, unsigned int anum, unsigned int index, block *bp)
{
/* verify the allocation block exists and is marked as in-use */
if (anum >= vol->mdb.drNmAlBlks)
ERROR(EIO, "read nonexistent allocation block");
else if (vol->vbm && ! BMTST(vol->vbm, anum))
ERROR(EIO, "read unallocated block");
return b_readlb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + index, bp);
fail:
return -1;
}
/*
* NAME: block->writeab()
* DESCRIPTION: write a block to an allocation block to a volume
*/
int b_writeab(hfsvol *vol,
unsigned int anum, unsigned int index, const block *bp)
{
/* verify the allocation block exists and is marked as in-use */
if (anum >= vol->mdb.drNmAlBlks)
ERROR(EIO, "write nonexistent allocation block");
else if (vol->vbm && ! BMTST(vol->vbm, anum))
ERROR(EIO, "write unallocated block");
if (v_dirty(vol) == -1)
goto fail;
return b_writelb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + index, bp);
fail:
return -1;
}
/*
* NAME: block->size()
* DESCRIPTION: return the number of physical blocks on a volume's medium
*/
unsigned long b_size(hfsvol *vol)
{
unsigned long low, high, mid;
block b;
high = os_seek(&vol->priv, vol->base, -1);
if (high != (unsigned long) -1 && high > 0)
return high;
/* manual size detection: first check there is at least 1 block in medium */
if (b_readpb(vol, 0, &b, 1) == -1)
ERROR(EIO, "size of medium indeterminable or empty");
for (low = 0, high = 2880;
high > 0 && b_readpb(vol, high - 1, &b, 1) != -1;
high <<= 1)
low = high - 1;
if (high == 0)
ERROR(EIO, "size of medium indeterminable or too large");
/* common case: 1440K floppy */
if (low == 2879 && b_readpb(vol, 2880, &b, 1) == -1)
return 2880;
/* binary search for other sizes */
while (low < high - 1)
{
mid = (low + high) >> 1;
if (b_readpb(vol, mid, &b, 1) == -1)
high = mid;
else
low = mid;
}
return low + 1;
fail:
return 0;
}

40
libhfs/block.h Normal file
View File

@ -0,0 +1,40 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: block.h,v 1.10 1998/11/02 22:08:53 rob Exp $
*/
int b_init(hfsvol *);
int b_flush(hfsvol *);
int b_finish(hfsvol *);
int b_readpb(hfsvol *, unsigned long, block *, unsigned int);
int b_writepb(hfsvol *, unsigned long, const block *, unsigned int);
int b_readlb(hfsvol *, unsigned long, block *);
int b_writelb(hfsvol *, unsigned long, const block *);
int b_readab(hfsvol *, unsigned int, unsigned int, block *);
int b_writeab(hfsvol *, unsigned int, unsigned int, const block *);
unsigned long b_size(hfsvol *);
# ifdef DEBUG
void b_showstats(const bcache *);
void b_dumpcache(const bcache *);
# endif

700
libhfs/btree.c Normal file
View File

@ -0,0 +1,700 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: btree.c,v 1.10 1998/11/02 22:08:54 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include "libhfs.h"
# include "btree.h"
# include "data.h"
# include "file.h"
# include "block.h"
# include "node.h"
/*
* NAME: btree->getnode()
* DESCRIPTION: retrieve a numbered node from a B*-tree file
*/
int bt_getnode(node *np, btree *bt, unsigned long nnum)
{
block *bp = &np->data;
const byte *ptr;
int i;
np->bt = bt;
np->nnum = nnum;
# if 0
fprintf(stderr, "BTREE: GET vol \"%s\" btree \"%s\" node %lu\n",
bt->f.vol->mdb.drVN, bt->f.name, np->nnum);
# endif
/* verify the node exists and is marked as in-use */
if (nnum > 0 && nnum >= bt->hdr.bthNNodes)
ERROR(EIO, "read nonexistent b*-tree node");
else if (bt->map && ! BMTST(bt->map, nnum))
ERROR(EIO, "read unallocated b*-tree node");
if (f_getblock(&bt->f, nnum, bp) == -1)
goto fail;
ptr = *bp;
d_fetchul(&ptr, &np->nd.ndFLink);
d_fetchul(&ptr, &np->nd.ndBLink);
d_fetchsb(&ptr, &np->nd.ndType);
d_fetchsb(&ptr, &np->nd.ndNHeight);
d_fetchuw(&ptr, &np->nd.ndNRecs);
d_fetchsw(&ptr, &np->nd.ndResv2);
if (np->nd.ndNRecs > HFS_MAX_NRECS)
ERROR(EIO, "too many b*-tree node records");
i = np->nd.ndNRecs + 1;
ptr = *bp + HFS_BLOCKSZ - (2 * i);
while (i--)
d_fetchuw(&ptr, &np->roff[i]);
return 0;
fail:
return -1;
}
/*
* NAME: btree->putnode()
* DESCRIPTION: store a numbered node into a B*-tree file
*/
int bt_putnode(node *np)
{
btree *bt = np->bt;
block *bp = &np->data;
byte *ptr;
int i;
# if 0
fprintf(stderr, "BTREE: PUT vol \"%s\" btree \"%s\" node %lu\n",
bt->f.vol->mdb.drVN, bt->f.name, np->nnum);
# endif
/* verify the node exists and is marked as in-use */
if (np->nnum > 0 && np->nnum >= bt->hdr.bthNNodes)
ERROR(EIO, "write nonexistent b*-tree node");
else if (bt->map && ! BMTST(bt->map, np->nnum))
ERROR(EIO, "write unallocated b*-tree node");
ptr = *bp;
d_storeul(&ptr, np->nd.ndFLink);
d_storeul(&ptr, np->nd.ndBLink);
d_storesb(&ptr, np->nd.ndType);
d_storesb(&ptr, np->nd.ndNHeight);
d_storeuw(&ptr, np->nd.ndNRecs);
d_storesw(&ptr, np->nd.ndResv2);
if (np->nd.ndNRecs > HFS_MAX_NRECS)
ERROR(EIO, "too many b*-tree node records");
i = np->nd.ndNRecs + 1;
ptr = *bp + HFS_BLOCKSZ - (2 * i);
while (i--)
d_storeuw(&ptr, np->roff[i]);
return f_putblock(&bt->f, np->nnum, bp);
fail:
return -1;
}
/*
* NAME: btree->readhdr()
* DESCRIPTION: read the header node of a B*-tree
*/
int bt_readhdr(btree *bt)
{
const byte *ptr;
byte *map = 0;
int i;
unsigned long nnum;
if (bt_getnode(&bt->hdrnd, bt, 0) == -1)
goto fail;
if (bt->hdrnd.nd.ndType != ndHdrNode ||
bt->hdrnd.nd.ndNRecs != 3 ||
bt->hdrnd.roff[0] != 0x00e ||
bt->hdrnd.roff[1] != 0x078 ||
bt->hdrnd.roff[2] != 0x0f8 ||
bt->hdrnd.roff[3] != 0x1f8)
ERROR(EIO, "malformed b*-tree header node");
/* read header record */
ptr = HFS_NODEREC(bt->hdrnd, 0);
d_fetchuw(&ptr, &bt->hdr.bthDepth);
d_fetchul(&ptr, &bt->hdr.bthRoot);
d_fetchul(&ptr, &bt->hdr.bthNRecs);
d_fetchul(&ptr, &bt->hdr.bthFNode);
d_fetchul(&ptr, &bt->hdr.bthLNode);
d_fetchuw(&ptr, &bt->hdr.bthNodeSize);
d_fetchuw(&ptr, &bt->hdr.bthKeyLen);
d_fetchul(&ptr, &bt->hdr.bthNNodes);
d_fetchul(&ptr, &bt->hdr.bthFree);
for (i = 0; i < 76; ++i)
d_fetchsb(&ptr, &bt->hdr.bthResv[i]);
if (bt->hdr.bthNodeSize != HFS_BLOCKSZ)
ERROR(EINVAL, "unsupported b*-tree node size");
/* read map record; construct btree bitmap */
/* don't set bt->map until we're done, since getnode() checks it */
map = ALLOC(byte, HFS_MAP1SZ);
if (map == 0)
ERROR(ENOMEM, 0);
memcpy(map, HFS_NODEREC(bt->hdrnd, 2), HFS_MAP1SZ);
bt->mapsz = HFS_MAP1SZ;
/* read continuation map records, if any */
nnum = bt->hdrnd.nd.ndFLink;
while (nnum)
{
node n;
byte *newmap;
if (bt_getnode(&n, bt, nnum) == -1)
goto fail;
if (n.nd.ndType != ndMapNode ||
n.nd.ndNRecs != 1 ||
n.roff[0] != 0x00e ||
n.roff[1] != 0x1fa)
ERROR(EIO, "malformed b*-tree map node");
newmap = REALLOC(map, byte, bt->mapsz + HFS_MAPXSZ);
if (newmap == 0)
ERROR(ENOMEM, 0);
map = newmap;
memcpy(map + bt->mapsz, HFS_NODEREC(n, 0), HFS_MAPXSZ);
bt->mapsz += HFS_MAPXSZ;
nnum = n.nd.ndFLink;
}
bt->map = map;
return 0;
fail:
FREE(map);
return -1;
}
/*
* NAME: btree->writehdr()
* DESCRIPTION: write the header node of a B*-tree
*/
int bt_writehdr(btree *bt)
{
byte *ptr, *map;
unsigned long mapsz, nnum;
int i;
ASSERT(bt->hdrnd.bt == bt &&
bt->hdrnd.nnum == 0 &&
bt->hdrnd.nd.ndType == ndHdrNode &&
bt->hdrnd.nd.ndNRecs == 3);
ptr = HFS_NODEREC(bt->hdrnd, 0);
d_storeuw(&ptr, bt->hdr.bthDepth);
d_storeul(&ptr, bt->hdr.bthRoot);
d_storeul(&ptr, bt->hdr.bthNRecs);
d_storeul(&ptr, bt->hdr.bthFNode);
d_storeul(&ptr, bt->hdr.bthLNode);
d_storeuw(&ptr, bt->hdr.bthNodeSize);
d_storeuw(&ptr, bt->hdr.bthKeyLen);
d_storeul(&ptr, bt->hdr.bthNNodes);
d_storeul(&ptr, bt->hdr.bthFree);
for (i = 0; i < 76; ++i)
d_storesb(&ptr, bt->hdr.bthResv[i]);
memcpy(HFS_NODEREC(bt->hdrnd, 2), bt->map, HFS_MAP1SZ);
if (bt_putnode(&bt->hdrnd) == -1)
goto fail;
map = bt->map + HFS_MAP1SZ;
mapsz = bt->mapsz - HFS_MAP1SZ;
nnum = bt->hdrnd.nd.ndFLink;
while (mapsz)
{
node n;
if (nnum == 0)
ERROR(EIO, "truncated b*-tree map");
if (bt_getnode(&n, bt, nnum) == -1)
goto fail;
if (n.nd.ndType != ndMapNode ||
n.nd.ndNRecs != 1 ||
n.roff[0] != 0x00e ||
n.roff[1] != 0x1fa)
ERROR(EIO, "malformed b*-tree map node");
memcpy(HFS_NODEREC(n, 0), map, HFS_MAPXSZ);
if (bt_putnode(&n) == -1)
goto fail;
map += HFS_MAPXSZ;
mapsz -= HFS_MAPXSZ;
nnum = n.nd.ndFLink;
}
bt->flags &= ~HFS_BT_UPDATE_HDR;
return 0;
fail:
return -1;
}
/* High-Level B*-Tree Routines ============================================= */
/*
* NAME: btree->space()
* DESCRIPTION: assert space for new records, or extend the file
*/
int bt_space(btree *bt, unsigned int nrecs)
{
unsigned int nnodes;
long space;
nnodes = nrecs * (bt->hdr.bthDepth + 1);
if (nnodes <= bt->hdr.bthFree)
goto done;
/* make sure the extents tree has room too */
if (bt != &bt->f.vol->ext)
{
if (bt_space(&bt->f.vol->ext, 1) == -1)
goto fail;
}
space = f_alloc(&bt->f);
if (space == -1)
goto fail;
nnodes = space * (bt->f.vol->mdb.drAlBlkSiz / bt->hdr.bthNodeSize);
bt->hdr.bthNNodes += nnodes;
bt->hdr.bthFree += nnodes;
bt->flags |= HFS_BT_UPDATE_HDR;
bt->f.vol->flags |= HFS_VOL_UPDATE_ALTMDB;
while (bt->hdr.bthNNodes > bt->mapsz * 8)
{
byte *newmap;
node mapnd;
/* extend tree map */
newmap = REALLOC(bt->map, byte, bt->mapsz + HFS_MAPXSZ);
if (newmap == 0)
ERROR(ENOMEM, 0);
memset(newmap + bt->mapsz, 0, HFS_MAPXSZ);
bt->map = newmap;
bt->mapsz += HFS_MAPXSZ;
n_init(&mapnd, bt, ndMapNode, 0);
if (n_new(&mapnd) == -1)
goto fail;
mapnd.nd.ndNRecs = 1;
mapnd.roff[1] = 0x1fa;
/* link the new map node */
if (bt->hdrnd.nd.ndFLink == 0)
{
bt->hdrnd.nd.ndFLink = mapnd.nnum;
mapnd.nd.ndBLink = 0;
}
else
{
node n;
unsigned long nnum;
nnum = bt->hdrnd.nd.ndFLink;
while (1)
{
if (bt_getnode(&n, bt, nnum) == -1)
goto fail;
if (n.nd.ndFLink == 0)
break;
nnum = n.nd.ndFLink;
}
n.nd.ndFLink = mapnd.nnum;
mapnd.nd.ndBLink = n.nnum;
if (bt_putnode(&n) == -1)
goto fail;
}
if (bt_putnode(&mapnd) == -1)
goto fail;
}
done:
return 0;
fail:
return -1;
}
/*
* NAME: insertx()
* DESCRIPTION: recursively locate a node and insert a record
*/
static
int insertx(node *np, byte *record, int *reclen)
{
node child;
byte *rec;
int result = 0;
if (n_search(np, record))
ERROR(EIO, "b*-tree record already exists");
switch (np->nd.ndType)
{
case ndIndxNode:
if (np->rnum == -1)
rec = HFS_NODEREC(*np, 0);
else
rec = HFS_NODEREC(*np, np->rnum);
if (bt_getnode(&child, np->bt, d_getul(HFS_RECDATA(rec))) == -1 ||
insertx(&child, record, reclen) == -1)
goto fail;
if (np->rnum == -1)
{
n_index(&child, rec, 0);
if (*reclen == 0)
{
result = bt_putnode(np);
goto done;
}
}
if (*reclen)
result = n_insert(np, record, reclen);
break;
case ndLeafNode:
result = n_insert(np, record, reclen);
break;
default:
ERROR(EIO, "unexpected b*-tree node");
}
done:
return result;
fail:
return -1;
}
/*
* NAME: btree->insert()
* DESCRIPTION: insert a new node record into a tree
*/
int bt_insert(btree *bt, const byte *record, unsigned int reclen)
{
node root;
byte newrec[HFS_MAX_RECLEN];
if (bt->hdr.bthRoot == 0)
{
/* create root node */
n_init(&root, bt, ndLeafNode, 1);
if (n_new(&root) == -1 ||
bt_putnode(&root) == -1)
goto fail;
bt->hdr.bthDepth = 1;
bt->hdr.bthRoot = root.nnum;
bt->hdr.bthFNode = root.nnum;
bt->hdr.bthLNode = root.nnum;
bt->flags |= HFS_BT_UPDATE_HDR;
}
else if (bt_getnode(&root, bt, bt->hdr.bthRoot) == -1)
goto fail;
memcpy(newrec, record, reclen);
if (insertx(&root, newrec, &reclen) == -1)
goto fail;
if (reclen)
{
byte oroot[HFS_MAX_RECLEN];
unsigned int orootlen;
/* root node was split; create a new root */
n_index(&root, oroot, &orootlen);
n_init(&root, bt, ndIndxNode, root.nd.ndNHeight + 1);
if (n_new(&root) == -1)
goto fail;
++bt->hdr.bthDepth;
bt->hdr.bthRoot = root.nnum;
bt->flags |= HFS_BT_UPDATE_HDR;
/* insert index records for new root */
n_search(&root, oroot);
n_insertx(&root, oroot, orootlen);
n_search(&root, newrec);
n_insertx(&root, newrec, reclen);
if (bt_putnode(&root) == -1)
goto fail;
}
++bt->hdr.bthNRecs;
bt->flags |= HFS_BT_UPDATE_HDR;
return 0;
fail:
return -1;
}
/*
* NAME: deletex()
* DESCRIPTION: recursively locate a node and delete a record
*/
static
int deletex(node *np, const byte *key, byte *record, int *flag)
{
node child;
byte *rec;
int found, result = 0;
found = n_search(np, key);
switch (np->nd.ndType)
{
case ndIndxNode:
if (np->rnum == -1)
ERROR(EIO, "b*-tree record not found");
rec = HFS_NODEREC(*np, np->rnum);
if (bt_getnode(&child, np->bt, d_getul(HFS_RECDATA(rec))) == -1 ||
deletex(&child, key, rec, flag) == -1)
goto fail;
if (*flag)
{
*flag = 0;
if (HFS_RECKEYLEN(rec) == 0)
{
result = n_delete(np, record, flag);
break;
}
if (np->rnum == 0)
{
/* propagate index record change into parent */
n_index(np, record, 0);
*flag = 1;
}
result = bt_putnode(np);
}
break;
case ndLeafNode:
if (found == 0)
ERROR(EIO, "b*-tree record not found");
result = n_delete(np, record, flag);
break;
default:
ERROR(EIO, "unexpected b*-tree node");
}
return result;
fail:
return -1;
}
/*
* NAME: btree->delete()
* DESCRIPTION: remove a node record from a tree
*/
int bt_delete(btree *bt, const byte *key)
{
node root;
byte record[HFS_MAX_RECLEN];
int flag = 0;
if (bt->hdr.bthRoot == 0)
ERROR(EIO, "empty b*-tree");
if (bt_getnode(&root, bt, bt->hdr.bthRoot) == -1 ||
deletex(&root, key, record, &flag) == -1)
goto fail;
if (bt->hdr.bthDepth > 1 && root.nd.ndNRecs == 1)
{
const byte *rec;
/* root only has one record; eliminate it and decrease the tree depth */
rec = HFS_NODEREC(root, 0);
--bt->hdr.bthDepth;
bt->hdr.bthRoot = d_getul(HFS_RECDATA(rec));
if (n_free(&root) == -1)
goto fail;
}
else if (bt->hdr.bthDepth == 1 && root.nd.ndNRecs == 0)
{
/* root node was deleted */
bt->hdr.bthDepth = 0;
bt->hdr.bthRoot = 0;
}
--bt->hdr.bthNRecs;
bt->flags |= HFS_BT_UPDATE_HDR;
return 0;
fail:
return -1;
}
/*
* NAME: btree->search()
* DESCRIPTION: locate a data record given a search key
*/
int bt_search(btree *bt, const byte *key, node *np)
{
int found = 0;
unsigned long nnum;
nnum = bt->hdr.bthRoot;
if (nnum == 0)
ERROR(ENOENT, 0);
while (1)
{
const byte *rec;
if (bt_getnode(np, bt, nnum) == -1)
{
found = -1;
goto fail;
}
found = n_search(np, key);
switch (np->nd.ndType)
{
case ndIndxNode:
if (np->rnum == -1)
ERROR(ENOENT, 0);
rec = HFS_NODEREC(*np, np->rnum);
nnum = d_getul(HFS_RECDATA(rec));
break;
case ndLeafNode:
if (! found)
ERROR(ENOENT, 0);
goto done;
default:
found = -1;
ERROR(EIO, "unexpected b*-tree node");
}
}
done:
fail:
return found;
}

33
libhfs/btree.h Normal file
View File

@ -0,0 +1,33 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: btree.h,v 1.8 1998/11/02 22:08:55 rob Exp $
*/
int bt_getnode(node *, btree *, unsigned long);
int bt_putnode(node *);
int bt_readhdr(btree *);
int bt_writehdr(btree *);
int bt_space(btree *, unsigned int);
int bt_insert(btree *, const byte *, unsigned int);
int bt_delete(btree *, const byte *);
int bt_search(btree *, const byte *, node *);

58
libhfs/config.h Normal file
View File

@ -0,0 +1,58 @@
/* config.h. Generated automatically by configure. */
/* config.h.in. Generated automatically from configure.in by autoheader. */
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: acconfig.h,v 1.5 1998/04/11 08:27:11 rob Exp $
*/
/*****************************************************************************
* Definitions selected automatically by `configure' *
*****************************************************************************/
/* Define to empty if the keyword does not work. */
/* #undef const */
/* Define to `unsigned' if <sys/types.h> doesn't define. */
/* #undef size_t */
/* Define if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define if your <sys/time.h> declares struct tm. */
/* #undef TM_IN_SYS_TIME */
/* Define if you want to enable diagnostic debugging support. */
/* #undef DEBUG */
/* Define if you have the mktime function. */
#define HAVE_MKTIME 1
/* Define if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/*****************************************************************************
* End of automatically configured definitions *
*****************************************************************************/
# ifdef DEBUG
# include <stdio.h>
# endif

485
libhfs/data.c Normal file
View File

@ -0,0 +1,485 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: data.c,v 1.7 1998/11/02 22:08:57 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <string.h>
# include <time.h>
# ifdef TM_IN_SYS_TIME
# include <sys/time.h>
# endif
# include "data.h"
# define TIMEDIFF 2082844800UL
static
time_t tzdiff = -1;
const
unsigned char hfs_charorder[256] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x22, 0x23, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
0x47, 0x48, 0x58, 0x5a, 0x5e, 0x60, 0x67, 0x69,
0x6b, 0x6d, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7f,
0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9f, 0xa1,
0xa3, 0xa5, 0xa8, 0xaa, 0xab, 0xac, 0xad, 0xae,
0x54, 0x48, 0x58, 0x5a, 0x5e, 0x60, 0x67, 0x69,
0x6b, 0x6d, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7f,
0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9f, 0xa1,
0xa3, 0xa5, 0xa8, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
0x4c, 0x50, 0x5c, 0x62, 0x7d, 0x81, 0x9a, 0x55,
0x4a, 0x56, 0x4c, 0x4e, 0x50, 0x5c, 0x62, 0x64,
0x65, 0x66, 0x6f, 0x70, 0x71, 0x72, 0x7d, 0x89,
0x8a, 0x8b, 0x81, 0x83, 0x9c, 0x9d, 0x9e, 0x9a,
0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0x95,
0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0x52, 0x85,
0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
0xc9, 0xca, 0xcb, 0x57, 0x8c, 0xcc, 0x52, 0x85,
0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0x26,
0x27, 0xd4, 0x20, 0x4a, 0x4e, 0x83, 0x87, 0x87,
0xd5, 0xd6, 0x24, 0x25, 0x2d, 0x2e, 0xd7, 0xd8,
0xa7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
/*
* NAME: data->getsb()
* DESCRIPTION: marshal 1 signed byte into local host format
*/
signed char d_getsb(register const unsigned char *ptr)
{
return ptr[0];
}
/*
* NAME: data->getub()
* DESCRIPTION: marshal 1 unsigned byte into local host format
*/
unsigned char d_getub(register const unsigned char *ptr)
{
return ptr[0];
}
/*
* NAME: data->getsw()
* DESCRIPTION: marshal 2 signed bytes into local host format
*/
signed short d_getsw(register const unsigned char *ptr)
{
return
((( signed short) ptr[0] << 8) |
((unsigned short) ptr[1] << 0));
}
/*
* NAME: data->getuw()
* DESCRIPTION: marshal 2 unsigned bytes into local host format
*/
unsigned short d_getuw(register const unsigned char *ptr)
{
return
(((unsigned short) ptr[0] << 8) |
((unsigned short) ptr[1] << 0));
}
/*
* NAME: data->getsl()
* DESCRIPTION: marshal 4 signed bytes into local host format
*/
signed long d_getsl(register const unsigned char *ptr)
{
return
((( signed long) ptr[0] << 24) |
((unsigned long) ptr[1] << 16) |
((unsigned long) ptr[2] << 8) |
((unsigned long) ptr[3] << 0));
}
/*
* NAME: data->getul()
* DESCRIPTION: marshal 4 unsigned bytes into local host format
*/
unsigned long d_getul(register const unsigned char *ptr)
{
return
(((unsigned long) ptr[0] << 24) |
((unsigned long) ptr[1] << 16) |
((unsigned long) ptr[2] << 8) |
((unsigned long) ptr[3] << 0));
}
/*
* NAME: data->putsb()
* DESCRIPTION: marshal 1 signed byte out in big-endian format
*/
void d_putsb(register unsigned char *ptr,
register signed char data)
{
*ptr = data;
}
/*
* NAME: data->putub()
* DESCRIPTION: marshal 1 unsigned byte out in big-endian format
*/
void d_putub(register unsigned char *ptr,
register unsigned char data)
{
*ptr = data;
}
/*
* NAME: data->putsw()
* DESCRIPTION: marshal 2 signed bytes out in big-endian format
*/
void d_putsw(register unsigned char *ptr,
register signed short data)
{
*ptr++ = ((unsigned short) data & 0xff00) >> 8;
*ptr = ((unsigned short) data & 0x00ff) >> 0;
}
/*
* NAME: data->putuw()
* DESCRIPTION: marshal 2 unsigned bytes out in big-endian format
*/
void d_putuw(register unsigned char *ptr,
register unsigned short data)
{
*ptr++ = (data & 0xff00) >> 8;
*ptr = (data & 0x00ff) >> 0;
}
/*
* NAME: data->putsl()
* DESCRIPTION: marshal 4 signed bytes out in big-endian format
*/
void d_putsl(register unsigned char *ptr,
register signed long data)
{
*ptr++ = ((unsigned long) data & 0xff000000UL) >> 24;
*ptr++ = ((unsigned long) data & 0x00ff0000UL) >> 16;
*ptr++ = ((unsigned long) data & 0x0000ff00UL) >> 8;
*ptr = ((unsigned long) data & 0x000000ffUL) >> 0;
}
/*
* NAME: data->putul()
* DESCRIPTION: marshal 4 unsigned bytes out in big-endian format
*/
void d_putul(register unsigned char *ptr,
register unsigned long data)
{
*ptr++ = (data & 0xff000000UL) >> 24;
*ptr++ = (data & 0x00ff0000UL) >> 16;
*ptr++ = (data & 0x0000ff00UL) >> 8;
*ptr = (data & 0x000000ffUL) >> 0;
}
/*
* NAME: data->fetchsb()
* DESCRIPTION: incrementally retrieve a signed byte of data
*/
void d_fetchsb(register const unsigned char **ptr,
register signed char *dest)
{
*dest = *(*ptr)++;
}
/*
* NAME: data->fetchub()
* DESCRIPTION: incrementally retrieve an unsigned byte of data
*/
void d_fetchub(register const unsigned char **ptr,
register unsigned char *dest)
{
*dest = *(*ptr)++;
}
/*
* NAME: data->fetchsw()
* DESCRIPTION: incrementally retrieve a signed word of data
*/
void d_fetchsw(register const unsigned char **ptr,
register signed short *dest)
{
*dest =
((( signed short) (*ptr)[0] << 8) |
((unsigned short) (*ptr)[1] << 0));
*ptr += 2;
}
/*
* NAME: data->fetchuw()
* DESCRIPTION: incrementally retrieve an unsigned word of data
*/
void d_fetchuw(register const unsigned char **ptr,
register unsigned short *dest)
{
*dest =
(((unsigned short) (*ptr)[0] << 8) |
((unsigned short) (*ptr)[1] << 0));
*ptr += 2;
}
/*
* NAME: data->fetchsl()
* DESCRIPTION: incrementally retrieve a signed long word of data
*/
void d_fetchsl(register const unsigned char **ptr,
register signed long *dest)
{
*dest =
((( signed long) (*ptr)[0] << 24) |
((unsigned long) (*ptr)[1] << 16) |
((unsigned long) (*ptr)[2] << 8) |
((unsigned long) (*ptr)[3] << 0));
*ptr += 4;
}
/*
* NAME: data->fetchul()
* DESCRIPTION: incrementally retrieve an unsigned long word of data
*/
void d_fetchul(register const unsigned char **ptr,
register unsigned long *dest)
{
*dest =
(((unsigned long) (*ptr)[0] << 24) |
((unsigned long) (*ptr)[1] << 16) |
((unsigned long) (*ptr)[2] << 8) |
((unsigned long) (*ptr)[3] << 0));
*ptr += 4;
}
/*
* NAME: data->storesb()
* DESCRIPTION: incrementally store a signed byte of data
*/
void d_storesb(register unsigned char **ptr,
register signed char data)
{
*(*ptr)++ = data;
}
/*
* NAME: data->storeub()
* DESCRIPTION: incrementally store an unsigned byte of data
*/
void d_storeub(register unsigned char **ptr,
register unsigned char data)
{
*(*ptr)++ = data;
}
/*
* NAME: data->storesw()
* DESCRIPTION: incrementally store a signed word of data
*/
void d_storesw(register unsigned char **ptr,
register signed short data)
{
*(*ptr)++ = ((unsigned short) data & 0xff00) >> 8;
*(*ptr)++ = ((unsigned short) data & 0x00ff) >> 0;
}
/*
* NAME: data->storeuw()
* DESCRIPTION: incrementally store an unsigned word of data
*/
void d_storeuw(register unsigned char **ptr,
register unsigned short data)
{
*(*ptr)++ = (data & 0xff00) >> 8;
*(*ptr)++ = (data & 0x00ff) >> 0;
}
/*
* NAME: data->storesl()
* DESCRIPTION: incrementally store a signed long word of data
*/
void d_storesl(register unsigned char **ptr,
register signed long data)
{
*(*ptr)++ = ((unsigned long) data & 0xff000000UL) >> 24;
*(*ptr)++ = ((unsigned long) data & 0x00ff0000UL) >> 16;
*(*ptr)++ = ((unsigned long) data & 0x0000ff00UL) >> 8;
*(*ptr)++ = ((unsigned long) data & 0x000000ffUL) >> 0;
}
/*
* NAME: data->storeul()
* DESCRIPTION: incrementally store an unsigned long word of data
*/
void d_storeul(register unsigned char **ptr,
register unsigned long data)
{
*(*ptr)++ = (data & 0xff000000UL) >> 24;
*(*ptr)++ = (data & 0x00ff0000UL) >> 16;
*(*ptr)++ = (data & 0x0000ff00UL) >> 8;
*(*ptr)++ = (data & 0x000000ffUL) >> 0;
}
/*
* NAME: data->fetchstr()
* DESCRIPTION: incrementally retrieve a string
*/
void d_fetchstr(const unsigned char **ptr, char *dest, unsigned size)
{
unsigned len;
len = d_getub(*ptr);
if (len > 0 && len < size)
memcpy(dest, *ptr + 1, len);
else
len = 0;
dest[len] = 0;
*ptr += size;
}
/*
* NAME: data->storestr()
* DESCRIPTION: incrementally store a string
*/
void d_storestr(unsigned char **ptr, const char *src, unsigned size)
{
unsigned len;
len = strlen(src);
if (len > --size)
len = 0;
d_storeub(ptr, len);
memcpy(*ptr, src, len);
memset(*ptr + len, 0, size - len);
*ptr += size;
}
/*
* NAME: data->relstring()
* DESCRIPTION: compare two strings as per MacOS for HFS
*/
int d_relstring(const char *str1, const char *str2)
{
register int diff;
while (*str1 && *str2)
{
diff = hfs_charorder[(unsigned char) *str1] -
hfs_charorder[(unsigned char) *str2];
if (diff)
return diff;
++str1, ++str2;
}
if (! *str1 && *str2)
return -1;
else if (*str1 && ! *str2)
return 1;
return 0;
}
/*
* NAME: calctzdiff()
* DESCRIPTION: calculate the timezone difference between local time and UTC
*/
static
void calctzdiff(void)
{
# ifdef HAVE_MKTIME
time_t t;
int isdst;
struct tm tm;
const struct tm *tmp;
time(&t);
isdst = localtime(&t)->tm_isdst;
tmp = gmtime(&t);
if (tmp)
{
tm = *tmp;
tm.tm_isdst = isdst;
tzdiff = t - mktime(&tm);
}
else
tzdiff = 0;
# else
tzdiff = 0;
# endif
}
/*
* NAME: data->ltime()
* DESCRIPTION: convert MacOS time to local time
*/
time_t d_ltime(unsigned long mtime)
{
if (tzdiff == -1)
calctzdiff();
return (time_t) (mtime - TIMEDIFF) - tzdiff;
}
/*
* NAME: data->mtime()
* DESCRIPTION: convert local time to MacOS time
*/
unsigned long d_mtime(time_t ltime)
{
if (tzdiff == -1)
calctzdiff();
return (unsigned long) (ltime + tzdiff) + TIMEDIFF;
}

58
libhfs/data.h Normal file
View File

@ -0,0 +1,58 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: data.h,v 1.7 1998/11/02 22:08:58 rob Exp $
*/
extern const unsigned char hfs_charorder[];
signed char d_getsb(register const unsigned char *);
unsigned char d_getub(register const unsigned char *);
signed short d_getsw(register const unsigned char *);
unsigned short d_getuw(register const unsigned char *);
signed long d_getsl(register const unsigned char *);
unsigned long d_getul(register const unsigned char *);
void d_putsb(register unsigned char *, register signed char);
void d_putub(register unsigned char *, register unsigned char);
void d_putsw(register unsigned char *, register signed short);
void d_putuw(register unsigned char *, register unsigned short);
void d_putsl(register unsigned char *, register signed long);
void d_putul(register unsigned char *, register unsigned long);
void d_fetchsb(register const unsigned char **, register signed char *);
void d_fetchub(register const unsigned char **, register unsigned char *);
void d_fetchsw(register const unsigned char **, register signed short *);
void d_fetchuw(register const unsigned char **, register unsigned short *);
void d_fetchsl(register const unsigned char **, register signed long *);
void d_fetchul(register const unsigned char **, register unsigned long *);
void d_storesb(register unsigned char **, register signed char);
void d_storeub(register unsigned char **, register unsigned char);
void d_storesw(register unsigned char **, register signed short);
void d_storeuw(register unsigned char **, register unsigned short);
void d_storesl(register unsigned char **, register signed long);
void d_storeul(register unsigned char **, register unsigned long);
void d_fetchstr(const unsigned char **, char *, unsigned);
void d_storestr(unsigned char **, const char *, unsigned);
int d_relstring(const char *, const char *);
time_t d_ltime(unsigned long);
unsigned long d_mtime(time_t);

520
libhfs/file.c Normal file
View File

@ -0,0 +1,520 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: file.c,v 1.9 1998/11/02 22:08:59 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <string.h>
# include <errno.h>
# include "libhfs.h"
# include "file.h"
# include "btree.h"
# include "record.h"
# include "volume.h"
/*
* NAME: file->init()
* DESCRIPTION: initialize file structure
*/
void f_init(hfsfile *file, hfsvol *vol, long cnid, const char *name)
{
int i;
file->vol = vol;
file->parid = 0;
strcpy(file->name, name);
file->cat.cdrType = cdrFilRec;
file->cat.cdrResrv2 = 0;
file->cat.u.fil.filFlags = 0;
file->cat.u.fil.filTyp = 0;
file->cat.u.fil.filUsrWds.fdType = 0;
file->cat.u.fil.filUsrWds.fdCreator = 0;
file->cat.u.fil.filUsrWds.fdFlags = 0;
file->cat.u.fil.filUsrWds.fdLocation.v = 0;
file->cat.u.fil.filUsrWds.fdLocation.h = 0;
file->cat.u.fil.filUsrWds.fdFldr = 0;
file->cat.u.fil.filFlNum = cnid;
file->cat.u.fil.filStBlk = 0;
file->cat.u.fil.filLgLen = 0;
file->cat.u.fil.filPyLen = 0;
file->cat.u.fil.filRStBlk = 0;
file->cat.u.fil.filRLgLen = 0;
file->cat.u.fil.filRPyLen = 0;
file->cat.u.fil.filCrDat = 0;
file->cat.u.fil.filMdDat = 0;
file->cat.u.fil.filBkDat = 0;
file->cat.u.fil.filFndrInfo.fdIconID = 0;
for (i = 0; i < 4; ++i)
file->cat.u.fil.filFndrInfo.fdUnused[i] = 0;
file->cat.u.fil.filFndrInfo.fdComment = 0;
file->cat.u.fil.filFndrInfo.fdPutAway = 0;
file->cat.u.fil.filClpSize = 0;
for (i = 0; i < 3; ++i)
{
file->cat.u.fil.filExtRec[i].xdrStABN = 0;
file->cat.u.fil.filExtRec[i].xdrNumABlks = 0;
file->cat.u.fil.filRExtRec[i].xdrStABN = 0;
file->cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
}
file->cat.u.fil.filResrv = 0;
f_selectfork(file, fkData);
file->flags = 0;
file->prev = 0;
file->next = 0;
}
/*
* NAME: file->selectfork()
* DESCRIPTION: choose a fork for file operations
*/
void f_selectfork(hfsfile *file, int fork)
{
file->fork = fork;
memcpy(&file->ext, fork == fkData ?
&file->cat.u.fil.filExtRec : &file->cat.u.fil.filRExtRec,
sizeof(ExtDataRec));
file->fabn = 0;
file->pos = 0;
}
/*
* NAME: file->getptrs()
* DESCRIPTION: make pointers to the current fork's lengths and extents
*/
void f_getptrs(hfsfile *file, ExtDataRec **extrec,
unsigned long **lglen, unsigned long **pylen)
{
if (file->fork == fkData)
{
if (extrec)
*extrec = &file->cat.u.fil.filExtRec;
if (lglen)
*lglen = &file->cat.u.fil.filLgLen;
if (pylen)
*pylen = &file->cat.u.fil.filPyLen;
}
else
{
if (extrec)
*extrec = &file->cat.u.fil.filRExtRec;
if (lglen)
*lglen = &file->cat.u.fil.filRLgLen;
if (pylen)
*pylen = &file->cat.u.fil.filRPyLen;
}
}
/*
* NAME: file->doblock()
* DESCRIPTION: read or write a numbered block from a file
*/
int f_doblock(hfsfile *file, unsigned long num, block *bp,
int (*func)(hfsvol *, unsigned int, unsigned int, block *))
{
unsigned int abnum;
unsigned int blnum;
unsigned int fabn;
int i;
abnum = num / file->vol->lpa;
blnum = num % file->vol->lpa;
/* locate the appropriate extent record */
fabn = file->fabn;
if (abnum < fabn)
{
ExtDataRec *extrec;
f_getptrs(file, &extrec, 0, 0);
fabn = file->fabn = 0;
memcpy(&file->ext, extrec, sizeof(ExtDataRec));
}
else
abnum -= fabn;
while (1)
{
unsigned int n;
for (i = 0; i < 3; ++i)
{
n = file->ext[i].xdrNumABlks;
if (abnum < n)
return func(file->vol, file->ext[i].xdrStABN + abnum, blnum, bp);
fabn += n;
abnum -= n;
}
if (v_extsearch(file, fabn, &file->ext, 0) <= 0)
goto fail;
file->fabn = fabn;
}
fail:
return -1;
}
/*
* NAME: file->addextent()
* DESCRIPTION: add an extent to a file
*/
int f_addextent(hfsfile *file, ExtDescriptor *blocks)
{
hfsvol *vol = file->vol;
ExtDataRec *extrec;
unsigned long *pylen;
unsigned int start, end;
node n;
int i;
f_getptrs(file, &extrec, 0, &pylen);
start = file->fabn;
end = *pylen / vol->mdb.drAlBlkSiz;
n.nnum = 0;
i = -1;
while (start < end)
{
for (i = 0; i < 3; ++i)
{
unsigned int num;
num = file->ext[i].xdrNumABlks;
start += num;
if (start == end)
break;
else if (start > end)
ERROR(EIO, "file extents exceed file physical length");
else if (num == 0)
ERROR(EIO, "empty file extent");
}
if (start == end)
break;
if (v_extsearch(file, start, &file->ext, &n) <= 0)
goto fail;
file->fabn = start;
}
if (i >= 0 &&
file->ext[i].xdrStABN + file->ext[i].xdrNumABlks == blocks->xdrStABN)
file->ext[i].xdrNumABlks += blocks->xdrNumABlks;
else
{
/* create a new extent descriptor */
if (++i < 3)
file->ext[i] = *blocks;
else
{
ExtKeyRec key;
byte record[HFS_MAX_EXTRECLEN];
unsigned int reclen;
/* record is full; create a new one */
file->ext[0] = *blocks;
for (i = 1; i < 3; ++i)
{
file->ext[i].xdrStABN = 0;
file->ext[i].xdrNumABlks = 0;
}
file->fabn = start;
r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, end);
r_packextrec(&key, &file->ext, record, &reclen);
if (bt_insert(&vol->ext, record, reclen) == -1)
goto fail;
i = -1;
}
}
if (i >= 0)
{
/* store the modified extent record */
if (file->fabn)
{
if ((n.nnum == 0 &&
v_extsearch(file, file->fabn, 0, &n) <= 0) ||
v_putextrec(&file->ext, &n) == -1)
goto fail;
}
else
memcpy(extrec, &file->ext, sizeof(ExtDataRec));
}
*pylen += blocks->xdrNumABlks * vol->mdb.drAlBlkSiz;
file->flags |= HFS_FILE_UPDATE_CATREC;
return 0;
fail:
return -1;
}
/*
* NAME: file->alloc()
* DESCRIPTION: reserve allocation blocks for a file
*/
long f_alloc(hfsfile *file)
{
hfsvol *vol = file->vol;
unsigned long clumpsz;
ExtDescriptor blocks;
clumpsz = file->cat.u.fil.filClpSize;
if (clumpsz == 0)
{
if (file == &vol->ext.f)
clumpsz = vol->mdb.drXTClpSiz;
else if (file == &vol->cat.f)
clumpsz = vol->mdb.drCTClpSiz;
else
clumpsz = vol->mdb.drClpSiz;
}
blocks.xdrNumABlks = clumpsz / vol->mdb.drAlBlkSiz;
if (v_allocblocks(vol, &blocks) == -1)
goto fail;
if (f_addextent(file, &blocks) == -1)
{
v_freeblocks(vol, &blocks);
goto fail;
}
return blocks.xdrNumABlks;
fail:
return -1;
}
/*
* NAME: file->trunc()
* DESCRIPTION: release allocation blocks unneeded by a file
*/
int f_trunc(hfsfile *file)
{
hfsvol *vol = file->vol;
ExtDataRec *extrec;
unsigned long *lglen, *pylen, alblksz, newpylen;
unsigned int dlen, start, end;
node n;
int i;
if (vol->flags & HFS_VOL_READONLY)
goto done;
f_getptrs(file, &extrec, &lglen, &pylen);
alblksz = vol->mdb.drAlBlkSiz;
newpylen = (*lglen / alblksz + (*lglen % alblksz != 0)) * alblksz;
if (newpylen > *pylen)
ERROR(EIO, "file size exceeds physical length");
else if (newpylen == *pylen)
goto done;
dlen = (*pylen - newpylen) / alblksz;
start = file->fabn;
end = newpylen / alblksz;
if (start >= end)
{
start = file->fabn = 0;
memcpy(&file->ext, extrec, sizeof(ExtDataRec));
}
n.nnum = 0;
i = -1;
while (start < end)
{
for (i = 0; i < 3; ++i)
{
unsigned int num;
num = file->ext[i].xdrNumABlks;
start += num;
if (start >= end)
break;
else if (num == 0)
ERROR(EIO, "empty file extent");
}
if (start >= end)
break;
if (v_extsearch(file, start, &file->ext, &n) <= 0)
goto fail;
file->fabn = start;
}
if (start > end)
{
ExtDescriptor blocks;
file->ext[i].xdrNumABlks -= start - end;
dlen -= start - end;
blocks.xdrStABN = file->ext[i].xdrStABN + file->ext[i].xdrNumABlks;
blocks.xdrNumABlks = start - end;
if (v_freeblocks(vol, &blocks) == -1)
goto fail;
}
*pylen = newpylen;
file->flags |= HFS_FILE_UPDATE_CATREC;
do
{
while (dlen && ++i < 3)
{
unsigned int num;
num = file->ext[i].xdrNumABlks;
start += num;
if (num == 0)
ERROR(EIO, "empty file extent");
else if (num > dlen)
ERROR(EIO, "file extents exceed physical size");
dlen -= num;
if (v_freeblocks(vol, &file->ext[i]) == -1)
goto fail;
file->ext[i].xdrStABN = 0;
file->ext[i].xdrNumABlks = 0;
}
if (file->fabn)
{
if (n.nnum == 0 &&
v_extsearch(file, file->fabn, 0, &n) <= 0)
goto fail;
if (file->ext[0].xdrNumABlks)
{
if (v_putextrec(&file->ext, &n) == -1)
goto fail;
}
else
{
if (bt_delete(&vol->ext, HFS_NODEREC(n, n.rnum)) == -1)
goto fail;
n.nnum = 0;
}
}
else
memcpy(extrec, &file->ext, sizeof(ExtDataRec));
if (dlen)
{
if (v_extsearch(file, start, &file->ext, &n) <= 0)
goto fail;
file->fabn = start;
i = -1;
}
}
while (dlen);
done:
return 0;
fail:
return -1;
}
/*
* NAME: file->flush()
* DESCRIPTION: flush all pending changes to an open file
*/
int f_flush(hfsfile *file)
{
hfsvol *vol = file->vol;
if (vol->flags & HFS_VOL_READONLY)
goto done;
if (file->flags & HFS_FILE_UPDATE_CATREC)
{
node n;
file->cat.u.fil.filStBlk = file->cat.u.fil.filExtRec[0].xdrStABN;
file->cat.u.fil.filRStBlk = file->cat.u.fil.filRExtRec[0].xdrStABN;
if (v_catsearch(vol, file->parid, file->name, 0, 0, &n) <= 0 ||
v_putcatrec(&file->cat, &n) == -1)
goto fail;
file->flags &= ~HFS_FILE_UPDATE_CATREC;
}
done:
return 0;
fail:
return -1;
}

45
libhfs/file.h Normal file
View File

@ -0,0 +1,45 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: file.h,v 1.6 1998/04/11 08:27:12 rob Exp $
*/
enum {
fkData = 0x00,
fkRsrc = 0xff
};
void f_init(hfsfile *, hfsvol *, long, const char *);
void f_selectfork(hfsfile *, int);
void f_getptrs(hfsfile *, ExtDataRec **, unsigned long **, unsigned long **);
int f_doblock(hfsfile *, unsigned long, block *,
int (*)(hfsvol *, unsigned int, unsigned int, block *));
# define f_getblock(file, num, bp) \
f_doblock((file), (num), (bp), b_readab)
# define f_putblock(file, num, bp) \
f_doblock((file), (num), (bp), \
(int (*)(hfsvol *, unsigned int, unsigned int, block *)) \
b_writeab)
int f_addextent(hfsfile *, ExtDescriptor *);
long f_alloc(hfsfile *);
int f_trunc(hfsfile *);
int f_flush(hfsfile *);

1848
libhfs/hfs.c Normal file

File diff suppressed because it is too large Load Diff

182
libhfs/hfs.h Normal file
View File

@ -0,0 +1,182 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: hfs.h,v 1.11 1998/11/02 22:09:01 rob Exp $
*/
# include <time.h>
# define HFS_BLOCKSZ 512
# define HFS_BLOCKSZ_BITS 9
# define HFS_MAX_FLEN 31
# define HFS_MAX_VLEN 27
typedef struct _hfsvol_ hfsvol;
typedef struct _hfsfile_ hfsfile;
typedef struct _hfsdir_ hfsdir;
typedef struct {
char name[HFS_MAX_VLEN + 1]; /* name of volume (MacOS Standard Roman) */
int flags; /* volume flags */
unsigned long totbytes; /* total bytes on volume */
unsigned long freebytes; /* free bytes on volume */
unsigned long alblocksz; /* volume allocation block size */
unsigned long clumpsz; /* default file clump size */
unsigned long numfiles; /* number of files in volume */
unsigned long numdirs; /* number of directories in volume */
time_t crdate; /* volume creation date */
time_t mddate; /* last volume modification date */
time_t bkdate; /* last volume backup date */
unsigned long blessed; /* CNID of MacOS System Folder */
} hfsvolent;
typedef struct {
char name[HFS_MAX_FLEN + 1]; /* catalog name (MacOS Standard Roman) */
int flags; /* bit flags */
unsigned long cnid; /* catalog node id (CNID) */
unsigned long parid; /* CNID of parent directory */
time_t crdate; /* date of creation */
time_t mddate; /* date of last modification */
time_t bkdate; /* date of last backup */
short fdflags; /* Macintosh Finder flags */
short fdcomment; /* Macintosh Comment ID */
struct {
signed short v; /* Finder icon vertical coordinate */
signed short h; /* horizontal coordinate */
} fdlocation;
union {
struct {
unsigned long dsize; /* size of data fork */
unsigned long rsize; /* size of resource fork */
char type[5]; /* file type code (plus null) */
char creator[5]; /* file creator code (plus null) */
} file;
struct {
unsigned short valence; /* number of items in directory */
struct {
signed short top; /* top edge of folder's rectangle */
signed short left; /* left edge */
signed short bottom; /* bottom edge */
signed short right; /* right edge */
} rect;
} dir;
} u;
} hfsdirent;
# define HFS_ISDIR 0x0001
# define HFS_ISLOCKED 0x0002
# define HFS_CNID_ROOTPAR 1
# define HFS_CNID_ROOTDIR 2
# define HFS_CNID_EXT 3
# define HFS_CNID_CAT 4
# define HFS_CNID_BADALLOC 5
# define HFS_FNDR_ISONDESK (1 << 0)
# define HFS_FNDR_COLOR 0x0e
# define HFS_FNDR_COLORRESERVED (1 << 4)
# define HFS_FNDR_REQUIRESSWITCHLAUNCH (1 << 5)
# define HFS_FNDR_ISSHARED (1 << 6)
# define HFS_FNDR_HASNOINITS (1 << 7)
# define HFS_FNDR_HASBEENINITED (1 << 8)
# define HFS_FNDR_RESERVED (1 << 9)
# define HFS_FNDR_HASCUSTOMICON (1 << 10)
# define HFS_FNDR_ISSTATIONERY (1 << 11)
# define HFS_FNDR_NAMELOCKED (1 << 12)
# define HFS_FNDR_HASBUNDLE (1 << 13)
# define HFS_FNDR_ISINVISIBLE (1 << 14)
# define HFS_FNDR_ISALIAS (1 << 15)
extern const char *hfs_error;
extern const unsigned char hfs_charorder[];
# define HFS_MODE_RDONLY 0
# define HFS_MODE_RDWR 1
# define HFS_MODE_ANY 2
# define HFS_MODE_MASK 0x0003
# define HFS_OPT_NOCACHE 0x0100
# define HFS_OPT_2048 0x0200
# define HFS_OPT_ZERO 0x0400
# define HFS_OPT_DC42HEADER 0x0800
# define HFS_SEEK_SET 0
# define HFS_SEEK_CUR 1
# define HFS_SEEK_END 2
hfsvol *hfs_mount(const char *, int, int);
int hfs_flush(hfsvol *);
void hfs_flushall(void);
int hfs_umount(hfsvol *);
void hfs_umountall(void);
hfsvol *hfs_getvol(const char *);
void hfs_setvol(hfsvol *);
int hfs_vstat(hfsvol *, hfsvolent *);
int hfs_vsetattr(hfsvol *, hfsvolent *);
int hfs_chdir(hfsvol *, const char *);
unsigned long hfs_getcwd(hfsvol *);
int hfs_setcwd(hfsvol *, unsigned long);
int hfs_dirinfo(hfsvol *, unsigned long *, char *);
hfsdir *hfs_opendir(hfsvol *, const char *);
int hfs_readdir(hfsdir *, hfsdirent *);
int hfs_closedir(hfsdir *);
hfsfile *hfs_create(hfsvol *, const char *, const char *, const char *);
hfsfile *hfs_open(hfsvol *, const char *);
int hfs_setfork(hfsfile *, int);
int hfs_getfork(hfsfile *);
unsigned long hfs_read(hfsfile *, void *, unsigned long);
unsigned long hfs_write(hfsfile *, const void *, unsigned long);
int hfs_truncate(hfsfile *, unsigned long);
unsigned long hfs_seek(hfsfile *, long, int);
int hfs_close(hfsfile *);
int hfs_stat(hfsvol *, const char *, hfsdirent *);
int hfs_fstat(hfsfile *, hfsdirent *);
int hfs_setattr(hfsvol *, const char *, const hfsdirent *);
int hfs_fsetattr(hfsfile *, const hfsdirent *);
int hfs_mkdir(hfsvol *, const char *);
int hfs_rmdir(hfsvol *, const char *);
int hfs_delete(hfsvol *, const char *);
int hfs_rename(hfsvol *, const char *, const char *);
int hfs_zero(const char *, unsigned int, unsigned long *);
int hfs_mkpart(const char *, unsigned long);
int hfs_nparts(const char *);
int hfs_format(const char *, int, int,
const char *, unsigned int, const unsigned long []);

227
libhfs/libhfs.h Normal file
View File

@ -0,0 +1,227 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: libhfs.h,v 1.7 1998/11/02 22:09:02 rob Exp $
*/
# include "hfs.h"
# include "apple.h"
extern int errno;
# define ERROR(code, str) \
do { hfs_error = (str), errno = (code); goto fail; } while (0)
# ifdef DEBUG
# define ASSERT(cond) do { if (! (cond)) abort(); } while (0)
# else
# define ASSERT(cond) /* nothing */
# endif
# define SIZE(type, n) ((size_t) (sizeof(type) * (n)))
# define ALLOC(type, n) ((type *) malloc(SIZE(type, n)))
# define ALLOCX(type, n) ((n) ? ALLOC(type, n) : (type *) 0)
# define FREE(ptr) ((ptr) ? (void) free((void *) ptr) : (void) 0)
# define REALLOC(ptr, type, n) \
((type *) ((ptr) ? realloc(ptr, SIZE(type, n)) : malloc(SIZE(type, n))))
# define REALLOCX(ptr, type, n) \
((n) ? REALLOC(ptr, type, n) : (FREE(ptr), (type *) 0))
# define BMTST(bm, num) \
(((const byte *) (bm))[(num) >> 3] & (0x80 >> ((num) & 0x07)))
# define BMSET(bm, num) \
(((byte *) (bm))[(num) >> 3] |= (0x80 >> ((num) & 0x07)))
# define BMCLR(bm, num) \
(((byte *) (bm))[(num) >> 3] &= ~(0x80 >> ((num) & 0x07)))
# define STRINGIZE(x) #x
# define STR(x) STRINGIZE(x)
typedef unsigned char byte;
typedef byte block[HFS_BLOCKSZ];
typedef struct _bucket_ {
int flags; /* bit flags */
unsigned int count; /* number of times this block is requested */
unsigned long bnum; /* logical block number */
block *data; /* pointer to block contents */
struct _bucket_ *cnext; /* next bucket in cache chain */
struct _bucket_ *cprev; /* previous bucket in cache chain */
struct _bucket_ *hnext; /* next bucket in hash chain */
struct _bucket_ **hprev; /* previous bucket's pointer to this bucket */
} bucket;
# define HFS_BUCKET_INUSE 0x01
# define HFS_BUCKET_DIRTY 0x02
# define HFS_CACHESZ 128
# define HFS_HASHSZ 32
# define HFS_BLOCKBUFSZ 16
typedef struct {
struct _hfsvol_ *vol; /* volume to which cache belongs */
bucket *tail; /* end of bucket chain */
unsigned int hits; /* number of cache hits */
unsigned int misses; /* number of cache misses */
bucket chain[HFS_CACHESZ]; /* cache bucket chain */
bucket *hash[HFS_HASHSZ]; /* hash table for bucket chain */
block pool[HFS_CACHESZ]; /* physical blocks in cache */
} bcache;
# define HFS_MAP1SZ 256
# define HFS_MAPXSZ 492
# define HFS_NODEREC(nd, rnum) ((nd).data + (nd).roff[rnum])
# define HFS_RECLEN(nd, rnum) ((nd).roff[(rnum) + 1] - (nd).roff[rnum])
# define HFS_RECKEYLEN(ptr) (*(const byte *) (ptr))
# define HFS_RECKEYSKIP(ptr) ((size_t) ((1 + HFS_RECKEYLEN(ptr) + 1) & ~1))
# define HFS_RECDATA(ptr) ((ptr) + HFS_RECKEYSKIP(ptr))
# define HFS_SETKEYLEN(ptr, x) (*(byte *) (ptr) = (x))
# define HFS_CATDATALEN sizeof(CatDataRec)
# define HFS_EXTDATALEN sizeof(ExtDataRec)
# define HFS_MAX_DATALEN (HFS_CATDATALEN > HFS_EXTDATALEN ? \
HFS_CATDATALEN : HFS_EXTDATALEN)
# define HFS_CATKEYLEN sizeof(CatKeyRec)
# define HFS_EXTKEYLEN sizeof(ExtKeyRec)
# define HFS_MAX_KEYLEN (HFS_CATKEYLEN > HFS_EXTKEYLEN ? \
HFS_CATKEYLEN : HFS_EXTKEYLEN)
# define HFS_MAX_CATRECLEN (HFS_CATKEYLEN + HFS_CATDATALEN)
# define HFS_MAX_EXTRECLEN (HFS_EXTKEYLEN + HFS_EXTDATALEN)
# define HFS_MAX_RECLEN (HFS_MAX_KEYLEN + HFS_MAX_DATALEN)
# define HFS_SIGWORD 0x4244
# define HFS_SIGWORD_MFS ((Integer) 0xd2d7)
# define HFS_ATRB_BUSY (1 << 6)
# define HFS_ATRB_HLOCKED (1 << 7)
# define HFS_ATRB_UMOUNTED (1 << 8)
# define HFS_ATRB_BBSPARED (1 << 9)
# define HFS_ATRB_BVINCONSIS (1 << 11)
# define HFS_ATRB_COPYPROT (1 << 14)
# define HFS_ATRB_SLOCKED (1 << 15)
struct _hfsfile_ {
struct _hfsvol_ *vol; /* pointer to volume descriptor */
unsigned long parid; /* parent directory ID of this file */
char name[HFS_MAX_FLEN + 1]; /* catalog name of this file */
CatDataRec cat; /* catalog information */
ExtDataRec ext; /* current extent record */
unsigned int fabn; /* starting file allocation block number */
int fork; /* current selected fork for I/O */
unsigned long pos; /* current file seek pointer */
int flags; /* bit flags */
struct _hfsfile_ *prev;
struct _hfsfile_ *next;
};
# define HFS_FILE_UPDATE_CATREC 0x01
# define HFS_MAX_NRECS 35 /* maximum based on minimum record size */
typedef struct _node_ {
struct _btree_ *bt; /* btree to which this node belongs */
unsigned long nnum; /* node index */
NodeDescriptor nd; /* node descriptor */
int rnum; /* current record index */
UInteger roff[HFS_MAX_NRECS + 1];
/* record offsets */
block data; /* raw contents of node */
} node;
struct _hfsdir_ {
struct _hfsvol_ *vol; /* associated volume */
unsigned long dirid; /* directory ID of interest (or 0) */
node n; /* current B*-tree node */
struct _hfsvol_ *vptr; /* current volume pointer */
struct _hfsdir_ *prev;
struct _hfsdir_ *next;
};
typedef void (*keyunpackfunc)(const byte *, void *);
typedef int (*keycomparefunc)(const void *, const void *);
typedef struct _btree_ {
hfsfile f; /* subset file information */
node hdrnd; /* header node */
BTHdrRec hdr; /* header record */
byte *map; /* usage bitmap */
unsigned long mapsz; /* number of bytes in bitmap */
int flags; /* bit flags */
keyunpackfunc keyunpack; /* key unpacking function */
keycomparefunc keycompare; /* key comparison function */
} btree;
# define HFS_BT_UPDATE_HDR 0x01
struct _hfsvol_ {
void *priv; /* OS-dependent private descriptor data */
long base; /* base of volume, for disk images with header */
int flags; /* bit flags */
int pnum; /* ordinal HFS partition number */
unsigned long vstart; /* logical block offset to start of volume */
unsigned long vlen; /* number of logical blocks in volume */
unsigned int lpa; /* number of logical blocks per allocation block */
bcache *cache; /* cache of recently used blocks */
MDB mdb; /* master directory block */
block *vbm; /* volume bitmap */
unsigned short vbmsz; /* number of blocks in bitmap */
btree ext; /* B*-tree control block for extents overflow file */
btree cat; /* B*-tree control block for catalog file */
unsigned long cwd; /* directory id of current working directory */
int refs; /* number of external references to this volume */
hfsfile *files; /* list of open files */
hfsdir *dirs; /* list of open directories */
struct _hfsvol_ *prev;
struct _hfsvol_ *next;
};
# define HFS_VOL_OPEN 0x0001
# define HFS_VOL_MOUNTED 0x0002
# define HFS_VOL_READONLY 0x0004
# define HFS_VOL_USINGCACHE 0x0008
# define HFS_VOL_UPDATE_MDB 0x0010
# define HFS_VOL_UPDATE_ALTMDB 0x0020
# define HFS_VOL_UPDATE_VBM 0x0040
# define HFS_VOL_OPT_MASK 0xff00
extern hfsvol *hfs_mounts;

470
libhfs/low.c Normal file
View File

@ -0,0 +1,470 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: low.c,v 1.8 1998/11/02 22:09:03 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include "libhfs.h"
# include "low.h"
# include "data.h"
# include "block.h"
# include "file.h"
/*
* NAME: low->getddr()
* DESCRIPTION: read a driver descriptor record
*/
int l_getddr(hfsvol *vol, Block0 *ddr)
{
block b;
const byte *ptr = b;
int i;
if (b_readpb(vol, 0, &b, 1) == -1)
goto fail;
d_fetchsw(&ptr, &ddr->sbSig);
d_fetchsw(&ptr, &ddr->sbBlkSize);
d_fetchsl(&ptr, &ddr->sbBlkCount);
d_fetchsw(&ptr, &ddr->sbDevType);
d_fetchsw(&ptr, &ddr->sbDevId);
d_fetchsl(&ptr, &ddr->sbData);
d_fetchsw(&ptr, &ddr->sbDrvrCount);
d_fetchsl(&ptr, &ddr->ddBlock);
d_fetchsw(&ptr, &ddr->ddSize);
d_fetchsw(&ptr, &ddr->ddType);
for (i = 0; i < 243; ++i)
d_fetchsw(&ptr, &ddr->ddPad[i]);
ASSERT(ptr - b == HFS_BLOCKSZ);
return 0;
fail:
return -1;
}
/*
* NAME: low->putddr()
* DESCRIPTION: write a driver descriptor record
*/
int l_putddr(hfsvol *vol, const Block0 *ddr)
{
block b;
byte *ptr = b;
int i;
d_storesw(&ptr, ddr->sbSig);
d_storesw(&ptr, ddr->sbBlkSize);
d_storesl(&ptr, ddr->sbBlkCount);
d_storesw(&ptr, ddr->sbDevType);
d_storesw(&ptr, ddr->sbDevId);
d_storesl(&ptr, ddr->sbData);
d_storesw(&ptr, ddr->sbDrvrCount);
d_storesl(&ptr, ddr->ddBlock);
d_storesw(&ptr, ddr->ddSize);
d_storesw(&ptr, ddr->ddType);
for (i = 0; i < 243; ++i)
d_storesw(&ptr, ddr->ddPad[i]);
ASSERT(ptr - b == HFS_BLOCKSZ);
if (b_writepb(vol, 0, &b, 1) == -1)
goto fail;
return 0;
fail:
return -1;
}
/*
* NAME: low->getpmentry()
* DESCRIPTION: read a partition map entry
*/
int l_getpmentry(hfsvol *vol, Partition *map, unsigned long bnum)
{
block b;
const byte *ptr = b;
int i;
if (b_readpb(vol, bnum, &b, 1) == -1)
goto fail;
d_fetchsw(&ptr, &map->pmSig);
d_fetchsw(&ptr, &map->pmSigPad);
d_fetchsl(&ptr, &map->pmMapBlkCnt);
d_fetchsl(&ptr, &map->pmPyPartStart);
d_fetchsl(&ptr, &map->pmPartBlkCnt);
strncpy((char *) map->pmPartName, (const char *) ptr, 32);
map->pmPartName[32] = 0;
ptr += 32;
strncpy((char *) map->pmParType, (const char *) ptr, 32);
map->pmParType[32] = 0;
ptr += 32;
d_fetchsl(&ptr, &map->pmLgDataStart);
d_fetchsl(&ptr, &map->pmDataCnt);
d_fetchsl(&ptr, &map->pmPartStatus);
d_fetchsl(&ptr, &map->pmLgBootStart);
d_fetchsl(&ptr, &map->pmBootSize);
d_fetchsl(&ptr, &map->pmBootAddr);
d_fetchsl(&ptr, &map->pmBootAddr2);
d_fetchsl(&ptr, &map->pmBootEntry);
d_fetchsl(&ptr, &map->pmBootEntry2);
d_fetchsl(&ptr, &map->pmBootCksum);
strncpy((char *) map->pmProcessor, (const char *) ptr, 16);
map->pmProcessor[16] = 0;
ptr += 16;
for (i = 0; i < 188; ++i)
d_fetchsw(&ptr, &map->pmPad[i]);
ASSERT(ptr - b == HFS_BLOCKSZ);
return 0;
fail:
return -1;
}
/*
* NAME: low->putpmentry()
* DESCRIPTION: write a partition map entry
*/
int l_putpmentry(hfsvol *vol, const Partition *map, unsigned long bnum)
{
block b;
byte *ptr = b;
int i;
d_storesw(&ptr, map->pmSig);
d_storesw(&ptr, map->pmSigPad);
d_storesl(&ptr, map->pmMapBlkCnt);
d_storesl(&ptr, map->pmPyPartStart);
d_storesl(&ptr, map->pmPartBlkCnt);
memset(ptr, 0, 32);
strncpy((char *) ptr, (const char *) map->pmPartName, 32);
ptr += 32;
memset(ptr, 0, 32);
strncpy((char *) ptr, (const char *) map->pmParType, 32);
ptr += 32;
d_storesl(&ptr, map->pmLgDataStart);
d_storesl(&ptr, map->pmDataCnt);
d_storesl(&ptr, map->pmPartStatus);
d_storesl(&ptr, map->pmLgBootStart);
d_storesl(&ptr, map->pmBootSize);
d_storesl(&ptr, map->pmBootAddr);
d_storesl(&ptr, map->pmBootAddr2);
d_storesl(&ptr, map->pmBootEntry);
d_storesl(&ptr, map->pmBootEntry2);
d_storesl(&ptr, map->pmBootCksum);
memset(ptr, 0, 16);
strncpy((char *) ptr, (const char *) map->pmProcessor, 16);
ptr += 16;
for (i = 0; i < 188; ++i)
d_storesw(&ptr, map->pmPad[i]);
ASSERT(ptr - b == HFS_BLOCKSZ);
if (b_writepb(vol, bnum, &b, 1) == -1)
goto fail;
return 0;
fail:
return -1;
}
/*
* NAME: low->getbb()
* DESCRIPTION: read a volume's boot blocks
*/
int l_getbb(hfsvol *vol, BootBlkHdr *bb, byte *bootcode)
{
block b;
const byte *ptr = b;
if (b_readlb(vol, 0, &b) == -1)
goto fail;
d_fetchsw(&ptr, &bb->bbID);
d_fetchsl(&ptr, &bb->bbEntry);
d_fetchsw(&ptr, &bb->bbVersion);
d_fetchsw(&ptr, &bb->bbPageFlags);
d_fetchstr(&ptr, bb->bbSysName, sizeof(bb->bbSysName));
d_fetchstr(&ptr, bb->bbShellName, sizeof(bb->bbShellName));
d_fetchstr(&ptr, bb->bbDbg1Name, sizeof(bb->bbDbg1Name));
d_fetchstr(&ptr, bb->bbDbg2Name, sizeof(bb->bbDbg2Name));
d_fetchstr(&ptr, bb->bbScreenName, sizeof(bb->bbScreenName));
d_fetchstr(&ptr, bb->bbHelloName, sizeof(bb->bbHelloName));
d_fetchstr(&ptr, bb->bbScrapName, sizeof(bb->bbScrapName));
d_fetchsw(&ptr, &bb->bbCntFCBs);
d_fetchsw(&ptr, &bb->bbCntEvts);
d_fetchsl(&ptr, &bb->bb128KSHeap);
d_fetchsl(&ptr, &bb->bb256KSHeap);
d_fetchsl(&ptr, &bb->bbSysHeapSize);
d_fetchsw(&ptr, &bb->filler);
d_fetchsl(&ptr, &bb->bbSysHeapExtra);
d_fetchsl(&ptr, &bb->bbSysHeapFract);
ASSERT(ptr - b == 148);
if (bootcode)
{
memcpy(bootcode, ptr, HFS_BOOTCODE1LEN);
if (b_readlb(vol, 1, &b) == -1)
goto fail;
memcpy(bootcode + HFS_BOOTCODE1LEN, b, HFS_BOOTCODE2LEN);
}
return 0;
fail:
return -1;
}
/*
* NAME: low->putbb()
* DESCRIPTION: write a volume's boot blocks
*/
int l_putbb(hfsvol *vol, const BootBlkHdr *bb, const byte *bootcode)
{
block b;
byte *ptr = b;
d_storesw(&ptr, bb->bbID);
d_storesl(&ptr, bb->bbEntry);
d_storesw(&ptr, bb->bbVersion);
d_storesw(&ptr, bb->bbPageFlags);
d_storestr(&ptr, bb->bbSysName, sizeof(bb->bbSysName));
d_storestr(&ptr, bb->bbShellName, sizeof(bb->bbShellName));
d_storestr(&ptr, bb->bbDbg1Name, sizeof(bb->bbDbg1Name));
d_storestr(&ptr, bb->bbDbg2Name, sizeof(bb->bbDbg2Name));
d_storestr(&ptr, bb->bbScreenName, sizeof(bb->bbScreenName));
d_storestr(&ptr, bb->bbHelloName, sizeof(bb->bbHelloName));
d_storestr(&ptr, bb->bbScrapName, sizeof(bb->bbScrapName));
d_storesw(&ptr, bb->bbCntFCBs);
d_storesw(&ptr, bb->bbCntEvts);
d_storesl(&ptr, bb->bb128KSHeap);
d_storesl(&ptr, bb->bb256KSHeap);
d_storesl(&ptr, bb->bbSysHeapSize);
d_storesw(&ptr, bb->filler);
d_storesl(&ptr, bb->bbSysHeapExtra);
d_storesl(&ptr, bb->bbSysHeapFract);
ASSERT(ptr - b == 148);
if (bootcode)
memcpy(ptr, bootcode, HFS_BOOTCODE1LEN);
else
memset(ptr, 0, HFS_BOOTCODE1LEN);
if (b_writelb(vol, 0, &b) == -1)
goto fail;
if (bootcode)
memcpy(&b, bootcode + HFS_BOOTCODE1LEN, HFS_BOOTCODE2LEN);
else
memset(&b, 0, HFS_BOOTCODE2LEN);
if (b_writelb(vol, 1, &b) == -1)
goto fail;
return 0;
fail:
return -1;
}
/*
* NAME: low->getmdb()
* DESCRIPTION: read a master directory block
*/
int l_getmdb(hfsvol *vol, MDB *mdb, int backup)
{
block b;
const byte *ptr = b;
int i;
if (b_readlb(vol, backup ? vol->vlen - 2 : 2, &b) == -1)
goto fail;
d_fetchsw(&ptr, &mdb->drSigWord);
d_fetchsl(&ptr, &mdb->drCrDate);
d_fetchsl(&ptr, &mdb->drLsMod);
d_fetchsw(&ptr, &mdb->drAtrb);
d_fetchuw(&ptr, &mdb->drNmFls);
d_fetchuw(&ptr, &mdb->drVBMSt);
d_fetchuw(&ptr, &mdb->drAllocPtr);
d_fetchuw(&ptr, &mdb->drNmAlBlks);
d_fetchul(&ptr, &mdb->drAlBlkSiz);
d_fetchul(&ptr, &mdb->drClpSiz);
d_fetchuw(&ptr, &mdb->drAlBlSt);
d_fetchsl(&ptr, &mdb->drNxtCNID);
d_fetchuw(&ptr, &mdb->drFreeBks);
d_fetchstr(&ptr, mdb->drVN, sizeof(mdb->drVN));
ASSERT(ptr - b == 64);
d_fetchsl(&ptr, &mdb->drVolBkUp);
d_fetchsw(&ptr, &mdb->drVSeqNum);
d_fetchul(&ptr, &mdb->drWrCnt);
d_fetchul(&ptr, &mdb->drXTClpSiz);
d_fetchul(&ptr, &mdb->drCTClpSiz);
d_fetchuw(&ptr, &mdb->drNmRtDirs);
d_fetchul(&ptr, &mdb->drFilCnt);
d_fetchul(&ptr, &mdb->drDirCnt);
for (i = 0; i < 8; ++i)
d_fetchsl(&ptr, &mdb->drFndrInfo[i]);
ASSERT(ptr - b == 124);
d_fetchuw(&ptr, &mdb->drEmbedSigWord);
d_fetchuw(&ptr, &mdb->drEmbedExtent.xdrStABN);
d_fetchuw(&ptr, &mdb->drEmbedExtent.xdrNumABlks);
d_fetchul(&ptr, &mdb->drXTFlSize);
for (i = 0; i < 3; ++i)
{
d_fetchuw(&ptr, &mdb->drXTExtRec[i].xdrStABN);
d_fetchuw(&ptr, &mdb->drXTExtRec[i].xdrNumABlks);
}
ASSERT(ptr - b == 146);
d_fetchul(&ptr, &mdb->drCTFlSize);
for (i = 0; i < 3; ++i)
{
d_fetchuw(&ptr, &mdb->drCTExtRec[i].xdrStABN);
d_fetchuw(&ptr, &mdb->drCTExtRec[i].xdrNumABlks);
}
ASSERT(ptr - b == 162);
return 0;
fail:
return -1;
}
/*
* NAME: low->putmdb()
* DESCRIPTION: write master directory block(s)
*/
int l_putmdb(hfsvol *vol, const MDB *mdb, int backup)
{
block b;
byte *ptr = b;
int i;
d_storesw(&ptr, mdb->drSigWord);
d_storesl(&ptr, mdb->drCrDate);
d_storesl(&ptr, mdb->drLsMod);
d_storesw(&ptr, mdb->drAtrb);
d_storeuw(&ptr, mdb->drNmFls);
d_storeuw(&ptr, mdb->drVBMSt);
d_storeuw(&ptr, mdb->drAllocPtr);
d_storeuw(&ptr, mdb->drNmAlBlks);
d_storeul(&ptr, mdb->drAlBlkSiz);
d_storeul(&ptr, mdb->drClpSiz);
d_storeuw(&ptr, mdb->drAlBlSt);
d_storesl(&ptr, mdb->drNxtCNID);
d_storeuw(&ptr, mdb->drFreeBks);
d_storestr(&ptr, mdb->drVN, sizeof(mdb->drVN));
ASSERT(ptr - b == 64);
d_storesl(&ptr, mdb->drVolBkUp);
d_storesw(&ptr, mdb->drVSeqNum);
d_storeul(&ptr, mdb->drWrCnt);
d_storeul(&ptr, mdb->drXTClpSiz);
d_storeul(&ptr, mdb->drCTClpSiz);
d_storeuw(&ptr, mdb->drNmRtDirs);
d_storeul(&ptr, mdb->drFilCnt);
d_storeul(&ptr, mdb->drDirCnt);
for (i = 0; i < 8; ++i)
d_storesl(&ptr, mdb->drFndrInfo[i]);
ASSERT(ptr - b == 124);
d_storeuw(&ptr, mdb->drEmbedSigWord);
d_storeuw(&ptr, mdb->drEmbedExtent.xdrStABN);
d_storeuw(&ptr, mdb->drEmbedExtent.xdrNumABlks);
d_storeul(&ptr, mdb->drXTFlSize);
for (i = 0; i < 3; ++i)
{
d_storeuw(&ptr, mdb->drXTExtRec[i].xdrStABN);
d_storeuw(&ptr, mdb->drXTExtRec[i].xdrNumABlks);
}
ASSERT(ptr - b == 146);
d_storeul(&ptr, mdb->drCTFlSize);
for (i = 0; i < 3; ++i)
{
d_storeuw(&ptr, mdb->drCTExtRec[i].xdrStABN);
d_storeuw(&ptr, mdb->drCTExtRec[i].xdrNumABlks);
}
ASSERT(ptr - b == 162);
memset(ptr, 0, HFS_BLOCKSZ - (ptr - b));
if (b_writelb(vol, 2, &b) == -1 ||
(backup && b_writelb(vol, vol->vlen - 2, &b) == -1))
goto fail;
return 0;
fail:
return -1;
}

44
libhfs/low.h Normal file
View File

@ -0,0 +1,44 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: low.h,v 1.6 1998/04/11 08:27:13 rob Exp $
*/
# define HFS_DDR_SIGWORD 0x4552
# define HFS_PM_SIGWORD 0x504d
# define HFS_PM_SIGWORD_OLD 0x5453
# define HFS_BB_SIGWORD 0x4c4b
# define HFS_BOOTCODE1LEN (HFS_BLOCKSZ - 148)
# define HFS_BOOTCODE2LEN HFS_BLOCKSZ
# define HFS_BOOTCODELEN (HFS_BOOTCODE1LEN + HFS_BOOTCODE2LEN)
int l_getddr(hfsvol *, Block0 *);
int l_putddr(hfsvol *, const Block0 *);
int l_getpmentry(hfsvol *, Partition *, unsigned long);
int l_putpmentry(hfsvol *, const Partition *, unsigned long);
int l_getbb(hfsvol *, BootBlkHdr *, byte *);
int l_putbb(hfsvol *, const BootBlkHdr *, const byte *);
int l_getmdb(hfsvol *, MDB *, int);
int l_putmdb(hfsvol *, const MDB *, int);

318
libhfs/medium.c Normal file
View File

@ -0,0 +1,318 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: medium.c,v 1.4 1998/11/02 22:09:04 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include "libhfs.h"
# include "block.h"
# include "low.h"
# include "medium.h"
/* Driver Descriptor Record Routines ======================================= */
/*
* NAME: medium->zeroddr()
* DESCRIPTION: write a new/empty driver descriptor record
*/
int m_zeroddr(hfsvol *vol)
{
Block0 ddr;
int i;
ASSERT(vol->pnum == 0 && vol->vlen != 0);
ddr.sbSig = HFS_DDR_SIGWORD;
ddr.sbBlkSize = HFS_BLOCKSZ;
ddr.sbBlkCount = vol->vlen;
ddr.sbDevType = 0;
ddr.sbDevId = 0;
ddr.sbData = 0;
ddr.sbDrvrCount = 0;
ddr.ddBlock = 0;
ddr.ddSize = 0;
ddr.ddType = 0;
for (i = 0; i < 243; ++i)
ddr.ddPad[i] = 0;
return l_putddr(vol, &ddr);
}
/* Partition Map Routines ================================================== */
/*
* NAME: medium->zeropm()
* DESCRIPTION: write new/empty partition map
*/
int m_zeropm(hfsvol *vol, unsigned int maxparts)
{
Partition map;
unsigned int i;
ASSERT(vol->pnum == 0 && vol->vlen != 0);
if (maxparts < 2)
ERROR(EINVAL, "must allow at least 2 partitions");
/* first entry: partition map itself */
map.pmSig = HFS_PM_SIGWORD;
map.pmSigPad = 0;
map.pmMapBlkCnt = 2;
map.pmPyPartStart = 1;
map.pmPartBlkCnt = maxparts;
strcpy((char *) map.pmPartName, "Apple");
strcpy((char *) map.pmParType, "Apple_partition_map");
map.pmLgDataStart = 0;
map.pmDataCnt = map.pmPartBlkCnt;
map.pmPartStatus = 0;
map.pmLgBootStart = 0;
map.pmBootSize = 0;
map.pmBootAddr = 0;
map.pmBootAddr2 = 0;
map.pmBootEntry = 0;
map.pmBootEntry2 = 0;
map.pmBootCksum = 0;
strcpy((char *) map.pmProcessor, "");
for (i = 0; i < 188; ++i)
map.pmPad[i] = 0;
if (l_putpmentry(vol, &map, 1) == -1)
goto fail;
/* second entry: rest of medium */
map.pmPyPartStart = 1 + maxparts;
map.pmPartBlkCnt = vol->vlen - 1 - maxparts;
strcpy((char *) map.pmPartName, "Extra");
strcpy((char *) map.pmParType, "Apple_Free");
map.pmDataCnt = map.pmPartBlkCnt;
if (l_putpmentry(vol, &map, 2) == -1)
goto fail;
/* zero rest of partition map's partition */
if (maxparts > 2)
{
block b;
memset(&b, 0, sizeof(b));
for (i = 3; i <= maxparts; ++i)
{
if (b_writepb(vol, i, &b, 1) == -1)
goto fail;
}
}
return 0;
fail:
return -1;
}
/*
* NAME: medium->findpmentry()
* DESCRIPTION: locate a partition map entry
*/
int m_findpmentry(hfsvol *vol, const char *type,
Partition *map, unsigned long *start)
{
unsigned long bnum;
int found = 0;
if (start && *start > 0)
{
bnum = *start;
if (bnum++ >= (unsigned long) map->pmMapBlkCnt)
ERROR(EINVAL, "partition not found");
}
else
bnum = 1;
while (1)
{
if (l_getpmentry(vol, map, bnum) == -1)
{
found = -1;
goto fail;
}
if (map->pmSig != HFS_PM_SIGWORD)
{
found = -1;
if (map->pmSig == HFS_PM_SIGWORD_OLD)
ERROR(EINVAL, "old partition map format not supported");
else
ERROR(EINVAL, "invalid partition map");
}
if (strcmp((char *) map->pmParType, type) == 0)
{
found = 1;
goto done;
}
if (bnum++ >= (unsigned long) map->pmMapBlkCnt)
ERROR(EINVAL, "partition not found");
}
done:
if (start)
*start = bnum;
fail:
return found;
}
/*
* NAME: medium->mkpart()
* DESCRIPTION: create a new partition from available free space
*/
int m_mkpart(hfsvol *vol,
const char *name, const char *type, unsigned long len)
{
Partition map;
unsigned int nparts, maxparts;
unsigned long bnum, start, remain;
int found;
if (strlen(name) > 32 ||
strlen(type) > 32)
ERROR(EINVAL, "partition name/type can each be at most 32 chars");
if (len == 0)
ERROR(EINVAL, "partition length must be > 0");
found = m_findpmentry(vol, "Apple_partition_map", &map, 0);
if (found == -1)
goto fail;
if (! found)
ERROR(EIO, "cannot find partition map's partition");
nparts = map.pmMapBlkCnt;
maxparts = map.pmPartBlkCnt;
bnum = 0;
do
{
found = m_findpmentry(vol, "Apple_Free", &map, &bnum);
if (found == -1)
goto fail;
if (! found)
ERROR(ENOSPC, "no available partitions");
}
while (len > (unsigned long) map.pmPartBlkCnt);
start = (unsigned long) map.pmPyPartStart + len;
remain = (unsigned long) map.pmPartBlkCnt - len;
if (remain && nparts >= maxparts)
ERROR(EINVAL, "must allocate all blocks in free space");
map.pmPartBlkCnt = len;
strcpy((char *) map.pmPartName, name);
strcpy((char *) map.pmParType, type);
map.pmLgDataStart = 0;
map.pmDataCnt = len;
map.pmPartStatus = 0;
if (l_putpmentry(vol, &map, bnum) == -1)
goto fail;
if (remain)
{
map.pmPyPartStart = start;
map.pmPartBlkCnt = remain;
strcpy((char *) map.pmPartName, "Extra");
strcpy((char *) map.pmParType, "Apple_Free");
map.pmDataCnt = remain;
if (l_putpmentry(vol, &map, ++nparts) == -1)
goto fail;
for (bnum = 1; bnum <= nparts; ++bnum)
{
if (l_getpmentry(vol, &map, bnum) == -1)
goto fail;
map.pmMapBlkCnt = nparts;
if (l_putpmentry(vol, &map, bnum) == -1)
goto fail;
}
}
return 0;
fail:
return -1;
}
/* Boot Blocks Routines ==================================================== */
/*
* NAME: medium->zerobb()
* DESCRIPTION: write new/empty volume boot blocks
*/
int m_zerobb(hfsvol *vol)
{
block b;
memset(&b, 0, sizeof(b));
if (b_writelb(vol, 0, &b) == -1 ||
b_writelb(vol, 1, &b) == -1)
goto fail;
return 0;
fail:
return -1;
}

42
libhfs/medium.h Normal file
View File

@ -0,0 +1,42 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: medium.h,v 1.3 1998/04/11 08:27:13 rob Exp $
*/
/*
* Partition Types:
*
* "Apple_partition_map" partition map
* "Apple_Driver" device driver
* "Apple_Driver43" SCSI Manager 4.3 device driver
* "Apple_MFS" Macintosh 64K ROM filesystem
* "Apple_HFS" Macintosh hierarchical filesystem
* "Apple_Unix_SVR2" Unix filesystem
* "Apple_PRODOS" ProDOS filesystem
* "Apple_Free" unused
* "Apple_Scratch" empty
*/
int m_zeroddr(hfsvol *);
int m_zeropm(hfsvol *, unsigned int);
int m_findpmentry(hfsvol *, const char *, Partition *, unsigned long *);
int m_mkpart(hfsvol *, const char *, const char *, unsigned long);
int m_zerobb(hfsvol *);

50
libhfs/memcmp.c Normal file
View File

@ -0,0 +1,50 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: memcmp.c,v 1.6 1998/04/11 16:22:48 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <sys/types.h>
/*
* NAME: memcmp()
* DESCRIPTION: compare memory areas
*/
int memcmp(const void *s1, const void *s2, size_t n)
{
register const unsigned char *c1, *c2;
c1 = s1;
c2 = s2;
while (n--)
{
register int diff;
diff = *c1++ - *c2++;
if (diff)
return diff;
}
return 0;
}

473
libhfs/node.c Normal file
View File

@ -0,0 +1,473 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: node.c,v 1.9 1998/11/02 22:09:05 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <stdlib.h>
# include <string.h>
# include <errno.h>
# include "libhfs.h"
# include "node.h"
# include "data.h"
# include "btree.h"
/* total bytes used by records (NOT including record offsets) */
# define NODEUSED(n) \
((size_t) ((n).roff[(n).nd.ndNRecs] - (n).roff[0]))
/* total bytes available for new records (INCLUDING record offsets) */
# define NODEFREE(n) \
((size_t) (HFS_BLOCKSZ - (n).roff[(n).nd.ndNRecs] - \
2 * ((n).nd.ndNRecs + 1)))
/*
* NAME: node->init()
* DESCRIPTION: construct an empty node
*/
void n_init(node *np, btree *bt, int type, int height)
{
np->bt = bt;
np->nnum = (unsigned long) -1;
np->nd.ndFLink = 0;
np->nd.ndBLink = 0;
np->nd.ndType = type;
np->nd.ndNHeight = height;
np->nd.ndNRecs = 0;
np->nd.ndResv2 = 0;
np->rnum = -1;
np->roff[0] = 0x00e;
memset(&np->data, 0, sizeof(np->data));
}
/*
* NAME: node->new()
* DESCRIPTION: allocate a new b*-tree node
*/
int n_new(node *np)
{
btree *bt = np->bt;
unsigned long num;
if (bt->hdr.bthFree == 0)
ERROR(EIO, "b*-tree full");
num = 0;
while (num < bt->hdr.bthNNodes && BMTST(bt->map, num))
++num;
if (num == bt->hdr.bthNNodes)
ERROR(EIO, "free b*-tree node not found");
np->nnum = num;
BMSET(bt->map, num);
--bt->hdr.bthFree;
bt->flags |= HFS_BT_UPDATE_HDR;
return 0;
fail:
return -1;
}
/*
* NAME: node->free()
* DESCRIPTION: deallocate and remove a b*-tree node
*/
int n_free(node *np)
{
btree *bt = np->bt;
node sib;
if (bt->hdr.bthFNode == np->nnum)
bt->hdr.bthFNode = np->nd.ndFLink;
if (bt->hdr.bthLNode == np->nnum)
bt->hdr.bthLNode = np->nd.ndBLink;
if (np->nd.ndFLink > 0)
{
if (bt_getnode(&sib, bt, np->nd.ndFLink) == -1)
goto fail;
sib.nd.ndBLink = np->nd.ndBLink;
if (bt_putnode(&sib) == -1)
goto fail;
}
if (np->nd.ndBLink > 0)
{
if (bt_getnode(&sib, bt, np->nd.ndBLink) == -1)
goto fail;
sib.nd.ndFLink = np->nd.ndFLink;
if (bt_putnode(&sib) == -1)
goto fail;
}
BMCLR(bt->map, np->nnum);
++bt->hdr.bthFree;
bt->flags |= HFS_BT_UPDATE_HDR;
return 0;
fail:
return -1;
}
/*
* NAME: compact()
* DESCRIPTION: clean up a node, removing deleted records
*/
static
void compact(node *np)
{
byte *ptr;
int offset, nrecs, i;
offset = 0x00e;
ptr = np->data + offset;
nrecs = 0;
for (i = 0; i < np->nd.ndNRecs; ++i)
{
const byte *rec;
int reclen;
rec = HFS_NODEREC(*np, i);
reclen = HFS_RECLEN(*np, i);
if (HFS_RECKEYLEN(rec) > 0)
{
np->roff[nrecs++] = offset;
offset += reclen;
if (ptr == rec)
ptr += reclen;
else
{
while (reclen--)
*ptr++ = *rec++;
}
}
}
np->roff[nrecs] = offset;
np->nd.ndNRecs = nrecs;
}
/*
* NAME: node->search()
* DESCRIPTION: locate a record in a node, or the record it should follow
*/
int n_search(node *np, const byte *pkey)
{
const btree *bt = np->bt;
byte key1[HFS_MAX_KEYLEN], key2[HFS_MAX_KEYLEN];
int i, comp = -1;
bt->keyunpack(pkey, key2);
for (i = np->nd.ndNRecs; i--; )
{
const byte *rec;
rec = HFS_NODEREC(*np, i);
if (HFS_RECKEYLEN(rec) == 0)
continue; /* deleted record */
bt->keyunpack(rec, key1);
comp = bt->keycompare(key1, key2);
if (comp <= 0)
break;
}
np->rnum = i;
return comp == 0;
}
/*
* NAME: node->index()
* DESCRIPTION: create an index record from a key and node pointer
*/
void n_index(const node *np, byte *record, unsigned int *reclen)
{
const byte *key = HFS_NODEREC(*np, 0);
if (np->bt == &np->bt->f.vol->cat)
{
/* force the key length to be 0x25 */
HFS_SETKEYLEN(record, 0x25);
memset(record + 1, 0, 0x25);
memcpy(record + 1, key + 1, HFS_RECKEYLEN(key));
}
else
memcpy(record, key, HFS_RECKEYSKIP(key));
d_putul(HFS_RECDATA(record), np->nnum);
if (reclen)
*reclen = HFS_RECKEYSKIP(record) + 4;
}
/*
* NAME: split()
* DESCRIPTION: divide a node into two and insert a record
*/
static
int split(node *left, byte *record, unsigned int *reclen)
{
btree *bt = left->bt;
node n, *right = &n, *side = 0;
int mark, i;
/* create a second node by cloning the first */
*right = *left;
if (n_new(right) == -1)
goto fail;
left->nd.ndFLink = right->nnum;
right->nd.ndBLink = left->nnum;
/* divide all records evenly between the two nodes */
mark = (NODEUSED(*left) + 2 * left->nd.ndNRecs + *reclen + 2) >> 1;
if (left->rnum == -1)
{
side = left;
mark -= *reclen + 2;
}
for (i = 0; i < left->nd.ndNRecs; ++i)
{
node *np;
byte *rec;
np = (mark > 0) ? right : left;
rec = HFS_NODEREC(*np, i);
mark -= HFS_RECLEN(*np, i) + 2;
HFS_SETKEYLEN(rec, 0);
if (left->rnum == i)
{
side = (mark > 0) ? left : right;
mark -= *reclen + 2;
}
}
compact(left);
compact(right);
/* insert the new record and store the modified nodes */
ASSERT(side);
n_search(side, record);
n_insertx(side, record, *reclen);
if (bt_putnode(left) == -1 ||
bt_putnode(right) == -1)
goto fail;
/* create an index record in the parent for the new node */
n_index(right, record, reclen);
/* update link pointers */
if (bt->hdr.bthLNode == left->nnum)
{
bt->hdr.bthLNode = right->nnum;
bt->flags |= HFS_BT_UPDATE_HDR;
}
if (right->nd.ndFLink > 0)
{
node sib;
if (bt_getnode(&sib, right->bt, right->nd.ndFLink) == -1)
goto fail;
sib.nd.ndBLink = right->nnum;
if (bt_putnode(&sib) == -1)
goto fail;
}
return 0;
fail:
return -1;
}
/*
* NAME: node->insertx()
* DESCRIPTION: insert a record into a node (which must already have room)
*/
void n_insertx(node *np, const byte *record, unsigned int reclen)
{
int rnum, i;
byte *ptr;
rnum = np->rnum + 1;
/* push other records down to make room */
for (ptr = HFS_NODEREC(*np, np->nd.ndNRecs) + reclen;
ptr > HFS_NODEREC(*np, rnum) + reclen; --ptr)
*(ptr - 1) = *(ptr - 1 - reclen);
++np->nd.ndNRecs;
for (i = np->nd.ndNRecs; i > rnum; --i)
np->roff[i] = np->roff[i - 1] + reclen;
/* write the new record */
memcpy(HFS_NODEREC(*np, rnum), record, reclen);
}
/*
* NAME: node->insert()
* DESCRIPTION: insert a new record into a node; return a record for parent
*/
int n_insert(node *np, byte *record, unsigned int *reclen)
{
/* check for free space */
if (np->nd.ndNRecs >= HFS_MAX_NRECS ||
*reclen + 2 > NODEFREE(*np))
return split(np, record, reclen);
n_insertx(np, record, *reclen);
*reclen = 0;
return bt_putnode(np);
}
/*
* NAME: join()
* DESCRIPTION: combine two nodes into a single node
*/
static
int join(node *left, node *right, byte *record, int *flag)
{
int i, offset;
/* copy records and offsets */
memcpy(HFS_NODEREC(*left, left->nd.ndNRecs),
HFS_NODEREC(*right, 0), NODEUSED(*right));
offset = left->roff[left->nd.ndNRecs] - right->roff[0];
for (i = 1; i <= right->nd.ndNRecs; ++i)
left->roff[++left->nd.ndNRecs] = offset + right->roff[i];
if (bt_putnode(left) == -1)
goto fail;
/* eliminate node and update link pointers */
if (n_free(right) == -1)
goto fail;
HFS_SETKEYLEN(record, 0);
*flag = 1;
return 0;
fail:
return -1;
}
/*
* NAME: node->delete()
* DESCRIPTION: remove a record from a node
*/
int n_delete(node *np, byte *record, int *flag)
{
byte *rec;
rec = HFS_NODEREC(*np, np->rnum);
HFS_SETKEYLEN(rec, 0);
compact(np);
if (np->nd.ndNRecs == 0)
{
if (n_free(np) == -1)
goto fail;
HFS_SETKEYLEN(record, 0);
*flag = 1;
return 0;
}
/* see if we can join with our left sibling */
if (np->nd.ndBLink > 0)
{
node left;
if (bt_getnode(&left, np->bt, np->nd.ndBLink) == -1)
goto fail;
if (np->nd.ndNRecs + left.nd.ndNRecs <= HFS_MAX_NRECS &&
NODEUSED(*np) + 2 * np->nd.ndNRecs <= NODEFREE(left))
return join(&left, np, record, flag);
}
if (np->rnum == 0)
{
/* special case: first record changed; update parent record key */
n_index(np, record, 0);
*flag = 1;
}
return bt_putnode(np);
fail:
return -1;
}

34
libhfs/node.h Normal file
View File

@ -0,0 +1,34 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: node.h,v 1.7 1998/11/02 22:09:06 rob Exp $
*/
void n_init(node *, btree *, int, int);
int n_new(node *);
int n_free(node *);
int n_search(node *, const byte *);
void n_index(const node *, byte *, unsigned int *);
void n_insertx(node *, const byte *, unsigned int);
int n_insert(node *, byte *, unsigned int *);
int n_delete(node *, byte *, int *);

29
libhfs/os.h Normal file
View File

@ -0,0 +1,29 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: os.h,v 1.6 1998/09/15 19:21:05 rob Exp $
*/
int os_open(void **, const char *, int);
int os_close(void **);
int os_same(void **, const char *);
unsigned long os_seek(void **priv, long base, unsigned long offset);
unsigned long os_read(void **, void *, unsigned long);
unsigned long os_write(void **, const void *, unsigned long);

200
libhfs/os/unix.c Executable file
View File

@ -0,0 +1,200 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: unix.c,v 1.8 1998/11/02 22:09:13 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# ifdef HAVE_FCNTL_H
# include <fcntl.h>
# else
int open(const char *, int, ...);
int fcntl(int, int, ...);
# endif
# ifdef HAVE_UNISTD_H
# include <unistd.h>
# else
int close(int);
off_t lseek(int, off_t, int);
ssize_t read(int, void *, size_t);
ssize_t write(int, const char *, size_t);
int stat(const char *, struct stat *);
int fstat(int, struct stat *);
# endif
# include <errno.h>
# include <sys/stat.h>
# include "libhfs.h"
# include "os.h"
/*
* NAME: os->open()
* DESCRIPTION: open and lock a new descriptor from the given path and mode
*/
int os_open(void **priv, const char *path, int mode)
{
int fd;
struct flock lock;
switch (mode)
{
case HFS_MODE_RDONLY:
mode = O_RDONLY;
break;
case HFS_MODE_RDWR:
default:
mode = O_RDWR;
break;
}
fd = open(path, mode);
if (fd == -1)
ERROR(errno, "error opening medium");
/* lock descriptor against concurrent access */
lock.l_type = (mode == O_RDONLY) ? F_RDLCK : F_WRLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
if (fcntl(fd, F_SETLK, &lock) == -1 &&
(errno == EACCES || errno == EAGAIN))
ERROR(EAGAIN, "unable to obtain lock for medium");
*priv = (void *) fd;
return 0;
fail:
if (fd != -1)
close(fd);
return -1;
}
/*
* NAME: os->close()
* DESCRIPTION: close an open descriptor
*/
int os_close(void **priv)
{
int fd = (int) *priv;
*priv = (void *) -1;
if (close(fd) == -1)
ERROR(errno, "error closing medium");
return 0;
fail:
return -1;
}
/*
* NAME: os->same()
* DESCRIPTION: return 1 if path is same as the open descriptor
*/
int os_same(void **priv, const char *path)
{
int fd = (int) *priv;
struct stat fdev, dev;
if (fstat(fd, &fdev) == -1 ||
stat(path, &dev) == -1)
ERROR(errno, "can't get path information");
return fdev.st_dev == dev.st_dev &&
fdev.st_ino == dev.st_ino;
fail:
return -1;
}
/*
* NAME: os->seek()
* DESCRIPTION: set a descriptor's seek pointer (offset in blocks)
*/
unsigned long os_seek(void **priv, long base, unsigned long offset)
{
int fd = (int) *priv;
off_t result;
/* offset == -1 special; seek to last block of device */
if (offset == (unsigned long) -1)
result = lseek(fd, 0, SEEK_END);
else
result = lseek(fd, (offset << HFS_BLOCKSZ_BITS) + base, SEEK_SET);
if (result == -1)
ERROR(errno, "error seeking medium");
result -= base;
return (unsigned long) (result >> HFS_BLOCKSZ_BITS);
fail:
return -1;
}
/*
* NAME: os->read()
* DESCRIPTION: read blocks from an open descriptor
*/
unsigned long os_read(void **priv, void *buf, unsigned long len)
{
int fd = (int) *priv;
ssize_t result;
result = read(fd, buf, len << HFS_BLOCKSZ_BITS);
if (result == -1)
ERROR(errno, "error reading from medium");
return (unsigned long) result >> HFS_BLOCKSZ_BITS;
fail:
return -1;
}
/*
* NAME: os->write()
* DESCRIPTION: write blocks to an open descriptor
*/
unsigned long os_write(void **priv, const void *buf, unsigned long len)
{
int fd = (int) *priv;
ssize_t result;
result = write(fd, buf, len << HFS_BLOCKSZ_BITS);
if (result == -1)
ERROR(errno, "error writing to medium");
return (unsigned long) result >> HFS_BLOCKSZ_BITS;
fail:
return -1;
}

562
libhfs/record.c Normal file
View File

@ -0,0 +1,562 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: record.c,v 1.9 1998/11/02 22:09:07 rob Exp $
*/
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# include <string.h>
# include "libhfs.h"
# include "record.h"
# include "data.h"
/*
* NAME: record->packcatkey()
* DESCRIPTION: pack a catalog record key
*/
void r_packcatkey(const CatKeyRec *key, byte *pkey, unsigned int *len)
{
const byte *start = pkey;
d_storesb(&pkey, key->ckrKeyLen);
d_storesb(&pkey, key->ckrResrv1);
d_storeul(&pkey, key->ckrParID);
d_storestr(&pkey, key->ckrCName, sizeof(key->ckrCName));
if (len)
*len = HFS_RECKEYSKIP(start);
}
/*
* NAME: record->unpackcatkey()
* DESCRIPTION: unpack a catalog record key
*/
void r_unpackcatkey(const byte *pkey, CatKeyRec *key)
{
d_fetchsb(&pkey, &key->ckrKeyLen);
d_fetchsb(&pkey, &key->ckrResrv1);
d_fetchul(&pkey, &key->ckrParID);
d_fetchstr(&pkey, key->ckrCName, sizeof(key->ckrCName));
}
/*
* NAME: record->packextkey()
* DESCRIPTION: pack an extents record key
*/
void r_packextkey(const ExtKeyRec *key, byte *pkey, unsigned int *len)
{
const byte *start = pkey;
d_storesb(&pkey, key->xkrKeyLen);
d_storesb(&pkey, key->xkrFkType);
d_storeul(&pkey, key->xkrFNum);
d_storeuw(&pkey, key->xkrFABN);
if (len)
*len = HFS_RECKEYSKIP(start);
}
/*
* NAME: record->unpackextkey()
* DESCRIPTION: unpack an extents record key
*/
void r_unpackextkey(const byte *pkey, ExtKeyRec *key)
{
d_fetchsb(&pkey, &key->xkrKeyLen);
d_fetchsb(&pkey, &key->xkrFkType);
d_fetchul(&pkey, &key->xkrFNum);
d_fetchuw(&pkey, &key->xkrFABN);
}
/*
* NAME: record->comparecatkeys()
* DESCRIPTION: compare two (packed) catalog record keys
*/
int r_comparecatkeys(const CatKeyRec *key1, const CatKeyRec *key2)
{
int diff;
diff = key1->ckrParID - key2->ckrParID;
if (diff)
return diff;
return d_relstring(key1->ckrCName, key2->ckrCName);
}
/*
* NAME: record->compareextkeys()
* DESCRIPTION: compare two (packed) extents record keys
*/
int r_compareextkeys(const ExtKeyRec *key1, const ExtKeyRec *key2)
{
int diff;
diff = key1->xkrFNum - key2->xkrFNum;
if (diff)
return diff;
diff = (unsigned char) key1->xkrFkType -
(unsigned char) key2->xkrFkType;
if (diff)
return diff;
return key1->xkrFABN - key2->xkrFABN;
}
/*
* NAME: record->packcatdata()
* DESCRIPTION: pack catalog record data
*/
void r_packcatdata(const CatDataRec *data, byte *pdata, unsigned int *len)
{
const byte *start = pdata;
int i;
d_storesb(&pdata, data->cdrType);
d_storesb(&pdata, data->cdrResrv2);
switch (data->cdrType)
{
case cdrDirRec:
d_storesw(&pdata, data->u.dir.dirFlags);
d_storeuw(&pdata, data->u.dir.dirVal);
d_storeul(&pdata, data->u.dir.dirDirID);
d_storesl(&pdata, data->u.dir.dirCrDat);
d_storesl(&pdata, data->u.dir.dirMdDat);
d_storesl(&pdata, data->u.dir.dirBkDat);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.top);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.left);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.bottom);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.right);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frFlags);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frLocation.v);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frLocation.h);
d_storesw(&pdata, data->u.dir.dirUsrInfo.frView);
d_storesw(&pdata, data->u.dir.dirFndrInfo.frScroll.v);
d_storesw(&pdata, data->u.dir.dirFndrInfo.frScroll.h);
d_storesl(&pdata, data->u.dir.dirFndrInfo.frOpenChain);
d_storesw(&pdata, data->u.dir.dirFndrInfo.frUnused);
d_storesw(&pdata, data->u.dir.dirFndrInfo.frComment);
d_storesl(&pdata, data->u.dir.dirFndrInfo.frPutAway);
for (i = 0; i < 4; ++i)
d_storesl(&pdata, data->u.dir.dirResrv[i]);
break;
case cdrFilRec:
d_storesb(&pdata, data->u.fil.filFlags);
d_storesb(&pdata, data->u.fil.filTyp);
d_storesl(&pdata, data->u.fil.filUsrWds.fdType);
d_storesl(&pdata, data->u.fil.filUsrWds.fdCreator);
d_storesw(&pdata, data->u.fil.filUsrWds.fdFlags);
d_storesw(&pdata, data->u.fil.filUsrWds.fdLocation.v);
d_storesw(&pdata, data->u.fil.filUsrWds.fdLocation.h);
d_storesw(&pdata, data->u.fil.filUsrWds.fdFldr);
d_storeul(&pdata, data->u.fil.filFlNum);
d_storeuw(&pdata, data->u.fil.filStBlk);
d_storeul(&pdata, data->u.fil.filLgLen);
d_storeul(&pdata, data->u.fil.filPyLen);
d_storeuw(&pdata, data->u.fil.filRStBlk);
d_storeul(&pdata, data->u.fil.filRLgLen);
d_storeul(&pdata, data->u.fil.filRPyLen);
d_storesl(&pdata, data->u.fil.filCrDat);
d_storesl(&pdata, data->u.fil.filMdDat);
d_storesl(&pdata, data->u.fil.filBkDat);
d_storesw(&pdata, data->u.fil.filFndrInfo.fdIconID);
for (i = 0; i < 4; ++i)
d_storesw(&pdata, data->u.fil.filFndrInfo.fdUnused[i]);
d_storesw(&pdata, data->u.fil.filFndrInfo.fdComment);
d_storesl(&pdata, data->u.fil.filFndrInfo.fdPutAway);
d_storeuw(&pdata, data->u.fil.filClpSize);
for (i = 0; i < 3; ++i)
{
d_storeuw(&pdata, data->u.fil.filExtRec[i].xdrStABN);
d_storeuw(&pdata, data->u.fil.filExtRec[i].xdrNumABlks);
}
for (i = 0; i < 3; ++i)
{
d_storeuw(&pdata, data->u.fil.filRExtRec[i].xdrStABN);
d_storeuw(&pdata, data->u.fil.filRExtRec[i].xdrNumABlks);
}
d_storesl(&pdata, data->u.fil.filResrv);
break;
case cdrThdRec:
for (i = 0; i < 2; ++i)
d_storesl(&pdata, data->u.dthd.thdResrv[i]);
d_storeul(&pdata, data->u.dthd.thdParID);
d_storestr(&pdata, data->u.dthd.thdCName,
sizeof(data->u.dthd.thdCName));
break;
case cdrFThdRec:
for (i = 0; i < 2; ++i)
d_storesl(&pdata, data->u.fthd.fthdResrv[i]);
d_storeul(&pdata, data->u.fthd.fthdParID);
d_storestr(&pdata, data->u.fthd.fthdCName,
sizeof(data->u.fthd.fthdCName));
break;
default:
ASSERT(0);
}
if (len)
*len += pdata - start;
}
/*
* NAME: record->unpackcatdata()
* DESCRIPTION: unpack catalog record data
*/
void r_unpackcatdata(const byte *pdata, CatDataRec *data)
{
int i;
d_fetchsb(&pdata, &data->cdrType);
d_fetchsb(&pdata, &data->cdrResrv2);
switch (data->cdrType)
{
case cdrDirRec:
d_fetchsw(&pdata, &data->u.dir.dirFlags);
d_fetchuw(&pdata, &data->u.dir.dirVal);
d_fetchul(&pdata, &data->u.dir.dirDirID);
d_fetchsl(&pdata, &data->u.dir.dirCrDat);
d_fetchsl(&pdata, &data->u.dir.dirMdDat);
d_fetchsl(&pdata, &data->u.dir.dirBkDat);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.top);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.left);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.bottom);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.right);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frFlags);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frLocation.v);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frLocation.h);
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frView);
d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frScroll.v);
d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frScroll.h);
d_fetchsl(&pdata, &data->u.dir.dirFndrInfo.frOpenChain);
d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frUnused);
d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frComment);
d_fetchsl(&pdata, &data->u.dir.dirFndrInfo.frPutAway);
for (i = 0; i < 4; ++i)
d_fetchsl(&pdata, &data->u.dir.dirResrv[i]);
break;
case cdrFilRec:
d_fetchsb(&pdata, &data->u.fil.filFlags);
d_fetchsb(&pdata, &data->u.fil.filTyp);
d_fetchsl(&pdata, &data->u.fil.filUsrWds.fdType);
d_fetchsl(&pdata, &data->u.fil.filUsrWds.fdCreator);
d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdFlags);
d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdLocation.v);
d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdLocation.h);
d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdFldr);
d_fetchul(&pdata, &data->u.fil.filFlNum);
d_fetchuw(&pdata, &data->u.fil.filStBlk);
d_fetchul(&pdata, &data->u.fil.filLgLen);
d_fetchul(&pdata, &data->u.fil.filPyLen);
d_fetchuw(&pdata, &data->u.fil.filRStBlk);
d_fetchul(&pdata, &data->u.fil.filRLgLen);
d_fetchul(&pdata, &data->u.fil.filRPyLen);
d_fetchsl(&pdata, &data->u.fil.filCrDat);
d_fetchsl(&pdata, &data->u.fil.filMdDat);
d_fetchsl(&pdata, &data->u.fil.filBkDat);
d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdIconID);
for (i = 0; i < 4; ++i)
d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdUnused[i]);
d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdComment);
d_fetchsl(&pdata, &data->u.fil.filFndrInfo.fdPutAway);
d_fetchuw(&pdata, &data->u.fil.filClpSize);
for (i = 0; i < 3; ++i)
{
d_fetchuw(&pdata, &data->u.fil.filExtRec[i].xdrStABN);
d_fetchuw(&pdata, &data->u.fil.filExtRec[i].xdrNumABlks);
}
for (i = 0; i < 3; ++i)
{
d_fetchuw(&pdata, &data->u.fil.filRExtRec[i].xdrStABN);
d_fetchuw(&pdata, &data->u.fil.filRExtRec[i].xdrNumABlks);
}
d_fetchsl(&pdata, &data->u.fil.filResrv);
break;
case cdrThdRec:
for (i = 0; i < 2; ++i)
d_fetchsl(&pdata, &data->u.dthd.thdResrv[i]);
d_fetchul(&pdata, &data->u.dthd.thdParID);
d_fetchstr(&pdata, data->u.dthd.thdCName,
sizeof(data->u.dthd.thdCName));
break;
case cdrFThdRec:
for (i = 0; i < 2; ++i)
d_fetchsl(&pdata, &data->u.fthd.fthdResrv[i]);
d_fetchul(&pdata, &data->u.fthd.fthdParID);
d_fetchstr(&pdata, data->u.fthd.fthdCName,
sizeof(data->u.fthd.fthdCName));
break;
default:
ASSERT(0);
}
}
/*
* NAME: record->packextdata()
* DESCRIPTION: pack extent record data
*/
void r_packextdata(const ExtDataRec *data, byte *pdata, unsigned int *len)
{
const byte *start = pdata;
int i;
for (i = 0; i < 3; ++i)
{
d_storeuw(&pdata, (*data)[i].xdrStABN);
d_storeuw(&pdata, (*data)[i].xdrNumABlks);
}
if (len)
*len += pdata - start;
}
/*
* NAME: record->unpackextdata()
* DESCRIPTION: unpack extent record data
*/
void r_unpackextdata(const byte *pdata, ExtDataRec *data)
{
int i;
for (i = 0; i < 3; ++i)
{
d_fetchuw(&pdata, &(*data)[i].xdrStABN);
d_fetchuw(&pdata, &(*data)[i].xdrNumABlks);
}
}
/*
* NAME: record->makecatkey()
* DESCRIPTION: construct a catalog record key
*/
void r_makecatkey(CatKeyRec *key, unsigned long parid, const char *name)
{
int len;
len = strlen(name) + 1;
key->ckrKeyLen = 0x05 + len + (len & 1);
key->ckrResrv1 = 0;
key->ckrParID = parid;
strcpy(key->ckrCName, name);
}
/*
* NAME: record->makeextkey()
* DESCRIPTION: construct an extents record key
*/
void r_makeextkey(ExtKeyRec *key,
int fork, unsigned long fnum, unsigned int fabn)
{
key->xkrKeyLen = 0x07;
key->xkrFkType = fork;
key->xkrFNum = fnum;
key->xkrFABN = fabn;
}
/*
* NAME: record->packcatrec()
* DESCRIPTION: create a packed catalog record
*/
void r_packcatrec(const CatKeyRec *key, const CatDataRec *data,
byte *precord, unsigned int *len)
{
r_packcatkey(key, precord, len);
r_packcatdata(data, HFS_RECDATA(precord), len);
}
/*
* NAME: record->packextrec()
* DESCRIPTION: create a packed extents record
*/
void r_packextrec(const ExtKeyRec *key, const ExtDataRec *data,
byte *precord, unsigned int *len)
{
r_packextkey(key, precord, len);
r_packextdata(data, HFS_RECDATA(precord), len);
}
/*
* NAME: record->packdirent()
* DESCRIPTION: make changes to a catalog record
*/
void r_packdirent(CatDataRec *data, const hfsdirent *ent)
{
switch (data->cdrType)
{
case cdrDirRec:
data->u.dir.dirCrDat = d_mtime(ent->crdate);
data->u.dir.dirMdDat = d_mtime(ent->mddate);
data->u.dir.dirBkDat = d_mtime(ent->bkdate);
data->u.dir.dirUsrInfo.frFlags = ent->fdflags;
data->u.dir.dirFndrInfo.frComment = ent->fdcomment;
data->u.dir.dirUsrInfo.frLocation.v = ent->fdlocation.v;
data->u.dir.dirUsrInfo.frLocation.h = ent->fdlocation.h;
data->u.dir.dirUsrInfo.frRect.top = ent->u.dir.rect.top;
data->u.dir.dirUsrInfo.frRect.left = ent->u.dir.rect.left;
data->u.dir.dirUsrInfo.frRect.bottom = ent->u.dir.rect.bottom;
data->u.dir.dirUsrInfo.frRect.right = ent->u.dir.rect.right;
break;
case cdrFilRec:
if (ent->flags & HFS_ISLOCKED)
data->u.fil.filFlags |= (1 << 0);
else
data->u.fil.filFlags &= ~(1 << 0);
data->u.fil.filCrDat = d_mtime(ent->crdate);
data->u.fil.filMdDat = d_mtime(ent->mddate);
data->u.fil.filBkDat = d_mtime(ent->bkdate);
data->u.fil.filUsrWds.fdFlags = ent->fdflags;
data->u.fil.filFndrInfo.fdComment = ent->fdcomment;
data->u.fil.filUsrWds.fdLocation.v = ent->fdlocation.v;
data->u.fil.filUsrWds.fdLocation.h = ent->fdlocation.h;
data->u.fil.filUsrWds.fdType =
d_getsl((const unsigned char *) ent->u.file.type);
data->u.fil.filUsrWds.fdCreator =
d_getsl((const unsigned char *) ent->u.file.creator);
break;
}
}
/*
* NAME: record->unpackdirent()
* DESCRIPTION: unpack catalog information into hfsdirent structure
*/
void r_unpackdirent(unsigned long parid, const char *name,
const CatDataRec *data, hfsdirent *ent)
{
strcpy(ent->name, name);
ent->parid = parid;
switch (data->cdrType)
{
case cdrDirRec:
ent->flags = HFS_ISDIR;
ent->cnid = data->u.dir.dirDirID;
ent->crdate = d_ltime(data->u.dir.dirCrDat);
ent->mddate = d_ltime(data->u.dir.dirMdDat);
ent->bkdate = d_ltime(data->u.dir.dirBkDat);
ent->fdflags = data->u.dir.dirUsrInfo.frFlags;
ent->fdcomment = data->u.dir.dirFndrInfo.frComment;
ent->fdlocation.v = data->u.dir.dirUsrInfo.frLocation.v;
ent->fdlocation.h = data->u.dir.dirUsrInfo.frLocation.h;
ent->u.dir.valence = data->u.dir.dirVal;
ent->u.dir.rect.top = data->u.dir.dirUsrInfo.frRect.top;
ent->u.dir.rect.left = data->u.dir.dirUsrInfo.frRect.left;
ent->u.dir.rect.bottom = data->u.dir.dirUsrInfo.frRect.bottom;
ent->u.dir.rect.right = data->u.dir.dirUsrInfo.frRect.right;
break;
case cdrFilRec:
ent->flags = (data->u.fil.filFlags & (1 << 0)) ? HFS_ISLOCKED : 0;
ent->cnid = data->u.fil.filFlNum;
ent->crdate = d_ltime(data->u.fil.filCrDat);
ent->mddate = d_ltime(data->u.fil.filMdDat);
ent->bkdate = d_ltime(data->u.fil.filBkDat);
ent->fdflags = data->u.fil.filUsrWds.fdFlags;
ent->fdcomment = data->u.fil.filFndrInfo.fdComment;
ent->fdlocation.v = data->u.fil.filUsrWds.fdLocation.v;
ent->fdlocation.h = data->u.fil.filUsrWds.fdLocation.h;
ent->u.file.dsize = data->u.fil.filLgLen;
ent->u.file.rsize = data->u.fil.filRLgLen;
d_putsl((unsigned char *) ent->u.file.type,
data->u.fil.filUsrWds.fdType);
d_putsl((unsigned char *) ent->u.file.creator,
data->u.fil.filUsrWds.fdCreator);
ent->u.file.type[4] = ent->u.file.creator[4] = 0;
break;
}
}

47
libhfs/record.h Normal file
View File

@ -0,0 +1,47 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: record.h,v 1.7 1998/11/02 22:09:08 rob Exp $
*/
void r_packcatkey(const CatKeyRec *, byte *, unsigned int *);
void r_unpackcatkey(const byte *, CatKeyRec *);
void r_packextkey(const ExtKeyRec *, byte *, unsigned int *);
void r_unpackextkey(const byte *, ExtKeyRec *);
int r_comparecatkeys(const CatKeyRec *, const CatKeyRec *);
int r_compareextkeys(const ExtKeyRec *, const ExtKeyRec *);
void r_packcatdata(const CatDataRec *, byte *, unsigned int *);
void r_unpackcatdata(const byte *, CatDataRec *);
void r_packextdata(const ExtDataRec *, byte *, unsigned int *);
void r_unpackextdata(const byte *, ExtDataRec *);
void r_makecatkey(CatKeyRec *, unsigned long, const char *);
void r_makeextkey(ExtKeyRec *, int, unsigned long, unsigned int);
void r_packcatrec(const CatKeyRec *, const CatDataRec *,
byte *, unsigned int *);
void r_packextrec(const ExtKeyRec *, const ExtDataRec *,
byte *, unsigned int *);
void r_packdirent(CatDataRec *, const hfsdirent *);
void r_unpackdirent(unsigned long, const char *,
const CatDataRec *, hfsdirent *);

29
libhfs/version.c Normal file
View File

@ -0,0 +1,29 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: version.c,v 1.11 1998/11/02 22:09:09 rob Exp $
*/
# include "version.h"
const char libhfs_rcsid[] =
"$Id: version.c,v 1.11 1998/11/02 22:09:09 rob Exp $";
const char libhfs_version[] = "libhfs version 3.2.6";
const char libhfs_copyright[] = "Copyright (C) 1996-1998 Robert Leslie";
const char libhfs_author[] = "Robert Leslie <rob@mars.org>";

26
libhfs/version.h Normal file
View File

@ -0,0 +1,26 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: version.h,v 1.6 1998/09/18 22:56:38 rob Exp $
*/
extern const char libhfs_rcsid[];
extern const char libhfs_version[];
extern const char libhfs_copyright[];
extern const char libhfs_author[];

1203
libhfs/volume.c Normal file

File diff suppressed because it is too large Load Diff

62
libhfs/volume.h Normal file
View File

@ -0,0 +1,62 @@
/*
* libhfs - library for reading and writing Macintosh HFS volumes
* Copyright (C) 1996-1998 Robert Leslie
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: volume.h,v 1.7 1998/11/02 22:09:12 rob Exp $
*/
void v_init(hfsvol *, int);
int v_open(hfsvol *, const char *, int);
int v_flush(hfsvol *);
int v_close(hfsvol *);
int v_same(hfsvol *, const char *);
int v_geometry(hfsvol *, int);
int v_readmdb(hfsvol *);
int v_writemdb(hfsvol *);
int v_readvbm(hfsvol *);
int v_writevbm(hfsvol *);
int v_mount(hfsvol *);
int v_dirty(hfsvol *);
int v_catsearch(hfsvol *, unsigned long, const char *,
CatDataRec *, char *, node *);
int v_extsearch(hfsfile *, unsigned int, ExtDataRec *, node *);
int v_getthread(hfsvol *, unsigned long, CatDataRec *, node *, int);
# define v_getdthread(vol, id, thread, np) \
v_getthread(vol, id, thread, np, cdrThdRec)
# define v_getfthread(vol, id, thread, np) \
v_getthread(vol, id, thread, np, cdrFThdRec)
int v_putcatrec(const CatDataRec *, node *);
int v_putextrec(const ExtDataRec *, node *);
int v_allocblocks(hfsvol *, ExtDescriptor *);
int v_freeblocks(hfsvol *, const ExtDescriptor *);
int v_resolve(hfsvol **, const char *, CatDataRec *, long *, char *, node *);
int v_adjvalence(hfsvol *, unsigned long, int, int);
int v_mkdir(hfsvol *, unsigned long, const char *);
int v_scavenge(hfsvol *);