diff --git a/src/bin/installer_tui.rs b/src/bin/installer_tui.rs
index 2fe0862a0bf8d4bfb91f3f8111fa03e688bc9c1b..5c4acbee634a4eaa55d719f7cacde851900bc56a 100644
--- a/src/bin/installer_tui.rs
+++ b/src/bin/installer_tui.rs
@@ -1,11 +1,25 @@
 extern crate arg_parser;
+#[macro_use] extern crate failure;
+extern crate pkgar;
+extern crate pkgar_core;
+extern crate pkgar_keys;
 extern crate redox_installer;
 extern crate serde;
 extern crate termion;
 extern crate toml;
 
+use pkgar::{PackageHead, ext::EntryExt};
+use pkgar_core::PackageSrc;
+use pkgar_keys::PublicKeyFile;
 use redox_installer::{Config, with_redoxfs};
-use std::{fs, io, process};
+use std::{
+    ffi::OsStr,
+    fs,
+    io::{self, Read, Write},
+    os::unix::fs::{MetadataExt, OpenOptionsExt, symlink},
+    path::Path,
+    process
+};
 use termion::input::TermRead;
 
 #[cfg(not(target_os = "redox"))]
@@ -76,24 +90,106 @@ fn format_size(size: u64) -> String {
     }
 }
 
