main.m 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  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:i:b:l:d:hcr"
  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", no_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. BOOL repackage = FALSE;
  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 'i':
  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 = TRUE;
  81. break;
  82. case 'c':
  83. clean = TRUE;
  84. break;
  85. default:
  86. cmd_help();
  87. return -1;
  88. }
  89. }
  90. if (repackage == TRUE && debFile) {
  91. DLog(@"\n [INFO] Repackaging file: %@...\n", debFile);
  92. InputPackage *output = [HelperClass packageForDeb:debFile];
  93. [output repackageInCurrentDirectoryWithArch:@"appletvos-arm64"];
  94. return 0;
  95. }
  96. //DLog(@"folder: %@ project: %@", folder, project);
  97. if (clean && bootstrapPath) {
  98. NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  99. NSString *listContents = [NSString stringWithContentsOfFile:statusFile encoding:NSUTF8StringEncoding error:nil];
  100. //clean up any errant spaces
  101. listContents = [listContents stringByReplacingOccurrencesOfString:@"\n\n\n" withString:@"\n\n"];
  102. listContents = [listContents stringByReplacingOccurrencesOfString:@"\n\n\n\n" withString:@"\n\n"];
  103. [listContents writeToFile:statusFile atomically:TRUE];
  104. DLog(@"\n [INFO] Cleaning status file: %@", statusFile);
  105. NSString *testStatusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  106. NSArray <StatusPackageModel*> *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
  107. __block NSMutableString *newStatusFile = [NSMutableString new];
  108. __block NSMutableArray *alreadyAdded = [NSMutableArray new];
  109. [installedPackages enumerateObjectsUsingBlock:^(StatusPackageModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  110. DLog(@"\n [INFO] Processing pacakge: %@", obj.package);
  111. if (![alreadyAdded containsObject:obj.package]){
  112. //duplicate check first
  113. NSArray *doubleCheck = [installedPackages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"package == %@", obj.package]];
  114. if (doubleCheck.count > 1){
  115. NSSortDescriptor *sortDesc = [NSSortDescriptor sortDescriptorWithKey:@"version" ascending:FALSE];
  116. StatusPackageModel *latestVersion = [[doubleCheck sortedArrayUsingDescriptors:@[sortDesc]] firstObject];
  117. DLog(@"\n [INFO] We found a duplicate package, choosing latestVersion: %@", latestVersion);
  118. [newStatusFile appendFormat:@"%@\n\n", latestVersion.rawString];
  119. [alreadyAdded addObject:obj.package];
  120. } else {
  121. [newStatusFile appendFormat:@"%@\n\n", obj.rawString];
  122. }
  123. } else {
  124. DLog(@"\n [INFO] We already had this package, skipping older duplicate: %@\n", obj.package);
  125. }
  126. }];
  127. testStatusFile = [testStatusFile stringByReplacingOccurrencesOfString:@"\n\n\n" withString:@"\n\n"];
  128. DLog(@"\n Writing new output file: %@\n", testStatusFile);
  129. [newStatusFile writeToFile:testStatusFile atomically:TRUE];
  130. DLog(@"\n Done!\n\n");
  131. return 0;
  132. }
  133. if (deletePackage && bootstrapPath) {
  134. __block NSString *postInstFile = nil;
  135. NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  136. NSArray <StatusPackageModel*> *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
  137. StatusPackageModel *model = [[installedPackages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"package == %@", deletePackage]] lastObject];
  138. if (model) {
  139. NSString *listName = [NSString stringWithFormat:@"%@.list", deletePackage];
  140. NSString *infoPath = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/info"];
  141. NSString *listFile = [infoPath stringByAppendingPathComponent:listName];
  142. NSArray *listFiles = [FM contentsOfDirectoryAtPath:infoPath error:nil];
  143. NSPredicate *blockPred = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
  144. if ([evaluatedObject containsString:deletePackage]){
  145. return TRUE;
  146. }
  147. return FALSE;
  148. }];
  149. NSArray *relatedFiles = [listFiles filteredArrayUsingPredicate:blockPred]; //any files in dpkg/info/deletePackage.*
  150. DLog(@"\nWe have found the package, determining list details from: %@", listFile);
  151. NSString *listContents = [NSString stringWithContentsOfFile:listFile encoding:NSUTF8StringEncoding error:nil];
  152. DLog(@"\nContents: %@\n", listContents);
  153. DLog(@"\nFound package info files: %@\n", relatedFiles);
  154. NSArray *files = [listContents componentsSeparatedByString:@"\n"];
  155. __block NSMutableArray *filesToProcess = [NSMutableArray new];
  156. [relatedFiles enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  157. NSString *fullPath = [infoPath stringByAppendingPathComponent:obj];
  158. if([FM fileExistsAtPath:fullPath]){
  159. if ([[obj pathExtension] isEqualToString:@"postinst"]){
  160. postInstFile = fullPath;
  161. }
  162. [filesToProcess addObject:fullPath];
  163. }
  164. }];
  165. [files enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  166. NSString *fullPath = [bootstrapPath stringByAppendingPathComponent:obj];
  167. BOOL isDir = FALSE;
  168. if([FM fileExistsAtPath:fullPath isDirectory:&isDir]){
  169. if (!isDir){
  170. DLog(@"found file: %@", fullPath);
  171. [filesToProcess addObject:fullPath];
  172. }
  173. }
  174. }];
  175. DLog(@"\nFiles to process: %@\n", filesToProcess);
  176. NSString *error = [NSString stringWithFormat:@" [WARNING] We are about to delete '%@' from the bootstrap, this cannot be undone!", deletePackage];
  177. if(![HelperClass shouldContinueWithError:error]){
  178. return -1;
  179. }
  180. [filesToProcess enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  181. [FM removeItemAtPath:obj error:nil];
  182. }];
  183. //TODO: delete the files here!
  184. NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  185. [FM copyItemAtPath:statusFile toPath:[statusFile stringByAppendingPathExtension:@"bak"] error:nil];
  186. __block NSMutableString *newStatusFile = [NSMutableString new];
  187. [installedPackages enumerateObjectsUsingBlock:^(StatusPackageModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  188. if (obj != model){
  189. [newStatusFile appendFormat:@"%@\n\n", obj.rawString];
  190. } else {
  191. DLog(@"skipping the model we want to delete: %@", obj);
  192. }
  193. }];
  194. [newStatusFile writeToFile:statusFile atomically:TRUE];
  195. if (postInstFile) {
  196. 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");
  197. DLog(@"\n\n%@ contents:\n%@\n\n", postInstFile, [NSString stringWithContentsOfFile:postInstFile encoding:NSUTF8StringEncoding error:nil]);
  198. }
  199. }
  200. return 0;
  201. }
  202. if (octalFile) {
  203. NSString *octal = [HelperClass octalFromSymbols:octalFile];
  204. DLog(@"%@", octal);
  205. return 0;
  206. }
  207. if (bootstrapListPath) {
  208. NSString *statusFile = [bootstrapListPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  209. NSArray *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
  210. DLog(@"%@", installedPackages);
  211. return 0;
  212. }
  213. if (debFile && bootstrapPath) {
  214. NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  215. NSArray *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
  216. //DLog(@"installedPackages: %@", installedPackages);
  217. DLog(@"\nProcessing file: %@\n", debFile);
  218. InputPackage *output = [HelperClass packageForDeb:debFile];
  219. DLog(@"\nFound package: '%@' at version: '%@'...\n", output.packageName, output.version );
  220. StatusPackageModel *model = [[installedPackages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"package == %@", output.packageName]] lastObject];
  221. if (model) {
  222. DLog(@"\n [WARNING] This package has already been installed! %@", model);
  223. NSComparisonResult result = [output.version compare:model.version options:NSNumericSearch];
  224. DLog(@"\n [WARNING] Comparing version numbers...");
  225. switch (result) {
  226. case NSOrderedSame:
  227. DLog(@"\n [WARNING] %@ and %@ match!", output.version, model.version);
  228. break;
  229. case NSOrderedAscending:
  230. DLog(@"\n [WARNING] Package version %@ is less than installed version %@!", output.version, model.version);
  231. break;
  232. case NSOrderedDescending:
  233. DLog(@"\n [WARNING] Package version %@ is greater than installed version %@!", output.version, model.version);
  234. break;
  235. default:
  236. break;
  237. }
  238. }
  239. ErrorReturn *safePackage = [output errorReturnForBootstrap:bootstrapPath];
  240. if (safePackage.returnStatus != 0) { //zero is success
  241. if (safePackage.returnStatus == 1) //ovewrwrites, just warnings!
  242. {
  243. NSString *error = [NSString stringWithFormat:@" [WARNING] %@ will overwrite the following files: \n\n\t%@\n\n", debFile.lastPathComponent, [safePackage.overwriteFiles componentsJoinedByString:@"\n\t"]];
  244. if(![HelperClass shouldContinueWithError:error]){
  245. return -1;
  246. }
  247. } else if (safePackage.returnStatus == 2) //bail!!"
  248. {
  249. return 2;
  250. }
  251. }
  252. //NSString *runPath = [NSString stringWithUTF8String:path];
  253. NSString *pwd = [[HelperClass returnForProcess:@"/bin/pwd"] componentsJoinedByString:@"\n"];
  254. //DLog(@"run path: %@", runPath);
  255. //DLog(@"pwd: %@", pwd);
  256. //DLog(@"%@", output);
  257. //DLog(@"list: %@", output.listfile);
  258. NSFileManager *man = [NSFileManager defaultManager];
  259. NSString *tmpPath = [pwd stringByAppendingPathComponent:output.packageName];
  260. NSString *debian = [tmpPath stringByAppendingPathComponent:@"DEBIAN"];
  261. [man createDirectoryAtPath:tmpPath withIntermediateDirectories:TRUE attributes:nil error:nil];
  262. DLog(@"\nExtracting deb for processing...\n");
  263. [HelperClass returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -x %@ %@", debFile, tmpPath]];
  264. NSString *bootstrapInfoPath = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/info"];
  265. NSString *listFile = [bootstrapInfoPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.list", output.packageName]];
  266. NSString *md5s = [bootstrapInfoPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.md5sums", output.packageName]];
  267. DLog(@"\nCreating list file '%@'...\n", listFile);
  268. [output.listfile writeToFile:listFile atomically:TRUE encoding:NSUTF8StringEncoding error:nil];
  269. DLog(@"\nGenerating md5sums...\n");
  270. 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];
  271. NSString *outputs = [[HelperClass returnForProcess:runString] componentsJoinedByString:@"\n"];
  272. DLog(@"\nCreating md5sum file '%@'...\n", md5s);
  273. [outputs writeToFile:md5s atomically:TRUE encoding:NSUTF8StringEncoding error:nil];
  274. [man createDirectoryAtPath:debian withIntermediateDirectories:TRUE attributes:nil error:nil];
  275. DLog(@"\nExtracting DEBIAN files for processing...\n");
  276. [HelperClass returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -e %@ %@", debFile, debian]];
  277. //NSString *nextPath = [tmpPath stringByAppendingPathComponent:@"DEBIAN"];
  278. DLog(@"\nCopying any necessary DEBIAN files to new locations...\n\n");
  279. NSArray *files = [man contentsOfDirectoryAtPath:debian error:nil];
  280. [files enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  281. NSString *fullPath = [debian stringByAppendingPathComponent:obj];
  282. if (![obj isEqualToString:@"control"]){
  283. DLog(@"Copying file... %@\n\n", fullPath);
  284. NSString *fileName = [NSString stringWithFormat:@"%@.%@", output.packageName, obj];
  285. NSString *newPath = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/info"];
  286. newPath = [newPath stringByAppendingPathComponent:fileName];
  287. //DLog(@"newPath: %@", newPath);
  288. [man copyItemAtPath:fullPath toPath:newPath error:nil];
  289. } else {
  290. NSMutableString *controlFile = [[NSMutableString alloc] initWithContentsOfFile:fullPath encoding:NSUTF8StringEncoding error:nil];
  291. [controlFile replaceOccurrencesOfString:@"iphoneos-arm" withString:@"appletvos-arm64" options:NSLiteralSearch range:NSMakeRange(0, [controlFile length])];
  292. [controlFile appendString:@"Status: install ok installed"];
  293. //DLog(@"control file: -%@-\n", controlFile);
  294. NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  295. [FM copyItemAtPath:statusFile toPath:[statusFile stringByAppendingPathExtension:@"bak"] error:nil];
  296. NSMutableString *statusContents = [[NSMutableString alloc] initWithContentsOfFile:statusFile encoding:NSUTF8StringEncoding error:nil];
  297. if (model) { //we found the package model, just replace the old string with our new one
  298. DLog(@"Updating status file for new package version...\n\n");
  299. [statusContents replaceOccurrencesOfString:model.rawString withString:controlFile options:NSLiteralSearch range:NSMakeRange(0, [statusContents length])];
  300. //[statusContents writeToFileWithoutAttributes:statusFile];
  301. [statusContents writeToFile:statusFile atomically:TRUE encoding:NSUTF8StringEncoding error:nil];
  302. } else {
  303. DLog(@"Updating status file for new package...\n\n");
  304. //DLog(@"down here??: -%@-\n", controlFile);
  305. [statusContents appendFormat:@"%@\n", controlFile];
  306. //DLog(@"\nnew status file: %@\n", statusContents);
  307. //[statusContents writeToFileWithoutAttributes:statusFile];
  308. [statusContents writeToFile:statusFile atomically:TRUE encoding:NSUTF8StringEncoding error:nil];
  309. }
  310. //DLog(@"\n\nwe down here??\n\n");
  311. //NSString *newString = [[HelperClass returnForProcess:[NSString stringWithFormat:@"/bin/cat %@", statusFile]] componentsJoinedByString:@"\n"];
  312. //DLog(@"%@", newString);
  313. //DLog(@"status contents: %@", statusContents);
  314. // [statusContents writeToFile:statusFile atomically:TRUE encoding:NSUTF8StringEncoding error:nil];
  315. }
  316. }];
  317. //finally actually install the package onto the bootstrap
  318. DLog(@"Extracting package onto bootstrap folder...\n\n");
  319. [HelperClass returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -x %@ %@", debFile, bootstrapPath]];
  320. //DLog(@"outputs: %@", outputs);
  321. ///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"
  322. DLog(@"Done!\n\n");
  323. return 0;
  324. }
  325. //if we got this far.. nothing happened, print help.
  326. cmd_help();
  327. }
  328. return 0;
  329. }