From cd370cd0cb683c26451bfb33100a391afafeebdb Mon Sep 17 00:00:00 2001 From: Theodore Dubois Date: Mon, 30 Oct 2017 17:16:56 -0700 Subject: [PATCH] Get alpine filesystem into iOS app --- app/AppDelegate.m | 19 +++++++++++-- app/TerminalViewController.m | 2 +- fs/fake.c | 52 +++++++++++++++++++++++++++------- fs/generic.c | 5 ++-- fs/path.c | 41 +++++++++++++++++++-------- iSH.xcodeproj/project.pbxproj | 53 ++++++++++++++++++++++++++++++----- kernel/fs.h | 1 + kernel/init.c | 1 + tools/fakefsify.py | 22 +++++---------- 9 files changed, 147 insertions(+), 49 deletions(-) diff --git a/app/AppDelegate.m b/app/AppDelegate.m index c40f8eac..23a0ef50 100644 --- a/app/AppDelegate.m +++ b/app/AppDelegate.m @@ -21,12 +21,25 @@ static void ios_handle_exit(int code) { @implementation AppDelegate - (int)startThings { - NSString *resourcePath = NSBundle.mainBundle.resourcePath; - int err = mount_root(&realfs, resourcePath.UTF8String); + NSFileManager *manager = NSFileManager.defaultManager; + NSURL *documents = [manager URLsForDirectory:NSDocumentDirectory + inDomains:NSUserDomainMask][0]; + NSURL *alpineRoot = [documents URLByAppendingPathComponent:@"alpine"]; + if (![manager fileExistsAtPath:alpineRoot.path]) { + NSURL *alpineMaster = [NSBundle.mainBundle URLForResource:@"alpine" withExtension:nil]; + NSError *error = nil; + [manager copyItemAtURL:alpineMaster toURL:alpineRoot error:&error]; + if (error != nil) { + NSLog(@"%@", error); + exit(1); + } + } + alpineRoot = [alpineRoot URLByAppendingPathComponent:@"data"]; + int err = mount_root(&fakefs, alpineRoot.fileSystemRepresentation); if (err < 0) return err; - char *program = "hello-libc-static"; + char *program = "/bin/ls"; char *argv[] = {program, NULL}; char *envp[] = {NULL}; err = create_init_process(program, argv, envp); diff --git a/app/TerminalViewController.m b/app/TerminalViewController.m index 000121f2..b5949f17 100644 --- a/app/TerminalViewController.m +++ b/app/TerminalViewController.m @@ -21,7 +21,7 @@ ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - self.textView.text = [self.textView.text stringByAppendingString:self.terminal.content]; + [self.textView performSelectorOnMainThread:@selector(setText:) withObject:self.terminal.content waitUntilDone:NO]; } - (void)viewDidLoad { diff --git a/fs/fake.c b/fs/fake.c index cef62040..a18303c9 100644 --- a/fs/fake.c +++ b/fs/fake.c @@ -6,6 +6,9 @@ #endif #include #include +#include + +#include "kernel/errno.h" #include "kernel/fs.h" struct ish_stat { @@ -34,19 +37,26 @@ static datum read_meta(struct mount *mount, const char *path, const char *type) return dbm_fetch(db, key); } +static int read_ish_stat(struct mount *mount, const char *path, struct ish_stat *stat) { + datum d = read_meta(mount, path, "meta"); + if (d.dptr == NULL) + return 0; + assert(d.dsize == sizeof(struct ish_stat)); + *stat = *(struct ish_stat *) d.dptr; + return 1; +} + static int fakefs_stat(struct mount *mount, char *path, struct statbuf *fake_stat, bool follow_links) { + struct ish_stat ishstat; + if (!read_ish_stat(mount, path, &ishstat)) + return _ENOENT; int err = realfs_stat(mount, path, fake_stat, follow_links); if (err < 0) return err; - datum d = read_meta(mount, path, "meta"); - if (d.dptr != NULL) { - assert(d.dsize == sizeof(struct ish_stat)); - struct ish_stat *ishstat = (void *) d.dptr; - fake_stat->mode = ishstat->mode; - fake_stat->uid = ishstat->uid; - fake_stat->gid = ishstat->gid; - fake_stat->rdev = ishstat->rdev; - } + fake_stat->mode = ishstat.mode; + fake_stat->uid = ishstat.uid; + fake_stat->gid = ishstat.gid; + fake_stat->rdev = ishstat.rdev; return 0; } @@ -59,12 +69,34 @@ static int fakefs_fstat(struct fd *fd, struct statbuf *fake_stat) { return fakefs_stat(fd->mount, path, fake_stat, false); } +ssize_t fakefs_readlink(struct mount *mount, char *path, char *buf, size_t bufsize) { + struct ish_stat ishstat; + if (!read_ish_stat(mount, path, &ishstat)) + return _ENOENT; + if (!S_ISLNK(ishstat.mode)) + return _EINVAL; + + ssize_t err = realfs_readlink(mount, path, buf, bufsize); + if (err == _EINVAL) { + // broken symlinks can't be included in an iOS app or else Xcode craps out + int fd = open(path, O_RDONLY); + if (fd < 0) + return err_map(errno); + int err = read(fd, buf, bufsize); + if (err < 0) + return err_map(errno); + close(fd); + return err; + } + return err; +} + const struct fs_ops fakefs = { .open = realfs_open, .unlink = realfs_unlink, .stat = fakefs_stat, .access = realfs_access, - .readlink = realfs_readlink, + .readlink = fakefs_readlink, .fstat = fakefs_fstat, .statfs = realfs_statfs, .flock = realfs_flock, diff --git a/fs/generic.c b/fs/generic.c index 9b345bf5..2c9bde64 100644 --- a/fs/generic.c +++ b/fs/generic.c @@ -50,8 +50,9 @@ struct fd *generic_openat(struct fd *at, const char *path_raw, int flags, int mo struct statbuf stat; err = fd->mount->fs->fstat(fd, &stat); if (err >= 0) { - int type = stat.mode & S_IFMT; - if (type == S_IFBLK || type == S_IFCHR) { + assert(!S_ISLNK(stat.mode)); + if (S_ISBLK(stat.mode) || S_ISCHR(stat.mode)) { + int type; if (stat.mode & S_IFBLK) type = DEV_BLOCK; else diff --git a/fs/path.c b/fs/path.c index 0e9f5ab8..a9f8cf11 100644 --- a/fs/path.c +++ b/fs/path.c @@ -11,25 +11,27 @@ int path_normalize(struct fd *at, const char *path, char *out, bool follow_links if (at != __NO_AT) { // start with root or cwd, depending on whether it starts with a slash - if (*p == '/') { + if (*p == '/') at = current->root; - while (*p == '/') - p++; - // if it does start with a slash, make sure to skip all the slashes - } else if (at == NULL) { + else if (at == NULL) at = current->pwd; - } if (at != NULL) { char at_path[MAX_PATH]; int err = at->ops->getpath(at, at_path); if (err < 0) return err; - strcpy(o, at_path); - n -= strlen(at_path); - o += strlen(at_path); + assert(path_is_normalized(at_path)); + if (at_path[0] != '/' && at_path[1] != '\0') { + strcpy(o, at_path); + n -= strlen(at_path); + o += strlen(at_path); + } } } + while (*p == '/') + p++; + while (*p != '\0') { if (p[0] == '.') { if (p[1] == '\0' || p[1] == '/') { @@ -72,6 +74,7 @@ int path_normalize(struct fd *at, const char *path, char *out, bool follow_links strcpy(possible_symlink, out); possible_symlink[o - out] = '\0'; struct mount *mount = find_mount_and_trim_path(possible_symlink); + assert(path_is_normalized(possible_symlink)); int res = mount->fs->readlink(mount, possible_symlink, c, MAX_PATH - (c - out)); if (res >= 0) { // readlink does not null terminate @@ -81,13 +84,29 @@ int path_normalize(struct fd *at, const char *path, char *out, bool follow_links memmove(out, c, strlen(c) + 1); char *expanded_path = possible_symlink; strcpy(expanded_path, out); - strcat(expanded_path, "/"); - strcat(expanded_path, p); + // if (*p) { + strcat(expanded_path, "/"); + strcat(expanded_path, p); + // } return path_normalize(__NO_AT, expanded_path, out, follow_links); } } } *o = '\0'; + assert(path_is_normalized(out)); return 0; } + +bool path_is_normalized(const char *path) { + while (*path != '\0') { + if (*path != '/') + return false; + path++; + if (*path == '/') + return false; + while (*path != '/' && *path != '\0') + path++; + } + return true; +} diff --git a/iSH.xcodeproj/project.pbxproj b/iSH.xcodeproj/project.pbxproj index 878f156a..1c6d2c44 100644 --- a/iSH.xcodeproj/project.pbxproj +++ b/iSH.xcodeproj/project.pbxproj @@ -3,13 +3,13 @@ archiveVersion = 1; classes = { }; - objectVersion = 48; + objectVersion = 47; objects = { /* Begin PBXBuildFile section */ BB0FC5921F980A6C00803272 /* Terminal.m in Sources */ = {isa = PBXBuildFile; fileRef = BB0FC5911F980A6B00803272 /* Terminal.m */; }; BB18B2871F97F6D00059FCD8 /* libsoftfloat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BB18B2741F97F1C40059FCD8 /* libsoftfloat.a */; }; - BB6CEEE61F97D69E00C07635 /* hello-libc-static in Resources */ = {isa = PBXBuildFile; fileRef = BB6CEEE51F97D69E00C07635 /* hello-libc-static */; }; + BB623CF91FA7C68800932047 /* alpine in Resources */ = {isa = PBXBuildFile; fileRef = BBF124901FA7C3100088FB50 /* alpine */; }; BB792B551F96D90D00FFB7A4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = BB792B541F96D90D00FFB7A4 /* AppDelegate.m */; }; BB792B581F96D90D00FFB7A4 /* TerminalViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BB792B571F96D90D00FFB7A4 /* TerminalViewController.m */; }; BB792B5B1F96D90D00FFB7A4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB792B591F96D90D00FFB7A4 /* Main.storyboard */; }; @@ -110,7 +110,6 @@ BB0FC5911F980A6B00803272 /* Terminal.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Terminal.m; sourceTree = ""; }; BB18B2741F97F1C40059FCD8 /* libsoftfloat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libsoftfloat.a; sourceTree = BUILT_PRODUCTS_DIR; }; BB18B27E1F97F24D0059FCD8 /* xcode-meson.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "xcode-meson.sh"; sourceTree = ""; }; - BB6CEEE51F97D69E00C07635 /* hello-libc-static */ = {isa = PBXFileReference; lastKnownFileType = file; path = "hello-libc-static"; sourceTree = ""; }; BB792B501F96D90D00FFB7A4 /* iSH.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iSH.app; sourceTree = BUILT_PRODUCTS_DIR; }; BB792B531F96D90D00FFB7A4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; BB792B541F96D90D00FFB7A4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -123,6 +122,7 @@ BB792B621F96D90D00FFB7A4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; BB792B721F96E2C000FFB7A4 /* libish.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libish.a; sourceTree = BUILT_PRODUCTS_DIR; }; BB792B891F97B3A800FFB7A4 /* no-clang-env.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "no-clang-env.sh"; sourceTree = ""; }; + BBF124901FA7C3100088FB50 /* alpine */ = {isa = PBXFileReference; lastKnownFileType = folder; path = alpine; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -253,7 +253,7 @@ BB0FC5811F98026700803272 /* fs */, BB0FC5631F98026700803272 /* kernel */, BB0FC55D1F98026600803272 /* util */, - BB6CEEE51F97D69E00C07635 /* hello-libc-static */, + BBF124901FA7C3100088FB50 /* alpine */, BB18B27F1F97F2590059FCD8 /* Scripts */, BB792B511F96D90D00FFB7A4 /* Products */, BB792B7D1F96E32B00FFB7A4 /* Frameworks */, @@ -319,6 +319,8 @@ buildPhases = ( BB792B4C1F96D90D00FFB7A4 /* Sources */, BB792B4D1F96D90D00FFB7A4 /* Frameworks */, + BBF1248B1FA7BF530088FB50 /* Download Alpine */, + BBF1248A1FA7BDBA0088FB50 /* Create Alpine Filesystem */, BB792B4E1F96D90D00FFB7A4 /* Resources */, ); buildRules = ( @@ -344,7 +346,7 @@ BB0FC54C1F97FB7E00803272 /* PBXTargetDependency */, ); name = libish; - productName = iah; + productName = ish; productReference = BB792B721F96E2C000FFB7A4 /* libish.a */; productType = "com.apple.product-type.library.static"; }; @@ -359,20 +361,23 @@ TargetAttributes = { BB18B2731F97F1C40059FCD8 = { CreatedOnToolsVersion = 9.0; + DevelopmentTeam = W2NF7BNM62; ProvisioningStyle = Automatic; }; BB792B4F1F96D90D00FFB7A4 = { CreatedOnToolsVersion = 9.0; + DevelopmentTeam = W2NF7BNM62; ProvisioningStyle = Automatic; }; BB792B711F96E2C000FFB7A4 = { CreatedOnToolsVersion = 9.0; + DevelopmentTeam = W2NF7BNM62; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = BB792B491F96D8E000FFB7A4 /* Build configuration list for PBXProject "iSH" */; - compatibilityVersion = "Xcode 8.0"; + compatibilityVersion = "Xcode 6.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -396,8 +401,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + BB623CF91FA7C68800932047 /* alpine in Resources */, BB792B601F96D90D00FFB7A4 /* LaunchScreen.storyboard in Resources */, - BB6CEEE61F97D69E00C07635 /* hello-libc-static in Resources */, BB792B5D1F96D90D00FFB7A4 /* Assets.xcassets in Resources */, BB792B5B1F96D90D00FFB7A4 /* Main.storyboard in Resources */, ); @@ -440,6 +445,40 @@ shellScript = "cd $TARGET_BUILD_DIR\n$SRCROOT/xcode-meson.sh\nninja libish.a"; showEnvVarsInLog = 0; }; + BBF1248A1FA7BDBA0088FB50 /* Create Alpine Filesystem */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/tools/fakefsify.py", + "$(DERIVED_FILE_DIR)/alpine.tar.gz", + ); + name = "Create Alpine Filesystem"; + outputPaths = ( + "$(SRCROOT)/alpine", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "rm -rf $SRCROOT/alpine\n$SRCROOT/tools/fakefsify.py $DERIVED_FILE_DIR/alpine.tar.gz $SRCROOT/alpine"; + showEnvVarsInLog = 0; + }; + BBF1248B1FA7BF530088FB50 /* Download Alpine */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Download Alpine"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/alpine.tar.gz", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "curl -L http://dl-cdn.alpinelinux.org/alpine/v3.6/releases/x86/alpine-minirootfs-3.6.2-x86.tar.gz -o $DERIVED_FILE_DIR/alpine.tar.gz"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/kernel/fs.h b/kernel/fs.h index a83450db..814756ac 100644 --- a/kernel/fs.h +++ b/kernel/fs.h @@ -178,6 +178,7 @@ void poll_destroy(struct poll *poll); // MAX_PATH, _ENAMETOOLONG is returned. The out buffer is expected to be at // least MAX_PATH in size. int path_normalize(struct fd *at, const char *path, char *out, bool follow_links); +bool path_is_normalized(const char *path); // real fs extern const struct fs_ops realfs; diff --git a/kernel/init.c b/kernel/init.c index e53a7f19..72b78477 100644 --- a/kernel/init.c +++ b/kernel/init.c @@ -14,6 +14,7 @@ int mount_root(const struct fs_ops *fs, const char *source) { mounts->source = strdup(source_realpath); mounts->fs = fs; mounts->next = NULL; + mounts->data = NULL; return 0; } diff --git a/tools/fakefsify.py b/tools/fakefsify.py index 029f18c3..3a9af543 100755 --- a/tools/fakefsify.py +++ b/tools/fakefsify.py @@ -2,7 +2,7 @@ import sys from pathlib import Path import struct -import urllib +import urllib.request import tarfile import dbm @@ -35,32 +35,24 @@ def extract_archive(archive, db): member.gid, rdev, ) - db[b'meta:' + bytes(path)] = metadata + meta_path = path.relative_to(data) + db[b'meta:/' + (bytes(meta_path) if meta_path.parts else b'')] = metadata if member.isdir(): path.mkdir(parents=True, exist_ok=True) elif member.issym(): - path.symlink_to(member.linkname) + path.write_text(member.linkname) elif member.isfile(): archive.extract(member, data) else: path.touch() _, archive_path, fs = sys.argv - fs = Path(fs) fs.mkdir(parents=True, exist_ok=True) data = fs/'data' -data.mkdir() -db = dbm.ndbm.open(str(fs/'meta'), 'c') -try: - archive = open(archive_path, 'rb') -except FileNotFoundError: - archive = urllib.request.urlopen(archive_path) - -with archive: - archive = tarfile.open(fileobj=archive) - with archive: - with db: +with open(archive_path, 'rb') as archive: + with tarfile.open(fileobj=archive) as archive: + with dbm.ndbm.open(str(fs/'meta'), 'c') as db: extract_archive(archive, db)