Skip to content

Commit

Permalink
Replace the APK HTTP server with a filesystem
Browse files Browse the repository at this point in the history
This avoids the local network permission prompt.
  • Loading branch information
tbodt committed Nov 28, 2020
1 parent d16eed9 commit 9991676
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 170 deletions.
8 changes: 8 additions & 0 deletions app/APKFilesystem.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//
// APKFilesystem.h
// iSH
//
// Created by Theodore Dubois on 11/27/20.
//

extern const struct fs_ops apkfs;
160 changes: 160 additions & 0 deletions app/APKFilesystem.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
//
// APKFilesystem.m
// iSH
//
// Created by Theodore Dubois on 11/27/20.
//

#import <Foundation/Foundation.h>
#include "kernel/errno.h"
#include "kernel/fs.h"
#include "fs/real.h"

static NSSet<NSString *> *ODRTags() {
static NSMutableSet<NSString *> *tags;
static dispatch_once_t once;
dispatch_once(&once, ^{
NSDictionary *plist = [NSDictionary dictionaryWithContentsOfURL:[NSBundle.mainBundle URLForResource:@"OnDemandResources" withExtension:@"plist"]];
NSDictionary *tagsDict = plist[@"NSBundleResourceRequestTags"];
tags = [NSMutableSet new];
for (NSString *key in tagsDict.keyEnumerator) {
[tags addObject:key];
// Add "directories", e.g. main:x86:APKINDEX.tar.gz also adds main: and main:x86:
for (NSUInteger i = 0; i < key.length; i++) {
if ([key characterAtIndex:i] == '/') {
[tags addObject:[key substringToIndex:i+1]];
}
}
}
[tags addObject:@":"];
});
return tags;
}

static NSString *TagForPath(const char *path) {
NSString *tag = [[NSString stringWithCString:path encoding:NSUTF8StringEncoding]
stringByReplacingOccurrencesOfString:@"/" withString:@":"];
if ([tag hasPrefix:@":"])
tag = [tag substringFromIndex:1];
return tag;
}

static int apkfs_path_type(const char *path) {
NSSet<NSString *> *odrTags = ODRTags();
NSString *tag = TagForPath(path);
if ([odrTags containsObject:tag])
return S_IFREG;
else if ([odrTags containsObject:[tag stringByAppendingString:@":"]])
return S_IFDIR;
else
return 0;
}

static const struct fd_ops apkfs_dir_ops;
static const struct fd_ops apkfs_file_ops;

static int apkfs_stat(struct mount *mount, const char *path, struct statbuf *stat) {
switch (apkfs_path_type(path)) {
case S_IFREG:
stat->mode = S_IFREG | 0444; break;
case S_IFDIR:
stat->mode = S_IFDIR | 0555; break;
default:
return _ENOENT;
}
return 0;
}
static int apkfs_fstat(struct fd *fd, struct statbuf *stat) {
if (fd->ops == &apkfs_dir_ops)
stat->mode = S_IFDIR | 0555;
else if (fd->ops == &apkfs_file_ops)
stat->mode = S_IFREG | 0444;
return 0;
}

static struct fd *apkfs_open(struct mount *mount, const char *path, int flags, int mode) {
int type = apkfs_path_type(path);
if (type == 0)
return ERR_PTR(_ENOENT);
if (type == S_IFDIR)
return fd_create(&apkfs_dir_ops);

if (type == S_IFREG) {
__block BOOL complete = NO;
__block NSError *error;
__block lock_t complete_lock;
__block cond_t complete_cond;
lock_init(&complete_lock);
cond_init(&complete_cond);

NSString *tag = TagForPath(path);
NSBundleResourceRequest *request = [[NSBundleResourceRequest alloc] initWithTags:[NSSet setWithObject:tag]];
[request beginAccessingResourcesWithCompletionHandler:^(NSError * _Nullable err) {
lock(&complete_lock);
error = err;
complete = YES;
notify(&complete_cond);
unlock(&complete_lock);
}];

while (!complete) {
int err = wait_for(&complete_cond, &complete_lock, NULL);
if (err == _EINTR) {
[request.progress cancel];
[request endAccessingResources];
return ERR_PTR(err);
}
}

if (error != nil) {
int err = _EIO;
if (error.code == NSBundleOnDemandResourceInvalidTagError)
err = _ENOENT;
if (error.code == NSBundleOnDemandResourceOutOfSpaceError)
err = _ENOSPC;
return ERR_PTR(err);
}

struct fd *fd = fd_create(&apkfs_file_ops);
if (fd == NULL) {
[request endAccessingResources];
return ERR_PTR(_ENOMEM);
}
const char *real_path = [request.bundle URLForResource:tag withExtension:nil].fileSystemRepresentation;
fd->real_fd = open(real_path, O_RDONLY);
if (fd->real_fd < 0) {
[request endAccessingResources];
return ERR_PTR(errno_map());
}
fd->data = (void *) CFBridgingRetain(request);
return fd;
}
return ERR_PTR(_ENOENT);
}