-fn dir_files(dir: &str, files: &mut Vec<String>) -> io::Result<()> {
-    for entry_res in fs::read_dir(&format!("file:/{}", dir))? {
-        let entry = entry_res?;
-        let path = entry.path();
-        let path_str = path.into_os_string().into_string().map_err(|_| {
-            io::Error::new(
-                io::ErrorKind::InvalidData,
-                "Failed to convert Path to &str"
-            )
-        })?;
-        let path_trimmed = path_str.trim_start_matches("file:/");
-        let metadata = entry.metadata()?;
-        if metadata.is_dir() {
-            dir_files(path_trimmed, files)?;
-        } else {
-            files.push(path_trimmed.to_string());
+fn copy_file(src: &Path, dest: &Path, buf: &mut [u8]) -> Result<(), failure::Error> {
+    if let Some(parent) = dest.parent() {
+        match fs::create_dir_all(&parent) {
+            Ok(()) => (),
+            Err(err) => {
+                return Err(format_err!("failed to create directory {}: {}", parent.display(), err));
+            }
         }
     }
+
+    let metadata = match fs::symlink_metadata(&src) {
+        Ok(ok) => ok,
+        Err(err) => {
+            return Err(format_err!("failed to read metadata of {}: {}", src.display(), err));
+        },
+    };
+
+    if metadata.file_type().is_symlink() {
+        let real_src = match fs::read_link(&src) {
+            Ok(ok) => ok,
+            Err(err) => {
+                return Err(format_err!("failed to read link {}: {}", src.display(), err));
+            }
+        };
+
+        match symlink(&real_src, &dest) {
+            Ok(()) => (),
+            Err(err) => {
+                return Err(format_err!("failed to copy link {} ({}) to {}: {}", src.display(), real_src.display(), dest.display(), err));
+            },
+        }
+    } else {
+        let mut src_file = match fs::File::open(&src) {
+            Ok(ok) => ok,
+            Err(err) => {
+                return Err(format_err!("failed to open file {}: {}", src.display(), err));
+            }
+        };
+
+        let mut dest_file = match fs::OpenOptions::new().write(true).create_new(true).mode(metadata.mode()).open(&dest) {
+            Ok(ok) => ok,
+            Err(err) => {
+                return Err(format_err!("failed to create file {}: {}", dest.display(), err));
+            }
+        };
+
+        loop {
+            let count = match src_file.read(buf) {
+                Ok(ok) => ok,
+                Err(err) => {
+                    return Err(format_err!("failed to read file {}: {}", src.display(), err));
+                }
+            };
+
+            if count == 0 {
+                break;
+            }
+
+            match dest_file.write_all(&buf[..count]) {
+                Ok(()) => (),
+                Err(err) => {
+                    return Err(format_err!("failed to write file {}: {}", dest.display(), err));
+                }
+            }
+        }
+    }
+
+    Ok(())
+}
+
+fn package_files(root_path: &Path, config: &mut Config, files: &mut Vec<String>) -> Result<(), pkgar::Error> {
+    //TODO: Remove packages from config where all files are located (and have valid shasum?)
+    config.packages.clear();
+
+    let pkey_path = "pkg/id_ed25519.pub.toml";
+    let pkey = PublicKeyFile::open(&root_path.join(pkey_path))?.pkey;
+    files.push(pkey_path.to_string());
+
+    for item_res in fs::read_dir(&root_path.join("pkg"))? {
+        let item = item_res?;
+        let pkg_path = item.path();
+        if pkg_path.extension() == Some(OsStr::new("pkgar_head")) {
+            let mut pkg = PackageHead::new(&pkg_path, &root_path, &pkey)?;
+            for entry in pkg.read_entries()? {
+                files.push(
+                    entry
+                        .check_path()?
+                        .to_str().unwrap()
+                        .to_string()
+                );
+            }
+            files.push(
+                pkg_path
+                    .strip_prefix(root_path).unwrap()
+                    .to_str().unwrap()
+                    .to_string()
+            );
+        }
+    }
+
     Ok(())
 }
 
@@ -158,16 +254,18 @@ fn choose_password() -> Option<String> {
 }
 
 fn main() {
+    let root_path = Path::new("file:");
+
     let disk_path = choose_disk();
 
     let password_opt = choose_password();
 
     let bootloader = {
-        let path = "file:/bootloader";
-        let mut bootloader = match fs::read(path) {
+        let path = root_path.join("bootloader");
+        let mut bootloader = match fs::read(&path) {
             Ok(ok) => ok,
             Err(err) => {
-                eprintln!("installer_tui: {}: failed to read: {}", path, err);
+                eprintln!("installer_tui: {}: failed to read: {}", path.display(), err);
                 process::exit(1);
             }
         };
@@ -182,26 +280,20 @@ fn main() {
 
     let res = with_redoxfs(&disk_path, password_opt.as_ref().map(|x| x.as_bytes()), &bootloader, |mount_path| -> Result<(), failure::Error> {
         let mut config: Config = {
-            let path = "file:/filesystem.toml";
-            match fs::read_to_string(path) {
+            let path = root_path.join("filesystem.toml");
+            match fs::read_to_string(&path) {
                 Ok(config_data) => {
                     match toml::from_str(&config_data) {
                         Ok(config) => {
                             config
                         },
                         Err(err) => {
-                            eprintln!("installer_tui: {}: failed to decode: {}", path, err);
-                            return Err(failure::Error::from_boxed_compat(
-                                Box::new(err))
-                            );
+                            return Err(format_err!("{}: failed to decode: {}", path.display(), err));
                         }
                     }
                 },
                 Err(err) => {
-                    eprintln!("installer_tui: {}: failed to read: {}", path, err);
-                    return Err(failure::Error::from_boxed_compat(
-                        Box::new(err))
-                    );
+                    return Err(format_err!("{}: failed to read: {}", path.display(), err));
                 }
             }
         };
@@ -213,55 +305,22 @@ fn main() {
             "kernel".to_string()
         ];
 
-        // Copy files from known directories
-        //TODO: Use package data
-        for dir in [
-            "bin",
-            "etc",
-            "include",
-            "lib",
-            "pkg",
-            "share",
-            "ssl",
-            "ui"
-        ].iter() {
-            if let Err(err) = dir_files(dir, &mut files) {
-                eprintln!("installer_tui: failed to read files from {}: {}", dir, err);
-                return Err(failure::Error::from_boxed_compat(
-                    Box::new(err))
-                );
-            }
+        // Copy files from locally installed packages
+        if let Err(err) = package_files(&root_path, &mut config, &mut files) {
+            return Err(format_err!("failed to read package files: {}", err));
         }
 
-        // Packages are copied by previous code
-        config.packages.clear();
+        // Sort and remove duplicates
+        files.sort();
+        files.dedup();
 
+        let mut buf = vec![0; 4 * 1024 * 1024];
         for (i, name) in files.iter().enumerate() {
             eprintln!("copy {} [{}/{}]", name, i, files.len());
 
-            let src = format!("file:/{}", name);
+            let src = root_path.join(name);
             let dest = mount_path.join(name);
-            if let Some(parent) = dest.parent() {
-                match fs::create_dir_all(&parent) {
-                    Ok(()) => (),
-                    Err(err) => {
-                        eprintln!("installer_tui: failed to create directory {}: {}", parent.display(), err);
-                        return Err(failure::Error::from_boxed_compat(
-                            Box::new(err))
-                        );
-                    }
-                }
-            }
-
-            match fs::copy(&src, &dest) {
-                Ok(_) => (),
-                Err(err) => {
-                    eprintln!("installer_tui: failed to copy file {} to {}: {}", src, dest.display(), err);
-                    return Err(failure::Error::from_boxed_compat(
-                        Box::new(err))
-                    );
-                }
-            }
+            copy_file(&src, &dest, &mut buf)?;
         }
 
         eprintln!("finished copying {} files", files.len());
@@ -285,7 +344,7 @@ fn main() {
             process::exit(0);
         },
         Err(err) => {
-            eprintln!("installer_tui: failed to install: {:?}", err);
+            eprintln!("installer_tui: failed to install: {}", err);
             process::exit(1);
         }
     }