main.m 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. //
  2. // main.m
  3. // octalConversion
  4. //
  5. // Created by Kevin Bradley on 6/28/18.
  6. // Copyright © 2018 nito. All rights reserved.
  7. //
  8. #import <Foundation/Foundation.h>
  9. #include <stdio.h>
  10. #include <errno.h>
  11. #include <libgen.h>
  12. #include <string.h>
  13. #include <stdlib.h>
  14. #include <unistd.h>
  15. #include <getopt.h>
  16. #import <sys/utsname.h>
  17. #import "HelperClass.h"
  18. /*
  19. fileType, @"fileType",octalPermissions, @"octalPermissions", octalUG, @"octalUG", size, @"size", date, @"date", time, @"time", linkDest, @"linkDest", fullPath, @"fullPath", nil];
  20. */
  21. #define OPTION_FLAGS "o:f:b:l:d:hc"
  22. char *progname;
  23. char *path;
  24. static struct option longopts[] = {
  25. { "octal", required_argument, NULL, 'o' },
  26. { "input", required_argument, NULL, 'i' },
  27. { "bootstrap", required_argument, NULL, 'b' },
  28. { "list", required_argument, NULL, 'l' },
  29. { "delete", required_argument, NULL, 'd' },
  30. { "help", no_argument, NULL, 'h' },
  31. { "clean", no_argument, NULL, 'c' },
  32. { "repackage", required_argument, NULL, 'r' },
  33. { NULL, 0, NULL, 0 }
  34. };
  35. void cmd_help(){
  36. printf("Usage: bootstrapTool [OPTIONS] FILE\n");
  37. printf("Makes various modifications to a raw bootstrap folder\n\n");
  38. printf(" -h, --help\t\t\tprints usage information\n");
  39. printf(" -i, --inoput\t\t\tthe deb package to install\n");
  40. printf(" -b, --bootstrap\t\tthe bootstrap folder\n");
  41. printf(" -d, --delete\t\t\tthe package to delete\n");
  42. printf(" -l, --list\t\t\tlist all the packages installed on this bootstrap\n");
  43. printf(" -c, --clean\t\t\tclean a bad status file from duplicate entries\n");
  44. printf(" -r, --repackage\t\t\trepackage to fix bad locations and to change architecture\n");
  45. printf("\n");
  46. }
  47. int main(int argc, const char * argv[]) {
  48. @autoreleasepool {
  49. setuid(0);
  50. setgid(0);
  51. // insert code here...
  52. progname = basename(argv[0]);
  53. path = dirname(argv[0]);
  54. int flag;
  55. NSString *octalFile = nil;
  56. NSString *debFile = nil;
  57. NSString *bootstrapPath = nil;
  58. NSString *bootstrapListPath = nil;
  59. NSString *deletePackage = nil;
  60. NSString *repackage = nil;
  61. BOOL clean = FALSE;
  62. while ((flag = getopt_long(argc, argv, OPTION_FLAGS, longopts, NULL)) != -1) {
  63. switch(flag) {
  64. case 'o':
  65. octalFile = [NSString stringWithUTF8String:optarg];
  66. break;
  67. case 'f':
  68. debFile = [NSString stringWithUTF8String:optarg];
  69. break;
  70. case 'b':
  71. bootstrapPath = [NSString stringWithUTF8String:optarg];
  72. break;
  73. case 'l':
  74. bootstrapListPath = [NSString stringWithUTF8String:optarg];
  75. break;
  76. case 'd':
  77. deletePackage = [NSString stringWithUTF8String:optarg];
  78. break;
  79. case 'r':
  80. repackage = [NSString stringWithUTF8String:optarg];
  81. break;
  82. case 'c':
  83. clean = TRUE;
  84. break;
  85. default:
  86. cmd_help();
  87. return -1;
  88. }
  89. }
  90. if (repackage) {
  91. NSString *pwd = [[HelperClass returnForProcess:@"/bin/pwd"] componentsJoinedByString:@"\n"];
  92. DLog(@"\nProcessing file: %@\n", repackage);
  93. InputPackage *output = [HelperClass packageForDeb:repackage];
  94. DLog(@"\nFound package: '%@' at version: '%@'...\n", output.packageName, output.version );
  95. NSString *tmpPath = [pwd stringByAppendingPathComponent:output.packageName];
  96. NSString *debian = [tmpPath stringByAppendingPathComponent:@"DEBIAN"];
  97. [FM createDirectoryAtPath:tmpPath withIntermediateDirectories:TRUE attributes:nil error:nil];
  98. DLog(@"\nExtracting package contents for processing...\n");
  99. [HelperClass returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -x %@ %@", debFile, tmpPath]];
  100. [FM createDirectoryAtPath:debian withIntermediateDirectories:TRUE attributes:nil error:nil];
  101. DLog(@"\nExtracting DEBIAN files for processing...\n");
  102. [HelperClass returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -e %@ %@", debFile, debian]];
  103. NSString *controlPath = [debian stringByAppendingPathComponent:@"control"];
  104. NSMutableString *controlFile = [[NSMutableString alloc] initWithContentsOfFile:controlPath encoding:NSUTF8StringEncoding error:nil];
  105. [controlFile replaceOccurrencesOfString:@"iphoneos-arm" withString:@"appletvos-arm64" options:NSLiteralSearch range:NSMakeRange(0, [controlFile length])];
  106. [controlFile writeToFile:controlPath atomically:TRUE];
  107. }
  108. //DLog(@"folder: %@ project: %@", folder, project);
  109. if (clean && bootstrapPath) {
  110. NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  111. NSString *listContents = [NSString stringWithContentsOfFile:statusFile encoding:NSUTF8StringEncoding error:nil];
  112. //clean up any errant spaces
  113. listContents = [listContents stringByReplacingOccurrencesOfString:@"\n\n\n" withString:@"\n\n"];
  114. listContents = [listContents stringByReplacingOccurrencesOfString:@"\n\n\n\n" withString:@"\n\n"];
  115. [listContents writeToFile:statusFile atomically:TRUE];
  116. DLog(@"\n [INFO] Cleaning status file: %@", statusFile);
  117. NSString *testStatusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  118. NSArray <StatusPackageModel*> *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
  119. __block NSMutableString *newStatusFile = [NSMutableString new];
  120. __block NSMutableArray *alreadyAdded = [NSMutableArray new];
  121. [installedPackages enumerateObjectsUsingBlock:^(StatusPackageModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  122. DLog(@"\n [INFO] Processing pacakge: %@", obj.package);
  123. if (![alreadyAdded containsObject:obj.package]){
  124. //duplicate check first
  125. NSArray *doubleCheck = [installedPackages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"package == %@", obj.package]];
  126. if (doubleCheck.count > 1){
  127. NSSortDescriptor *sortDesc = [NSSortDescriptor sortDescriptorWithKey:@"version" ascending:FALSE];
  128. StatusPackageModel *latestVersion = [[doubleCheck sortedArrayUsingDescriptors:@[sortDesc]] firstObject];
  129. DLog(@"\n [INFO] We found a duplicate package, choosing latestVersion: %@", latestVersion);
  130. [newStatusFile appendFormat:@"%@\n\n", latestVersion.rawString];
  131. [alreadyAdded addObject:obj.package];
  132. } else {
  133. [newStatusFile appendFormat:@"%@\n\n", obj.rawString];
  134. }
  135. } else {
  136. DLog(@"\n [INFO] We already had this package, skipping older duplicate: %@\n", obj.package);
  137. }
  138. }];
  139. testStatusFile = [testStatusFile stringByReplacingOccurrencesOfString:@"\n\n\n" withString:@"\n\n"];
  140. DLog(@"\n Writing new output file: %@\n", testStatusFile);
  141. [newStatusFile writeToFile:testStatusFile atomically:TRUE];
  142. DLog(@"\n Done!\n\n");
  143. return 0;
  144. }
  145. if (deletePackage && bootstrapPath) {
  146. __block NSString *postInstFile = nil;
  147. NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  148. NSArray <StatusPackageModel*> *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
  149. StatusPackageModel *model = [[installedPackages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"package == %@", deletePackage]] lastObject];
  150. if (model) {
  151. NSString *listName = [NSString stringWithFormat:@"%@.list", deletePackage];
  152. NSString *infoPath = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/info"];
  153. NSString *listFile = [infoPath stringByAppendingPathComponent:listName];
  154. NSArray *listFiles = [FM contentsOfDirectoryAtPath:infoPath error:nil];
  155. NSPredicate *blockPred = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
  156. if ([evaluatedObject containsString:deletePackage]){
  157. return TRUE;
  158. }
  159. return FALSE;
  160. }];
  161. NSArray *relatedFiles = [listFiles filteredArrayUsingPredicate:blockPred]; //any files in dpkg/info/deletePackage.*
  162. DLog(@"\nWe have found the package, determining list details from: %@", listFile);
  163. NSString *listContents = [NSString stringWithContentsOfFile:listFile encoding:NSUTF8StringEncoding error:nil];
  164. DLog(@"\nContents: %@\n", listContents);
  165. DLog(@"\nFound package info files: %@\n", relatedFiles);
  166. NSArray *files = [listContents componentsSeparatedByString:@"\n"];
  167. __block NSMutableArray *filesToProcess = [NSMutableArray new];
  168. [relatedFiles enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  169. NSString *fullPath = [infoPath stringByAppendingPathComponent:obj];
  170. if([FM fileExistsAtPath:fullPath]){
  171. if ([[obj pathExtension] isEqualToString:@"postinst"]){
  172. postInstFile = fullPath;
  173. }
  174. [filesToProcess addObject:fullPath];
  175. }
  176. }];
  177. [files enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  178. NSString *fullPath = [bootstrapPath stringByAppendingPathComponent:obj];
  179. BOOL isDir = FALSE;
  180. if([FM fileExistsAtPath:fullPath isDirectory:&isDir]){
  181. if (!isDir){
  182. DLog(@"found file: %@", fullPath);
  183. [filesToProcess addObject:fullPath];
  184. }
  185. }
  186. }];
  187. DLog(@"\nFiles to process: %@\n", filesToProcess);
  188. NSString *error = [NSString stringWithFormat:@" [WARNING] We are about to delete '%@' from the bootstrap, this cannot be undone!", deletePackage];
  189. if(![HelperClass shouldContinueWithError:error]){
  190. return -1;
  191. }
  192. [filesToProcess enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  193. [FM removeItemAtPath:obj error:nil];
  194. }];
  195. //TODO: delete the files here!
  196. NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  197. [FM copyItemAtPath:statusFile toPath:[statusFile stringByAppendingPathExtension:@"bak"] error:nil];
  198. __block NSMutableString *newStatusFile = [NSMutableString new];
  199. [installedPackages enumerateObjectsUsingBlock:^(StatusPackageModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  200. if (obj != model){
  201. [newStatusFile appendFormat:@"%@\n\n", obj.rawString];
  202. } else {
  203. DLog(@"skipping the model we want to delete: %@", obj);
  204. }
  205. }];
  206. [newStatusFile writeToFile:statusFile atomically:TRUE];
  207. if (postInstFile) {
  208. DLog(@"\n [WARNING] We found a postinst file, will not run this due to potential unexpected consequences off of the target device, displaying contents so you can determine if any additional steps are necessary\n");
  209. DLog(@"\n\n%@ contents:\n%@\n\n", postInstFile, [NSString stringWithContentsOfFile:postInstFile encoding:NSUTF8StringEncoding error:nil]);
  210. }
  211. }
  212. return 0;
  213. }
  214. if (octalFile) {
  215. NSString *octal = [HelperClass octalFromSymbols:octalFile];
  216. DLog(@"%@", octal);
  217. return 0;
  218. }
  219. if (bootstrapListPath) {
  220. NSString *statusFile = [bootstrapListPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  221. NSArray *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
  222. DLog(@"%@", installedPackages);
  223. return 0;
  224. }
  225. if (debFile && bootstrapPath) {
  226. NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  227. NSArray *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
  228. //DLog(@"installedPackages: %@", installedPackages);
  229. DLog(@"\nProcessing file: %@\n", debFile);
  230. InputPackage *output = [HelperClass packageForDeb:debFile];
  231. DLog(@"\nFound package: '%@' at version: '%@'...\n", output.packageName, output.version );
  232. StatusPackageModel *model = [[installedPackages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"package == %@", output.packageName]] lastObject];
  233. if (model) {
  234. DLog(@"\n [WARNING] This package has already been installed! %@", model);
  235. NSComparisonResult result = [output.version compare:model.version options:NSNumericSearch];
  236. DLog(@"\n [WARNING] Comparing version numbers...");
  237. switch (result) {
  238. case NSOrderedSame:
  239. DLog(@"\n [WARNING] %@ and %@ match!", output.version, model.version);
  240. break;
  241. case NSOrderedAscending:
  242. DLog(@"\n [WARNING] Package version %@ is less than installed version %@!", output.version, model.version);
  243. break;
  244. case NSOrderedDescending:
  245. DLog(@"\n [WARNING] Package version %@ is greater than installed version %@!", output.version, model.version);
  246. break;
  247. default:
  248. break;
  249. }
  250. }
  251. ErrorReturn *safePackage = [output errorReturnForBootstrap:bootstrapPath];
  252. if (safePackage.returnStatus != 0) { //zero is success
  253. if (safePackage.returnStatus == 1) //ovewrwrites, just warnings!
  254. {
  255. NSString *error = [NSString stringWithFormat:@" [WARNING] %@ will overwrite the following files: \n\n\t%@\n\n", debFile.lastPathComponent, [safePackage.overwriteFiles componentsJoinedByString:@"\n\t"]];
  256. if(![HelperClass shouldContinueWithError:error]){
  257. return -1;
  258. }
  259. } else if (safePackage.returnStatus == 2) //bail!!"
  260. {
  261. return 2;
  262. }
  263. }
  264. //NSString *runPath = [NSString stringWithUTF8String:path];
  265. NSString *pwd = [[HelperClass returnForProcess:@"/bin/pwd"] componentsJoinedByString:@"\n"];
  266. //DLog(@"run path: %@", runPath);
  267. //DLog(@"pwd: %@", pwd);
  268. //DLog(@"%@", output);
  269. //DLog(@"list: %@", output.listfile);
  270. NSFileManager *man = [NSFileManager defaultManager];
  271. NSString *tmpPath = [pwd stringByAppendingPathComponent:output.packageName];
  272. NSString *debian = [tmpPath stringByAppendingPathComponent:@"DEBIAN"];
  273. [man createDirectoryAtPath:tmpPath withIntermediateDirectories:TRUE attributes:nil error:nil];
  274. DLog(@"\nExtracting deb for processing...\n");
  275. [HelperClass returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -x %@ %@", debFile, tmpPath]];
  276. NSString *bootstrapInfoPath = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/info"];
  277. NSString *listFile = [bootstrapInfoPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.list", output.packageName]];
  278. NSString *md5s = [bootstrapInfoPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.md5sums", output.packageName]];
  279. DLog(@"\nCreating list file '%@'...\n", listFile);
  280. [output.listfile writeToFile:listFile atomically:TRUE encoding:NSUTF8StringEncoding error:nil];
  281. DLog(@"\nGenerating md5sums...\n");
  282. NSString *runString = [NSString stringWithFormat:@"/usr/bin/find %@ -type f -not -path \"*.DS_Store*\" -exec /sbin/md5 -r {} \\; | /usr/bin/awk '{ print $1 \" \" $2 }' | /usr/bin/sed \"s|%@||g\"", tmpPath, tmpPath];
  283. NSString *outputs = [[HelperClass returnForProcess:runString] componentsJoinedByString:@"\n"];
  284. DLog(@"\nCreating md5sum file '%@'...\n", md5s);
  285. [outputs writeToFile:md5s atomically:TRUE encoding:NSUTF8StringEncoding error:nil];
  286. [man createDirectoryAtPath:debian withIntermediateDirectories:TRUE attributes:nil error:nil];
  287. DLog(@"\nExtracting DEBIAN files for processing...\n");
  288. [HelperClass returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -e %@ %@", debFile, debian]];
  289. //NSString *nextPath = [tmpPath stringByAppendingPathComponent:@"DEBIAN"];
  290. DLog(@"\nCopying any necessary DEBIAN files to new locations...\n\n");
  291. NSArray *files = [man contentsOfDirectoryAtPath:debian error:nil];
  292. [files enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  293. NSString *fullPath = [debian stringByAppendingPathComponent:obj];
  294. if (![obj isEqualToString:@"control"]){
  295. DLog(@"Copying file... %@\n\n", fullPath);
  296. NSString *fileName = [NSString stringWithFormat:@"%@.%@", output.packageName, obj];
  297. NSString *newPath = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/info"];
  298. newPath = [newPath stringByAppendingPathComponent:fileName];
  299. //DLog(@"newPath: %@", newPath);
  300. [man copyItemAtPath:fullPath toPath:newPath error:nil];
  301. } else {
  302. NSMutableString *controlFile = [[NSMutableString alloc] initWithContentsOfFile:fullPath encoding:NSUTF8StringEncoding error:nil];
  303. [controlFile replaceOccurrencesOfString:@"iphoneos-arm" withString:@"appletvos-arm64" options:NSLiteralSearch range:NSMakeRange(0, [controlFile length])];
  304. [controlFile appendString:@"Status: install ok installed"];
  305. //DLog(@"control file: -%@-\n", controlFile);
  306. NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  307. [FM copyItemAtPath:statusFile toPath:[statusFile stringByAppendingPathExtension:@"bak"] error:nil];
  308. NSMutableString *statusContents = [[NSMutableString alloc] initWithContentsOfFile:statusFile encoding:NSUTF8StringEncoding error:nil];
  309. if (model) { //we found the package model, just replace the old string with our new one
  310. DLog(@"Updating status file for new package version...\n\n");
  311. [statusContents replaceOccurrencesOfString:model.rawString withString:controlFile options:NSLiteralSearch range:NSMakeRange(0, [statusContents length])];
  312. //[statusContents writeToFileWithoutAttributes:statusFile];
  313. [statusContents writeToFile:statusFile atomically:TRUE encoding:NSUTF8StringEncoding error:nil];
  314. } else {
  315. DLog(@"Updating status file for new package...\n\n");
  316. //DLog(@"down here??: -%@-\n", controlFile);
  317. [statusContents appendFormat:@"%@\n", controlFile];
  318. //DLog(@"\nnew status file: %@\n", statusContents);
  319. //[statusContents writeToFileWithoutAttributes:statusFile];
  320. [statusContents writeToFile:statusFile atomically:TRUE encoding:NSUTF8StringEncoding error:nil];
  321. }
  322. //DLog(@"\n\nwe down here??\n\n");
  323. //NSString *newString = [[HelperClass returnForProcess:[NSString stringWithFormat:@"/bin/cat %@", statusFile]] componentsJoinedByString:@"\n"];
  324. //DLog(@"%@", newString);
  325. //DLog(@"status contents: %@", statusContents);
  326. // [statusContents writeToFile:statusFile atomically:TRUE encoding:NSUTF8StringEncoding error:nil];
  327. }
  328. }];
  329. //finally actually install the package onto the bootstrap
  330. DLog(@"Extracting package onto bootstrap folder...\n\n");
  331. [HelperClass returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -x %@ %@", debFile, bootstrapPath]];
  332. //DLog(@"outputs: %@", outputs);
  333. ///usr/bin/find "$TARGET_DIR" -type f -not -path "$TARGET_DIR/DEBIAN/*" -exec "/sbin/md5 -r" {} \; | /usr/bin/awk '{ print $1 " " $2 }' | /usr/bin/sed "s|$TARGET_DIR/||g"
  334. DLog(@"Done!\n\n");
  335. return 0;
  336. }
  337. //if we got this far.. nothing happened, print help.
  338. cmd_help();
  339. }
  340. return 0;
  341. }