static int apkfs_close(struct fd *fd) {
NSBundleResourceRequest *request = CFBridgingRelease(fd->data);
[request endAccessingResources];
return 0;
}

static int apkfs_dir_readdir(struct fd *fd, struct dir_entry *entry) {
return 0;
}

const struct fs_ops apkfs = {
.name = "apk", .magic = 0x61706b20,
.stat = apkfs_stat,
.open = apkfs_open,
.fstat = apkfs_fstat,
.close = apkfs_close,
};

static const struct fd_ops apkfs_dir_ops = {
.readdir = apkfs_dir_readdir,
};
static const struct fd_ops apkfs_file_ops = {
.read = realfs_read,
.lseek = realfs_lseek,
.close = realfs_close,
};
16 changes: 0 additions & 16 deletions app/APKServer.h

This file was deleted.

139 changes: 0 additions & 139 deletions app/APKServer.m

This file was deleted.

16 changes: 10 additions & 6 deletions app/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#import "Roots.h"
#import "TerminalViewController.h"
#import "UserPreferences.h"
#import "APKServer.h"
#import "APKFilesystem.h"
#include "kernel/init.h"
#include "kernel/calls.h"
#include "fs/dyndev.h"
Expand All @@ -32,7 +32,6 @@ @interface AppDelegate ()
@property BOOL exiting;
@property NSString *unameVersion;
@property SCNetworkReachabilityRef reachability;
@property APKServer *apkServer;

@end

Expand Down Expand Up @@ -76,14 +75,23 @@ - (int)boot {
return err;

// /etc/ish-version is the last ish version that opened this root. Not used for anything yet, but could be used to know whether to change the root if needed in a future update.
BOOL has_ish_version = NO;
struct fd *ish_version = generic_open("/etc/ish-version", O_WRONLY_|O_TRUNC_, 0644);
if (!IS_ERR(ish_version)) {
has_ish_version = YES;
NSString *version = NSBundle.mainBundle.infoDictionary[(__bridge NSString *) kCFBundleVersionKey];
NSString *file = [NSString stringWithFormat:@"%@\n", version];
ish_version->ops->write(ish_version, file.UTF8String, [file lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
fd_close(ish_version);
}

if (has_ish_version && [NSBundle.mainBundle URLForResource:@"OnDemandResources" withExtension:@"plist"] != nil) {
fs_register(&apkfs);
generic_mkdirat(AT_PWD, "/ios", 0755);
generic_mkdirat(AT_PWD, "/ios/apk", 0755);
do_mount(&apkfs, "apk", "/ios/apk", "", 0);
}

// create some device nodes
// this will do nothing if they already exist
generic_mknodat(AT_PWD, "/dev/tty1", S_IFCHR|0666, dev_make(TTY_CONSOLE_MAJOR, 1));
Expand Down Expand Up @@ -240,10 +248,6 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
SCNetworkReachabilitySetCallback(self.reachability, NetworkReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop(self.reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);

if ([NSBundle.mainBundle URLForResource:@"OnDemandResources" withExtension:@"plist"] != nil) {
self.apkServer = [APKServer new];
}

if (self.window != nil) {
// For iOS <13, where the app delegate owns the window instead of the scene
if ([NSUserDefaults.standardUserDefaults boolForKey:@"recovery"]) {
Expand Down
2 changes: 1 addition & 1 deletion fs/stat.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ static dword_t sys_stat_path(fd_t at_f, addr_t path_addr, addr_t statbuf_addr, b
struct fd *at = at_fd(at_f);
if (at == NULL)
return _EBADF;
struct statbuf stat;
struct statbuf stat = {};
if ((err = generic_statat(at, path, &stat, follow_links)) < 0)
return err;
struct newstat64 newstat = stat_convert_newstat64(stat);
Expand Down
4 changes: 2 additions & 2 deletions iSH.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ ISH_LOGGER_macosx = dprintf

ENABLE_APK_ODRS = NO
ROOTFS_URL = $(ROOTFS_URL_IF_ODRS_$(ENABLE_APK_ODRS))
ROOTFS_URL_IF_ODRS_YES = github.com/ish-app/roots/releases/download/ge69d2565ba237547370b89fc5468bde4b3352767/appstore-apk.tar.gz
ROOTFS_URL_IF_ODRS_NO = github.com/ish-app/roots/releases/download/ge69d2565ba237547370b89fc5468bde4b3352767/appstore.tar.gz
ROOTFS_URL_IF_ODRS_YES = github.com/ish-app/roots/releases/download/gd12a2d01040f909622203bddb1ace9f7c204ab65/appstore-apk.tar.gz
ROOTFS_URL_IF_ODRS_NO = github.com/ish-app/roots/releases/download/gd12a2d01040f909622203bddb1ace9f7c204ab65/appstore.tar.gz

PRODUCT_APP_GROUP_IDENTIFIER = group.$(ROOT_BUNDLE_IDENTIFIER)
Loading

0 comments on commit 9991676

Please sign in to comment.